add identicons

This commit is contained in:
hzrd149 2023-02-07 17:04:18 -06:00
parent 8972037609
commit 42c2eeca2f
10 changed files with 112 additions and 34 deletions

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# TODO
- Adding loading state to `useUserMetadata` so views can show loading state

View File

@ -15,6 +15,7 @@
"@emotion/styled": "^11.10.5",
"framer-motion": "^7.10.3",
"idb": "^7.1.1",
"identicon.js": "^2.3.3",
"moment": "^2.29.4",
"noble-secp256k1": "^1.2.14",
"react": "^18.2.0",
@ -27,6 +28,7 @@
},
"devDependencies": {
"@changesets/cli": "^2.26.0",
"@types/identicon.js": "^2.3.1",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"@vitejs/plugin-react": "^3.0.0",

View File

@ -1,6 +1,5 @@
import React from "react";
import {
Avatar,
Box,
Button,
Card,
@ -19,30 +18,31 @@ import moment from "moment";
import { PostModal } from "./post-modal";
import { NostrEvent } from "../types/nostr-event";
import { useUserMetadata } from "../hooks/use-user-metadata";
import useSubject from "../hooks/use-subject";
import settings from "../services/settings";
import { UserAvatarLink } from "./user-avatar-link";
import { getUserFullName } from "../helpers/user-metadata";
export type PostProps = {
event: NostrEvent;
};
export const Post = React.memo(({ event }: PostProps) => {
const { isOpen, onClose, onOpen } = useDisclosure();
const userMetadata = useUserMetadata(event.pubkey);
const metadata = useUserMetadata(event.pubkey);
const isLong = event.content.length > 800;
const username = metadata
? getUserFullName(metadata) || event.pubkey
: event.pubkey;
return (
<Card>
<CardHeader>
<HStack spacing="4">
<Flex flex="1" gap="4" alignItems="center" flexWrap="wrap">
<Avatar src={userMetadata?.picture} />
<UserAvatarLink pubkey={event.pubkey} />
<Box>
<Heading size="sm">
<Link to={`/user/${event.pubkey}`}>
{userMetadata?.name ?? event.pubkey}
</Link>
<Link to={`/user/${event.pubkey}`}>{username}</Link>
</Heading>
<Text>{moment(event.created_at * 1000).fromNow()}</Text>
</Box>

View File

@ -0,0 +1,28 @@
import { Tooltip } from "@chakra-ui/react";
import { Link } from "react-router-dom";
import { useUserMetadata } from "../hooks/use-user-metadata";
import { UserAvatar } from "./user-avatar";
export type UserAvatarProps = {
pubkey: string;
};
export const UserAvatarLink = ({ pubkey }: UserAvatarProps) => {
const metadata = useUserMetadata(pubkey);
let label = "Loading...";
if (metadata?.display_name && metadata?.name) {
label = `${metadata.display_name} (${metadata.name})`;
} else if (metadata?.name) {
label = metadata.name;
} else {
label = pubkey;
}
return (
<Tooltip label={label}>
<Link to={`/user/${pubkey}`}>
<UserAvatar pubkey={pubkey} />
</Link>
</Tooltip>
);
};

View File

@ -0,0 +1,28 @@
import React, { useMemo } from "react";
import { Avatar } from "@chakra-ui/react";
import Identicon from "identicon.js";
import { useUserMetadata } from "../hooks/use-user-metadata";
const cache: Record<string, Identicon> = {};
function getIdenticon(pubkey: string) {
if (!cache[pubkey]) {
cache[pubkey] = new Identicon(pubkey, { format: "svg" });
}
return cache[pubkey];
}
export type UserAvatarProps = {
pubkey: string;
};
export const UserAvatar = React.memo(({ pubkey }: UserAvatarProps) => {
const metadata = useUserMetadata(pubkey);
const url = useMemo(() => {
return (
metadata?.picture ??
`data:image/svg+xml;base64,${getIdenticon(pubkey).toString()}`
);
}, [metadata]);
return <Avatar src={url} />;
});

View File

@ -0,0 +1,9 @@
import { Kind0ParsedContent } from "../types/nostr-event";
export function getUserFullName(metadata: Kind0ParsedContent) {
if (metadata?.display_name && metadata?.name) {
return `${metadata.display_name} (${metadata.name})`;
} else if (metadata?.name) {
return metadata.name;
}
}

View File

@ -1,29 +1,24 @@
import { Avatar, HStack } from "@chakra-ui/react";
import { HStack } from "@chakra-ui/react";
import { Link } from "react-router-dom";
import { useUserMetadata } from "../hooks/use-user-metadata";
const UserAvatar = ({ pubkey }: { pubkey: string }) => {
const metadata = useUserMetadata(pubkey);
return (
<Link to={`/user/${pubkey}`}>
<Avatar src={metadata?.picture} />
</Link>
);
};
import { UserAvatar } from "../components/user-avatar";
import { UserAvatarLink } from "../components/user-avatar-link";
export const HomeView = () => {
return (
<>
<HStack spacing=".5rem">
<UserAvatar pubkey="32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245" />
<UserAvatar pubkey="6b0d4c8d9dc59e110d380b0429a02891f1341a0fa2ba1b1cf83a3db4d47e3964" />
<UserAvatar pubkey="00000000827ffaa94bfea288c3dfce4422c794fbb96625b6b31e9049f729d700" />
<UserAvatar pubkey="82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2" />
<UserAvatar pubkey="85080d3bad70ccdcd7f74c29a44f55bb85cbcd3dd0cbb957da1d215bdb931204" />
<UserAvatar pubkey="e88a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411" />
<UserAvatar pubkey="8c0da4862130283ff9e67d889df264177a508974e2feb96de139804ea66d6168" />
<UserAvatar pubkey="266815e0c9210dfa324c6cba3573b14bee49da4209a9456f9484e5106cd408a5" />
<UserAvatarLink pubkey="32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245" />
<UserAvatarLink pubkey="6b0d4c8d9dc59e110d380b0429a02891f1341a0fa2ba1b1cf83a3db4d47e3964" />
<UserAvatarLink pubkey="00000000827ffaa94bfea288c3dfce4422c794fbb96625b6b31e9049f729d700" />
<UserAvatarLink pubkey="82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2" />
<UserAvatarLink pubkey="85080d3bad70ccdcd7f74c29a44f55bb85cbcd3dd0cbb957da1d215bdb931204" />
<UserAvatarLink pubkey="e88a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411" />
<UserAvatarLink pubkey="8c0da4862130283ff9e67d889df264177a508974e2feb96de139804ea66d6168" />
<UserAvatarLink pubkey="c4eabae1be3cf657bc1855ee05e69de9f059cb7a059227168b80b89761cbc4e0" />
<UserAvatarLink pubkey="e33fe65f1fde44c6dc17eeb38fdad0fceaf1cae8722084332ed1e32496291d42" />
<UserAvatarLink pubkey="c5cfda98d01f152b3493d995eed4cdb4d9e55a973925f6f9ea24769a5a21e778" />
<UserAvatarLink pubkey="3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d" />
<UserAvatarLink pubkey="266815e0c9210dfa324c6cba3573b14bee49da4209a9456f9484e5106cd408a5" />
</HStack>
</>
);

View File

@ -1,6 +1,4 @@
import React from "react";
import {
Avatar,
Box,
Heading,
HStack,
@ -10,13 +8,14 @@ import {
TabPanel,
TabPanels,
Tabs,
Text,
VStack,
} from "@chakra-ui/react";
import { useParams } from "react-router-dom";
import { UserPostsTab } from "./posts";
import { useUserMetadata } from "../../hooks/use-user-metadata";
import ReactMarkdown from "react-markdown";
import { UserAvatar } from "../../components/user-avatar";
import { getUserFullName } from "../../helpers/user-metadata";
export const UserView = () => {
const { pubkey } = useParams();
@ -26,15 +25,16 @@ export const UserView = () => {
}
const metadata = useUserMetadata(pubkey);
const label = metadata ? getUserFullName(metadata) || pubkey : pubkey;
return (
<VStack alignItems="stretch" spacing={4}>
{" "}
<HStack spacing={4}>
<Avatar src={metadata?.picture} />
<UserAvatar pubkey={pubkey} />
<Box>
<Heading>{metadata?.name ?? pubkey}</Heading>
<Text>{metadata?.display_name}</Text>
<Heading>{label}</Heading>
{/* <Text>{metadata?.name}</Text> */}
</Box>
</HStack>
{metadata?.about ? (

View File

@ -29,6 +29,9 @@ export const UserPostsTab = ({ pubkey }: { pubkey: string }) => {
return () => s.unsubscribe();
}, [sub]);
// clear events when pubkey changes
useEffect(() => setEvents({}), [pubkey]);
const timeline = Object.values(events).sort(
(a, b) => b.created_at - a.created_at
);

View File

@ -2465,6 +2465,11 @@
dependencies:
"@types/unist" "*"
"@types/identicon.js@^2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@types/identicon.js/-/identicon.js-2.3.1.tgz#bbfe440a3229d6930c12d933ebcbe8603957ea75"
integrity sha512-QyPIfllzfVTHVJ/xX5+cOKpWuX7Zv0EKQbzTCbIn6QjMOg4bn1j73Av1LIIvRqkDV+TErJuonwZg/IOl4tbPDQ==
"@types/is-ci@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/is-ci/-/is-ci-3.0.0.tgz#7e8910af6857601315592436f030aaa3ed9783c3"
@ -3616,6 +3621,11 @@ idb@^7.0.1, idb@^7.1.1:
resolved "https://registry.yarnpkg.com/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b"
integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==
identicon.js@^2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/identicon.js/-/identicon.js-2.3.3.tgz#c505b8d60ecc6ea13bbd991a33964c44c1ad60a1"
integrity sha512-/qgOkXKZ7YbeCYbawJ9uQQ3XJ3uBg9VDpvHjabCAPp6aRMhjLaFAxG90+1TxzrhKaj6AYpVGrx6UXQfQA41UEA==
ignore@^5.2.0:
version "5.2.4"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"