UNPKG

@replyke/core

Version:

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

67 lines 3.05 kB
"use strict"; 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 chat_context_1 = require("../../context/chat-context"); /** * Manages the typing indicator for a conversation. * * Sender side protocol (§6.5 of design): * 1. On first keystroke after idle: emit `typing:start` immediately. * 2. Re-emit `typing:start` every 2 s as a keep-alive while typing continues. * 3. On send / blur / input clear: cancel keep-alive, emit `typing:stop`. */ function useTypingIndicator({ conversationId, }) { const { socket } = (0, chat_context_1.useChatContext)(); // Array of other users currently typing (current user already excluded by ChatProvider) const typingUsers = (0, hooks_1.useReplykeSelector)((0, chatSlice_1.selectTypingUsers)(conversationId)); // Whether we (the current user) are currently emitting typing events const isTypingRef = (0, react_1.useRef)(false); // Keep-alive interval: re-emits typing:start every 2 s const keepAliveRef = (0, react_1.useRef)(null); const stopKeepAlive = (0, react_1.useCallback)(() => { if (keepAliveRef.current !== null) { clearInterval(keepAliveRef.current); keepAliveRef.current = null; } }, []); const startTyping = (0, react_1.useCallback)(() => { if (!socket || !conversationId) return; if (!isTypingRef.current) { // First keystroke after idle — emit immediately isTypingRef.current = true; socket.emit("typing:start", { conversationId }); // Start the 2-second keep-alive keepAliveRef.current = setInterval(() => { socket.emit("typing:start", { conversationId }); }, 2000); } // Subsequent keystrokes while already typing: keep-alive handles re-emitting }, [socket, conversationId]); const stopTyping = (0, react_1.useCallback)(() => { if (!socket || !conversationId) return; if (!isTypingRef.current) return; // Cancel keep-alive BEFORE emitting stop to avoid a scheduled keep-alive // firing after the stop and briefly re-adding the user to receivers' typing lists stopKeepAlive(); isTypingRef.current = false; socket.emit("typing:stop", { conversationId }); }, [socket, conversationId, stopKeepAlive]); // Clean up on unmount or conversation change (0, react_1.useEffect)(() => { return () => { if (isTypingRef.current && socket && conversationId) { stopKeepAlive(); socket.emit("typing:stop", { conversationId }); isTypingRef.current = false; } }; }, [socket, conversationId, stopKeepAlive]); return { typingUsers, startTyping, stopTyping }; } exports.default = useTypingIndicator; //# sourceMappingURL=useTypingIndicator.js.map