terry-staging #16
@@ -36,19 +36,52 @@ export function FavoritesProvider({ children }: { children: ReactNode }) {
|
||||
const [pendingIds, setPendingIds] = useState<Set<string>>(() => new Set());
|
||||
const pendingAfterLoginRef = useRef<string | null>(null);
|
||||
const lastAddressRef = useRef<string | null>(null);
|
||||
const knownIdsRef = useRef<Set<string>>(new Set());
|
||||
const inFlightIdsRef = useRef<Set<string>>(new Set());
|
||||
const queuedIdsRef = useRef<Set<string>>(new Set());
|
||||
const batchTimerRef = useRef<number | null>(null);
|
||||
const tokenRef = useRef<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
tokenRef.current = status === "loggedIn" ? token : null;
|
||||
}, [status, token]);
|
||||
|
||||
const clearFavoriteStatus = useCallback(() => {
|
||||
knownIdsRef.current = new Set();
|
||||
inFlightIdsRef.current = new Set();
|
||||
queuedIdsRef.current = new Set();
|
||||
if (batchTimerRef.current !== null) {
|
||||
window.clearTimeout(batchTimerRef.current);
|
||||
batchTimerRef.current = null;
|
||||
}
|
||||
setFavoriteIds(new Set());
|
||||
setKnownIds(new Set());
|
||||
setPendingIds(new Set());
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const nextAddress = status === "loggedIn" ? address : null;
|
||||
if (lastAddressRef.current === nextAddress) return;
|
||||
lastAddressRef.current = nextAddress;
|
||||
setFavoriteIds(new Set());
|
||||
setKnownIds(new Set());
|
||||
setPendingIds(new Set());
|
||||
clearFavoriteStatus();
|
||||
if (!nextAddress) pendingAfterLoginRef.current = null;
|
||||
}, [address, status]);
|
||||
}, [address, clearFavoriteStatus, status]);
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
if (batchTimerRef.current !== null) {
|
||||
window.clearTimeout(batchTimerRef.current);
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const markFavorite = useCallback((resourceId: string, favorited: boolean) => {
|
||||
setKnownIds((prev) => new Set(prev).add(resourceId));
|
||||
setKnownIds((prev) => {
|
||||
const next = new Set(prev).add(resourceId);
|
||||
knownIdsRef.current = next;
|
||||
return next;
|
||||
});
|
||||
setFavoriteIds((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (favorited) next.add(resourceId);
|
||||
@@ -57,17 +90,22 @@ export function FavoritesProvider({ children }: { children: ReactNode }) {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const ensureFavoriteIds = useCallback(
|
||||
async (resourceIds: string[]) => {
|
||||
if (!token || status !== "loggedIn") return;
|
||||
const missing = [...new Set(resourceIds)].filter(
|
||||
(id) => !knownIds.has(id),
|
||||
);
|
||||
if (missing.length === 0) return;
|
||||
const ids = await getFavoriteIds(token, missing);
|
||||
const flushFavoriteIdBatch = useCallback(async () => {
|
||||
const requestToken = tokenRef.current;
|
||||
const requestIds = Array.from(queuedIdsRef.current);
|
||||
queuedIdsRef.current.clear();
|
||||
if (!requestToken || requestIds.length === 0) {
|
||||
requestIds.forEach((id) => inFlightIdsRef.current.delete(id));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const ids = await getFavoriteIds(requestToken, requestIds);
|
||||
if (tokenRef.current !== requestToken) return;
|
||||
setKnownIds((prev) => {
|
||||
const next = new Set(prev);
|
||||
missing.forEach((id) => next.add(id));
|
||||
requestIds.forEach((id) => next.add(id));
|
||||
knownIdsRef.current = next;
|
||||
return next;
|
||||
});
|
||||
setFavoriteIds((prev) => {
|
||||
@@ -75,8 +113,35 @@ export function FavoritesProvider({ children }: { children: ReactNode }) {
|
||||
ids.forEach((id) => next.add(id));
|
||||
return next;
|
||||
});
|
||||
} finally {
|
||||
requestIds.forEach((id) => inFlightIdsRef.current.delete(id));
|
||||
if (queuedIdsRef.current.size > 0 && batchTimerRef.current === null) {
|
||||
batchTimerRef.current = window.setTimeout(() => {
|
||||
batchTimerRef.current = null;
|
||||
void flushFavoriteIdBatch();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
const ensureFavoriteIds = useCallback(
|
||||
async (resourceIds: string[]) => {
|
||||
if (!token || status !== "loggedIn") return;
|
||||
const missing = [...new Set(resourceIds)].filter(
|
||||
(id) => !knownIdsRef.current.has(id) && !inFlightIdsRef.current.has(id),
|
||||
);
|
||||
if (missing.length === 0) return;
|
||||
missing.forEach((id) => {
|
||||
queuedIdsRef.current.add(id);
|
||||
inFlightIdsRef.current.add(id);
|
||||
});
|
||||
if (batchTimerRef.current !== null) return;
|
||||
batchTimerRef.current = window.setTimeout(() => {
|
||||
batchTimerRef.current = null;
|
||||
void flushFavoriteIdBatch();
|
||||
}, 0);
|
||||
},
|
||||
[knownIds, status, token],
|
||||
[flushFavoriteIdBatch, status, token],
|
||||
);
|
||||
|
||||
const runFavoriteMutation = useCallback(
|
||||
|
||||
Reference in New Issue
Block a user