From 38cc6e6f3a0ff56ab62f034771e73f2d849fc6ae Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Mon, 3 Nov 2025 15:27:02 +0530 Subject: [PATCH] add room to active thread atom --- .../pages/client/ClientInitStorageAtom.tsx | 12 ++- src/app/state/hooks/roomToActiveThread.ts | 55 ++++++++++++++ src/app/state/roomToActiveThread.ts | 75 +++++++++++++++++++ 3 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 src/app/state/hooks/roomToActiveThread.ts create mode 100644 src/app/state/roomToActiveThread.ts diff --git a/src/app/pages/client/ClientInitStorageAtom.tsx b/src/app/pages/client/ClientInitStorageAtom.tsx index 1abee707..308a0212 100644 --- a/src/app/pages/client/ClientInitStorageAtom.tsx +++ b/src/app/pages/client/ClientInitStorageAtom.tsx @@ -8,6 +8,8 @@ import { makeNavToActivePathAtom } from '../../state/navToActivePath'; import { NavToActivePathProvider } from '../../state/hooks/navToActivePath'; import { makeOpenedSidebarFolderAtom } from '../../state/openedSidebarFolder'; import { OpenedSidebarFolderProvider } from '../../state/hooks/openedSidebarFolder'; +import { makeRoomToActiveThreadAtom } from '../../state/roomToActiveThread'; +import { RoomToActiveThreadProvider } from '../../state/hooks/roomToActiveThread'; type ClientInitStorageAtomProps = { children: ReactNode; @@ -22,15 +24,19 @@ export function ClientInitStorageAtom({ children }: ClientInitStorageAtomProps) const navToActivePathAtom = useMemo(() => makeNavToActivePathAtom(userId), [userId]); + const roomToActiveThreadAtom = useMemo(() => makeRoomToActiveThreadAtom(userId), [userId]); + const openedSidebarFolderAtom = useMemo(() => makeOpenedSidebarFolderAtom(userId), [userId]); return ( - - {children} - + + + {children} + + diff --git a/src/app/state/hooks/roomToActiveThread.ts b/src/app/state/hooks/roomToActiveThread.ts new file mode 100644 index 00000000..7abb5974 --- /dev/null +++ b/src/app/state/hooks/roomToActiveThread.ts @@ -0,0 +1,55 @@ +import { createContext, useCallback, useContext } from 'react'; +import { useAtomValue, useSetAtom } from 'jotai'; +import { RoomToActiveThreadAtom } from '../roomToActiveThread'; + +const RoomToActiveThreadAtomContext = createContext(null); +export const RoomToActiveThreadProvider = RoomToActiveThreadAtomContext.Provider; + +export const useRoomToActiveThreadAtom = (): RoomToActiveThreadAtom => { + const anAtom = useContext(RoomToActiveThreadAtomContext); + + if (!anAtom) { + throw new Error('RoomToActiveThreadAtom is not provided!'); + } + + return anAtom; +}; + +export const useThreadSelector = (roomId: string): ((threadId: string) => void) => { + const roomToActiveThreadAtom = useRoomToActiveThreadAtom(); + const setRoomToActiveThread = useSetAtom(roomToActiveThreadAtom); + + const onThreadSelect = useCallback( + (threadId: string) => { + setRoomToActiveThread({ + type: 'PUT', + roomId, + threadId, + }); + }, + [roomId, setRoomToActiveThread] + ); + + return onThreadSelect; +}; + +export const useActiveThread = (roomId: string): string | undefined => { + const roomToActiveThreadAtom = useRoomToActiveThreadAtom(); + const roomToActiveThread = useAtomValue(roomToActiveThreadAtom); + + return roomToActiveThread.get(roomId); +}; + +export const useThreadClose = (roomId: string): (() => void) => { + const roomToActiveThreadAtom = useRoomToActiveThreadAtom(); + const setRoomToActiveThread = useSetAtom(roomToActiveThreadAtom); + + const closeThread = useCallback(() => { + setRoomToActiveThread({ + type: 'DELETE', + roomId, + }); + }, [roomId, setRoomToActiveThread]); + + return closeThread; +}; diff --git a/src/app/state/roomToActiveThread.ts b/src/app/state/roomToActiveThread.ts new file mode 100644 index 00000000..e156c63a --- /dev/null +++ b/src/app/state/roomToActiveThread.ts @@ -0,0 +1,75 @@ +import { atom, WritableAtom } from 'jotai'; +import produce from 'immer'; +import { + atomWithLocalStorage, + getLocalStorageItem, + setLocalStorageItem, +} from './utils/atomWithLocalStorage'; + +const ROOM_TO_ACTIVE_THREAD = 'roomToActiveThread'; + +const getStoreKey = (userId: string): string => `${ROOM_TO_ACTIVE_THREAD}${userId}`; + +type RoomToActiveThread = Map; + +type RoomToActiveThreadAction = + | { + type: 'PUT'; + roomId: string; + threadId: string; + } + | { + type: 'DELETE'; + roomId: string; + }; + +export type RoomToActiveThreadAtom = WritableAtom< + RoomToActiveThread, + [RoomToActiveThreadAction], + undefined +>; + +export const makeRoomToActiveThreadAtom = (userId: string): RoomToActiveThreadAtom => { + const storeKey = getStoreKey(userId); + + const baseRoomToActiveThread = atomWithLocalStorage( + storeKey, + (key) => { + const obj: Record = getLocalStorageItem(key, {}); + return new Map(Object.entries(obj)); + }, + (key, value) => { + const obj: Record = Object.fromEntries(value); + setLocalStorageItem(key, obj); + } + ); + + const navToActivePathAtom = atom( + (get) => get(baseRoomToActiveThread), + (get, set, action) => { + if (action.type === 'DELETE') { + set( + baseRoomToActiveThread, + produce(get(baseRoomToActiveThread), (draft) => { + draft.delete(action.roomId); + }) + ); + return; + } + if (action.type === 'PUT') { + set( + baseRoomToActiveThread, + produce(get(baseRoomToActiveThread), (draft) => { + draft.set(action.roomId, action.threadId); + }) + ); + } + } + ); + + return navToActivePathAtom; +}; + +export const clearRoomToActiveThreadStore = (userId: string) => { + localStorage.removeItem(getStoreKey(userId)); +};