Completes the safety loop. useMuteList reads the viewer's kind-10000 mute list plus optimistic local mutes (lib/localMutes.ts); useMuteUser republishes the list with the target added, records it locally and invalidates content queries. useAdminModeration carries a new mutedPubkeys set, and the shared filterAdminHidden/filterAdminHiddenMixed drop muted authors — so every list hook filters them with no per-hook changes (admin tooling with includeHidden still sees them). The flagged notice gains a Mute action.
67 lines
2.5 KiB
TypeScript
67 lines
2.5 KiB
TypeScript
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import { useNostr } from '@nostrify/react';
|
|
|
|
import { useNostrPublish } from '@/hooks/useNostrPublish';
|
|
import { useCurrentUser } from '@/hooks/useCurrentUser';
|
|
import { useToast } from '@/hooks/useToast';
|
|
import { useTranslation } from '@/contexts/language';
|
|
import { onlyVerified } from '@/lib/verifyEvents';
|
|
import { addLocalMute, getLocalMutes } from '@/lib/localMutes';
|
|
import { MUTE_LIST_KIND } from '@/hooks/useMuteList';
|
|
|
|
/**
|
|
* Mute a pubkey by republishing the viewer's NIP-51 kind-10000 mute list with
|
|
* the target added, preserving any entries already there. Optimistically records
|
|
* it locally and invalidates the content queries so the muted account drops out
|
|
* of the lists immediately.
|
|
*/
|
|
export function useMuteUser() {
|
|
const { nostr } = useNostr();
|
|
const { mutateAsync: publish } = useNostrPublish();
|
|
const { user } = useCurrentUser();
|
|
const { toast } = useToast();
|
|
const { t } = useTranslation();
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async (target: string) => {
|
|
if (!user) throw new Error(t('walletUi.trust.loginToMute'));
|
|
|
|
let existing: string[] = [];
|
|
try {
|
|
const signal = AbortSignal.any([AbortSignal.timeout(8000)]);
|
|
const events = onlyVerified(await nostr.query(
|
|
[{ kinds: [MUTE_LIST_KIND], authors: [user.pubkey], limit: 1 }],
|
|
{ signal },
|
|
));
|
|
existing = (events[0]?.tags ?? [])
|
|
.filter(([name]) => name === 'p')
|
|
.map(([, value]) => value)
|
|
.filter(Boolean);
|
|
} catch {
|
|
// Relay unreachable — publish with what we know locally rather than fail.
|
|
}
|
|
|
|
const merged = new Set([...existing, ...getLocalMutes(), target]);
|
|
await publish({
|
|
kind: MUTE_LIST_KIND,
|
|
content: '',
|
|
tags: [...merged].map((pk) => ['p', pk]),
|
|
});
|
|
},
|
|
onSuccess: (_data, target) => {
|
|
addLocalMute(target);
|
|
queryClient.invalidateQueries({ queryKey: ['mute-list', user?.pubkey] });
|
|
// Refresh content so the muted account's items drop out of the lists.
|
|
queryClient.invalidateQueries();
|
|
toast({ title: t('walletUi.trust.muted'), description: t('walletUi.trust.mutedDesc') });
|
|
},
|
|
onError: (error) => {
|
|
toast({
|
|
title: t('walletUi.trust.muteFailed'),
|
|
description: error instanceof Error ? error.message : undefined,
|
|
variant: 'destructive',
|
|
});
|
|
},
|
|
});
|
|
}
|