Add trait-based hide rules and searchable block list

This commit is contained in:
Ryan Salazar
2026-06-26 22:24:42 -04:00
parent dd075da6a4
commit 44c27c17b6
12 changed files with 1183 additions and 13 deletions

View File

@@ -0,0 +1,155 @@
'use client';
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
type ReactNode,
} from 'react';
import { useAuth } from '@/lib/auth/context';
import {
addHideFromRule,
loadHideFromRules,
removeHideFromRule,
type HideFromRule,
} from '@/lib/privacy-rules';
import {
blockProfile as blockProfileLib,
loadBlockList,
unblockAll as unblockAllLib,
unblockByUsername as unblockByUsernameLib,
unblockProfile as unblockProfileLib,
type BlockEntry,
} from '@/lib/block-list';
import type { Profile } from '@/types';
interface PrivacyContextValue {
hideRules: HideFromRule[];
blockList: BlockEntry[];
addHideRule: (rule: Omit<HideFromRule, 'id' | 'created_at' | 'label'>) => void;
removeHideRule: (id: string) => void;
blockProfile: (profile: Profile) => void;
unblockProfile: (blockedUserId: string) => void;
unblockByUsername: (username: string) => boolean;
unblockAll: () => void;
isBlocked: (userId: string) => boolean;
refresh: () => void;
}
const PrivacyContext = createContext<PrivacyContextValue | null>(null);
export function PrivacyProvider({ children }: { children: ReactNode }) {
const { user } = useAuth();
const userId = user?.id ?? null;
const [hideRules, setHideRules] = useState<HideFromRule[]>([]);
const [blockList, setBlockList] = useState<BlockEntry[]>([]);
const refresh = useCallback(() => {
setHideRules(loadHideFromRules(userId));
setBlockList(loadBlockList(userId));
}, [userId]);
useEffect(() => {
refresh();
}, [refresh]);
useEffect(() => {
const onHide = () => refresh();
const onBlock = () => refresh();
window.addEventListener('eg-hide-rules', onHide);
window.addEventListener('eg-block-list', onBlock);
return () => {
window.removeEventListener('eg-hide-rules', onHide);
window.removeEventListener('eg-block-list', onBlock);
};
}, [refresh]);
const addHideRule = useCallback(
(rule: Omit<HideFromRule, 'id' | 'created_at' | 'label'>) => {
const next = addHideFromRule(rule, userId);
setHideRules(next);
},
[userId]
);
const removeHideRule = useCallback(
(id: string) => {
const next = removeHideFromRule(id, userId);
setHideRules(next);
},
[userId]
);
const blockProfile = useCallback(
(profile: Profile) => {
const next = blockProfileLib(profile, userId);
setBlockList(next);
},
[userId]
);
const unblockProfile = useCallback(
(blockedUserId: string) => {
const next = unblockProfileLib(blockedUserId, userId);
setBlockList(next);
},
[userId]
);
const unblockByUsername = useCallback(
(username: string) => {
const { blocks, removed } = unblockByUsernameLib(username, userId);
if (removed) setBlockList(blocks);
return !!removed;
},
[userId]
);
const unblockAll = useCallback(() => {
setBlockList(unblockAllLib(userId));
}, [userId]);
const isBlocked = useCallback(
(targetId: string) => blockList.some((b) => b.id === targetId),
[blockList]
);
const value = useMemo(
() => ({
hideRules,
blockList,
addHideRule,
removeHideRule,
blockProfile,
unblockProfile,
unblockByUsername,
unblockAll,
isBlocked,
refresh,
}),
[
hideRules,
blockList,
addHideRule,
removeHideRule,
blockProfile,
unblockProfile,
unblockByUsername,
unblockAll,
isBlocked,
refresh,
]
);
return <PrivacyContext.Provider value={value}>{children}</PrivacyContext.Provider>;
}
export function usePrivacy() {
const ctx = useContext(PrivacyContext);
if (!ctx) throw new Error('usePrivacy must be used within PrivacyProvider');
return ctx;
}