UNPKG

@replyke/core

Version:

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

144 lines 7.24 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const react_1 = require("react"); const hooks_1 = require("../../../store/hooks"); const chatSlice_1 = require("../../../store/slices/chatSlice"); const useAxiosPrivate_1 = __importDefault(require("../../../config/useAxiosPrivate")); const useProject_1 = __importDefault(require("../../projects/useProject")); const handleError_1 = require("../../../utils/handleError"); function useChatMessages({ conversationId, parentId, limit = 50, includeFiles, }) { const dispatch = (0, hooks_1.useReplykeDispatch)(); const { projectId } = (0, useProject_1.default)(); const axios = (0, useAxiosPrivate_1.default)(); const isThread = Boolean(parentId); // Read from the correct Redux bucket const mainMessages = (0, hooks_1.useReplykeSelector)((0, chatSlice_1.selectMessages)(conversationId)); const mainLoading = (0, hooks_1.useReplykeSelector)((0, chatSlice_1.selectMessagesLoading)(conversationId)); const mainHasMore = (0, hooks_1.useReplykeSelector)((0, chatSlice_1.selectMessagesHasMore)(conversationId)); const threadMessages = (0, hooks_1.useReplykeSelector)((0, chatSlice_1.selectThreadReplies)(parentId ?? "")); const threadLoading = (0, hooks_1.useReplykeSelector)((0, chatSlice_1.selectThreadLoading)(parentId ?? "")); const threadHasMore = (0, hooks_1.useReplykeSelector)((0, chatSlice_1.selectThreadHasMore)(parentId ?? "")); const messages = isThread ? threadMessages : mainMessages; const loading = isThread ? threadLoading : mainLoading; const hasMore = isThread ? threadHasMore : mainHasMore; // Keep fresh refs to message arrays so loadOlder can read cursors without // closing over stale state const mainMessagesRef = (0, react_1.useRef)(mainMessages); mainMessagesRef.current = mainMessages; const threadMessagesRef = (0, react_1.useRef)(threadMessages); threadMessagesRef.current = threadMessages; // Fetch a page of messages. // `before` is an ISO 8601 timestamp — the server queries messages created // before this point in time (not a UUID cursor). const fetchPage = (0, react_1.useCallback)(async (before) => { if (!projectId || !conversationId) return; const params = { limit, sort: isThread ? "asc" : "desc", }; if (parentId) params.parentId = parentId; if (before) params.before = before; if (includeFiles) params.include = "files"; try { const response = await axios.get(`/${projectId}/chat/conversations/${conversationId}/messages`, { params }); const { messages: items, hasMore: more } = response.data; if (isThread) { // Thread replies come back ASC — dispatch as-is dispatch((0, chatSlice_1.setThreadReplies)({ parentMessageId: parentId, messages: items, hasMore: more, })); } else { // Main stream comes back DESC (newest first for cursor efficiency). // Redux stores messages ASC — reverse before dispatching. const ascending = [...items].reverse(); ascending.forEach((msg) => dispatch((0, chatSlice_1.upsertMessage)(msg))); dispatch((0, chatSlice_1.setMessagesHasMore)({ conversationId, hasMore: more })); } } catch (err) { (0, handleError_1.handleError)(err, "Failed to load messages"); } }, [projectId, conversationId, parentId, isThread, limit, includeFiles, axios, dispatch]); // Initial fetch on mount (0, react_1.useEffect)(() => { if (!projectId || !conversationId) return; const initialFetch = async () => { if (isThread) { dispatch((0, chatSlice_1.setThreadLoading)({ parentMessageId: parentId, loading: true })); } else { dispatch((0, chatSlice_1.setMessagesLoading)({ conversationId, loading: true })); } await fetchPage(null); if (isThread) { dispatch((0, chatSlice_1.setThreadLoading)({ parentMessageId: parentId, loading: false })); } else { dispatch((0, chatSlice_1.setMessagesLoading)({ conversationId, loading: false })); } }; initialFetch(); // Only re-run when the conversation/thread identity changes // eslint-disable-next-line react-hooks/exhaustive-deps }, [projectId, conversationId, parentId]); // Load more messages: // - Main stream: fetch older messages using `before` cursor (oldest loaded createdAt) // - Thread: fetch newer replies using `after` cursor (newest loaded createdAt), append const loadOlder = (0, react_1.useCallback)(async () => { if (loading || !hasMore) return; if (isThread) { const currentItems = threadMessagesRef.current; const newest = currentItems[currentItems.length - 1]; if (!newest || !parentId || !projectId || !conversationId) return; const after = new Date(newest.createdAt).toISOString(); dispatch((0, chatSlice_1.setThreadLoading)({ parentMessageId: parentId, loading: true })); try { const response = await axios.get(`/${projectId}/chat/conversations/${conversationId}/messages`, { params: { parentId, after, limit, sort: "asc", ...(includeFiles ? { include: "files" } : {}), }, }); const { messages: newItems, hasMore: more } = response.data; dispatch((0, chatSlice_1.setThreadReplies)({ parentMessageId: parentId, messages: [...currentItems, ...newItems], hasMore: more, })); } catch (err) { (0, handleError_1.handleError)(err, "Failed to load more thread replies"); } finally { dispatch((0, chatSlice_1.setThreadLoading)({ parentMessageId: parentId, loading: false })); } return; } const oldest = mainMessagesRef.current[0]; if (!oldest) return; const before = new Date(oldest.createdAt).toISOString(); dispatch((0, chatSlice_1.setMessagesLoading)({ conversationId, loading: true })); await fetchPage(before); dispatch((0, chatSlice_1.setMessagesLoading)({ conversationId, loading: false })); }, [loading, hasMore, isThread, parentId, projectId, conversationId, limit, includeFiles, axios, dispatch, fetchPage]); return { messages, loading, hasMore, loadOlder }; } exports.default = useChatMessages; //# sourceMappingURL=useChatMessages.js.map