UNPKG

@replyke/core

Version:

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

130 lines 5.13 kB
import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import useFetchManyComments from "./useFetchManyComments"; import { handleError } from "../../utils/handleError"; import { addCommentsToTree as addCommentsToTreeHandler } from "../../helpers/addCommentsToTree"; import { removeCommentFromTree as removeCommentFromTreeHandler } from "../../helpers/removeCommentFromTree"; import { markCommentAsDeletedInTree as markCommentAsDeletedInTreeHandler } from "../../helpers/markCommentAsDeletedInTree"; function useEntityComments(props) { const { entityId, limit = 10, defaultSortBy = "new", include } = props; const fetchManyComments = useFetchManyComments(); const loading = useRef(true); const [loadingState, setLoadingState] = useState(true); // required to trigger rerenders const hasMore = useRef(true); const [hasMoreState, setHasMoreState] = useState(true); // required to trigger rerenders const [sortBy, setSortBy] = useState(defaultSortBy); const [page, setPage] = useState(1); const [entityCommentsTree, setEntityCommentsTree] = useState({}); const { comments, newComments } = useMemo(() => { const allRootComments = Object.values(entityCommentsTree).filter((item) => !item.comment.parentId); const comments = allRootComments .filter((entry) => !entry.new) .map((entry) => entry.comment); const newComments = allRootComments .filter((entry) => !!entry.new) .map((entry) => entry.comment) .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); return { comments, newComments }; }, [entityCommentsTree]); const addCommentsToTree = (newComments, newlyAdded) => { addCommentsToTreeHandler(setEntityCommentsTree, newComments, newlyAdded); }; const removeCommentFromTree = useCallback(({ commentId }) => { setEntityCommentsTree((prevTree) => removeCommentFromTreeHandler(prevTree, commentId)); }, [setEntityCommentsTree]); const markCommentAsDeleted = useCallback(({ commentId }) => { setEntityCommentsTree((prevTree) => markCommentAsDeletedInTreeHandler(prevTree, commentId)); }, [setEntityCommentsTree]); const resetComments = useCallback(async () => { if (!entityId) { return; } try { loading.current = true; setLoadingState(true); hasMore.current = true; setHasMoreState(true); setEntityCommentsTree({}); setPage(1); const response = await fetchManyComments({ entityId, page: 1, sortBy, limit, include, }); if (response) { const { data: newComments, pagination } = response; addCommentsToTree(newComments); hasMore.current = pagination.hasMore; setHasMoreState(pagination.hasMore); } } catch (err) { handleError(err, "Failed to reset entity comments:"); } finally { loading.current = false; setLoadingState(false); } }, [fetchManyComments, limit, sortBy, entityId]); const loadMore = () => { if (loading.current || !hasMore.current) return; setPage((prevPage) => { return prevPage + 1; }); }; useEffect(() => { resetComments(); }, [resetComments]); // useEffect to get a new batch of comments useEffect(() => { const loadMoreComments = async () => { if (!entityId) { return; } try { loading.current = true; setLoadingState(true); const response = await fetchManyComments({ entityId, page, sortBy, limit, }); if (response) { const { data: newComments, pagination } = response; addCommentsToTree(newComments); hasMore.current = pagination.hasMore; setHasMoreState(pagination.hasMore); } } catch (err) { handleError(err, "Loading more comments failed:"); } finally { loading.current = false; setLoadingState(false); } }; // We only load more if th page changed if (page > 1 && hasMore.current && !loading.current) { loadMoreComments(); } }, [page]); return { entityCommentsTree, comments, newComments, loading: loadingState, // we use the state to trigger renders hasMore: hasMoreState, sortBy, setSortBy, loadMore, addCommentsToTree, removeCommentFromTree, markCommentAsDeleted, }; } export default useEntityComments; //# sourceMappingURL=useEntityComments.js.map