2023-02-07 20:04:50 +00:00
|
|
|
import "./Nip05.css";
|
2023-02-10 11:12:11 +00:00
|
|
|
import { useQuery } from "react-query";
|
2023-03-02 17:47:02 +00:00
|
|
|
import Icon from "Icons/Icon";
|
2023-02-11 20:05:46 +00:00
|
|
|
import { HexKey } from "@snort/nostr";
|
2023-01-16 17:48:25 +00:00
|
|
|
|
|
|
|
interface NostrJson {
|
2023-02-07 20:04:50 +00:00
|
|
|
names: Record<string, string>;
|
2023-01-16 17:48:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async function fetchNip05Pubkey(name: string, domain: string) {
|
|
|
|
if (!name || !domain) {
|
|
|
|
return undefined;
|
|
|
|
}
|
2023-01-16 23:20:33 +00:00
|
|
|
try {
|
2023-02-09 12:26:54 +00:00
|
|
|
const res = await fetch(`https://${domain}/.well-known/nostr.json?name=${encodeURIComponent(name)}`);
|
2023-01-16 23:20:33 +00:00
|
|
|
const data: NostrJson = await res.json();
|
2023-02-09 12:26:54 +00:00
|
|
|
const match = Object.keys(data.names).find(n => {
|
2023-01-16 23:20:33 +00:00
|
|
|
return n.toLowerCase() === name.toLowerCase();
|
|
|
|
});
|
|
|
|
return match ? data.names[match] : undefined;
|
|
|
|
} catch (error) {
|
2023-02-07 20:04:50 +00:00
|
|
|
return undefined;
|
2023-01-16 23:20:33 +00:00
|
|
|
}
|
2023-01-16 17:48:25 +00:00
|
|
|
}
|
|
|
|
|
2023-02-07 20:04:50 +00:00
|
|
|
const VERIFICATION_CACHE_TIME = 24 * 60 * 60 * 1000;
|
|
|
|
const VERIFICATION_STALE_TIMEOUT = 10 * 60 * 1000;
|
2023-01-16 17:48:25 +00:00
|
|
|
|
2023-02-09 22:22:16 +00:00
|
|
|
export function useIsVerified(pubkey: HexKey, nip05?: string, bypassCheck?: boolean) {
|
2023-02-07 20:04:50 +00:00
|
|
|
const [name, domain] = nip05 ? nip05.split("@") : [];
|
2023-02-09 22:22:16 +00:00
|
|
|
const { isError, isSuccess, data } = useQuery(
|
|
|
|
["nip05", nip05],
|
|
|
|
() => (bypassCheck ? Promise.resolve(pubkey) : fetchNip05Pubkey(name, domain)),
|
|
|
|
{
|
|
|
|
retry: false,
|
|
|
|
retryOnMount: false,
|
|
|
|
cacheTime: VERIFICATION_CACHE_TIME,
|
|
|
|
staleTime: VERIFICATION_STALE_TIMEOUT,
|
|
|
|
}
|
|
|
|
);
|
2023-02-07 20:04:50 +00:00
|
|
|
const isVerified = isSuccess && data === pubkey;
|
|
|
|
const cantVerify = isSuccess && data !== pubkey;
|
|
|
|
return { isVerified, couldNotVerify: isError || cantVerify };
|
2023-01-16 17:48:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface Nip05Params {
|
2023-02-07 20:04:50 +00:00
|
|
|
nip05?: string;
|
|
|
|
pubkey: HexKey;
|
2023-02-09 22:22:16 +00:00
|
|
|
verifyNip?: boolean;
|
2023-01-16 17:48:25 +00:00
|
|
|
}
|
|
|
|
|
2023-02-09 22:22:16 +00:00
|
|
|
const Nip05 = ({ nip05, pubkey, verifyNip = true }: Nip05Params) => {
|
|
|
|
const [name, domain] = nip05 ? nip05.split("@") : [];
|
2023-02-07 20:04:50 +00:00
|
|
|
const isDefaultUser = name === "_";
|
2023-02-09 22:22:16 +00:00
|
|
|
const { isVerified, couldNotVerify } = useIsVerified(pubkey, nip05, !verifyNip);
|
2023-01-16 17:48:25 +00:00
|
|
|
|
|
|
|
return (
|
2023-02-09 12:26:54 +00:00
|
|
|
<div className={`flex nip05${couldNotVerify ? " failed" : ""}`} onClick={ev => ev.stopPropagation()}>
|
2023-02-12 12:31:48 +00:00
|
|
|
{!isDefaultUser && isVerified && <span className="nick">{`${name}@`}</span>}
|
2023-02-10 11:12:11 +00:00
|
|
|
{isVerified && (
|
|
|
|
<>
|
2023-02-14 20:58:47 +00:00
|
|
|
<span className="domain" data-domain={domain?.toLowerCase()}>
|
2023-02-10 11:12:11 +00:00
|
|
|
{domain}
|
|
|
|
</span>
|
2023-03-02 17:47:02 +00:00
|
|
|
<Icon name="badge" className="badge" />
|
2023-02-10 11:12:11 +00:00
|
|
|
</>
|
|
|
|
)}
|
2023-01-16 17:48:25 +00:00
|
|
|
</div>
|
2023-02-07 20:04:50 +00:00
|
|
|
);
|
|
|
|
};
|
2023-01-16 17:48:25 +00:00
|
|
|
|
2023-02-07 20:04:50 +00:00
|
|
|
export default Nip05;
|