big update: added reactions, groups, media share
This commit is contained in:
+83
-4
@@ -1,4 +1,4 @@
|
||||
import type { Contact, Conversation, Message, SendMessageResult, User } from './types'
|
||||
import type { Contact, Conversation, Media, Member, Message, SendMessageResult, User } from './types'
|
||||
|
||||
const API_BASE = '/api/v1'
|
||||
|
||||
@@ -98,6 +98,29 @@ async function request<T>(path: string, options: RequestOptions = {}): Promise<T
|
||||
return readJson<T>(res)
|
||||
}
|
||||
|
||||
async function requestForm<T>(path: string, form: FormData, authRetry = true): Promise<T> {
|
||||
const res = await fetch(`${API_BASE}${path}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
body: form,
|
||||
})
|
||||
|
||||
if (res.status === 401 && authRetry) {
|
||||
try {
|
||||
await refreshSession()
|
||||
} catch {
|
||||
throw new UnauthorizedError()
|
||||
}
|
||||
return requestForm<T>(path, form, false)
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
throw new ApiError(await readError(res), res.status)
|
||||
}
|
||||
|
||||
return readJson<T>(res)
|
||||
}
|
||||
|
||||
export interface ApiClient {
|
||||
register(username: string, displayName: string, password: string): Promise<User>
|
||||
login(username: string, password: string): Promise<User>
|
||||
@@ -110,8 +133,20 @@ export interface ApiClient {
|
||||
removeContact(userId: number): Promise<void>
|
||||
listConversations(): Promise<Conversation[]>
|
||||
createDM(userId: number): Promise<Conversation>
|
||||
createGroup(name: string, memberIds: number[]): Promise<Conversation>
|
||||
listMembers(conversationId: number): Promise<Member[]>
|
||||
addMember(conversationId: number, userId: number): Promise<void>
|
||||
removeMember(conversationId: number, userId: number): Promise<void>
|
||||
listMessages(conversationId: number, before?: number, limit?: number): Promise<Message[]>
|
||||
sendMessage(conversationId: number, body: string, tempId: string): Promise<SendMessageResult>
|
||||
sendMessage(
|
||||
conversationId: number,
|
||||
body: string,
|
||||
tempId: string,
|
||||
mediaId?: number,
|
||||
): Promise<SendMessageResult>
|
||||
addReaction(conversationId: number, messageId: number, emoji: string): Promise<Message>
|
||||
removeReaction(conversationId: number, messageId: number, emoji: string): Promise<Message>
|
||||
uploadMedia(file: File): Promise<Media>
|
||||
}
|
||||
|
||||
export const api: ApiClient = {
|
||||
@@ -167,17 +202,61 @@ export const api: ApiClient = {
|
||||
return request<Conversation>('/conversations', { method: 'POST', body: { user_id: userId } })
|
||||
},
|
||||
|
||||
createGroup(name, memberIds) {
|
||||
return request<Conversation>('/conversations', {
|
||||
method: 'POST',
|
||||
body: { type: 'group', name, member_ids: memberIds },
|
||||
})
|
||||
},
|
||||
|
||||
listMembers(conversationId) {
|
||||
return request<Member[]>(`/conversations/${conversationId}/members`)
|
||||
},
|
||||
|
||||
addMember(conversationId, userId) {
|
||||
return request<void>(`/conversations/${conversationId}/members`, {
|
||||
method: 'POST',
|
||||
body: { user_id: userId },
|
||||
})
|
||||
},
|
||||
|
||||
removeMember(conversationId, userId) {
|
||||
return request<void>(`/conversations/${conversationId}/members/${userId}`, { method: 'DELETE' })
|
||||
},
|
||||
|
||||
listMessages(conversationId, before, limit = 50) {
|
||||
const params = new URLSearchParams({ limit: String(limit) })
|
||||
if (before !== undefined) params.set('before', String(before))
|
||||
return request<Message[]>(`/conversations/${conversationId}/messages?${params.toString()}`)
|
||||
},
|
||||
|
||||
async sendMessage(conversationId, body, tempId) {
|
||||
async sendMessage(conversationId, body, tempId, mediaId) {
|
||||
const payload: { body: string; temp_id: string; media_id?: number } = { body, temp_id: tempId }
|
||||
if (mediaId !== undefined) payload.media_id = mediaId
|
||||
const message = await request<Message>(`/conversations/${conversationId}/messages`, {
|
||||
method: 'POST',
|
||||
body: { body, temp_id: tempId },
|
||||
body: payload,
|
||||
})
|
||||
return { temp_id: tempId, message }
|
||||
},
|
||||
|
||||
addReaction(conversationId, messageId, emoji) {
|
||||
return request<Message>(`/conversations/${conversationId}/messages/${messageId}/reactions`, {
|
||||
method: 'PUT',
|
||||
body: { emoji },
|
||||
})
|
||||
},
|
||||
|
||||
removeReaction(conversationId, messageId, emoji) {
|
||||
return request<Message>(
|
||||
`/conversations/${conversationId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}`,
|
||||
{ method: 'DELETE' },
|
||||
)
|
||||
},
|
||||
|
||||
uploadMedia(file) {
|
||||
const form = new FormData()
|
||||
form.append('file', file)
|
||||
return requestForm<Media>('/media', form)
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user