add user DB and cache nip-05 verifications #65

Merged
verbiricha merged 10 commits from caching into main 2023-01-15 12:52:52 +00:00
6 changed files with 38 additions and 13 deletions
Showing only changes of commit 6c3ed97b34 - Show all commits

View File

@ -36,16 +36,20 @@ export function useIsVerified(nip05, pubkey) {
) )
const isVerified = isSuccess && data === pubkey const isVerified = isSuccess && data === pubkey
const cantVerify = isSuccess && data !== pubkey const cantVerify = isSuccess && data !== pubkey
return { name, domain: domain?.toLowerCase(), isVerified, couldNotVerify: isError || cantVerify } return { isVerified, couldNotVerify: isError || cantVerify }
} }
const Nip05 = ({ name, domain, isVerified, couldNotVerify }) => { const Nip05 = ({ nip05, pubkey, defaultUsername = '' }) => {
const [name, domain] = nip05 ? nip05.split('@') : []
const isDefaultUser = name === '_' const isDefaultUser = name === '_'
const { isVerified, couldNotVerify } = useIsVerified(nip05, pubkey)
return ( return (
<div className="flex nip05" onClick={(ev) => ev.stopPropagation()}> <div className="flex nip05" onClick={(ev) => ev.stopPropagation()}>
{!isDefaultUser && <div className="nick">{name}</div>} <div className="nick">
<div className={`domain ${isVerified ? 'text-gradient' : ''}`} data-domain={isVerified ? domain : ''}> {isDefaultUser ? defaultUsername : name}
</div>
<div className={`domain text-gradient`} data-domain={domain.toLowerCase()}>
{domain} {domain}
</div> </div>
<span className="badge"> <span className="badge">

View File

@ -9,6 +9,7 @@ import Text from "./Text";
import { eventLink, hexToBech32 } from "../Util"; import { eventLink, hexToBech32 } from "../Util";
import NoteFooter from "./NoteFooter"; import NoteFooter from "./NoteFooter";
import NoteTime from "./NoteTime"; import NoteTime from "./NoteTime";
import Nip05 from "./Nip05";
export default function Note(props) { export default function Note(props) {
const navigate = useNavigate(); const navigate = useNavigate();

View File

@ -18,3 +18,13 @@
.pfp a:hover { .pfp a:hover {
text-decoration: underline; text-decoration: underline;
} }
.pfp .nip05 {
font-size: 14px;
margin: 0;
}
.pfp .profile-name {
display: flex;
flex-direction: row;
}

View File

@ -6,6 +6,7 @@ import { Link, useNavigate } from "react-router-dom";
import useProfile from "../feed/ProfileFeed"; import useProfile from "../feed/ProfileFeed";
import { hexToBech32, profileLink } from "../Util"; import { hexToBech32, profileLink } from "../Util";
import LazyImage from "./LazyImage"; import LazyImage from "./LazyImage";
import Nip05 from "./Nip05";
export default function ProfileImage({ pubkey, subHeader, showUsername = true, className, link }) { export default function ProfileImage({ pubkey, subHeader, showUsername = true, className, link }) {
const navigate = useNavigate(); const navigate = useNavigate();
@ -26,7 +27,17 @@ export default function ProfileImage({ pubkey, subHeader, showUsername = true, c
<div className={`pfp ${className}`}> <div className={`pfp ${className}`}>
<LazyImage src={hasImage ? user.picture : Nostrich} onClick={() => navigate(link ?? profileLink(pubkey))} /> <LazyImage src={hasImage ? user.picture : Nostrich} onClick={() => navigate(link ?? profileLink(pubkey))} />
{showUsername && (<div className="f-grow"> {showUsername && (<div className="f-grow">
<Link key={pubkey} to={link ?? profileLink(pubkey)}>{name}</Link> <div className="profile-name">
<Link key={pubkey} to={link ?? profileLink(pubkey)}>
{user?.nip05 ? (
<Nip05
nip05={user.nip05}
pubkey={user.pubkey}
defaultUsername={user.display_name || user.name}
/>
): name}
</Link>
</div>
{subHeader ? <>{subHeader}</> : null} {subHeader ? <>{subHeader}</> : null}
</div> </div>
)} )}

View File

@ -4,7 +4,7 @@ import { useLiveQuery } from "dexie-react-hooks";
import ReactTextareaAutocomplete from "@webscopeio/react-textarea-autocomplete"; import ReactTextareaAutocomplete from "@webscopeio/react-textarea-autocomplete";
// @ts-expect-error // @ts-expect-error
import Nip05, { useIsVerified } from "./Nip05"; import Nip05 from "./Nip05";
import "@webscopeio/react-textarea-autocomplete/style.css"; import "@webscopeio/react-textarea-autocomplete/style.css";
import "./Textarea.css"; import "./Textarea.css";
// @ts-expect-error // @ts-expect-error
@ -25,7 +25,6 @@ function searchUsers(query: string, users: User[]) {
} }
const UserItem = ({ pubkey, display_name, picture, nip05, ...rest }: User) => { const UserItem = ({ pubkey, display_name, picture, nip05, ...rest }: User) => {
const { isVerified, couldNotVerify, name, domain } = useIsVerified(nip05, pubkey)
return ( return (
<div key={pubkey} className="user-item"> <div key={pubkey} className="user-item">
<div className="user-picture"> <div className="user-picture">
@ -33,7 +32,7 @@ const UserItem = ({ pubkey, display_name, picture, nip05, ...rest }: User) => {
</div> </div>
<div className="user-details"> <div className="user-details">
<strong>{display_name || rest.name}</strong> <strong>{display_name || rest.name}</strong>
<Nip05 name={name} domain={domain} isVerified={isVerified} couldNotVerify={couldNotVerify} /> <Nip05 nip05={nip05} pubkey={pubkey} />
</div> </div>
</div> </div>
) )

View File

@ -13,7 +13,7 @@ import { extractLnAddress, parseId, hexToBech32 } from "../Util";
import Timeline from "../element/Timeline"; import Timeline from "../element/Timeline";
import Text from '../element/Text' import Text from '../element/Text'
import LNURLTip from "../element/LNURLTip"; import LNURLTip from "../element/LNURLTip";
import Nip05, { useIsVerified } from "../element/Nip05"; import Nip05 from "../element/Nip05";
import Copy from "../element/Copy"; import Copy from "../element/Copy";
import ProfilePreview from "../element/ProfilePreview"; import ProfilePreview from "../element/ProfilePreview";
import FollowersList from "../element/FollowersList"; import FollowersList from "../element/FollowersList";
@ -37,9 +37,9 @@ export default function ProfilePage() {
const [showLnQr, setShowLnQr] = useState(false); const [showLnQr, setShowLnQr] = useState(false);
const [tab, setTab] = useState(ProfileTab.Notes); const [tab, setTab] = useState(ProfileTab.Notes);
const about = Text({ content: user?.about }) const about = Text({ content: user?.about })
const { name, domain, isVerified, couldNotVerify } = useIsVerified(user?.nip05, user?.pubkey)
const avatarUrl = (user?.picture?.length ?? 0) === 0 ? Nostrich : user?.picture const avatarUrl = (user?.picture?.length ?? 0) === 0 ? Nostrich : user?.picture
const backgroundImage = `url(${avatarUrl})` const backgroundImage = `url(${avatarUrl})`
const domain = user?.nip05 && user.nip05.split('@')[1]
useEffect(() => { useEffect(() => {
setTab(ProfileTab.Notes); setTab(ProfileTab.Notes);
@ -50,7 +50,7 @@ export default function ProfilePage() {
<div className="name"> <div className="name">
<h2>{user?.display_name || user?.name || 'Nostrich'}</h2> <h2>{user?.display_name || user?.name || 'Nostrich'}</h2>
<Copy text={params.id} /> <Copy text={params.id} />
{user?.nip05 && <Nip05 name={name} domain={domain} isVerified={isVerified} couldNotVerify={couldNotVerify} />} {user?.nip05 && <Nip05 nip05={user.nip05} pubkey={user.pubkey} />}
</div> </div>
) )
} }
@ -103,7 +103,7 @@ export default function ProfilePage() {
function avatar() { function avatar() {
return ( return (
<div className="avatar-wrapper"> <div className="avatar-wrapper">
<div style={{ '--img-url': backgroundImage }} className="avatar" data-domain={isVerified ? domain : ''}> <div style={{ '--img-url': backgroundImage }} className="avatar" data-domain={domain?.toLowerCase()}>
</div> </div>
</div> </div>
) )