mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-03-17 21:31:43 +01:00
add identicons
This commit is contained in:
parent
8972037609
commit
42c2eeca2f
3
README.md
Normal file
3
README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# TODO
|
||||
|
||||
- Adding loading state to `useUserMetadata` so views can show loading state
|
@ -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",
|
||||
|
@ -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>
|
||||
|
28
src/components/user-avatar-link.tsx
Normal file
28
src/components/user-avatar-link.tsx
Normal 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>
|
||||
);
|
||||
};
|
28
src/components/user-avatar.tsx
Normal file
28
src/components/user-avatar.tsx
Normal 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} />;
|
||||
});
|
9
src/helpers/user-metadata.ts
Normal file
9
src/helpers/user-metadata.ts
Normal 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;
|
||||
}
|
||||
}
|
@ -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>
|
||||
</>
|
||||
);
|
||||
|
@ -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 ? (
|
||||
|
@ -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
|
||||
);
|
||||
|
10
yarn.lock
10
yarn.lock
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user