mirror of
https://github.com/hoornet/vega.git
synced 2026-05-10 22:29:11 -07:00
Add NIP-05 badges, hashtag pages, keyword muting, suggestion dismissal, notification poller
- Article cover: aspect-video replaces max-h-72 for consistent 16:9 - NIP-05 verification badge on note cards with 1-hour TTL cache - Dedicated hashtag feed pages (clicking #tag opens live feed, not search) - Keyword muting: word-boundary matching, applied across all feed views - Follow suggestion dismissal: persistent "don't suggest again" per person - Background notification poller (60s): mentions, zaps, new followers - All notification types independently toggleable in settings - Centralized notification firing (removed inline store notifications)
This commit is contained in:
51
src/hooks/useNip05Verified.ts
Normal file
51
src/hooks/useNip05Verified.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
type VerifyStatus = "valid" | "invalid";
|
||||
|
||||
const cache = new Map<string, { status: VerifyStatus; checkedAt: number }>();
|
||||
const TTL = 3600000; // 1 hour
|
||||
|
||||
async function verifyNip05(pubkey: string, nip05: string): Promise<VerifyStatus> {
|
||||
const parts = nip05.split("@");
|
||||
if (parts.length !== 2) return "invalid";
|
||||
const [name, domain] = parts;
|
||||
try {
|
||||
const res = await fetch(`https://${domain}/.well-known/nostr.json?name=${encodeURIComponent(name)}`);
|
||||
if (!res.ok) return "invalid";
|
||||
const json = await res.json();
|
||||
const resolved = json?.names?.[name];
|
||||
return resolved === pubkey ? "valid" : "invalid";
|
||||
} catch {
|
||||
return "invalid";
|
||||
}
|
||||
}
|
||||
|
||||
export function useNip05Verified(pubkey: string, nip05: string | undefined): "valid" | "invalid" | "checking" | null {
|
||||
const [status, setStatus] = useState<"valid" | "invalid" | "checking" | null>(() => {
|
||||
if (!nip05) return null;
|
||||
const cached = cache.get(pubkey);
|
||||
if (cached && Date.now() - cached.checkedAt < TTL) return cached.status;
|
||||
return "checking";
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!nip05) { setStatus(null); return; }
|
||||
|
||||
const cached = cache.get(pubkey);
|
||||
if (cached && Date.now() - cached.checkedAt < TTL) {
|
||||
setStatus(cached.status);
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
setStatus("checking");
|
||||
verifyNip05(pubkey, nip05).then((result) => {
|
||||
if (cancelled) return;
|
||||
cache.set(pubkey, { status: result, checkedAt: Date.now() });
|
||||
setStatus(result);
|
||||
});
|
||||
return () => { cancelled = true; };
|
||||
}, [pubkey, nip05]);
|
||||
|
||||
return status;
|
||||
}
|
||||
Reference in New Issue
Block a user