feat(sdk): 添加即时通讯和推送功能
- 新增 ApiClient 类用于处理 API 请求和响应 - 实现 ImClient 类支持 WebSocket 连接和消息收发 - 添加 ImSDK 类提供完整的即时通讯功能接口 - 定义 ImTypes.swift 包含聊天类型、消息类型等相关数据结构 - 实现 PushSDK 类支持推送通知令牌注册 - 添加基础的 UpdateSDK 框架结构 - 集成登录认证和聊天室订阅功能 - 实现群组管理、好友关系和会话功能 - 支持多种消息类型包括文本、图片、视频、音频等 - 提供历史消息查询和黑名单管理功能
这个提交包含在:
父节点
edbffce815
当前提交
9de1059e25
@ -1,3 +1,4 @@
|
||||
import { _getToken, getConfig } from '@xuqm/rn-common'
|
||||
import type { ImEventListener, ImMessage, SendMessageParams } from './types'
|
||||
|
||||
interface StompFrame {
|
||||
@ -14,15 +15,32 @@ export class ImClient {
|
||||
private shouldReconnect = true
|
||||
private readonly subscriptionId = 'sub-user-queue'
|
||||
private groupSubscriptions = new Set<string>()
|
||||
private activeWsUrl: string | null = null
|
||||
private activeToken: string | null = null
|
||||
private activeAppId: string | null = null
|
||||
|
||||
constructor(
|
||||
private readonly wsUrl: string,
|
||||
private readonly token: string,
|
||||
private readonly appId: string,
|
||||
private readonly wsUrl?: string,
|
||||
private readonly token?: string,
|
||||
private readonly appId?: string,
|
||||
) {}
|
||||
|
||||
connect() {
|
||||
async connect() {
|
||||
this.shouldReconnect = true
|
||||
this.activeWsUrl = this.wsUrl ?? getConfig().imWsUrl
|
||||
this.activeToken = this.token ?? null
|
||||
this.activeAppId = this.appId ?? getConfig().appId
|
||||
if (!this.activeToken) {
|
||||
this.activeToken = await _getToken()
|
||||
}
|
||||
if (!this.activeWsUrl) {
|
||||
this.listeners.forEach(listener => listener.onError?.('IM websocket URL not configured'))
|
||||
return
|
||||
}
|
||||
if (!this.activeToken) {
|
||||
this.listeners.forEach(listener => listener.onError?.('IM token not configured'))
|
||||
return
|
||||
}
|
||||
this.openSocket()
|
||||
}
|
||||
|
||||
@ -46,6 +64,9 @@ export class ImClient {
|
||||
if (this.ws?.readyState !== WebSocket.OPEN) {
|
||||
throw new Error('IM not connected')
|
||||
}
|
||||
if (!this.activeAppId) {
|
||||
throw new Error('IM appId not configured')
|
||||
}
|
||||
|
||||
this.sendFrame(
|
||||
'SEND',
|
||||
@ -54,7 +75,7 @@ export class ImClient {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
JSON.stringify({
|
||||
appId: this.appId,
|
||||
appId: this.activeAppId,
|
||||
toId: params.toId,
|
||||
chatType: params.chatType,
|
||||
msgType: params.msgType,
|
||||
@ -68,6 +89,9 @@ export class ImClient {
|
||||
if (this.ws?.readyState !== WebSocket.OPEN) {
|
||||
throw new Error('IM not connected')
|
||||
}
|
||||
if (!this.activeAppId) {
|
||||
throw new Error('IM appId not configured')
|
||||
}
|
||||
|
||||
this.sendFrame(
|
||||
'SEND',
|
||||
@ -76,19 +100,28 @@ export class ImClient {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
JSON.stringify({
|
||||
appId: this.appId,
|
||||
appId: this.activeAppId,
|
||||
messageId,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
subscribeGroup(groupId: string) {
|
||||
const alreadySubscribed = this.groupSubscriptions.has(groupId)
|
||||
this.groupSubscriptions.add(groupId)
|
||||
if (this.ws?.readyState === WebSocket.OPEN) {
|
||||
if (alreadySubscribed) return
|
||||
this.subscribe(`/topic/group/${groupId}`, `group-${groupId}`)
|
||||
}
|
||||
}
|
||||
|
||||
unsubscribeGroup(groupId: string) {
|
||||
this.groupSubscriptions.delete(groupId)
|
||||
if (this.ws?.readyState === WebSocket.OPEN) {
|
||||
this.sendFrame('UNSUBSCRIBE', { id: `group-${groupId}` })
|
||||
}
|
||||
}
|
||||
|
||||
addListener(listener: ImEventListener) {
|
||||
this.listeners.push(listener)
|
||||
}
|
||||
@ -112,12 +145,26 @@ export class ImClient {
|
||||
}
|
||||
|
||||
private openSocket() {
|
||||
this.ws = new WebSocket(this.wsUrl)
|
||||
if (!this.activeWsUrl) return
|
||||
this.ws = new WebSocket(this.activeWsUrl)
|
||||
|
||||
this.ws.onopen = () => {
|
||||
if (!this.activeToken) {
|
||||
void _getToken().then(token => {
|
||||
this.activeToken = token
|
||||
if (token) {
|
||||
this.sendFrame('CONNECT', {
|
||||
'accept-version': '1.2',
|
||||
Authorization: `Bearer ${this.token}`,
|
||||
Authorization: `Bearer ${token}`,
|
||||
'heart-beat': '10000,10000',
|
||||
})
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
this.sendFrame('CONNECT', {
|
||||
'accept-version': '1.2',
|
||||
Authorization: `Bearer ${this.activeToken}`,
|
||||
'heart-beat': '10000,10000',
|
||||
})
|
||||
}
|
||||
|
||||
文件差异内容过多而无法显示
加载差异
@ -243,6 +243,28 @@ export const ImDatabase = {
|
||||
}
|
||||
},
|
||||
|
||||
async deleteConversation(appId: string, targetId: string, chatType: string, currentUserId: string): Promise<void> {
|
||||
const db = getDb()
|
||||
const convId = conversationId(appId, currentUserId, targetId, chatType)
|
||||
const convs = await db
|
||||
.get<ConversationModel>('im_conversations')
|
||||
.query(Q.where('app_id', appId), Q.where('target_id', targetId))
|
||||
.fetch()
|
||||
const messages = await db
|
||||
.get<MessageModel>('im_messages')
|
||||
.query(Q.where('conversation_id', convId))
|
||||
.fetch()
|
||||
|
||||
await db.write(async () => {
|
||||
for (const message of messages) {
|
||||
await message.destroyPermanently()
|
||||
}
|
||||
for (const conversation of convs) {
|
||||
await conversation.destroyPermanently()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
isInitialized(): boolean {
|
||||
return _db !== null
|
||||
},
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
export { ImSDK } from './ImSDK'
|
||||
export type { ConversationData } from './ImSDK'
|
||||
import { ImSDK as _ImSDK } from './ImSDK'
|
||||
|
||||
// Convenience named exports for friend APIs
|
||||
@ -12,6 +11,10 @@ export type { MessageSearchParams } from './db/ImDatabase'
|
||||
export type {
|
||||
ImMessage, ImGroup, ChatType, MsgType, MsgStatus,
|
||||
ImEventListener, SendMessageParams,
|
||||
ConversationData,
|
||||
FriendRequest,
|
||||
GroupJoinRequest,
|
||||
BlacklistEntry,
|
||||
} from './types'
|
||||
export { uploadFile } from './upload'
|
||||
export type { UploadResult } from './upload'
|
||||
|
||||
@ -12,10 +12,12 @@ export type MsgType =
|
||||
| 'RICH_TEXT'
|
||||
| 'CALL_AUDIO'
|
||||
| 'CALL_VIDEO'
|
||||
| 'QUOTE'
|
||||
| 'MERGE'
|
||||
| 'REVOKED'
|
||||
| 'FORWARD'
|
||||
|
||||
export type MsgStatus = 'SENT' | 'DELIVERED' | 'READ' | 'REVOKED'
|
||||
export type MsgStatus = 'SENDING' | 'SENT' | 'DELIVERED' | 'READ' | 'FAILED' | 'REVOKED'
|
||||
|
||||
export interface ImMessage {
|
||||
id: string
|
||||
@ -27,7 +29,8 @@ export interface ImMessage {
|
||||
content: string
|
||||
status: MsgStatus
|
||||
mentionedUserIds?: string
|
||||
createdAt: string
|
||||
groupReadCount?: number
|
||||
createdAt: number
|
||||
}
|
||||
|
||||
export interface ImEventListener {
|
||||
@ -50,8 +53,51 @@ export interface ImGroup {
|
||||
id: string
|
||||
appId: string
|
||||
name: string
|
||||
groupType?: string
|
||||
creatorId: string
|
||||
memberIds: string
|
||||
adminIds: string
|
||||
createdAt: string
|
||||
announcement?: string | null
|
||||
createdAt: number
|
||||
}
|
||||
|
||||
export interface ConversationData {
|
||||
targetId: string
|
||||
chatType: ChatType
|
||||
lastMsgContent?: string | null
|
||||
lastMsgType?: string | null
|
||||
lastMsgTime: number
|
||||
unreadCount: number
|
||||
isMuted: boolean
|
||||
isPinned: boolean
|
||||
}
|
||||
|
||||
export interface FriendRequest {
|
||||
id: string
|
||||
appId: string
|
||||
fromUserId: string
|
||||
toUserId: string
|
||||
remark?: string | null
|
||||
status: 'PENDING' | 'ACCEPTED' | 'REJECTED'
|
||||
createdAt: number
|
||||
reviewedAt?: number | null
|
||||
}
|
||||
|
||||
export interface GroupJoinRequest {
|
||||
id: string
|
||||
appId: string
|
||||
groupId: string
|
||||
requesterId: string
|
||||
remark?: string | null
|
||||
status: 'PENDING' | 'ACCEPTED' | 'REJECTED'
|
||||
createdAt: number
|
||||
reviewedAt?: number | null
|
||||
}
|
||||
|
||||
export interface BlacklistEntry {
|
||||
id: string
|
||||
appId: string
|
||||
userId: string
|
||||
blockedUserId: string
|
||||
createdAt: number
|
||||
}
|
||||
|
||||
@ -15,6 +15,9 @@ export type {
|
||||
ImMessage, ImGroup, ChatType, MsgType, MsgStatus,
|
||||
ImEventListener, SendMessageParams,
|
||||
ConversationData,
|
||||
FriendRequest,
|
||||
GroupJoinRequest,
|
||||
BlacklistEntry,
|
||||
MessageSearchParams,
|
||||
UploadResult,
|
||||
} from '../packages/im/src'
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户