diff --git a/src/core/http.ts b/src/core/http.ts index a9ae9ef..876a410 100644 --- a/src/core/http.ts +++ b/src/core/http.ts @@ -42,6 +42,22 @@ async function request( return json.data } +export async function uploadFile(file: File): Promise<{ url: string; thumbnailUrl?: string; hash: string; size: number; originalName: string; mimeType: string; ext: string }> { + const token = _tokenGetter() + const formData = new FormData() + formData.append('file', file) + + const res = await fetch(`${_baseUrl}/api/file/upload`, { + method: 'POST', + headers: token ? { Authorization: `Bearer ${token}` } : {}, + body: formData, + }) + + const json: ApiResponse = await res.json() + if (json.code !== 200) throw new Error(json.message) + return json.data as { url: string; thumbnailUrl?: string; hash: string; size: number; originalName: string; mimeType: string; ext: string } +} + export const http = { get: (path: string, query?: Record) => request('GET', path, undefined, query), post: (path: string, body?: unknown, query?: Record) => request('POST', path, body, query), diff --git a/src/im/ImClient.ts b/src/im/ImClient.ts index cf6d5c5..6fdd89a 100644 --- a/src/im/ImClient.ts +++ b/src/im/ImClient.ts @@ -140,6 +140,7 @@ export class ImClient { // Don't return early — also emit as message for consistency with old behavior } this.emit('message', message) + this.emit('conversations') } catch { // ignore malformed frames } diff --git a/src/im/api.ts b/src/im/api.ts index 3a05062..88b7c61 100644 --- a/src/im/api.ts +++ b/src/im/api.ts @@ -1,5 +1,5 @@ import { getConfig } from '../core/sdk' -import { http } from '../core/http' +import { http, uploadFile } from '../core/http' import type { BlacklistCheckResult, BlacklistEntry, @@ -39,6 +39,48 @@ export function sendMessage(params: { toId: string; chatType: ChatType; msgType: return http.post('/api/im/messages/send', params, appQuery()) } +export async function sendImageMessage(toId: string, chatType: ChatType, file: File, width?: number, height?: number): Promise { + const result = await uploadFile(file) + const content = JSON.stringify({ + url: result.url, + ...(width !== undefined ? { width } : {}), + ...(height !== undefined ? { height } : {}), + ...(result.thumbnailUrl ? { thumbnailUrl: result.thumbnailUrl } : {}), + }) + return sendMessage({ toId, chatType, msgType: 'IMAGE', content }) +} + +export async function sendVideoMessage(toId: string, chatType: ChatType, file: File, width?: number, height?: number, duration?: number): Promise { + const result = await uploadFile(file) + const content = JSON.stringify({ + url: result.url, + ...(width !== undefined ? { width } : {}), + ...(height !== undefined ? { height } : {}), + ...(duration !== undefined ? { duration } : {}), + ...(result.thumbnailUrl ? { thumbnailUrl: result.thumbnailUrl } : {}), + }) + return sendMessage({ toId, chatType, msgType: 'VIDEO', content }) +} + +export async function sendFileMessage(toId: string, chatType: ChatType, file: File): Promise { + const result = await uploadFile(file) + const content = JSON.stringify({ + url: result.url, + name: result.originalName, + size: result.size, + }) + return sendMessage({ toId, chatType, msgType: 'FILE', content }) +} + +export async function sendAudioMessage(toId: string, chatType: ChatType, file: File, duration?: number): Promise { + const result = await uploadFile(file) + const content = JSON.stringify({ + url: result.url, + ...(duration !== undefined ? { duration } : {}), + }) + return sendMessage({ toId, chatType, msgType: 'AUDIO', content }) +} + export function createGroup(params: { name: string; memberIds: string[]; groupType?: string; avatar?: string; announcement?: string }): Promise { return http.post('/api/im/groups', params, appQuery()) } diff --git a/src/im/useIm.ts b/src/im/useIm.ts index 862ac1a..3e7f17c 100644 --- a/src/im/useIm.ts +++ b/src/im/useIm.ts @@ -174,6 +174,9 @@ export function useIm(): UseImReturn { markMessageRevoked(msgId) void refreshConversations().catch(() => {}) }) + im.on('conversations', () => { + void refreshConversations().catch(() => {}) + }) im.on('error', (e) => { error.value = e }) im.connect() client.value = im diff --git a/src/index.ts b/src/index.ts index 7a79124..6589f9c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -41,6 +41,10 @@ export { sendFriendRequest, sendGroupJoinRequest, sendMessage, + sendImageMessage, + sendVideoMessage, + sendFileMessage, + sendAudioMessage, setConversationGroup, setConversationHidden, setConversationMuted, diff --git a/src/types/index.ts b/src/types/index.ts index 82567d8..cd77efb 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,6 +1,5 @@ export interface SDKConfig { appKey: string - appSecret: string debug?: boolean baseUrl?: string wsUrl?: string @@ -170,6 +169,7 @@ export interface ImEventMap { connected: () => void disconnected: (code: number, reason: string) => void error: (err: Event) => void + conversations: () => void } export interface ApiResponse {