feat(phase3): forum channels + tags + card UI
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { create } from 'zustand';
|
||||
import { api } from '../lib/api.ts';
|
||||
|
||||
export type ChannelType = 'text' | 'voice';
|
||||
export type ChannelType = 'text' | 'voice' | 'forum';
|
||||
|
||||
export interface Channel {
|
||||
id: string;
|
||||
@@ -10,6 +10,7 @@ export interface Channel {
|
||||
type: ChannelType;
|
||||
category: string | null;
|
||||
position: number;
|
||||
slowmode_seconds?: number;
|
||||
}
|
||||
|
||||
interface ChannelState {
|
||||
|
||||
@@ -15,15 +15,26 @@ export interface Thread {
|
||||
message_count: number;
|
||||
last_message_at: string | null;
|
||||
created_at: string;
|
||||
tag_ids?: string[];
|
||||
}
|
||||
|
||||
export interface ForumTag {
|
||||
id: string;
|
||||
tag_id: string;
|
||||
name: string;
|
||||
emoji: string | null;
|
||||
color: string | null;
|
||||
}
|
||||
|
||||
export interface CreateThreadData {
|
||||
name: string;
|
||||
message_id?: string;
|
||||
tag_ids?: string[];
|
||||
}
|
||||
|
||||
interface ThreadState {
|
||||
threadsByParent: Record<string, Thread[]>;
|
||||
tagsByForum: Record<string, ForumTag[]>;
|
||||
activeThreadId: string | null;
|
||||
messagesByThread: Record<string, Message[]>;
|
||||
isLoading: boolean;
|
||||
@@ -35,10 +46,14 @@ interface ThreadState {
|
||||
fetchThreadMessages: (threadId: string, before?: string) => Promise<void>;
|
||||
sendThreadMessage: (threadId: string, content: string) => Promise<Message>;
|
||||
addThreadMessage: (message: Message) => void;
|
||||
fetchForumTags: (forumChannelId: string) => Promise<void>;
|
||||
createForumTag: (forumChannelId: string, data: { name: string; emoji?: string; color?: string }) => Promise<ForumTag>;
|
||||
deleteForumTag: (tagId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export const useThreadStore = create<ThreadState>((set) => ({
|
||||
threadsByParent: {},
|
||||
tagsByForum: {},
|
||||
activeThreadId: null,
|
||||
messagesByThread: {},
|
||||
isLoading: false,
|
||||
@@ -113,4 +128,31 @@ export const useThreadStore = create<ThreadState>((set) => ({
|
||||
if (list.some((m) => m.id === message.id)) return state;
|
||||
return { messagesByThread: { ...state.messagesByThread, [message.channel_id]: [...list, message] } };
|
||||
}),
|
||||
|
||||
fetchForumTags: async (forumChannelId) => {
|
||||
const tags = await api.get<ForumTag[]>(`/channels/${forumChannelId}/forum-tags`);
|
||||
set((state) => ({
|
||||
tagsByForum: { ...state.tagsByForum, [forumChannelId]: Array.isArray(tags) ? tags : [] },
|
||||
}));
|
||||
},
|
||||
|
||||
createForumTag: async (forumChannelId, data) => {
|
||||
const tag = await api.post<ForumTag>(`/channels/${forumChannelId}/forum-tags`, data);
|
||||
set((state) => {
|
||||
const list = state.tagsByForum[forumChannelId] || [];
|
||||
return { tagsByForum: { ...state.tagsByForum, [forumChannelId]: [...list, tag] } };
|
||||
});
|
||||
return tag;
|
||||
},
|
||||
|
||||
deleteForumTag: async (tagId) => {
|
||||
await api.delete(`/forum-tags/${tagId}`);
|
||||
set((state) => {
|
||||
const next: Record<string, ForumTag[]> = {};
|
||||
for (const forumId of Object.keys(state.tagsByForum)) {
|
||||
next[forumId] = state.tagsByForum[forumId].filter((t) => t.id !== tagId && t.tag_id !== tagId);
|
||||
}
|
||||
return { tagsByForum: next };
|
||||
});
|
||||
},
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user