diff --git a/xuqm-sdk/src/main/ets/core/Types.ets b/xuqm-sdk/src/main/ets/core/Types.ets index 68dffdb..ebe2030 100644 --- a/xuqm-sdk/src/main/ets/core/Types.ets +++ b/xuqm-sdk/src/main/ets/core/Types.ets @@ -1,6 +1,5 @@ export interface SDKConfig { appKey: string - appSecret: string debug: boolean } diff --git a/xuqm-sdk/src/main/ets/im/ImClient.ets b/xuqm-sdk/src/main/ets/im/ImClient.ets index ba72287..91f4cb4 100644 --- a/xuqm-sdk/src/main/ets/im/ImClient.ets +++ b/xuqm-sdk/src/main/ets/im/ImClient.ets @@ -1,7 +1,8 @@ import webSocket from '@ohos.net.webSocket' +import http from '@ohos.net.http' import { HttpClient } from '../core/HttpClient' import { SDKContext } from '../core/SDKContext' -import { DEFAULT_IM_WS_URL } from '../core/Endpoints' +import { DEFAULT_API_BASE_URL, DEFAULT_IM_WS_URL } from '../core/Endpoints' import type { ChatType, ConversationData, @@ -23,6 +24,7 @@ export interface ImEventDelegate { onRead?(msg: ImMessage): void onRevoke?(data: RevokeData): void onError?(message: string): void + onConversationsChange?(conversations: ConversationData[]): void } export interface RevokeData { @@ -155,8 +157,10 @@ export class ImClient { this.delegate?.onRevoke?.({ msgId: message.id, operatorId: message.fromId }) } this.delegate?.onMessage?.(message) + this.notifyConversations() } else if (frame.type === 'REVOKE') { this.delegate?.onRevoke?.(frame.payload as RevokeData) + this.notifyConversations() } } catch { // ignore malformed frames @@ -705,6 +709,80 @@ export class ImClient { return parts.join('&') } + async sendImageMessage(toId: string, chatType: ChatType, filePath: string, width?: number, height?: number): Promise { + const uploadResult = await this.uploadFile(filePath) + const url = (uploadResult as Record)['url'] ?? '' + const thumbnailUrl = (uploadResult as Record)['thumbnailUrl'] + const contentObj: Record = { url } + if (width !== undefined) contentObj.width = width + if (height !== undefined) contentObj.height = height + if (thumbnailUrl) contentObj.thumbnailUrl = thumbnailUrl + return this.send({ toId, chatType, msgType: 'IMAGE', content: JSON.stringify(contentObj) }) + } + + async sendVideoMessage(toId: string, chatType: ChatType, filePath: string, width?: number, height?: number, duration?: number): Promise { + const uploadResult = await this.uploadFile(filePath) + const url = (uploadResult as Record)['url'] ?? '' + const thumbnailUrl = (uploadResult as Record)['thumbnailUrl'] + const contentObj: Record = { url } + if (width !== undefined) contentObj.width = width + if (height !== undefined) contentObj.height = height + if (duration !== undefined) contentObj.duration = duration + if (thumbnailUrl) contentObj.thumbnailUrl = thumbnailUrl + return this.send({ toId, chatType, msgType: 'VIDEO', content: JSON.stringify(contentObj) }) + } + + async sendFileMessage(toId: string, chatType: ChatType, filePath: string): Promise { + const uploadResult = await this.uploadFile(filePath) + const url = (uploadResult as Record)['url'] ?? '' + const name = (uploadResult as Record)['originalName'] ?? '' + const size = (uploadResult as Record)['size'] ?? 0 + const contentObj: Record = { url, name, size } + return this.send({ toId, chatType, msgType: 'FILE', content: JSON.stringify(contentObj) }) + } + + async sendAudioMessage(toId: string, chatType: ChatType, filePath: string, duration?: number): Promise { + const uploadResult = await this.uploadFile(filePath) + const url = (uploadResult as Record)['url'] ?? '' + const contentObj: Record = { url } + if (duration !== undefined) contentObj.duration = duration + return this.send({ toId, chatType, msgType: 'AUDIO', content: JSON.stringify(contentObj) }) + } + + private async uploadFile(filePath: string): Promise { + const token = SDKContext.getToken() + const url = DEFAULT_API_BASE_URL.replace(/\/$/, '') + '/api/file/upload' + const client = http.createHttp() + try { + const header: Record = {} + if (token) header.Authorization = 'Bearer ' + token + const multipartForm = new http.MultipartForm() + multipartForm.addFile('file', filePath, filePath.substring(filePath.lastIndexOf('/') + 1)) + const options: http.HttpRequestOptions = { + method: http.RequestMethod.POST, + header, + extraData: multipartForm, + connectTimeout: 60000, + readTimeout: 60000, + } + const res = await client.request(url, options) + const json = JSON.parse(res.result as string) as { code: number; message: string; data: Object } + if (json.code !== 200) throw new Error(json.message) + return json.data + } finally { + client.destroy() + } + } + + private async notifyConversations(): Promise { + try { + const conversations = await this.listConversations() + this.delegate?.onConversationsChange?.(conversations) + } catch { + // ignore + } + } + disconnect(): void { this.destroyed = true if (this.reconnectTimer !== null) {