Something went wrong. Try again.
A Bsky-like frontend using the atprotocol natively.
Something went wrong. Try again.
4.6 kB · 161 lines
TSX
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162import { useState, useEffect, useCallback, useRef } from 'react';import type { Did, AtUri } from '../api/types';import { atprotoClient } from '../api/client';import { MuteBlockContext, type MuteBlockContextValue } from './muteBlockContext';import { getIgnoreBlocks } from '../settings/storage';
async function loadAllMutes(): Promise<Set<Did>> { const dids = new Set<Did>(); let cursor: string | undefined; do { const resp = await atprotoClient.getMutes(100, cursor); for (const m of resp.mutes) { dids.add(m.did); } cursor = resp.cursor; } while (cursor); return dids;}
async function loadAllBlocks(): Promise<Set<Did>> { const dids = new Set<Did>(); let cursor: string | undefined; do { const resp = await atprotoClient.getBlocks(100, cursor); for (const b of resp.blocks) { dids.add(b.did); } cursor = resp.cursor; } while (cursor); return dids;}
export default function MuteBlockProvider({ children }: { children: React.ReactNode }) { const [mutedDids, setMutedDids] = useState<Set<Did>>(new Set()); const [blockedDids, setBlockedDids] = useState<Set<Did>>(new Set()); const [blockedByDids, setBlockedByDids] = useState<Set<Did>>(new Set()); const [loaded, setLoaded] = useState(false); const [ignoreBlocks, setIgnoreBlocks] = useState(() => getIgnoreBlocks()); const loadingRef = useRef(false);
// Listen for storage changes so the toggle in settings takes effect immediately useEffect(() => { const handler = () => setIgnoreBlocks(getIgnoreBlocks()); window.addEventListener('storage', handler); return () => window.removeEventListener('storage', handler); }, []);
const load = useCallback(async () => { if (loadingRef.current) return; loadingRef.current = true; try { const [mutes, blocks] = await Promise.all([loadAllMutes(), loadAllBlocks()]); setMutedDids(mutes); setBlockedDids(blocks); setLoaded(true); } catch (err) { console.error('Failed to load mutes/blocks:', err); setLoaded(true); } finally { loadingRef.current = false; } }, []);
useEffect(() => { let cancelled = false; loadingRef.current = true; Promise.all([loadAllMutes(), loadAllBlocks()]) .then(([mutes, blocks]) => { if (cancelled) return; setMutedDids(mutes); setBlockedDids(blocks); setLoaded(true); loadingRef.current = false; }) .catch((err) => { if (cancelled) return; console.error('Failed to load mutes/blocks:', err); setLoaded(true); loadingRef.current = false; }); return () => { cancelled = true; }; }, []);
const isMuted = useCallback((did: Did) => mutedDids.has(did), [mutedDids]); const isBlocked = useCallback((did: Did) => blockedDids.has(did), [blockedDids]); const isBlockedBy = useCallback((did: Did) => blockedByDids.has(did), [blockedByDids]);
const addBlockedBy = useCallback((did: Did) => { setBlockedByDids((prev) => { if (prev.has(did)) return prev; const next = new Set(prev); next.add(did); return next; }); }, []);
const muteActor = useCallback(async (did: Did) => { await atprotoClient.muteActor(did); setMutedDids((prev) => { const next = new Set(prev); next.add(did); return next; }); }, []);
const unmuteActor = useCallback(async (did: Did) => { await atprotoClient.unmuteActor(did); setMutedDids((prev) => { const next = new Set(prev); next.delete(did); return next; }); }, []);
const blockActor = useCallback(async (did: Did): Promise<string> => { const result = await atprotoClient.block(did); setBlockedDids((prev) => { const next = new Set(prev); next.add(did); return next; }); return result.uri; }, []);
const unblockActor = useCallback(async (blockUri: AtUri, did: Did) => { await atprotoClient.unblock(blockUri); setBlockedDids((prev) => { const next = new Set(prev); next.delete(did); return next; }); }, []);
const reload = useCallback(async () => { loadingRef.current = false; await load(); }, [load]);
const value: MuteBlockContextValue = { mutedDids, blockedDids, blockedByDids, loaded, ignoreBlocks, isMuted, isBlocked, isBlockedBy, addBlockedBy, muteActor, unmuteActor, blockActor, unblockActor, reload, };
return ( <MuteBlockContext.Provider value={value}> {children} </MuteBlockContext.Provider> );}