@replyke/core
Version:
Replyke: Build interactive apps with social features like comments, votes, feeds, user lists, notifications, and more.
67 lines • 3.05 kB
JavaScript
;
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