diff --git a/src/Element/Collapsed.tsx b/src/Element/Collapsed.tsx index e3679d49..c96202d7 100644 --- a/src/Element/Collapsed.tsx +++ b/src/Element/Collapsed.tsx @@ -1,5 +1,6 @@ -import { ReactNode } from "react"; +import { useState, ReactNode } from "react"; +import ChevronDown from "Icons/ChevronDown"; import ShowMore from "Element/ShowMore"; interface CollapsedProps { @@ -36,4 +37,26 @@ export const CollapsedIcon = ({ icon, children, collapsed }: CollapsedIconProps) ); }; +interface CollapsedSectionProps { + title: ReactNode; + children: ReactNode; +} + +export const CollapsedSection = ({ title, children }: CollapsedSectionProps) => { + const [collapsed, setCollapsed] = useState(true); + const icon = ( +
setCollapsed(!collapsed)}> + +
+ ); + return ( +
+

setCollapsed(!collapsed)}>{title}

+ + {children} + +
+ ); +}; + export default Collapsed; diff --git a/src/Element/Nip05.css b/src/Element/Nip05.css index ce2b0639..d7bc21ab 100644 --- a/src/Element/Nip05.css +++ b/src/Element/Nip05.css @@ -5,10 +5,6 @@ font-weight: normal; } -.nip05.failed { - text-decoration: line-through; -} - .nip05 .domain { color: var(--font-secondary-color); background-color: var(--font-secondary-color); diff --git a/src/Element/Nip05.tsx b/src/Element/Nip05.tsx index 0feaaf12..d3b967ce 100644 --- a/src/Element/Nip05.tsx +++ b/src/Element/Nip05.tsx @@ -29,14 +29,18 @@ async function fetchNip05Pubkey(name: string, domain: string) { const VERIFICATION_CACHE_TIME = 24 * 60 * 60 * 1000; const VERIFICATION_STALE_TIMEOUT = 10 * 60 * 1000; -export function useIsVerified(pubkey: HexKey, nip05?: string) { +export function useIsVerified(pubkey: HexKey, nip05?: string, bypassCheck?: boolean) { const [name, domain] = nip05 ? nip05.split("@") : []; - const { isError, isSuccess, data } = useQuery(["nip05", nip05], () => fetchNip05Pubkey(name, domain), { - retry: false, - retryOnMount: false, - cacheTime: VERIFICATION_CACHE_TIME, - staleTime: VERIFICATION_STALE_TIMEOUT, - }); + 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, + } + ); const isVerified = isSuccess && data === pubkey; const cantVerify = isSuccess && data !== pubkey; return { isVerified, couldNotVerify: isError || cantVerify }; @@ -45,12 +49,13 @@ export function useIsVerified(pubkey: HexKey, nip05?: string) { export interface Nip05Params { nip05?: string; pubkey: HexKey; + verifyNip?: boolean; } -const Nip05 = (props: Nip05Params) => { - const [name, domain] = props.nip05 ? props.nip05.split("@") : []; +const Nip05 = ({ nip05, pubkey, verifyNip = true }: Nip05Params) => { + const [name, domain] = nip05 ? nip05.split("@") : []; const isDefaultUser = name === "_"; - const { isVerified, couldNotVerify } = useIsVerified(props.pubkey, props.nip05); + const { isVerified, couldNotVerify } = useIsVerified(pubkey, nip05, !verifyNip); return (
ev.stopPropagation()}> diff --git a/src/Element/Nip5Service.tsx b/src/Element/Nip5Service.tsx index b5724054..fc77f083 100644 --- a/src/Element/Nip5Service.tsx +++ b/src/Element/Nip5Service.tsx @@ -1,7 +1,10 @@ -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useState, ChangeEvent } from "react"; import { useIntl, FormattedMessage } from "react-intl"; import { useSelector } from "react-redux"; import { useNavigate } from "react-router-dom"; + +import { unwrap } from "Util"; +import { formatShort } from "Number"; import { ServiceProvider, ServiceConfig, @@ -29,11 +32,14 @@ type Nip05ServiceProps = { link: string; supportLink: string; helpText?: boolean; + autoUpdate?: boolean; + onChange?(h: string): void; + onSuccess?(h: string): void; }; export default function Nip5Service(props: Nip05ServiceProps) { const navigate = useNavigate(); - const { helpText = true } = props; + const { helpText = true, autoUpdate = true } = props; const { formatMessage } = useIntl(); const pubkey = useSelector((s: RootState) => s.login.publicKey); const user = useUserProfile(pubkey); @@ -48,6 +54,22 @@ export default function Nip5Service(props: Nip05ServiceProps) { const [showInvoice, setShowInvoice] = useState(false); const [registerStatus, setRegisterStatus] = useState(); + const onHandleChange = (e: ChangeEvent) => { + const h = e.target.value.toLowerCase(); + setHandle(h); + if (props.onChange) { + props.onChange(`${h}@${domain}`); + } + }; + + const onDomainChange = (e: ChangeEvent) => { + const d = e.target.value; + setDomain(d); + if (props.onChange) { + props.onChange(`${handle}@${d}`); + } + }; + const domainConfig = useMemo(() => serviceConfig?.domains.find(a => a.name === domain), [domain, serviceConfig]); useEffect(() => { @@ -113,6 +135,9 @@ export default function Nip5Service(props: Nip05ServiceProps) { setRegisterStatus(status); setRegisterResponse(undefined); setError(undefined); + if (autoUpdate) { + updateProfile(handle, domain); + } } } }, 2_000); @@ -151,12 +176,16 @@ export default function Nip5Service(props: Nip05ServiceProps) { async function updateProfile(handle: string, domain: string) { if (user) { + const nip05 = `${handle}@${domain}`; const newProfile = { ...user, - nip05: `${handle}@${domain}`, + nip05, } as UserMetadata; const ev = await publisher.metadata(newProfile); publisher.broadcast(ev); + if (props.onSuccess) { + props.onSuccess(nip05); + } if (helpText) { navigate("/settings"); } @@ -185,14 +214,9 @@ export default function Nip5Service(props: Nip05ServiceProps) { {error && {error.error}} {!registerStatus && (
- setHandle(e.target.value.toLowerCase())} - /> +  @  - {serviceConfig?.domains.map(a => ( ))} @@ -202,7 +226,10 @@ export default function Nip5Service(props: Nip05ServiceProps) { {availabilityResponse?.available && !registerStatus && (
- +
{availabilityResponse.quote?.data.type}
@@ -256,12 +283,16 @@ export default function Nip5Service(props: Nip05ServiceProps) {

-

- -

- updateProfile(handle, domain)}> - - + {!autoUpdate && ( + <> +

+ +

+ updateProfile(handle, domain)}> + + + + )}
)} diff --git a/src/Element/ProfileImage.tsx b/src/Element/ProfileImage.tsx index f8bf66bc..ba1c8377 100644 --- a/src/Element/ProfileImage.tsx +++ b/src/Element/ProfileImage.tsx @@ -15,12 +15,22 @@ export interface ProfileImageProps { showUsername?: boolean; className?: string; link?: string; + defaultNip?: string; + verifyNip?: boolean; } -export default function ProfileImage({ pubkey, subHeader, showUsername = true, className, link }: ProfileImageProps) { +export default function ProfileImage({ + pubkey, + subHeader, + showUsername = true, + className, + link, + defaultNip, + verifyNip, +}: ProfileImageProps) { const navigate = useNavigate(); const user = useUserProfile(pubkey); - const nip05 = user?.nip05; + const nip05 = defaultNip || user?.nip05; const name = useMemo(() => { return getDisplayName(user, pubkey); @@ -36,7 +46,7 @@ export default function ProfileImage({ pubkey, subHeader, showUsername = true, c
{name} - {nip05 && } + {nip05 && }
{subHeader}
diff --git a/src/Pages/new/GetVerified.tsx b/src/Pages/new/GetVerified.tsx index e53cec38..5fde1194 100644 --- a/src/Pages/new/GetVerified.tsx +++ b/src/Pages/new/GetVerified.tsx @@ -1,14 +1,22 @@ +import { useState } from "react"; import { FormattedMessage } from "react-intl"; import { useNavigate } from "react-router-dom"; +import { useSelector } from "react-redux"; import { services } from "Pages/Verification"; import Nip5Service from "Element/Nip5Service"; import ProfileImage from "Element/ProfileImage"; +import type { RootState } from "State/Store"; +import { useUserProfile } from "Feed/ProfileFeed"; import messages from "./messages"; export default function GetVerified() { const navigate = useNavigate(); + const { publicKey } = useSelector((s: RootState) => s.login); + const user = useUserProfile(publicKey); + const [isVerified, setIsVerified] = useState(true); + const [nip05, setNip05] = useState(`${user?.name || "nostrich"}@snort.social`); const onNext = async () => { navigate("/new/import"); @@ -26,7 +34,7 @@ export default function GetVerified() {
- + {user?.pubkey && }

@@ -45,28 +53,55 @@ export default function GetVerified() {

-

- -

-

- -

-
- -
-

- -

-

- -

-
- -
+ {!isVerified && ( + <> +

+ +

+

+ +

+
+ setIsVerified(true)} + /> +
+ + )} + {!isVerified && ( + <> +

+ +

+

+ +

+
+ setIsVerified(true)} + /> +
+ + )}
- + {!isVerified && ( + + )} + {isVerified && ( + + )}
); diff --git a/src/Pages/new/NewUserFlow.tsx b/src/Pages/new/NewUserFlow.tsx index c35f787a..37d67c0c 100644 --- a/src/Pages/new/NewUserFlow.tsx +++ b/src/Pages/new/NewUserFlow.tsx @@ -1,38 +1,14 @@ -import { useState, ReactNode } from "react"; import { useSelector } from "react-redux"; import { FormattedMessage } from "react-intl"; import { useNavigate } from "react-router-dom"; -import ChevronDown from "Icons/ChevronDown"; -import { CollapsedIcon } from "Element/Collapsed"; +import { CollapsedSection } from "Element/Collapsed"; import Copy from "Element/Copy"; import { RootState } from "State/Store"; import { hexToBech32 } from "Util"; import messages from "./messages"; -interface CollapsedSectionProps { - title: ReactNode; - children: ReactNode; -} - -const CollapsedSection = ({ title, children }: CollapsedSectionProps) => { - const [collapsed, setCollapsed] = useState(true); - const icon = ( -
setCollapsed(!collapsed)}> - -
- ); - return ( -
-

setCollapsed(!collapsed)}>{title}

- - {children} - -
- ); -}; - const WhatIsSnort = () => { return ( }> diff --git a/src/Pages/new/index.css b/src/Pages/new/index.css index 864a2c20..2d1f508a 100644 --- a/src/Pages/new/index.css +++ b/src/Pages/new/index.css @@ -112,27 +112,6 @@ height: 16px; } -.new-user .faq { - position: relative; -} - -.new-user .faq h3, -.new-user .faq svg { - cursor: pointer; -} - -.new-user .faq .collapse-icon { - position: absolute; - top: 0; - right: 0; - transition: transform 300ms ease-in-out; -} - -.new-user .faq .collapse-icon.flip { - transform: rotate(180deg); - transition: transform 300ms ease-in-out; -} - .new-user input { width: 100%; max-width: 568px; diff --git a/src/Pages/new/messages.js b/src/Pages/new/messages.js index fb75cbf1..237f5746 100644 --- a/src/Pages/new/messages.js +++ b/src/Pages/new/messages.js @@ -44,7 +44,7 @@ export default defineMessages({ EasierToFind: "Make your profile easier to find and share", Funding: "Fund developers and platforms providing NIP-05 verification services", NameSquatting: - "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your verification (not your account - nobody can take that away) for violating this rule.", + "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", PreviewOnSnort: "Preview on snort", GetSnortId: "Get a Snort identifier", GetSnortIdHelp: diff --git a/src/index.css b/src/index.css index 901af72a..becfc3ac 100644 --- a/src/index.css +++ b/src/index.css @@ -549,3 +549,24 @@ body.scroll-lock { button.tall { height: 40px; } + +.collapsable-section { + position: relative; +} + +.collapsable-section h3, +.collapsable-section svg { + cursor: pointer; +} + +.collapsable-section .collapse-icon { + position: absolute; + top: 0; + right: 0; + transition: transform 300ms ease-in-out; +} + +.collapsable-section .collapse-icon.flip { + transform: rotate(180deg); + transition: transform 300ms ease-in-out; +}