feat(mobile): 接入移动端订单与消息中心
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import { useInfiniteQuery, useQuery } from '@tanstack/react-query'
|
||||
|
||||
import { apiClient, unwrapData } from '@/shared/api/client'
|
||||
import type { ApiResponse } from '@/shared/api/types'
|
||||
import type { AppNotification, ChatConversation, ChatMessage, ChatPage } from '@/features/messages/types'
|
||||
|
||||
const PAGE_SIZE = 20
|
||||
|
||||
export function fetchConversations(page = 1) {
|
||||
return unwrapData<Partial<ChatPage<ChatConversation>>>(
|
||||
apiClient.get<ApiResponse<Partial<ChatPage<ChatConversation>>>>('/chats', {
|
||||
params: { page, page_size: PAGE_SIZE },
|
||||
})
|
||||
).then(data => ({
|
||||
items: data.items ?? [],
|
||||
total: data.total ?? 0,
|
||||
page: data.page ?? page,
|
||||
page_size: data.page_size ?? PAGE_SIZE,
|
||||
}))
|
||||
}
|
||||
|
||||
export function useConversations(enabled = true) {
|
||||
return useInfiniteQuery({
|
||||
queryKey: ['chat-conversations'],
|
||||
queryFn: ({ pageParam }) => fetchConversations(pageParam),
|
||||
initialPageParam: 1,
|
||||
getNextPageParam: last =>
|
||||
last.page * last.page_size < last.total ? last.page + 1 : undefined,
|
||||
enabled,
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchNotifications(page = 1) {
|
||||
return unwrapData<Partial<ChatPage<AppNotification>>>(
|
||||
apiClient.get<ApiResponse<Partial<ChatPage<AppNotification>>>>('/notifications', {
|
||||
params: { page, page_size: PAGE_SIZE },
|
||||
})
|
||||
).then(data => ({
|
||||
items: data.items ?? [],
|
||||
total: data.total ?? 0,
|
||||
page: data.page ?? page,
|
||||
page_size: data.page_size ?? PAGE_SIZE,
|
||||
}))
|
||||
}
|
||||
|
||||
export function useNotifications(enabled = true) {
|
||||
return useInfiniteQuery({
|
||||
queryKey: ['notifications'],
|
||||
queryFn: ({ pageParam }) => fetchNotifications(pageParam),
|
||||
initialPageParam: 1,
|
||||
getNextPageParam: last =>
|
||||
last.page * last.page_size < last.total ? last.page + 1 : undefined,
|
||||
enabled,
|
||||
})
|
||||
}
|
||||
|
||||
export function useConversation(id?: string, enabled = true) {
|
||||
return useQuery({
|
||||
queryKey: ['chat-conversation', id],
|
||||
queryFn: () =>
|
||||
unwrapData<ChatConversation>(apiClient.get<ApiResponse<ChatConversation>>(`/chats/${id}`)),
|
||||
enabled: Boolean(id) && enabled,
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchChatMessages(id: number | string, page = 1) {
|
||||
return unwrapData<Partial<ChatPage<ChatMessage>>>(
|
||||
apiClient.get<ApiResponse<Partial<ChatPage<ChatMessage>>>>(`/chats/${id}/messages`, {
|
||||
params: { page, page_size: PAGE_SIZE },
|
||||
})
|
||||
).then(data => ({
|
||||
items: data.items ?? [],
|
||||
total: data.total ?? 0,
|
||||
page: data.page ?? page,
|
||||
page_size: data.page_size ?? PAGE_SIZE,
|
||||
}))
|
||||
}
|
||||
|
||||
export function useChatMessages(id?: string, enabled = true) {
|
||||
return useInfiniteQuery({
|
||||
queryKey: ['chat-messages', id],
|
||||
queryFn: ({ pageParam }) => fetchChatMessages(id as string, pageParam),
|
||||
initialPageParam: 1,
|
||||
getNextPageParam: last =>
|
||||
last.page * last.page_size < last.total ? last.page + 1 : undefined,
|
||||
enabled: Boolean(id) && enabled,
|
||||
refetchInterval: 6000,
|
||||
})
|
||||
}
|
||||
|
||||
export async function sendChatMessage(id: number | string, content: string) {
|
||||
const { data } = await apiClient.post<ApiResponse<ChatMessage>>(`/chats/${id}/messages`, {
|
||||
content,
|
||||
})
|
||||
return data.data
|
||||
}
|
||||
|
||||
export async function markConversationRead(id: number | string) {
|
||||
await apiClient.post(`/chats/${id}/read`)
|
||||
}
|
||||
|
||||
export async function markNotificationRead(id: number | string) {
|
||||
await apiClient.post(`/notifications/${id}/read`)
|
||||
}
|
||||
|
||||
export async function markAllNotificationsRead() {
|
||||
await apiClient.put('/notifications/read-all')
|
||||
}
|
||||
|
||||
export async function ensureSupportConversation() {
|
||||
return unwrapData<ChatConversation>(
|
||||
apiClient.post<ApiResponse<ChatConversation>>('/chats/support', { scene: 'general' })
|
||||
)
|
||||
}
|
||||
|
||||
export function useChatUnreadCount(enabled = true) {
|
||||
return useQuery({
|
||||
queryKey: ['chat-unread-count'],
|
||||
queryFn: () =>
|
||||
unwrapData<{ unread_count: number }>(
|
||||
apiClient.get<ApiResponse<{ unread_count: number }>>(
|
||||
'/chats/unread-count',
|
||||
{ skipErrorHandler: true } as never
|
||||
)
|
||||
)
|
||||
.then(data => data.unread_count ?? 0)
|
||||
.catch(() => 0),
|
||||
enabled,
|
||||
refetchInterval: 20_000,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
export interface ChatParticipant {
|
||||
id: number
|
||||
participant_type: string
|
||||
participant_id: number
|
||||
role: string
|
||||
display_name: string
|
||||
avatar_url: string
|
||||
last_read_at?: string | null
|
||||
joined_at: string
|
||||
}
|
||||
|
||||
export interface ChatConversation {
|
||||
id: number
|
||||
order_id?: number | null
|
||||
listing_id?: number | null
|
||||
latest_order_id?: number | null
|
||||
latest_order_no?: string
|
||||
latest_order_status?: string
|
||||
type: string
|
||||
support_scene?: string
|
||||
title: string
|
||||
status: string
|
||||
role: string
|
||||
participants?: ChatParticipant[]
|
||||
last_message_id?: number | null
|
||||
last_message_preview: string
|
||||
last_message_at?: string | null
|
||||
last_sender_type?: string
|
||||
unread_count: number
|
||||
needs_reply: boolean
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface ChatPage<T> {
|
||||
items: T[]
|
||||
total: number
|
||||
page: number
|
||||
page_size: number
|
||||
}
|
||||
|
||||
export interface ChatMessage {
|
||||
id: number
|
||||
conversation_id: number
|
||||
sender_type: string
|
||||
sender_id: number
|
||||
sender_role: string
|
||||
sender_name: string
|
||||
sender_avatar: string
|
||||
is_self: boolean
|
||||
is_read: boolean
|
||||
content_type: string
|
||||
content: string
|
||||
attachment_urls: string[]
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export interface AppNotification {
|
||||
id: number
|
||||
type: string
|
||||
title: string
|
||||
content: string
|
||||
biz_type: string
|
||||
biz_id?: number | null
|
||||
read_at?: string | null
|
||||
created_at: string
|
||||
}
|
||||
Reference in New Issue
Block a user