nexus/ui/src/context/ChatPanelContext.tsx
Mikkel Georgsen 2a0724839b feat(21-03): add chat API client, ChatPanelProvider, and TanStack Query hooks
- Create chatApi with full CRUD + archive/pin/message endpoints
- Create ChatPanelContext with localStorage persistence (nexus:chat-panel-open)
- Create useChatConversations with useInfiniteQuery and conversation mutations
- Create useChatMessages with useInfiniteQuery and useSendMessage
- Wrap app tree with ChatPanelProvider in main.tsx
2026-04-01 13:08:27 +02:00

60 lines
1.6 KiB
TypeScript

import { createContext, useCallback, useContext, useState, type ReactNode } from "react";
const STORAGE_KEY = "nexus:chat-panel-open";
interface ChatPanelContextValue {
chatOpen: boolean;
setChatOpen: (open: boolean) => void;
toggleChat: () => void;
activeConversationId: string | null;
setActiveConversationId: (id: string | null) => void;
}
const ChatPanelContext = createContext<ChatPanelContextValue | null>(null);
function readPreference(): boolean {
try {
const raw = localStorage.getItem(STORAGE_KEY);
return raw === "true";
} catch {
return false;
}
}
function writePreference(open: boolean) {
try {
localStorage.setItem(STORAGE_KEY, String(open));
} catch { /* ignore */ }
}
export function ChatPanelProvider({ children }: { children: ReactNode }) {
const [chatOpen, setChatOpenState] = useState(readPreference);
const [activeConversationId, setActiveConversationId] = useState<string | null>(null);
const setChatOpen = useCallback((open: boolean) => {
setChatOpenState(open);
writePreference(open);
}, []);
const toggleChat = useCallback(() => {
setChatOpenState((prev) => {
const next = !prev;
writePreference(next);
return next;
});
}, []);
return (
<ChatPanelContext.Provider
value={{ chatOpen, setChatOpen, toggleChat, activeConversationId, setActiveConversationId }}
>
{children}
</ChatPanelContext.Provider>
);
}
export function useChatPanel() {
const ctx = useContext(ChatPanelContext);
if (!ctx) throw new Error("useChatPanel must be used within ChatPanelProvider");
return ctx;
}