86 lines
2.4 KiB
TypeScript
86 lines
2.4 KiB
TypeScript
import { useEffect, useState, useCallback, useRef } from "react";
|
|
import { usePusherClient } from "@/lib/pusher-provider";
|
|
import type { ChannelMessage } from "@/lib/pusher-client";
|
|
|
|
interface UseChannelResult {
|
|
/** Current set of humanIds present in the channel */
|
|
presence: string[];
|
|
/** Messages received on this channel (since the hook mounted) */
|
|
messages: ChannelMessage[];
|
|
/** Send a message to the channel */
|
|
sendMessage: (payload: unknown) => void;
|
|
}
|
|
|
|
/**
|
|
* Subscribe to a pusher channel. Manages presence tracking and message delivery.
|
|
* Subscribes on mount, unsubscribes on unmount.
|
|
*
|
|
* @param channelId - The channel to subscribe to, or null to skip.
|
|
*/
|
|
export function useChannel(channelId: string | null): UseChannelResult {
|
|
const client = usePusherClient();
|
|
const [presence, setPresence] = useState<string[]>([]);
|
|
const [messages, setMessages] = useState<ChannelMessage[]>([]);
|
|
|
|
useEffect(() => {
|
|
if (!client || !channelId) {
|
|
setPresence([]);
|
|
setMessages([]);
|
|
return;
|
|
}
|
|
|
|
client.subscribe(channelId);
|
|
|
|
const onSubscribed = (msg: { presence?: string[] }) => {
|
|
setPresence(msg.presence ?? []);
|
|
};
|
|
|
|
const onJoin = (msg: { humanId?: string }) => {
|
|
if (msg.humanId) {
|
|
setPresence((prev) =>
|
|
prev.includes(msg.humanId!) ? prev : [...prev, msg.humanId!],
|
|
);
|
|
}
|
|
};
|
|
|
|
const onLeave = (msg: { humanId?: string }) => {
|
|
if (msg.humanId) {
|
|
setPresence((prev) => prev.filter((id) => id !== msg.humanId));
|
|
}
|
|
};
|
|
|
|
const onMessage = (msg: { humanId?: string; payload?: unknown }) => {
|
|
if (msg.humanId) {
|
|
setMessages((prev) => [
|
|
...prev,
|
|
{ humanId: msg.humanId!, payload: msg.payload },
|
|
]);
|
|
}
|
|
};
|
|
|
|
client.on(channelId, "subscribed", onSubscribed);
|
|
client.on(channelId, "join", onJoin);
|
|
client.on(channelId, "leave", onLeave);
|
|
client.on(channelId, "message", onMessage);
|
|
|
|
return () => {
|
|
client.off(channelId, "subscribed", onSubscribed);
|
|
client.off(channelId, "join", onJoin);
|
|
client.off(channelId, "leave", onLeave);
|
|
client.off(channelId, "message", onMessage);
|
|
client.unsubscribe(channelId);
|
|
};
|
|
}, [client, channelId]);
|
|
|
|
const sendMessage = useCallback(
|
|
(payload: unknown) => {
|
|
if (client && channelId) {
|
|
client?.sendMessage(channelId, payload);
|
|
}
|
|
},
|
|
[client, channelId],
|
|
);
|
|
|
|
return { presence, messages, sendMessage };
|
|
}
|