waveform/lib/context/ServerContext.tsx
Chneemann 0cb26f62f0
All checks were successful
Deploy Waveform to VPS / deploy (push) Successful in 3m51s
feat(channels): add EditCategoryModal and category settings action in sidebar
2026-09-14 18:54:36 +02:00

114 lines
3.5 KiB
TypeScript

/**
* @file lib/context/ServerContext.tsx
* @description Context for managing active server state and members across sidebars and mobile drawers.
*/
"use client";
import { createContext, useContext, useState } from "react";
import type { Server, Channel, Category } from "@/db/schema";
/** Extended server type containing associated channels and categories. */
export type ServerWithChannels = Server & {
channels: Channel[];
categories: Category[];
};
/** Member representation within a server context. */
export interface ServerMember {
id: string;
name: string;
isOnline?: boolean;
}
/** Shape of the server context state and dispatch functions. */
interface ServerContextType {
activeServer: ServerWithChannels | null;
setActiveServer: (server: ServerWithChannels | null) => void;
addChannel: (channel: Channel) => void;
removeChannel: (channelId: string) => void;
updateChannel: (channel: Channel) => void;
addCategory: (category: Category) => void;
updateCategory: (category: Category) => void;
removeCategory: (categoryId: string) => void;
members: ServerMember[];
setMembers: (members: ServerMember[]) => void;
}
const ServerContext = createContext<ServerContextType | null>(null);
/** Provides active server, channel, and member management state to child components. */
export function ServerProvider({ children }: { children: React.ReactNode }) {
const [activeServer, setActiveServer] = useState<ServerWithChannels | null>(
null,
);
const [members, setMembers] = useState<ServerMember[]>([]);
/** Updates the active server state using a partial updater function. */
const updateServer = (
fn: (prev: ServerWithChannels) => Partial<ServerWithChannels>,
) => {
setActiveServer((prev) => (prev ? { ...prev, ...fn(prev) } : null));
};
/** Adds a new channel to the active server. */
const addChannel = (ch: Channel) =>
updateServer((s) => ({ channels: [...s.channels, ch] }));
/** Removes a channel by ID from the active server. */
const removeChannel = (id: string) =>
updateServer((s) => ({ channels: s.channels.filter((c) => c.id !== id) }));
/** Updates an existing channel in the active server. */
const updateChannel = (ch: Channel) =>
updateServer((s) => ({
channels: s.channels.map((c) => (c.id === ch.id ? ch : c)),
}));
/** Adds a new category to the active server. */
const addCategory = (cat: Category) =>
updateServer((s) => ({ categories: [...(s.categories || []), cat] }));
/** Updates an existing category in the active server. */
const updateCategory = (cat: Category) =>
updateServer((s) => ({
categories: s.categories.map((c) => (c.id === cat.id ? cat : c)),
}));
/** Removes a category by ID and unassigns its channels. */
const removeCategory = (id: string) =>
updateServer((s) => ({
categories: s.categories.filter((c) => c.id !== id),
channels: s.channels.map((c) =>
c.categoryId === id ? { ...c, categoryId: null } : c,
),
}));
return (
<ServerContext.Provider
value={{
activeServer,
setActiveServer,
addChannel,
removeChannel,
updateChannel,
addCategory,
updateCategory,
removeCategory,
members,
setMembers,
}}
>
{children}
</ServerContext.Provider>
);
}
/** Custom hook to consume active server context state. */
export function useActiveServer() {
const context = useContext(ServerContext);
if (!context) {
throw new Error("useActiveServer must be used within a ServerProvider");
}
return context;
}