terry-wallet-login #15

Merged
terry merged 95 commits from terry-wallet-login into terry-staging 2026-06-05 16:32:43 +00:00
2 changed files with 43 additions and 7 deletions
Showing only changes of commit abfd92b16a - Show all commits

View File

@@ -23,6 +23,7 @@ type FavoriteStatus = "unknown" | "favorited" | "notFavorited";
type FavoritesContextValue = { type FavoritesContextValue = {
favoriteIds: Set<string>; favoriteIds: Set<string>;
pendingIds: Set<string>; pendingIds: Set<string>;
mutationVersion: number;
statusFor: (resourceId: string) => FavoriteStatus; statusFor: (resourceId: string) => FavoriteStatus;
ensureFavoriteIds: (resourceIds: string[]) => Promise<void>; ensureFavoriteIds: (resourceIds: string[]) => Promise<void>;
toggleFavorite: (resourceId: string) => Promise<boolean | null>; toggleFavorite: (resourceId: string) => Promise<boolean | null>;
@@ -45,6 +46,7 @@ export function FavoritesProvider({ children }: { children: ReactNode }) {
const [favoriteIds, setFavoriteIds] = useState<Set<string>>(() => new Set()); const [favoriteIds, setFavoriteIds] = useState<Set<string>>(() => new Set());
const [knownIds, setKnownIds] = useState<Set<string>>(() => new Set()); const [knownIds, setKnownIds] = useState<Set<string>>(() => new Set());
const [pendingIds, setPendingIds] = useState<Set<string>>(() => new Set()); const [pendingIds, setPendingIds] = useState<Set<string>>(() => new Set());
const [mutationVersion, setMutationVersion] = useState(0);
const pendingAfterLoginRef = useRef<string | null>(null); const pendingAfterLoginRef = useRef<string | null>(null);
const lastAddressRef = useRef<string | null>(null); const lastAddressRef = useRef<string | null>(null);
const knownIdsRef = useRef<Set<string>>(new Set()); const knownIdsRef = useRef<Set<string>>(new Set());
@@ -170,6 +172,7 @@ export function FavoritesProvider({ children }: { children: ReactNode }) {
showToast( showToast(
currentlyFavorite ? t("favoriteRemoved") : t("favoriteAdded"), currentlyFavorite ? t("favoriteRemoved") : t("favoriteAdded"),
); );
setMutationVersion((value) => value + 1);
return nextFavorited; return nextFavorited;
} catch (error) { } catch (error) {
markFavorite(resourceId, currentlyFavorite); markFavorite(resourceId, currentlyFavorite);
@@ -221,6 +224,7 @@ export function FavoritesProvider({ children }: { children: ReactNode }) {
() => ({ () => ({
favoriteIds, favoriteIds,
pendingIds, pendingIds,
mutationVersion,
statusFor, statusFor,
ensureFavoriteIds, ensureFavoriteIds,
toggleFavorite, toggleFavorite,
@@ -230,6 +234,7 @@ export function FavoritesProvider({ children }: { children: ReactNode }) {
ensureFavoriteIds, ensureFavoriteIds,
favoriteIds, favoriteIds,
markFavorite, markFavorite,
mutationVersion,
pendingIds, pendingIds,
statusFor, statusFor,
toggleFavorite, toggleFavorite,

View File

@@ -12,6 +12,17 @@ import { useWallet } from "../../wallet/WalletProvider";
const pageSize = 50; const pageSize = 50;
type FavoritePosts = Awaited<ReturnType<typeof listFavorites>>["items"];
type FavoriteListCache = {
address: string;
lang: Lang;
mutationVersion: number;
posts: FavoritePosts;
};
let favoriteListCache: FavoriteListCache | null = null;
function useCategories(lang: Lang): Category[] { function useCategories(lang: Lang): Category[] {
const [categories, setCategories] = useState<Category[]>(() => { const [categories, setCategories] = useState<Category[]>(() => {
const cached = readJSONCache<Category[]>( const cached = readJSONCache<Category[]>(
@@ -43,11 +54,9 @@ function useCategories(lang: Lang): Category[] {
export default function Favorites() { export default function Favorites() {
const { lang, t } = useI18n(); const { lang, t } = useI18n();
const wallet = useWallet(); const wallet = useWallet();
const { markFavorite } = useFavorites(); const { markFavorite, mutationVersion } = useFavorites();
const categories = useCategories(lang); const categories = useCategories(lang);
const [posts, setPosts] = useState< const [posts, setPosts] = useState<FavoritePosts>([]);
Awaited<ReturnType<typeof listFavorites>>["items"]
>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [loaded, setLoaded] = useState(false); const [loaded, setLoaded] = useState(false);
const [error, setError] = useState(""); const [error, setError] = useState("");
@@ -56,7 +65,7 @@ export default function Favorites() {
useSetPageTitle(t("favorites")); useSetPageTitle(t("favorites"));
useEffect(() => { useEffect(() => {
if (!wallet.token || wallet.status !== "loggedIn") { if (!wallet.token || wallet.status !== "loggedIn" || !wallet.address) {
setPosts([]); setPosts([]);
setLoading(false); setLoading(false);
setLoaded(false); setLoaded(false);
@@ -64,18 +73,40 @@ export default function Favorites() {
return; return;
} }
const walletAddress = wallet.address;
const walletToken = wallet.token;
if (
reloadKey === 0 &&
favoriteListCache?.address === walletAddress &&
favoriteListCache.lang === lang &&
favoriteListCache.mutationVersion === mutationVersion
) {
setPosts(favoriteListCache.posts);
setLoading(false);
setLoaded(true);
setError("");
return;
}
let cancelled = false; let cancelled = false;
setLoading(true); setLoading(true);
setLoaded(false); setLoaded(false);
setError(""); setError("");
listFavorites(wallet.token, { listFavorites(walletToken, {
limit: pageSize, limit: pageSize,
includeUnavailable: true, includeUnavailable: true,
}) })
.then((data) => { .then((data) => {
if (cancelled) return; if (cancelled) return;
const items = itemsOrEmpty(data.items); const items = itemsOrEmpty(data.items);
favoriteListCache = {
address: walletAddress,
lang,
mutationVersion,
posts: items,
};
setPosts(items); setPosts(items);
items.forEach((post) => markFavorite(post.id, true)); items.forEach((post) => markFavorite(post.id, true));
setLoaded(true); setLoaded(true);
@@ -97,7 +128,7 @@ export default function Favorites() {
return () => { return () => {
cancelled = true; cancelled = true;
}; };
}, [markFavorite, reloadKey, t, wallet]); }, [lang, markFavorite, mutationVersion, reloadKey, t, wallet]);
if (wallet.status === "loading") { if (wallet.status === "loading") {
return ( return (