Files
juwan-frontend/store/chat.ts
T

138 lines
3.8 KiB
TypeScript

import { create } from "zustand"
import { generateId } from "@/lib/id"
import { mockChatMessages, mockChatSessions, mockUsers } from "@/lib/mock"
import type { ChatMessage, ChatSession, Order } from "@/lib/types"
interface ChatState {
sessions: ChatSession[]
messages: ChatMessage[]
ensureOrderSession: (order: Order) => ChatSession
sendTextMessage: (sessionId: string, actorId: string, content: string) => void
sendImageMessage: (sessionId: string, actorId: string, imageUrl: string) => void
}
function resolveAvatar(userId: string) {
return mockUsers.find((user) => user.id === userId)?.avatar ?? ""
}
function shouldReadonly(status: Order["status"]) {
return status === "pending_review" || status === "completed" || status === "cancelled"
}
export const useChatStore = create<ChatState>((set, get) => ({
sessions: mockChatSessions,
messages: mockChatMessages,
ensureOrderSession: (order) => {
const existing = get().sessions.find(
(session) => session.type === "order" && session.orderId === order.id,
)
const readonly = shouldReadonly(order.status)
if (existing) {
if (existing.readonly !== readonly) {
set((state) => ({
sessions: state.sessions.map((session) =>
session.id === existing.id ? { ...session, readonly } : session,
),
}))
}
return get().sessions.find((session) => session.id === existing.id) ?? existing
}
const session: ChatSession = {
id: `chat-${order.id}`,
type: "order",
orderId: order.id,
participants: [
{
id: order.consumerId,
name: order.consumerName,
avatar: resolveAvatar(order.consumerId),
},
{
id: order.playerId,
name: order.playerName,
avatar: resolveAvatar(order.playerId),
},
],
unreadCount: 0,
readonly,
}
set((state) => ({
sessions: [session, ...state.sessions],
}))
return session
},
sendTextMessage: (sessionId, actorId, content) => {
const text = content.trim()
if (!text) return
const session = get().sessions.find((item) => item.id === sessionId)
if (!session || session.readonly) return
const sender = session.participants.find((participant) => participant.id === actorId)
if (!sender) return
const now = new Date().toISOString()
const message: ChatMessage = {
id: generateId("msg"),
sessionId,
senderId: actorId,
senderName: sender.name,
senderAvatar: sender.avatar,
type: "text",
content: text,
createdAt: now,
}
set((state) => ({
messages: [...state.messages, message],
sessions: state.sessions.map((session) =>
session.id === sessionId
? {
...session,
lastMessage: text,
lastMessageAt: now,
}
: session,
),
}))
},
sendImageMessage: (sessionId, actorId, imageUrl) => {
const content = imageUrl.trim()
if (!content) return
const session = get().sessions.find((item) => item.id === sessionId)
if (!session || session.readonly) return
const sender = session.participants.find((participant) => participant.id === actorId)
if (!sender) return
const now = new Date().toISOString()
const message: ChatMessage = {
id: generateId("msg"),
sessionId,
senderId: actorId,
senderName: sender.name,
senderAvatar: sender.avatar,
type: "image",
content,
createdAt: now,
}
set((state) => ({
messages: [...state.messages, message],
sessions: state.sessions.map((item) =>
item.id === sessionId
? {
...item,
lastMessage: "[图片]",
lastMessageAt: now,
}
: item,
),
}))
},
}))