2026-05-01 21:27:39 +08:00
|
|
|
import { apiRequest, getConfig, getDeviceInfo, getUserId as getCommonUserId } from '@xuqm/rn-common'
|
2026-04-25 16:41:19 +08:00
|
|
|
import type { PushVendor } from '@xuqm/rn-common'
|
2026-05-02 22:57:55 +08:00
|
|
|
import { detectVendorNative, registerPushNative, addPushTokenListener } from './NativePush'
|
2026-04-24 16:16:31 +08:00
|
|
|
|
2026-04-25 16:41:19 +08:00
|
|
|
export type { PushVendor }
|
2026-04-24 16:16:31 +08:00
|
|
|
|
2026-05-01 21:27:39 +08:00
|
|
|
type PendingDeviceToken = {
|
|
|
|
|
token: string
|
|
|
|
|
vendor?: PushVendor
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let currentUserId: string | null = null
|
|
|
|
|
let pendingToken: PendingDeviceToken | null = null
|
2026-05-02 22:57:55 +08:00
|
|
|
let _tokenUnsubscribe: (() => void) | null = null
|
2026-05-01 21:27:39 +08:00
|
|
|
|
|
|
|
|
async function registerPendingToken(): Promise<void> {
|
|
|
|
|
const userId = currentUserId ?? getCommonUserId()
|
|
|
|
|
if (!userId || !pendingToken) return
|
|
|
|
|
const device = await getDeviceInfo()
|
|
|
|
|
const config = getConfig()
|
|
|
|
|
await apiRequest('/api/push/register', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
params: {
|
|
|
|
|
appId: config.appId,
|
|
|
|
|
userId,
|
|
|
|
|
vendor: pendingToken.vendor ?? device.pushVendor,
|
|
|
|
|
token: pendingToken.token,
|
|
|
|
|
platform: device.platform,
|
|
|
|
|
deviceId: device.deviceId,
|
|
|
|
|
brand: device.brand,
|
|
|
|
|
model: device.model,
|
|
|
|
|
osVersion: device.osVersion,
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-24 16:16:31 +08:00
|
|
|
export const PushSDK = {
|
2026-05-01 21:27:39 +08:00
|
|
|
async initialize(userId?: string): Promise<void> {
|
|
|
|
|
currentUserId = userId ?? getCommonUserId()
|
|
|
|
|
await registerPendingToken()
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
async setDeviceToken(token: string, vendor?: PushVendor): Promise<void> {
|
|
|
|
|
pendingToken = { token, vendor }
|
|
|
|
|
await registerPendingToken()
|
|
|
|
|
},
|
|
|
|
|
|
2026-05-02 22:57:55 +08:00
|
|
|
/**
|
|
|
|
|
* Auto-detect vendor and request native push registration.
|
|
|
|
|
* On Android this attempts to register with the vendor SDK (Huawei, Xiaomi, OPPO, vivo, Honor, FCM).
|
|
|
|
|
* On iOS this requests APNs registration.
|
|
|
|
|
* Listen for token updates via onPushToken(callback).
|
|
|
|
|
*/
|
|
|
|
|
async requestNativeRegistration(): Promise<void> {
|
|
|
|
|
await registerPushNative()
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Listen for push tokens from the native layer.
|
|
|
|
|
* Call setDeviceToken(token, vendor) inside the callback to register with the server.
|
|
|
|
|
*/
|
|
|
|
|
onPushToken(callback: (token: string, vendor: string) => void): () => void {
|
|
|
|
|
if (_tokenUnsubscribe) {
|
|
|
|
|
_tokenUnsubscribe()
|
|
|
|
|
_tokenUnsubscribe = null
|
|
|
|
|
}
|
|
|
|
|
_tokenUnsubscribe = addPushTokenListener((event) => {
|
|
|
|
|
callback(event.token, event.vendor)
|
|
|
|
|
})
|
|
|
|
|
return () => {
|
|
|
|
|
if (_tokenUnsubscribe) {
|
|
|
|
|
_tokenUnsubscribe()
|
|
|
|
|
_tokenUnsubscribe = null
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
2026-04-24 16:16:31 +08:00
|
|
|
/**
|
|
|
|
|
* Register a push device token for the given user.
|
2026-04-25 16:41:19 +08:00
|
|
|
* If vendor is omitted, it is auto-detected from the device brand.
|
2026-04-24 16:16:31 +08:00
|
|
|
*
|
|
|
|
|
* @param userId - The logged-in user's ID
|
2026-04-25 16:41:19 +08:00
|
|
|
* @param token - The device push token from the vendor SDK
|
|
|
|
|
* @param vendor - Optional; auto-detected when not provided
|
2026-04-24 16:16:31 +08:00
|
|
|
*/
|
2026-04-25 16:41:19 +08:00
|
|
|
async registerToken(userId: string, token: string, vendor?: PushVendor): Promise<void> {
|
2026-05-01 21:27:39 +08:00
|
|
|
currentUserId = userId
|
|
|
|
|
pendingToken = { token, vendor }
|
2026-04-24 16:16:31 +08:00
|
|
|
const config = getConfig()
|
2026-04-25 16:41:19 +08:00
|
|
|
const device = await getDeviceInfo()
|
2026-04-24 16:16:31 +08:00
|
|
|
await apiRequest('/api/push/register', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
params: {
|
2026-04-25 16:41:19 +08:00
|
|
|
appId: config.appId,
|
2026-04-24 16:16:31 +08:00
|
|
|
userId,
|
2026-04-25 16:41:19 +08:00
|
|
|
vendor: vendor ?? device.pushVendor,
|
2026-04-24 16:16:31 +08:00
|
|
|
token,
|
2026-04-25 16:41:19 +08:00
|
|
|
platform: device.platform,
|
|
|
|
|
deviceId: device.deviceId,
|
|
|
|
|
brand: device.brand,
|
|
|
|
|
model: device.model,
|
|
|
|
|
osVersion: device.osVersion,
|
2026-04-24 16:16:31 +08:00
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
async unregisterToken(userId: string): Promise<void> {
|
|
|
|
|
const config = getConfig()
|
2026-04-25 16:41:19 +08:00
|
|
|
const { deviceId } = await getDeviceInfo()
|
2026-04-24 16:16:31 +08:00
|
|
|
await apiRequest('/api/push/unregister', {
|
|
|
|
|
method: 'DELETE',
|
2026-04-25 16:41:19 +08:00
|
|
|
params: { appId: config.appId, userId, deviceId },
|
2026-04-24 16:16:31 +08:00
|
|
|
})
|
2026-05-01 21:27:39 +08:00
|
|
|
if (currentUserId === userId) currentUserId = null
|
|
|
|
|
if (pendingToken && currentUserId === null) pendingToken = null
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
async logout(userId?: string): Promise<void> {
|
|
|
|
|
const targetUserId = userId ?? currentUserId ?? getCommonUserId()
|
|
|
|
|
if (!targetUserId) return
|
|
|
|
|
await PushSDK.unregisterToken(targetUserId)
|
2026-04-24 16:16:31 +08:00
|
|
|
},
|
|
|
|
|
}
|