UNPKG

@replyke/core

Version:

Replyke: Build interactive apps with social features like comments, votes, feeds, user lists, notifications, and more.

216 lines 9.3 kB
import { useCallback, useMemo, useRef } from "react"; import { useReplykeDispatch, useReplykeSelector } from "../../store/hooks"; import { setLoading, openCollection, goBack, goToRoot, setCurrentCollection, setSubCollections, updateCollectionInSubCollections, addNewCollectionAndNavigate, handleCollectionDeletion, prependCollectionEntity, removeCollectionEntity, insertCollectionEntityAt, resetCollections, handleError, } from "../../store/slices/collectionsSlice"; import { useLazyFetchRootCollectionQuery, useLazyFetchSubCollectionsQuery, useCreateCollectionMutation, useUpdateCollectionMutation, useDeleteCollectionMutation, useAddToCollectionMutation, useRemoveFromCollectionMutation, } from "../../store/api/collectionsApi"; import { handleError as handleErrorUtil } from "../../utils/handleError"; /** * Redux-powered hook that provides all collection actions * This replaces the individual hooks and provides a centralized way to manage collections */ export function useCollectionsActions() { const dispatch = useReplykeDispatch(); // Use a ref so removeFromCollection can read the latest entities without being in deps const entitiesByCollectionId = useReplykeSelector((state) => state.replyke.collections.entitiesByCollectionId); const entitiesByCollectionIdRef = useRef(entitiesByCollectionId); entitiesByCollectionIdRef.current = entitiesByCollectionId; // RTK Query hooks const [fetchRootCollectionQuery] = useLazyFetchRootCollectionQuery(); const [fetchSubCollectionsQuery] = useLazyFetchSubCollectionsQuery(); const [createCollectionMutation] = useCreateCollectionMutation(); const [updateCollectionMutation] = useUpdateCollectionMutation(); const [deleteCollectionMutation] = useDeleteCollectionMutation(); const [addToCollectionMutation] = useAddToCollectionMutation(); const [removeFromCollectionMutation] = useRemoveFromCollectionMutation(); // Navigation actions const openCollectionAction = useCallback((collection) => { dispatch(openCollection(collection)); }, [dispatch]); const goBackAction = useCallback(() => { dispatch(goBack()); }, [dispatch]); const goToRootAction = useCallback(() => { dispatch(goToRoot()); }, [dispatch]); // Fetch root collection const fetchRootCollection = useCallback(async ({ projectId }) => { if (!projectId) { console.warn("Can't fetch root collection without projectId."); return; } dispatch(setLoading(true)); try { const result = await fetchRootCollectionQuery({ projectId }).unwrap(); if (result) { // Set parentId to null for root collection const rootCollection = { ...result, parentId: null }; dispatch(setCurrentCollection(rootCollection)); } } catch (err) { handleErrorUtil(err, "Failed fetching root collection"); dispatch(handleError()); } finally { dispatch(setLoading(false)); } }, [dispatch, fetchRootCollectionQuery]); // Fetch sub-collections const fetchSubCollections = useCallback(async ({ projectId, collectionId }) => { if (!projectId || !collectionId) { console.warn("Can't fetch sub-collections without projectId and collectionId."); return; } dispatch(setLoading(true)); try { const result = await fetchSubCollectionsQuery({ projectId, collectionId }).unwrap(); if (result) { dispatch(setSubCollections({ collections: result, parentCollectionId: collectionId })); } } catch (err) { handleErrorUtil(err, "Failed fetching sub-collections"); dispatch(handleError()); } finally { dispatch(setLoading(false)); } }, [dispatch, fetchSubCollectionsQuery]); // Create collection const createCollection = useCallback(async ({ projectId, parentCollectionId, collectionName, }) => { if (!projectId || !parentCollectionId || !collectionName) { console.error("Missing required parameters for creating collection."); return; } try { const result = await createCollectionMutation({ projectId, parentCollectionId, collectionName, }).unwrap(); if (result) { dispatch(addNewCollectionAndNavigate(result)); } } catch (err) { handleErrorUtil(err, "Failed to create collection"); } }, [createCollectionMutation, dispatch]); // Update collection const updateCollection = useCallback(async ({ projectId, collectionId, update, }) => { if (!projectId || !collectionId) { console.error("Missing required parameters for updating collection."); return; } try { const result = await updateCollectionMutation({ projectId, collectionId, update, }).unwrap(); if (result) { // Check if it's the current collection or a sub-collection dispatch(updateCollectionInSubCollections(result)); } } catch (err) { handleErrorUtil(err, "Failed to update collection"); } }, [updateCollectionMutation, dispatch]); // Delete collection const deleteCollection = useCallback(async ({ projectId, collection, }) => { if (!projectId || !collection) { console.error("Missing required parameters for deleting collection."); return; } try { await deleteCollectionMutation({ projectId, collectionId: collection.id, }).unwrap(); dispatch(handleCollectionDeletion({ collectionId: collection.id, parentId: collection.parentId })); } catch (err) { handleErrorUtil(err, "Failed to delete collection"); } }, [deleteCollectionMutation, dispatch]); // Add entity to collection — optimistically prepends to Redux state, reverts on error const addToCollection = useCallback(async ({ projectId, collectionId, entity, }) => { if (!projectId || !collectionId || !entity?.id) { console.error("Missing required parameters for adding to collection."); return; } dispatch(prependCollectionEntity({ collectionId, entity })); try { await addToCollectionMutation({ projectId, collectionId, entityId: entity.id, }).unwrap(); } catch (err) { dispatch(removeCollectionEntity({ collectionId, entityId: entity.id })); handleErrorUtil(err, "Failed to add entity to collection"); throw err; } }, [addToCollectionMutation, dispatch]); // Remove entity from collection — optimistically removes from Redux state, reverts on error const removeFromCollection = useCallback(async ({ projectId, collectionId, entityId, }) => { if (!projectId || !collectionId || !entityId) { console.error("Missing required parameters for removing from collection."); return; } const currentList = entitiesByCollectionIdRef.current[collectionId] ?? []; const originalIndex = currentList.findIndex((e) => e.id === entityId); const entityToRestore = originalIndex !== -1 ? currentList[originalIndex] : undefined; dispatch(removeCollectionEntity({ collectionId, entityId })); try { await removeFromCollectionMutation({ projectId, collectionId, entityId, }).unwrap(); } catch (err) { if (entityToRestore) { dispatch(insertCollectionEntityAt({ collectionId, entity: entityToRestore, index: originalIndex })); } handleErrorUtil(err, "Failed to remove entity from collection"); throw err; } }, [removeFromCollectionMutation, dispatch]); // Reset collections const resetCollectionsAction = useCallback(() => { dispatch(resetCollections()); }, [dispatch]); return useMemo(() => ({ // Navigation openCollection: openCollectionAction, goBack: goBackAction, goToRoot: goToRootAction, // Data fetching fetchRootCollection, fetchSubCollections, // CRUD operations createCollection, updateCollection, deleteCollection, addToCollection, removeFromCollection, // Utility resetCollections: resetCollectionsAction, }), [ openCollectionAction, goBackAction, goToRootAction, fetchRootCollection, fetchSubCollections, createCollection, updateCollection, deleteCollection, addToCollection, removeFromCollection, resetCollectionsAction, ]); } export default useCollectionsActions; //# sourceMappingURL=useCollectionsActions.js.map