Files
dumpsterChat/web/src/stores/channel.ts
T
hobokenchicken 883b775c2f feat: restore last active channel on login
- Channel store saves {serverId, channelId} to localStorage on select
- ServerBar restores last server+channel after fetching servers
- Fetches channels for the saved server, then sets the channel if valid
- Gracefully handles missing/deleted servers or channels
2026-07-02 15:44:55 -04:00

99 lines
2.8 KiB
TypeScript

import { create } from 'zustand';
import { api } from '../lib/api.ts';
export type ChannelType = 'text' | 'voice' | 'forum' | 'calendar' | 'docs' | 'list';
export interface Channel {
id: string;
server_id: string;
name: string;
type: ChannelType;
category: string | null;
position: number;
slowmode_seconds?: number;
group_id?: string | null;
}
interface ChannelState {
channelsByServer: Record<string, Channel[]>;
activeChannelId: string | null;
isLoading: boolean;
error: string | null;
fetchChannels: (serverId: string) => Promise<void>;
setActiveChannel: (id: string | null) => void;
addChannel: (channel: Channel) => void;
updateChannel: (channel: Channel) => void;
removeChannel: (id: string) => void;
}
export const useChannelStore = create<ChannelState>((set) => ({
channelsByServer: {},
activeChannelId: null,
isLoading: false,
error: null,
fetchChannels: async (serverId) => {
set({ isLoading: true, error: null });
try {
const channels = await api.get<Channel[]>(`/servers/${serverId}/channels`);
set((state) => ({
channelsByServer: { ...state.channelsByServer, [serverId]: channels },
isLoading: false,
}));
} catch (error) {
set({
isLoading: false,
error: error instanceof Error ? error.message : 'Failed to fetch channels',
});
}
},
setActiveChannel: (id) => {
set({ activeChannelId: id });
if (id) {
// ponytail: persist last active channel for session restore
const state = useChannelStore.getState();
for (const [serverId, channels] of Object.entries(state.channelsByServer)) {
if (channels.some((c) => c.id === id)) {
localStorage.setItem('dumpster:lastChannel', JSON.stringify({ serverId, channelId: id }));
break;
}
}
}
},
addChannel: (channel) =>
set((state) => {
const list = state.channelsByServer[channel.server_id] || [];
return {
channelsByServer: {
...state.channelsByServer,
[channel.server_id]: [...list, channel],
},
};
}),
updateChannel: (channel) =>
set((state) => {
const list = state.channelsByServer[channel.server_id] || [];
return {
channelsByServer: {
...state.channelsByServer,
[channel.server_id]: list.map((c) => (c.id === channel.id ? channel : c)),
},
};
}),
removeChannel: (id) =>
set((state) => {
const next: Record<string, Channel[]> = {};
for (const serverId of Object.keys(state.channelsByServer)) {
next[serverId] = state.channelsByServer[serverId].filter((c) => c.id !== id);
}
return {
channelsByServer: next,
activeChannelId: state.activeChannelId === id ? null : state.activeChannelId,
};
}),
}));