Tích hợp push notification React Native

TypeScript, RN 0.71+. Dựa trên @react-native-firebase/messaging và react-native-callkeep.

⚛️ React Native

TypeScript, RN 0.71+. Dựa trên @react-native-firebase/messaging và react-native-callkeep.

Tải SDK trong bảng điều khiển (mục Hướng dẫn tích hợp) sau khi tạo tài khoản miễn phí.

  1. Cài đặt

    Giải nén file tải về vào thư mục gốc của project (tạo ra thư mục PushConsoleSDK/), cấu hình Firebase theo rnfirebase.io, rồi:

    Terminal
    npm install ./PushConsoleSDK/react-native/push-console \
      @react-native-firebase/app @react-native-firebase/messaging \
      react-native-callkeep react-native-voip-push-notification \
      @react-native-async-storage/async-storage react-native-device-info
    cd ios && pod install

    React Native 0.71+. Expo cần development build (không chạy trong Expo Go).

  2. Handler nền trong index.js

    index.js
    // index.js — đăng ký TRƯỚC AppRegistry
    import { AppRegistry } from 'react-native';
    import messaging from '@react-native-firebase/messaging';
    import PushConsole from '@pushconsole/react-native';
    import App from './App';
    import { name as appName } from './app.json';
    
    messaging().setBackgroundMessageHandler(async (message) => {
      await PushConsole.handleBackgroundMessage(message); // Android: hiện màn hình cuộc gọi
    });
    
    AppRegistry.registerComponent(appName, () => App);
  3. Khởi tạo trong component gốc

    App.tsx
    import { useEffect } from 'react';
    import PushConsole from '@pushconsole/react-native';
    
    export default function App() {
      useEffect(() => {
        PushConsole.initialize(
          { appId: 'YOUR_APP_ID', serverUrl: 'https://tingpush.com', appName: 'MyApp' },
          {
            onCallEvent: ({ action, call }) => {
              if (action === 'accepted') {
                // Người dùng bấm Nghe → kết nối cuộc gọi (call.id, call.data.room)
              }
            },
            onNotificationOpened: (data) => { /* điều hướng theo data */ },
          },
        );
      }, []);
      // ...
    }
  4. Gắn user khi đăng nhập

    TypeScript
    await PushConsole.login('user-123');       // sau khi đăng nhập
    await PushConsole.logout();                // khi đăng xuất
    await PushConsole.setTags({ plan: 'vip' });
    PushConsole.endCall(callId);               // kết thúc cuộc gọi
  5. iOS: bật capability và sửa AppDelegate (cho cuộc gọi)

    Xcode → Signing & Capabilities: Push Notifications, Background Modes (Remote notifications + Voice over IP). VoIP push bắt buộc báo CallKit bằng code native:

    ios/AppDelegate.mm
    // AppDelegate.mm
    #import <PushKit/PushKit.h>
    #import "RNCallKeep.h"
    #import "RNVoipPushNotificationManager.h"
    
    @interface AppDelegate () <PKPushRegistryDelegate>
    @end
    
    // Trong didFinishLaunchingWithOptions, trước return:
    //   [RNVoipPushNotificationManager voipRegistration];
    
    - (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type {
      [RNVoipPushNotificationManager didUpdatePushCredentials:credentials forType:(NSString *)type];
    }
    
    - (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload
                 forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion {
      NSDictionary *p = payload.dictionaryPayload;
      NSString *uuid = p[@"call_id"] ?: [[NSUUID UUID] UUIDString];
      NSString *name = p[@"caller_name"] ?: @"";
      NSString *handle = p[@"caller_handle"] ?: name;
      BOOL hasVideo = [p[@"is_video"] isEqualToString:@"true"];
    
      [RNVoipPushNotificationManager didReceiveIncomingPushWithPayload:payload forType:(NSString *)type];
      [RNCallKeep reportNewIncomingCall:uuid handle:handle handleType:@"generic" hasVideo:hasVideo
                    localizedCallerName:name supportsHolding:NO supportsDTMF:NO supportsGrouping:NO
                     supportsUngrouping:NO fromPushKit:YES payload:p withCompletionHandler:completion];
    }

    AppDelegate.swift (RN 0.77+): làm tương tự, thêm các header vào bridging header.

  6. Android: khai báo ConnectionService

    android/app/src/main/AndroidManifest.xml
    <uses-permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.CALL_PHONE" />
    
    <application>
      <service android:name="io.wazo.callkeep.VoiceConnectionService"
          android:label="Wazo"
          android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
          android:foregroundServiceType="camera|microphone"
          android:exported="true">
        <intent-filter><action android:name="android.telecom.ConnectionService" /></intent-filter>
      </service>
      <service android:name="io.wazo.callkeep.RNCallKeepBackgroundMessagingService" />
    </application>

    Lần đầu, Android hỏi quyền "tài khoản gọi điện"; một số máy cần bật trong Cài đặt → Ứng dụng gọi điện.

✅ Kiểm tra sau khi tích hợp

  1. Chạy app trên máy thật, bật log: thấy [PushConsole] Đã đăng ký thiết bị ….
  2. Tab Thiết bị của dự án: thiết bị xuất hiện; máy iOS có badge VoIP nếu dùng cuộc gọi.
  3. Tab Gửi thông báo: gửi thử tới thiết bị đó.
  4. Chọn 📞 Cuộc gọi, gửi thử khi app đang mở, khi đã tắt app và khi màn hình đang khoá.

Sẵn sàng tích hợp?

Tạo tài khoản để nhận App ID, API Key và tải SDK. Code mẫu trong bảng điều khiển được điền sẵn thông tin dự án của bạn.