Settings -> Trust is now functional, not read-only. useSetTrustProvider publishes a kind-10040 designation for a custom provider (npub or 64-hex + wss relay, the five 30382:<type> rows) and can clear it to fall back to the community provider. The card validates input, prompts the signer, and invalidates the provider + assertion queries so scores refresh. Docs updated.
74 lines
2.6 KiB
TypeScript
74 lines
2.6 KiB
TypeScript
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import { nip19 } from 'nostr-tools';
|
|
|
|
import { useNostrPublish } from '@/hooks/useNostrPublish';
|
|
import { useCurrentUser } from '@/hooks/useCurrentUser';
|
|
import { useToast } from '@/hooks/useToast';
|
|
import { useTranslation } from '@/contexts/language';
|
|
import { NIP85_PROVIDER_LIST_KIND, NIP85_USER_ASSERTION_KIND, type TrustProvider } from '@/lib/nip85';
|
|
|
|
/** The assertion types we designate (the normative kind-30382 set). */
|
|
const ASSERTION_TYPES = ['rank', 'followers', 'reporters', 'muters', 'hops'] as const;
|
|
|
|
/** Accept an npub or a 64-char hex pubkey; return hex or null. */
|
|
export function normalizeProviderPubkey(input: string): string | null {
|
|
const value = input.trim();
|
|
if (/^[0-9a-f]{64}$/i.test(value)) return value.toLowerCase();
|
|
if (value.startsWith('npub1')) {
|
|
try {
|
|
const decoded = nip19.decode(value);
|
|
if (decoded.type === 'npub') return decoded.data;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/** A relay hint must be a websocket URL. */
|
|
export function normalizeProviderRelay(input: string): string | null {
|
|
const value = input.trim();
|
|
if (/^wss?:\/\/.+/i.test(value)) return value;
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Publish (or clear) the viewer's NIP-85 kind-10040 provider designation.
|
|
* Passing `null` clears it, so the app falls back to the community provider.
|
|
* This is a signed action, so it prompts the signer like any other publish.
|
|
*/
|
|
export function useSetTrustProvider() {
|
|
const { mutateAsync: publish } = useNostrPublish();
|
|
const { user } = useCurrentUser();
|
|
const { toast } = useToast();
|
|
const { t } = useTranslation();
|
|
const queryClient = useQueryClient();
|
|
|
|
const mutation = useMutation({
|
|
mutationFn: async (provider: TrustProvider | null) => {
|
|
if (!user) throw new Error(t('trustUi.loggedOutProvider'));
|
|
|
|
const relay = provider?.relay;
|
|
const tags: string[][] =
|
|
provider && relay
|
|
? ASSERTION_TYPES.map((type) => [`${NIP85_USER_ASSERTION_KIND}:${type}`, provider.pubkey, relay])
|
|
: [];
|
|
|
|
await publish({ kind: NIP85_PROVIDER_LIST_KIND, content: '', tags });
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['trust-provider', user?.pubkey] });
|
|
queryClient.invalidateQueries({ queryKey: ['trust-assertions-batch', user?.pubkey ?? ''] });
|
|
toast({ title: t('trustUi.providerSaved') });
|
|
},
|
|
onError: (error) => {
|
|
toast({
|
|
title: t('trustUi.publishFailed'),
|
|
description: error instanceof Error ? error.message : undefined,
|
|
variant: 'destructive',
|
|
});
|
|
},
|
|
});
|
|
|
|
return mutation;
|
|
}
|