mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
129 lines
3.5 KiB
TypeScript
129 lines
3.5 KiB
TypeScript
import * as React from "react";
|
|
import { useQueryClient } from "@tanstack/react-query";
|
|
|
|
import { remindersQueryKey } from "@/features/reminders/hooks";
|
|
import { relayClient } from "@/shared/api/relayClient";
|
|
import {
|
|
KIND_APPROVAL_REQUEST,
|
|
KIND_EVENT_REMINDER,
|
|
KIND_REMINDER,
|
|
} from "@/shared/constants/kinds";
|
|
|
|
const HOME_FEED_ACTION_KINDS = [KIND_APPROVAL_REQUEST, KIND_REMINDER] as const;
|
|
const LIVE_HOME_FEED_RETRY_BASE_MS = 1_000;
|
|
const LIVE_HOME_FEED_RETRY_MAX_MS = 30_000;
|
|
|
|
export function useLiveHomeFeedActions(
|
|
pubkey: string | undefined,
|
|
onHomeFeedEvent: () => void,
|
|
) {
|
|
const queryClient = useQueryClient();
|
|
const handleLiveHomeFeedEvent = React.useEffectEvent(() => {
|
|
onHomeFeedEvent();
|
|
});
|
|
const handleLiveReminderEvent = React.useEffectEvent(
|
|
(normalizedPubkey: string) => {
|
|
onHomeFeedEvent();
|
|
void queryClient.invalidateQueries({
|
|
queryKey: remindersQueryKey(normalizedPubkey),
|
|
});
|
|
},
|
|
);
|
|
|
|
React.useEffect(() => {
|
|
const normalizedPubkey = pubkey?.trim().toLowerCase() ?? "";
|
|
if (!normalizedPubkey) {
|
|
return;
|
|
}
|
|
|
|
let isCancelled = false;
|
|
let disposers: Array<() => Promise<void>> = [];
|
|
let retryTimer: ReturnType<typeof globalThis.setTimeout> | null = null;
|
|
let retryAttempt = 0;
|
|
const since = Math.floor(Date.now() / 1_000);
|
|
|
|
const disposeAll = (currentDisposers: Array<() => Promise<void>>) => {
|
|
void Promise.allSettled(currentDisposers.map((dispose) => dispose()));
|
|
};
|
|
const scheduleRetry = () => {
|
|
if (isCancelled) {
|
|
return;
|
|
}
|
|
|
|
const delay = Math.min(
|
|
LIVE_HOME_FEED_RETRY_MAX_MS,
|
|
LIVE_HOME_FEED_RETRY_BASE_MS * 2 ** Math.min(retryAttempt, 5),
|
|
);
|
|
retryAttempt += 1;
|
|
retryTimer = globalThis.setTimeout(startSubscriptions, delay);
|
|
};
|
|
const startSubscriptions = () => {
|
|
if (isCancelled) {
|
|
return;
|
|
}
|
|
|
|
void Promise.allSettled([
|
|
relayClient.subscribeLive(
|
|
{
|
|
kinds: [...HOME_FEED_ACTION_KINDS],
|
|
"#p": [normalizedPubkey],
|
|
limit: 50,
|
|
since,
|
|
},
|
|
handleLiveHomeFeedEvent,
|
|
),
|
|
relayClient.subscribeLive(
|
|
{
|
|
authors: [normalizedPubkey],
|
|
kinds: [KIND_EVENT_REMINDER],
|
|
limit: 50,
|
|
since,
|
|
},
|
|
() => {
|
|
handleLiveReminderEvent(normalizedPubkey);
|
|
},
|
|
),
|
|
]).then((results) => {
|
|
const nextDisposers = results.flatMap((result) =>
|
|
result.status === "fulfilled" ? [result.value] : [],
|
|
);
|
|
const rejectedResults = results.filter(
|
|
(result) => result.status === "rejected",
|
|
);
|
|
for (const result of rejectedResults) {
|
|
console.error(
|
|
"Failed to subscribe to live home feed actions; retrying",
|
|
result.reason,
|
|
);
|
|
}
|
|
|
|
if (isCancelled) {
|
|
disposeAll(nextDisposers);
|
|
return;
|
|
}
|
|
|
|
if (rejectedResults.length > 0 || nextDisposers.length === 0) {
|
|
disposeAll(nextDisposers);
|
|
scheduleRetry();
|
|
return;
|
|
}
|
|
|
|
retryAttempt = 0;
|
|
disposers = nextDisposers;
|
|
});
|
|
};
|
|
|
|
startSubscriptions();
|
|
|
|
return () => {
|
|
isCancelled = true;
|
|
if (retryTimer !== null) {
|
|
globalThis.clearTimeout(retryTimer);
|
|
}
|
|
const currentDisposers = disposers;
|
|
disposers = [];
|
|
disposeAll(currentDisposers);
|
|
};
|
|
}, [pubkey]);
|
|
}
|