sync: phase 1 backend + frontend from server
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
import { create } from "zustand";
|
||||
import { api } from "../lib/api.ts";
|
||||
|
||||
export interface ConversationMember {
|
||||
id: string;
|
||||
username: string;
|
||||
display_name: string;
|
||||
avatar: string;
|
||||
}
|
||||
|
||||
export interface Conversation {
|
||||
id: string;
|
||||
type: "dm" | "group_dm";
|
||||
name: string;
|
||||
members: ConversationMember[];
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface ConversationMessage {
|
||||
id: string;
|
||||
conversation_id: string;
|
||||
author_id: string;
|
||||
author_username: string;
|
||||
author_display_name: string | null;
|
||||
content: string;
|
||||
created_at: string;
|
||||
edited_at: string | null;
|
||||
}
|
||||
|
||||
interface ConversationState {
|
||||
conversations: Conversation[];
|
||||
activeConversationId: string | null;
|
||||
messagesByConversation: Record<string, ConversationMessage[]>;
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
fetchConversations: () => Promise<void>;
|
||||
createConversation: (userIds: string[]) => Promise<Conversation>;
|
||||
setActiveConversation: (id: string | null) => void;
|
||||
fetchMessages: (conversationId: string, before?: string) => Promise<void>;
|
||||
sendMessage: (conversationId: string, content: string) => Promise<ConversationMessage>;
|
||||
addMessage: (message: ConversationMessage) => void;
|
||||
}
|
||||
|
||||
export const useConversationStore = create<ConversationState>((set, _get) => ({
|
||||
conversations: [],
|
||||
activeConversationId: null,
|
||||
messagesByConversation: {},
|
||||
isLoading: false,
|
||||
error: null,
|
||||
|
||||
fetchConversations: async () => {
|
||||
try {
|
||||
const conversations = await api.get<Conversation[]>("/conversations");
|
||||
set({ conversations: Array.isArray(conversations) ? conversations : [] });
|
||||
} catch (error) {
|
||||
set({
|
||||
error: error instanceof Error ? error.message : "Failed to fetch conversations",
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
createConversation: async (userIds) => {
|
||||
const conversation = await api.post<Conversation>("/conversations", { user_ids: userIds });
|
||||
set((state) => ({
|
||||
conversations: [conversation, ...state.conversations],
|
||||
activeConversationId: conversation.id,
|
||||
}));
|
||||
return conversation;
|
||||
},
|
||||
|
||||
setActiveConversation: (id) => set({ activeConversationId: id }),
|
||||
|
||||
fetchMessages: async (conversationId, before) => {
|
||||
set({ isLoading: true });
|
||||
try {
|
||||
const params = before ? "?before=" + encodeURIComponent(before) : "";
|
||||
const messages = await api.get<ConversationMessage[]>(
|
||||
`/conversations/${conversationId}/messages${params}`,
|
||||
);
|
||||
set((state) => ({
|
||||
messagesByConversation: {
|
||||
...state.messagesByConversation,
|
||||
[conversationId]: Array.isArray(messages) ? messages : [],
|
||||
},
|
||||
isLoading: false,
|
||||
}));
|
||||
} catch (error) {
|
||||
set({ isLoading: false, error: error instanceof Error ? error.message : "Failed" });
|
||||
}
|
||||
},
|
||||
|
||||
sendMessage: async (conversationId, content) => {
|
||||
const message = await api.post<ConversationMessage>(
|
||||
`/conversations/${conversationId}/messages`,
|
||||
{ content },
|
||||
);
|
||||
set((state) => ({
|
||||
messagesByConversation: {
|
||||
...state.messagesByConversation,
|
||||
[conversationId]: [
|
||||
...(state.messagesByConversation[conversationId] || []),
|
||||
message,
|
||||
],
|
||||
},
|
||||
}));
|
||||
return message;
|
||||
},
|
||||
|
||||
addMessage: (message) => {
|
||||
set((state) => ({
|
||||
messagesByConversation: {
|
||||
...state.messagesByConversation,
|
||||
[message.conversation_id]: [
|
||||
...(state.messagesByConversation[message.conversation_id] || []),
|
||||
message,
|
||||
],
|
||||
},
|
||||
}));
|
||||
},
|
||||
}));
|
||||
Reference in New Issue
Block a user