Files
dumpsterChat/web/src/stores/member.ts
T
hobokenchicken fd7fa4a147 feat(ui): BOTS section in member list
Members API appends server bots (is_bot). Sidebar groups ONLINE / OFFLINE / BOTS.
Bots get green BOT badge, no kick menu or profile. Mentions and DMs skip bots.
2026-07-15 20:37:05 -04:00

67 lines
1.9 KiB
TypeScript

import { create } from "zustand";
import { api } from "../lib/api.ts";
export interface Member {
id: string;
username: string;
display_name: string;
nickname?: string;
avatar: string;
status: "online" | "idle" | "dnd" | "offline";
status_text: string;
is_bot?: boolean;
bot_type?: string;
}
interface MemberState {
membersByServer: Record<string, Member[]>;
isLoading: boolean;
fetchMembers: (serverId: string) => Promise<void>;
updateMemberStatus: (userId: string, status: Member["status"]) => void;
setMemberNickname: (serverId: string, userId: string, nickname: string) => Promise<void>;
}
export const useMemberStore = create<MemberState>((set) => ({
membersByServer: {},
isLoading: false,
fetchMembers: async (serverId) => {
set({ isLoading: true });
try {
const members = await api.get<Member[]>(
`/servers/${serverId}/members`,
);
set((state) => ({
membersByServer: { ...state.membersByServer, [serverId]: members },
isLoading: false,
}));
} catch {
set({ isLoading: false });
}
},
updateMemberStatus: (userId, status) =>
set((state) => {
const next: Record<string, Member[]> = {};
for (const [serverId, list] of Object.entries(state.membersByServer)) {
next[serverId] = list.map((m) =>
m.id === userId ? { ...m, status } : m,
);
}
return { membersByServer: next };
}),
setMemberNickname: async (serverId, userId, nickname) => {
await api.patch(`/servers/${serverId}/members/${userId}`, { nickname });
set((state) => {
const list = state.membersByServer[serverId] || [];
const updated = list.map((m) =>
m.id === userId ? { ...m, nickname: nickname || undefined } : m
);
return {
membersByServer: { ...state.membersByServer, [serverId]: updated },
};
});
},
}));