v3: follows, notifications, invite-only registration, unread markers

This commit is contained in:
khannurien
2026-03-21 18:42:47 +00:00
parent 7c098e7c4c
commit 608c6bc6a8
55 changed files with 4743 additions and 884 deletions

View File

@@ -1,4 +1,4 @@
import { useCallback, useMemo } from "react";
import { useCallback, useState } from "react";
import { useNavigationType } from "react-router";
const TTL = 10 * 60 * 1000; // 10 minutes
@@ -13,7 +13,12 @@ interface FeedCacheEntry<T> {
export interface FeedCacheResult<T> {
cached: Omit<FeedCacheEntry<T>, "savedAt"> | null;
saveState: (items: T[], page: number, hasMore: boolean, scrollY: number) => void;
saveState: (
items: T[],
page: number,
hasMore: boolean,
scrollY: number,
) => void;
}
export function useFeedCache<T>(
@@ -22,9 +27,8 @@ export function useFeedCache<T>(
): FeedCacheResult<T> {
const navType = useNavigationType();
// Read ONCE on mount. Empty deps is intentional — nav type and cache are only
// relevant at the moment the component first mounts.
const cached = useMemo<Omit<FeedCacheEntry<T>, "savedAt"> | null>(() => {
// useState initializer runs exactly once on mount — correct for a cache read.
const [cached] = useState<Omit<FeedCacheEntry<T>, "savedAt"> | null>(() => {
if (navType !== "POP") return null;
try {
const raw = sessionStorage.getItem(key);
@@ -35,15 +39,20 @@ export function useFeedCache<T>(
} catch {
return null;
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
});
const saveState = useCallback(
(items: T[], page: number, hasMore: boolean, scrollY: number) => {
try {
sessionStorage.setItem(
key,
JSON.stringify({ items, page, hasMore, scrollY, savedAt: Date.now() }),
JSON.stringify({
items,
page,
hasMore,
scrollY,
savedAt: Date.now(),
}),
);
} catch {
// QuotaExceededError or SecurityError — degrade silently

4
src/hooks/useFollows.ts Normal file
View File

@@ -0,0 +1,4 @@
import { useContext } from "react";
import { FollowContext } from "../contexts/FollowContext.ts";
export const useFollows = () => useContext(FollowContext);