2026-04-28 17:25:27 +08:00
|
|
|
|
import { Platform, NativeModules, AppState, Vibration } from 'react-native';
|
2026-04-27 23:21:08 +08:00
|
|
|
|
import { pushService } from './pushService';
|
2026-04-28 00:20:07 +08:00
|
|
|
|
import * as Notifications from 'expo-notifications';
|
2026-04-28 08:53:28 +08:00
|
|
|
|
import { getNotificationPreferencesSync } from './notificationPreferences';
|
|
|
|
|
|
import { vibrateOnMessage } from '../platform/messageVibrationService';
|
2026-04-27 23:21:08 +08:00
|
|
|
|
|
|
|
|
|
|
type JPushNotificationMessage = {
|
|
|
|
|
|
messageID: string;
|
|
|
|
|
|
title: string;
|
|
|
|
|
|
content: string;
|
|
|
|
|
|
extras: Record<string, string>;
|
|
|
|
|
|
notificationType?: string;
|
|
|
|
|
|
notificationEventType?: 'notificationArrived' | 'notificationOpened';
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
type JPushCallback = (message: JPushNotificationMessage) => void;
|
|
|
|
|
|
|
|
|
|
|
|
let JPush: typeof import('jpush-react-native').default | null = null;
|
|
|
|
|
|
let jpushAppKey = '';
|
|
|
|
|
|
let jpushChannel = 'developer-default';
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const nativeModule = NativeModules.JPushModule;
|
|
|
|
|
|
if (nativeModule) {
|
|
|
|
|
|
JPush = require('jpush-react-native').default;
|
|
|
|
|
|
const Constants = require('expo-constants').default;
|
|
|
|
|
|
const extra = Constants?.expoConfig?.extra || Constants?.extra || {};
|
|
|
|
|
|
jpushAppKey = extra.jpushAppKey || '';
|
2026-05-12 01:28:46 +08:00
|
|
|
|
jpushChannel = extra.jpushChannel || 'developer-default';
|
2026-04-27 23:21:08 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
console.warn('[JPush] native module not linked, skipping');
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
console.warn('[JPush] jpush-react-native not available');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getAuthStore() {
|
|
|
|
|
|
return require('@/stores').useAuthStore;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class JPushService {
|
|
|
|
|
|
private registrationID: string | null = null;
|
|
|
|
|
|
private isInitialized = false;
|
|
|
|
|
|
private initPromise: Promise<boolean> | null = null;
|
|
|
|
|
|
private notificationCallback: JPushCallback | null = null;
|
|
|
|
|
|
private connectResolved = false;
|
|
|
|
|
|
private listenersRegistered = false;
|
|
|
|
|
|
|
|
|
|
|
|
async initialize(): Promise<boolean> {
|
|
|
|
|
|
if (this.isInitialized) return true;
|
|
|
|
|
|
if (Platform.OS === 'web') return false;
|
|
|
|
|
|
if (!JPush) return false;
|
|
|
|
|
|
if (this.initPromise) return this.initPromise;
|
|
|
|
|
|
|
|
|
|
|
|
this.initPromise = this._doInit();
|
|
|
|
|
|
return this.initPromise;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private _registerListeners(): void {
|
|
|
|
|
|
if (this.listenersRegistered) return;
|
|
|
|
|
|
this.listenersRegistered = true;
|
|
|
|
|
|
|
|
|
|
|
|
JPush!.addConnectEventListener((result: { connectEnable: boolean }) => {
|
|
|
|
|
|
console.log('[JPush] connect event:', result.connectEnable);
|
|
|
|
|
|
if (result.connectEnable && !this.connectResolved) {
|
|
|
|
|
|
this.connectResolved = true;
|
|
|
|
|
|
this._onConnected();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
JPush!.addNotificationListener((result: any) => {
|
|
|
|
|
|
console.log('[JPush] notification:', result);
|
2026-04-28 08:53:28 +08:00
|
|
|
|
if (result.notificationEventType === 'notificationArrived') {
|
|
|
|
|
|
const prefs = getNotificationPreferencesSync();
|
|
|
|
|
|
if (prefs.vibrationEnabled && AppState.currentState === 'active') {
|
|
|
|
|
|
vibrateOnMessage('notification').catch(() => {});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-04-27 23:21:08 +08:00
|
|
|
|
if (this.notificationCallback) {
|
|
|
|
|
|
this.notificationCallback({
|
|
|
|
|
|
messageID: result.messageID || '',
|
|
|
|
|
|
title: result.title || '',
|
|
|
|
|
|
content: result.content || '',
|
|
|
|
|
|
extras: result.extras || {},
|
|
|
|
|
|
notificationType: result.extras?.notification_type,
|
|
|
|
|
|
notificationEventType: result.notificationEventType,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
JPush!.addCustomMessageListener((result: any) => {
|
|
|
|
|
|
console.log('[JPush] custom message:', result);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
JPush!.addLocalNotificationListener((result: any) => {
|
|
|
|
|
|
console.log('[JPush] local notification:', result);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
JPush!.addTagAliasListener((result: any) => {
|
|
|
|
|
|
console.log('[JPush] tag/alias result:', result);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
JPush!.addCommandEventListener((result: any) => {
|
|
|
|
|
|
console.log('[JPush] command result:', result);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async _doInit(): Promise<boolean> {
|
|
|
|
|
|
try {
|
|
|
|
|
|
JPush!.setLoggerEnable(true);
|
|
|
|
|
|
|
|
|
|
|
|
// Register listeners only once (idempotent guard)
|
|
|
|
|
|
this._registerListeners();
|
|
|
|
|
|
|
2026-04-28 17:25:27 +08:00
|
|
|
|
// 退后台保持极光长连接,确保推送实时到达
|
|
|
|
|
|
this._setBackgroundKeepLongConn(true);
|
|
|
|
|
|
|
2026-04-27 23:21:08 +08:00
|
|
|
|
// 初始化 SDK
|
|
|
|
|
|
JPush!.init({
|
|
|
|
|
|
appKey: jpushAppKey,
|
|
|
|
|
|
channel: jpushChannel,
|
|
|
|
|
|
production: !__DEV__,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-04-28 08:53:28 +08:00
|
|
|
|
if (Platform.OS === 'android') {
|
|
|
|
|
|
this._setupAndroidNotificationChannel();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-27 23:21:08 +08:00
|
|
|
|
console.log('[JPush] init called, waiting for registration...');
|
|
|
|
|
|
|
|
|
|
|
|
// 轮询获取 RegistrationID,间隔逐渐增大
|
|
|
|
|
|
const regID = await this._pollRegistrationID();
|
|
|
|
|
|
if (regID) {
|
|
|
|
|
|
this.registrationID = regID;
|
|
|
|
|
|
this.isInitialized = true;
|
|
|
|
|
|
console.log('[JPush] initialized with registrationID:', regID);
|
|
|
|
|
|
await this.registerDeviceToServer();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 即使没拿到 registrationID,也标记为已初始化
|
|
|
|
|
|
// connect 事件回来后会自动获取
|
|
|
|
|
|
this.isInitialized = true;
|
|
|
|
|
|
console.warn('[JPush] init done but no registrationID yet, will retry on connect');
|
|
|
|
|
|
return true;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('[JPush] initialization failed:', error);
|
|
|
|
|
|
this.initPromise = null;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private _onConnected(): void {
|
2026-04-28 08:53:28 +08:00
|
|
|
|
if (this.registrationID) return;
|
2026-04-27 23:21:08 +08:00
|
|
|
|
console.log('[JPush] connected, fetching registrationID...');
|
|
|
|
|
|
this._fetchRegistrationID();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-28 08:53:28 +08:00
|
|
|
|
private _setupAndroidNotificationChannel(): void {
|
|
|
|
|
|
if (!JPush) return;
|
|
|
|
|
|
try {
|
|
|
|
|
|
(JPush as any).setChannelAndSound({
|
|
|
|
|
|
channel: '推送通知',
|
|
|
|
|
|
channelId: 'jpush_notification',
|
|
|
|
|
|
});
|
|
|
|
|
|
console.log('[JPush] Android notification channel configured');
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.warn('[JPush] setChannelAndSound failed:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-28 17:25:27 +08:00
|
|
|
|
// Ensure notification channel has sound & vibration enabled via expo-notifications
|
2026-04-28 08:53:28 +08:00
|
|
|
|
try {
|
|
|
|
|
|
Notifications.setNotificationChannelAsync('jpush_notification', {
|
|
|
|
|
|
name: '推送通知',
|
|
|
|
|
|
importance: Notifications.AndroidImportance.HIGH,
|
|
|
|
|
|
vibrationPattern: [0, 250, 250, 250],
|
|
|
|
|
|
enableLights: true,
|
|
|
|
|
|
showBadge: true,
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.warn('[JPush] expo-notifications channel setup failed:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-28 17:25:27 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 设置退后台是否保持极光长连接
|
|
|
|
|
|
* @see https://docs.jiguang.cn/jpush/client/Android/android_api#退后台是否保持极光长连接-api
|
|
|
|
|
|
*/
|
|
|
|
|
|
private _setBackgroundKeepLongConn(keep: boolean): void {
|
|
|
|
|
|
if (!JPush) return;
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (Platform.OS === 'android') {
|
|
|
|
|
|
const jpushModule = NativeModules.JPushModule;
|
|
|
|
|
|
if (jpushModule?.setKeepLongConnInBackground) {
|
|
|
|
|
|
jpushModule.setKeepLongConnInBackground(keep);
|
|
|
|
|
|
console.log('[JPush] setKeepLongConnInBackground:', keep);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.warn('[JPush] setKeepLongConnInBackground not available in native module');
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (Platform.OS === 'ios') {
|
|
|
|
|
|
JPush.setBackgroundEnable(keep);
|
|
|
|
|
|
console.log('[JPush] setBackgroundEnable:', keep);
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.warn('[JPush] setBackgroundKeepLongConn failed:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-27 23:21:08 +08:00
|
|
|
|
private _fetchRegistrationID(): void {
|
|
|
|
|
|
if (!JPush) return;
|
|
|
|
|
|
JPush.getRegistrationID((result: any) => {
|
|
|
|
|
|
console.log('[JPush] getRegistrationID result:', JSON.stringify(result));
|
|
|
|
|
|
const regID = result?.registerID || result?.registrationID;
|
|
|
|
|
|
if (regID) {
|
|
|
|
|
|
this.registrationID = regID;
|
|
|
|
|
|
this.isInitialized = true;
|
|
|
|
|
|
console.log('[JPush] got registrationID:', regID);
|
|
|
|
|
|
this.registerDeviceToServer();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.warn('[JPush] getRegistrationID returned empty');
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private _pollRegistrationID(): Promise<string | null> {
|
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
|
// 轮询策略: 2s, 4s, 6s, 8s, 10s (总计 30s)
|
|
|
|
|
|
const delays = [2000, 4000, 6000, 8000, 10000];
|
|
|
|
|
|
let attempt = 0;
|
|
|
|
|
|
|
|
|
|
|
|
const tryFetch = () => {
|
|
|
|
|
|
if (!JPush) {
|
|
|
|
|
|
resolve(null);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
JPush.getRegistrationID((result: any) => {
|
|
|
|
|
|
const regID = result?.registerID || result?.registrationID;
|
|
|
|
|
|
console.log(`[JPush] poll attempt ${attempt + 1}, regID:`, regID || '(empty)');
|
|
|
|
|
|
|
|
|
|
|
|
if (regID) {
|
|
|
|
|
|
resolve(regID);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
attempt++;
|
|
|
|
|
|
if (attempt < delays.length) {
|
|
|
|
|
|
setTimeout(tryFetch, delays[attempt] - (attempt > 0 ? delays[attempt - 1] : 0));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
resolve(null);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
setTimeout(tryFetch, delays[0]);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async registerDeviceToServer(): Promise<void> {
|
|
|
|
|
|
if (!this.registrationID) return;
|
|
|
|
|
|
|
|
|
|
|
|
const useAuthStore = getAuthStore();
|
|
|
|
|
|
const { isAuthenticated } = useAuthStore.getState();
|
|
|
|
|
|
if (!isAuthenticated) return;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const deviceType = Platform.OS === 'ios' ? 'ios' : 'android';
|
|
|
|
|
|
const deviceID = await this.getDeviceID();
|
|
|
|
|
|
|
|
|
|
|
|
await pushService.registerDevice({
|
|
|
|
|
|
device_id: deviceID,
|
|
|
|
|
|
device_type: deviceType as 'ios' | 'android',
|
|
|
|
|
|
push_token: this.registrationID,
|
|
|
|
|
|
device_name: `${Platform.OS === 'ios' ? 'iOS' : 'Android'} Device`,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
console.log('[JPush] device registered to server');
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('[JPush] failed to register device to server:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async getDeviceID(): Promise<string> {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const AsyncStorage = require('@react-native-async-storage/async-storage').default;
|
|
|
|
|
|
const DEVICE_ID_KEY = 'withyou_device_id';
|
|
|
|
|
|
let deviceId = await AsyncStorage.getItem(DEVICE_ID_KEY);
|
|
|
|
|
|
if (deviceId) return deviceId;
|
|
|
|
|
|
const timestamp = Date.now().toString(36);
|
|
|
|
|
|
const random = Math.random().toString(36).substring(2, 8);
|
|
|
|
|
|
deviceId = `${Platform.OS}_${timestamp}_${random}`;
|
|
|
|
|
|
await AsyncStorage.setItem(DEVICE_ID_KEY, deviceId);
|
|
|
|
|
|
return deviceId;
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
return `${Platform.OS}_${Date.now()}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
onNotification(callback: JPushCallback): void {
|
|
|
|
|
|
this.notificationCallback = callback;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setTags(tags: string[]): void {
|
|
|
|
|
|
if (!JPush || !this.isInitialized) return;
|
|
|
|
|
|
try {
|
|
|
|
|
|
JPush.addTags({ sequence: Date.now(), tags });
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('[JPush] setTags failed:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setAlias(alias: string): void {
|
|
|
|
|
|
if (!JPush || !this.isInitialized) return;
|
|
|
|
|
|
try {
|
|
|
|
|
|
JPush.setAlias({ sequence: Date.now(), alias });
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('[JPush] setAlias failed:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async setAliasForUser(userID: string): Promise<void> {
|
|
|
|
|
|
this.setAlias(userID);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
deleteAlias(): void {
|
|
|
|
|
|
if (!JPush || !this.isInitialized) return;
|
|
|
|
|
|
try {
|
|
|
|
|
|
JPush.deleteAlias({ sequence: Date.now() });
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('[JPush] deleteAlias failed:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
getRegistrationIDValue(): string | null {
|
|
|
|
|
|
return this.registrationID;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
isJPushAvailable(): boolean {
|
|
|
|
|
|
return this.isInitialized && this.registrationID !== null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
addLocalNotification(params: {
|
|
|
|
|
|
messageID: string;
|
|
|
|
|
|
title: string;
|
|
|
|
|
|
content: string;
|
|
|
|
|
|
extras: Record<string, string>;
|
|
|
|
|
|
}): void {
|
|
|
|
|
|
if (!JPush || !this.isInitialized) return;
|
|
|
|
|
|
try {
|
2026-04-28 08:53:28 +08:00
|
|
|
|
if (Platform.OS === 'android') {
|
|
|
|
|
|
const localParams = {
|
|
|
|
|
|
...params,
|
|
|
|
|
|
priority: 1,
|
|
|
|
|
|
};
|
|
|
|
|
|
JPush.addLocalNotification(localParams);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
JPush.addLocalNotification(params);
|
|
|
|
|
|
}
|
2026-04-28 17:25:27 +08:00
|
|
|
|
|
|
|
|
|
|
// JPush local notification does not support sound/vibration directly.
|
|
|
|
|
|
// Trigger them manually based on user preferences.
|
|
|
|
|
|
const prefs = getNotificationPreferencesSync();
|
|
|
|
|
|
if (prefs.soundEnabled) {
|
|
|
|
|
|
// Play system default notification sound via a silent expo-notification
|
|
|
|
|
|
Notifications.scheduleNotificationAsync({
|
|
|
|
|
|
content: { title: '', body: '', sound: true, vibrate: [] },
|
|
|
|
|
|
trigger: null,
|
|
|
|
|
|
}).catch(() => {});
|
|
|
|
|
|
}
|
|
|
|
|
|
if (prefs.vibrationEnabled) {
|
|
|
|
|
|
// Use React Native Vibration API for device-level vibration
|
|
|
|
|
|
Vibration.vibrate([0, 250, 250, 250]);
|
|
|
|
|
|
}
|
2026-04-27 23:21:08 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('[JPush] addLocalNotification failed:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
clearAllNotifications(): void {
|
|
|
|
|
|
if (!JPush || !this.isInitialized) return;
|
|
|
|
|
|
try {
|
|
|
|
|
|
JPush.clearAllNotifications();
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('[JPush] clearAllNotifications failed:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setBadge(badge: number): void {
|
|
|
|
|
|
if (!JPush || !this.isInitialized || Platform.OS !== 'ios') return;
|
|
|
|
|
|
try {
|
|
|
|
|
|
JPush.setBadge({ badge, appBadge: badge });
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('[JPush] setBadge failed:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-28 00:20:07 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 请求通知权限(使用 expo-notifications,统一处理 iOS/Android)
|
|
|
|
|
|
*/
|
|
|
|
|
|
async requestNotificationPermission(): Promise<boolean> {
|
|
|
|
|
|
if (Platform.OS === 'web') return false;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { status } = await Notifications.requestPermissionsAsync();
|
|
|
|
|
|
return status === 'granted';
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.warn('[JPush] request notification permission failed:', error);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查通知权限是否已开启
|
|
|
|
|
|
*/
|
|
|
|
|
|
async checkNotificationPermission(): Promise<boolean> {
|
|
|
|
|
|
if (Platform.OS === 'web') return false;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { status } = await Notifications.getPermissionsAsync();
|
|
|
|
|
|
return status === 'granted';
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-27 23:21:08 +08:00
|
|
|
|
cleanup(): void {
|
|
|
|
|
|
this.isInitialized = false;
|
|
|
|
|
|
this.registrationID = null;
|
|
|
|
|
|
this.notificationCallback = null;
|
|
|
|
|
|
this.initPromise = null;
|
|
|
|
|
|
this.connectResolved = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export const jpushService = new JPushService();
|
|
|
|
|
|
export default jpushService;
|