add date picker to notifications

make user avatars square
This commit is contained in:
hzrd149 2024-03-20 20:50:55 -05:00
parent 8d46272558
commit f9443afccc
37 changed files with 311 additions and 135 deletions

View File

@ -0,0 +1,5 @@
---
"nostrudel": minor
---
Add date picker to notifictions

View File

@ -0,0 +1,5 @@
---
"nostrudel": minor
---
Make user avatars square

View File

@ -8,7 +8,7 @@ import { aTagToAddressPointer, eTagToEventPointer } from "../../helpers/nostr/ev
import { EmbedEventPointer } from "../embed-event";
import UserAvatarLink from "../user/user-avatar-link";
import UserLink from "../user/user-link";
import { UserDnsIdentityIcon } from "../user/user-dns-identity-icon";
import UserDnsIdentity from "../user/user-dns-identity";
function EventTag({ tag }: { tag: Tag }) {
const expand = useDisclosure();
@ -62,7 +62,7 @@ function EventTag({ tag }: { tag: Tag }) {
<Box>
<UserLink pubkey={pubkey} fontWeight="bold" />
<br />
<UserDnsIdentityIcon pubkey={pubkey} />
<UserDnsIdentity pubkey={pubkey} />
</Box>
</Flex>
)}

View File

@ -5,7 +5,6 @@ import { Link as RouterLink } from "react-router-dom";
import { NostrEvent } from "../../../types/nostr-event";
import UserAvatarLink from "../../user/user-avatar-link";
import UserLink from "../../user/user-link";
import { UserDnsIdentityIcon } from "../../user/user-dns-identity-icon";
import useSubject from "../../../hooks/use-subject";
import appSettings from "../../../services/settings/app-settings";
import EventVerificationIcon from "../../common-event/event-verification-icon";
@ -36,15 +35,14 @@ export default function EmbeddedNote({ event, ...props }: Omit<CardProps, "child
<TrustProvider event={event}>
<Card as={LinkBox} {...props}>
<Flex p="2" gap="2" alignItems="center">
<UserAvatarLink pubkey={event.pubkey} size="xs" />
<UserAvatarLink pubkey={event.pubkey} size="sm" />
<UserLink pubkey={event.pubkey} fontWeight="bold" isTruncated fontSize="lg" />
<UserDnsIdentityIcon pubkey={event.pubkey} onlyIcon />
<HoverLinkOverlay as={RouterLink} to={to} onClick={handleClick} />
<Spacer />
{showSignatureVerification && <EventVerificationIcon event={event} />}
<NoteLink noteId={event.id} color="current" whiteSpace="nowrap">
<Timestamp timestamp={event.created_at} />
</NoteLink>
<HoverLinkOverlay as={RouterLink} to={to} onClick={handleClick} />
<Spacer />
{showSignatureVerification && <EventVerificationIcon event={event} />}
</Flex>
<CompactNoteContent px="2" event={event} maxLength={96} />
</Card>

View File

@ -5,7 +5,7 @@ import { getSharableEventAddress } from "../../../helpers/nip19";
import { NostrEvent } from "../../../types/nostr-event";
import UserAvatarLink from "../../user/user-avatar-link";
import UserLink from "../../user/user-link";
import { UserDnsIdentityIcon } from "../../user/user-dns-identity-icon";
import UserDnsIdentity from "../../user/user-dns-identity";
import {
embedEmoji,
embedNostrHashtags,
@ -44,7 +44,7 @@ export default function EmbeddedUnknown({ event, ...props }: Omit<CardProps, "ch
<CardHeader display="flex" gap="2" alignItems="center" p="2" pb="0" flexWrap="wrap">
<UserAvatarLink pubkey={event.pubkey} size="xs" />
<UserLink pubkey={event.pubkey} isTruncated fontWeight="bold" fontSize="md" />
<UserDnsIdentityIcon pubkey={event.pubkey} onlyIcon />
<UserDnsIdentity pubkey={event.pubkey} onlyIcon />
<Text>kind: {event.kind}</Text>
<Timestamp timestamp={event.created_at} />
<ButtonGroup ml="auto">

View File

@ -56,14 +56,15 @@ export default function AccountSwitcher() {
<Flex direction="column" gap="2">
<Box
as="button"
borderRadius="30"
borderRadius="lg"
borderWidth={1}
display="flex"
gap="2"
mb="2"
alignItems="center"
flexGrow={1}
overflow="hidden"
overflowX="hidden"
overflowY="visible"
onClick={onToggle}
>
<UserAvatar pubkey={account.pubkey} noProxy size="md" />

View File

@ -32,7 +32,6 @@ declare module "yet-another-react-lightbox" {
import { NostrEvent } from "../types/nostr-event";
import UserAvatarLink from "./user/user-avatar-link";
import UserLink from "./user/user-link";
import { UserDnsIdentityIcon } from "./user/user-dns-identity-icon";
import styled from "@emotion/styled";
import { getSharableEventAddress } from "../helpers/nip19";
@ -107,7 +106,6 @@ function EventSlideHeader({ event, ...props }: { event: NostrEvent } & Omit<Flex
<Flex gap="2" alignItems="center" p="2" {...props}>
<UserAvatarLink pubkey={event.pubkey} size={["xs", "sm"]} />
<UserLink pubkey={event.pubkey} isTruncated fontWeight="bold" fontSize="lg" />
<UserDnsIdentityIcon pubkey={event.pubkey} onlyIcon />
<Spacer />
<Button as={RouterLink} to={`/n/${encoded}`} colorScheme="primary" size="sm">
View Note

View File

@ -13,7 +13,7 @@ import { matchSorter } from "match-sorter";
import { Emoji, useContextEmojis } from "../providers/global/emoji-provider";
import { useUserSearchDirectoryContext } from "../providers/global/user-directory-provider";
import UserAvatar from "./user/user-avatar";
import { UserDnsIdentityIcon } from "./user/user-dns-identity-icon";
import UserDnsIdentity from "./user/user-dns-identity";
export type PeopleToken = { pubkey: string; names: string[] };
type Token = Emoji | PeopleToken;
@ -39,7 +39,7 @@ const Item = ({ entity }: ItemComponentProps<Token>) => {
return (
<span>
<UserAvatar pubkey={entity.pubkey} size="xs" /> {entity.names[0]}{" "}
<UserDnsIdentityIcon pubkey={entity.pubkey} onlyIcon />
<UserDnsIdentity pubkey={entity.pubkey} onlyIcon />
</span>
);
} else return null;

View File

@ -6,7 +6,7 @@ import { useRegisterIntersectionEntity } from "../../providers/local/intersectio
import { getEventUID } from "../../helpers/nostr/event";
import Timestamp from "../timestamp";
import UserLink from "../user/user-link";
import { UserDnsIdentityIcon } from "../user/user-dns-identity-icon";
import UserDnsIdentity from "../user/user-dns-identity";
import useEventReactions from "../../hooks/use-event-reactions";
import EventReactionButtons from "../event-reactions/event-reactions";
import { IconThreadButton } from "../../views/dms/components/thread-button";
@ -49,7 +49,7 @@ export default function MessageBubble({
{showHeader && (
<CardHeader px="2" pt="2" pb="0" gap="2" display="flex" alignItems="center">
<UserLink pubkey={message.pubkey} fontWeight="bold" />
<UserDnsIdentityIcon pubkey={message.pubkey} onlyIcon />
<UserDnsIdentity pubkey={message.pubkey} onlyIcon />
{actionPosition === "header" && (
<ButtonGroup size="xs" variant="ghost" ml="auto">
{actions}

View File

@ -20,7 +20,6 @@ import { Link as RouterLink } from "react-router-dom";
import NoteMenu from "../note-menu";
import UserLink from "../../user/user-link";
import { UserDnsIdentityIcon } from "../../user/user-dns-identity-icon";
import NoteZapButton from "../note-zap-button";
import { ExpandProvider } from "../../../providers/local/expanded";
import useSubject from "../../../hooks/use-subject";
@ -152,9 +151,11 @@ export function TimelineNote({
)}
<CardHeader p="2">
<Flex flex="1" gap="2" alignItems="center">
<UserAvatarLink pubkey={event.pubkey} size={["xs", "sm"]} />
<UserAvatarLink pubkey={event.pubkey} size="sm" />
<UserLink pubkey={event.pubkey} isTruncated fontWeight="bold" fontSize="lg" />
<UserDnsIdentityIcon pubkey={event.pubkey} onlyIcon />
<Link as={RouterLink} whiteSpace="nowrap" color="current" to={`/n/${getSharableEventAddress(event)}`}>
<Timestamp timestamp={event.created_at} />
</Link>
<POWIcon event={event} boxSize={5} />
<Flex grow={1} />
{showSignatureVerification && <EventVerificationIcon event={event} />}
@ -166,9 +167,6 @@ export function TimelineNote({
onClick={() => singleEventService.handleEvent(event)}
/>
)}
<Link as={RouterLink} whiteSpace="nowrap" color="current" to={`/n/${getSharableEventAddress(event)}`}>
<Timestamp timestamp={event.created_at} />
</Link>
</Flex>
<NoteCommunityMetadata event={event} />
{showReplyLine && <ReplyLine event={event} />}

View File

@ -6,7 +6,7 @@ import { Link as RouterLink } from "react-router-dom";
import { NostrEvent } from "../../../types/nostr-event";
import TimelineNote from "../../note/timeline-note";
import UserAvatar from "../../user/user-avatar";
import { UserDnsIdentityIcon } from "../../user/user-dns-identity-icon";
import UserDnsIdentity from "../../user/user-dns-identity";
import UserLink from "../../user/user-link";
import { TrustProvider } from "../../../providers/local/trust";
import { useRegisterIntersectionEntity } from "../../../providers/local/intersection-observer";
@ -41,7 +41,7 @@ function RepostEvent({ event }: { event: NostrEvent }) {
<Heading size="sm" display="inline" isTruncated whiteSpace="pre">
<UserLink pubkey={event.pubkey} />
</Heading>
<UserDnsIdentityIcon pubkey={event.pubkey} onlyIcon />
<UserDnsIdentity pubkey={event.pubkey} onlyIcon />
<Text as="span" whiteSpace="pre">
{communityCoordinate ? `Shared to` : `Shared`}
</Text>

View File

@ -1,5 +1,5 @@
import { forwardRef, memo, useMemo } from "react";
import { Avatar, AvatarProps } from "@chakra-ui/react";
import { Avatar, AvatarBadge, AvatarProps } from "@chakra-ui/react";
import { useAsync } from "react-use";
import useUserMetadata from "../../hooks/use-user-metadata";
@ -9,11 +9,19 @@ import { Kind0ParsedContent, getUserDisplayName } from "../../helpers/nostr/user
import useAppSettings from "../../hooks/use-app-settings";
import useCurrentAccount from "../../hooks/use-current-account";
import { buildImageProxyURL } from "../../helpers/image";
import UserDnsIdentityIcon, { useDnsIdentityColor } from "./user-dns-identity-icon";
import styled from "@emotion/styled";
export const UserIdenticon = memo(({ pubkey }: { pubkey: string }) => {
const { value: identicon } = useAsync(() => getIdenticon(pubkey), [pubkey]);
return identicon ? <img src={`data:image/svg+xml;base64,${identicon}`} width="100%" /> : null;
return identicon ? (
<img
src={`data:image/svg+xml;base64,${identicon}`}
width="100%"
style={{ borderRadius: "var(--chakra-radii-lg)" }}
/>
) : null;
});
const RESIZE_PROFILE_SIZE = 96;
@ -22,19 +30,54 @@ export type UserAvatarProps = Omit<MetadataAvatarProps, "pubkey" | "metadata"> &
pubkey: string;
relay?: string;
};
export const UserAvatar = forwardRef<HTMLDivElement, UserAvatarProps>(({ pubkey, noProxy, relay, ...props }, ref) => {
const metadata = useUserMetadata(pubkey, relay ? [relay] : undefined);
return <MetadataAvatar pubkey={pubkey} metadata={metadata} noProxy={noProxy} ref={ref} {...props} />;
});
export const UserAvatar = forwardRef<HTMLDivElement, UserAvatarProps>(
({ pubkey, noProxy, relay, size, ...props }, ref) => {
const metadata = useUserMetadata(pubkey, relay ? [relay] : undefined);
const color = useDnsIdentityColor(pubkey);
return (
<MetadataAvatar
pubkey={pubkey}
metadata={metadata}
noProxy={noProxy}
ref={ref}
borderColor={color}
borderStyle="none"
size={size}
{...props}
>
{size !== "xs" && (
<UserDnsIdentityIcon
pubkey={pubkey}
position="absolute"
right={-1}
bottom={-1}
bgColor="white"
borderRadius="50%"
boxSize="1em"
/>
)}
</MetadataAvatar>
);
},
);
UserAvatar.displayName = "UserAvatar";
const StyledAvatar = styled(Avatar)`
img {
border-radius: var(--chakra-radii-lg);
border-width: 0.18rem;
border-color: inherit;
border-style: solid;
}
`;
export type MetadataAvatarProps = Omit<AvatarProps, "src"> & {
metadata?: Kind0ParsedContent;
pubkey?: string;
noProxy?: boolean;
};
export const MetadataAvatar = forwardRef<HTMLDivElement, MetadataAvatarProps>(
({ pubkey, metadata, noProxy, ...props }, ref) => {
({ pubkey, metadata, noProxy, children, ...props }, ref) => {
const { imageProxy, proxyUserMedia, hideUsernames } = useAppSettings();
const account = useCurrentAccount();
const picture = useMemo(() => {
@ -53,14 +96,16 @@ export const MetadataAvatar = forwardRef<HTMLDivElement, MetadataAvatarProps>(
}, [metadata?.picture, imageProxy, proxyUserMedia, hideUsernames, account]);
return (
<Avatar
<StyledAvatar
src={picture}
icon={pubkey ? <UserIdenticon pubkey={pubkey} /> : undefined}
overflow="hidden"
// overflow="hidden"
title={getUserDisplayName(metadata, pubkey ?? "")}
ref={ref}
{...props}
/>
>
{children}
</StyledAvatar>
);
},
);

View File

@ -1,10 +1,31 @@
import { Text, Tooltip } from "@chakra-ui/react";
import { IconProps, useColorMode } from "@chakra-ui/react";
import { QuestionIcon, QuestionOutlineIcon } from "@chakra-ui/icons";
import useDnsIdentity from "../../hooks/use-dns-identity";
import useUserMetadata from "../../hooks/use-user-metadata";
import { VerificationFailed, VerificationMissing, VerifiedIcon } from "../icons";
export function UserDnsIdentityIcon({ pubkey, onlyIcon }: { pubkey: string; onlyIcon?: boolean }) {
export function useDnsIdentityColor(pubkey: string) {
const metadata = useUserMetadata(pubkey);
const identity = useDnsIdentity(metadata?.nip05);
const { colorMode } = useColorMode();
if (!metadata?.nip05) {
return colorMode === "light" ? "gray.200" : "gray.800";
}
if (identity === undefined) {
return "yellow.500";
} else if (identity === null) {
return "red.500";
} else if (pubkey === identity.pubkey) {
return "purple.500";
} else {
return "red.500";
}
}
export default function UserDnsIdentityIcon({ pubkey, ...props }: { pubkey: string } & IconProps) {
const metadata = useUserMetadata(pubkey);
const identity = useDnsIdentity(metadata?.nip05);
@ -12,26 +33,13 @@ export function UserDnsIdentityIcon({ pubkey, onlyIcon }: { pubkey: string; only
return null;
}
const renderIcon = () => {
if (identity === undefined) {
return <VerificationFailed color="yellow.500" />;
} else if (identity === null) {
return <VerificationMissing color="red.500" />;
} else if (pubkey === identity.pubkey) {
return <VerifiedIcon color="purple.500" />;
} else {
return <VerificationFailed color="red.500" />;
}
};
if (onlyIcon) {
return <Tooltip label={metadata.nip05}>{renderIcon()}</Tooltip>;
if (identity === undefined) {
return <VerificationFailed color="yellow.500" {...props} />;
} else if (identity === null) {
return <VerificationMissing color="red.500" {...props} />;
} else if (pubkey === identity.pubkey) {
return <VerifiedIcon color="purple.500" {...props} />;
} else {
return <VerificationFailed color="red.500" {...props} />;
}
return (
<Text as="span" whiteSpace="nowrap">
{metadata.nip05.startsWith("_@") ? metadata.nip05.substr(2) : metadata.nip05} {renderIcon()}
</Text>
);
}
export default UserDnsIdentityIcon;

View File

@ -0,0 +1,25 @@
import { Text, Tooltip } from "@chakra-ui/react";
import useUserMetadata from "../../hooks/use-user-metadata";
import UserDnsIdentityIcon from "./user-dns-identity-icon";
export default function UserDnsIdentity({ pubkey, onlyIcon }: { pubkey: string; onlyIcon?: boolean }) {
const metadata = useUserMetadata(pubkey);
if (!metadata?.nip05) {
return null;
}
if (onlyIcon) {
return (
<Tooltip label={metadata.nip05}>
<UserDnsIdentityIcon pubkey={pubkey} />
</Tooltip>
);
}
return (
<Text as="span" whiteSpace="nowrap">
{metadata.nip05.startsWith("_@") ? metadata.nip05.substr(2) : metadata.nip05}{" "}
<UserDnsIdentityIcon pubkey={pubkey} />
</Text>
);
}

View File

@ -25,7 +25,7 @@ import IntersectionObserverProvider from "../../../providers/local/intersection-
import UserLink from "../../../components/user/user-link";
import HoverLinkOverlay from "../../../components/hover-link-overlay";
import UserAvatar from "../../../components/user/user-avatar";
import { UserDnsIdentityIcon } from "../../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../../components/user/user-dns-identity";
import ChannelJoinButton from "./channel-join-button";
import { ExternalLinkIcon } from "../../../components/icons";
import { CHANNELS_LIST_KIND } from "../../../helpers/nostr/lists";
@ -37,7 +37,7 @@ function UserCard({ pubkey }: { pubkey: string }) {
<Card as={LinkBox} direction="row" alignItems="center" gap="2" p="2">
<UserAvatar pubkey={pubkey} size="sm" />
<HoverLinkOverlay as={UserLink} pubkey={pubkey} fontWeight="bold" />
<UserDnsIdentityIcon pubkey={pubkey} onlyIcon />
<UserDnsIdentity pubkey={pubkey} onlyIcon />
</Card>
);
}

View File

@ -23,7 +23,7 @@ import useSubject from "../../../hooks/use-subject";
import { useTimelineCurserIntersectionCallback } from "../../../hooks/use-timeline-cursor-intersection-callback";
import TimelineActionAndStatus from "../../../components/timeline-page/timeline-action-and-status";
import UserLink from "../../../components/user/user-link";
import { UserDnsIdentityIcon } from "../../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../../components/user/user-dns-identity";
import UserAvatarLink from "../../../components/user/user-avatar-link";
function UserCard({ pubkey }: { pubkey: string }) {
@ -32,7 +32,7 @@ function UserCard({ pubkey }: { pubkey: string }) {
<UserAvatarLink pubkey={pubkey} />
<Flex direction="column" flex={1} overflow="hidden">
<UserLink pubkey={pubkey} fontWeight="bold" />
<UserDnsIdentityIcon pubkey={pubkey} />
<UserDnsIdentity pubkey={pubkey} />
</Flex>
</Flex>
);

View File

@ -13,7 +13,7 @@ import useCurrentAccount from "../../hooks/use-current-account";
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
import TimelineActionAndStatus from "../../components/timeline-page/timeline-action-and-status";
import { UserDnsIdentityIcon } from "../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../components/user/user-dns-identity";
import { useDecryptionContext } from "../../providers/global/dycryption-provider";
import SendMessageForm from "./components/send-message-form";
import { groupMessages } from "../../helpers/nostr/dms";
@ -123,7 +123,7 @@ function DirectMessageChatPage({ pubkey }: { pubkey: string }) {
/>
<UserAvatar pubkey={pubkey} size="sm" />
<UserLink pubkey={pubkey} fontWeight="bold" />
<UserDnsIdentityIcon pubkey={pubkey} onlyIcon />
<UserDnsIdentity pubkey={pubkey} onlyIcon />
</Flex>
<ButtonGroup ml="auto">
{!autoDecryptDMs && (

View File

@ -21,7 +21,7 @@ import UserName from "../../components/user/user-name";
import { useDecryptionContainer } from "../../providers/global/dycryption-provider";
import { NostrEvent } from "../../types/nostr-event";
import { CheckIcon } from "../../components/icons";
import { UserDnsIdentityIcon } from "../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../components/user/user-dns-identity";
function MessagePreview({ message, pubkey }: { message: NostrEvent; pubkey: string }) {
const ref = useRef<HTMLParagraphElement | null>(null);
@ -50,7 +50,7 @@ function ConversationCard({ conversation }: { conversation: KnownConversation })
<Flex direction="column" gap="1" overflow="hidden" flex={1}>
<Flex gap="2" alignItems="center" overflow="hidden">
<UserName pubkey={conversation.correspondent} isTruncated />
<UserDnsIdentityIcon onlyIcon pubkey={conversation.correspondent} />
<UserDnsIdentity onlyIcon pubkey={conversation.correspondent} />
<Timestamp flexShrink={0} timestamp={lastMessage.created_at} ml="auto" />
{hasResponded(conversation) && <CheckIcon boxSize={4} color="green.500" />}
</Flex>

View File

@ -17,7 +17,7 @@ import { NostrEvent } from "../../../types/nostr-event";
import UserAvatar from "../../../components/user/user-avatar";
import HoverLinkOverlay from "../../../components/hover-link-overlay";
import UserName from "../../../components/user/user-name";
import { UserDnsIdentityIcon } from "../../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../../components/user/user-dns-identity";
import { nip19 } from "nostr-tools";
import { useDecryptionContainer, useDecryptionContext } from "../../../providers/global/dycryption-provider";
import Timestamp from "../../../components/timestamp";
@ -38,7 +38,7 @@ function Conversation({ conversation }: { conversation: KnownConversation }) {
<HoverLinkOverlay as={RouterLink} to={`/dm/${nip19.npubEncode(conversation.correspondent)}`}>
<UserName pubkey={conversation.correspondent} />
</HoverLinkOverlay>
<UserDnsIdentityIcon pubkey={conversation.correspondent} onlyIcon />
<UserDnsIdentity pubkey={conversation.correspondent} onlyIcon />
</Flex>
{lastReceived && <MessagePreview message={lastReceived} pubkey={conversation.correspondent} />}
</Flex>

View File

@ -5,7 +5,7 @@ import { nip19 } from "nostr-tools";
import useUserMetadata from "../../../hooks/use-user-metadata";
import { getUserDisplayName } from "../../../helpers/nostr/user-metadata";
import UserAvatar from "../../../components/user/user-avatar";
import { UserDnsIdentityIcon } from "../../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../../components/user/user-dns-identity";
import { NostrEvent } from "../../../types/nostr-event";
import useAsyncErrorHandler from "../../../hooks/use-async-error-handler";
import { listRemovePerson } from "../../../helpers/nostr/lists";
@ -35,7 +35,7 @@ export default function UserCard({ pubkey, relay, list, ...props }: UserCardProp
{getUserDisplayName(metadata, pubkey)}
</Heading>
</Link>
<UserDnsIdentityIcon pubkey={pubkey} />
<UserDnsIdentity pubkey={pubkey} />
</Flex>
{account?.pubkey === list.pubkey ? (
<Button variant="outline" colorScheme="orange" onClick={handleRemoveFromList} size="sm">

View File

@ -2,7 +2,7 @@ import { MutableRefObject, PropsWithChildren, forwardRef } from "react";
import { Divider, Flex, Heading, useDisclosure } from "@chakra-ui/react";
import dayjs from "dayjs";
import { ExpandableToggleButton } from "../notification-item";
import { ExpandableToggleButton } from "./notification-item";
const specialNames = {
[dayjs().startOf("day").unix()]: "Today",

View File

@ -2,23 +2,30 @@ import { ReactNode, forwardRef, memo, useMemo, useRef } from "react";
import { AvatarGroup, Flex, IconButton, IconButtonProps, Text, useDisclosure } from "@chakra-ui/react";
import { kinds, nip18, nip25 } from "nostr-tools";
import useCurrentAccount from "../../hooks/use-current-account";
import { NostrEvent, isATag, isETag } from "../../types/nostr-event";
import { useRegisterIntersectionEntity } from "../../providers/local/intersection-observer";
import { parseZapEvent } from "../../helpers/nostr/zaps";
import { readablizeSats } from "../../helpers/bolt11";
import { getEventUID, getThreadReferences, isMentionedInContent, parseCoordinate } from "../../helpers/nostr/event";
import { EmbedEvent, EmbedEventPointer } from "../../components/embed-event";
import EmbeddedUnknown from "../../components/embed-event/event-types/embedded-unknown";
import { ErrorBoundary } from "../../components/error-boundary";
import { TrustProvider } from "../../providers/local/trust";
import Heart from "../../components/icons/heart";
import UserAvatarLink from "../../components/user/user-avatar-link";
import { AtIcon, ChevronDownIcon, ChevronUpIcon, LightningIcon, ReplyIcon, RepostIcon } from "../../components/icons";
import useSingleEvent from "../../hooks/use-single-event";
import { TORRENT_COMMENT_KIND } from "../../helpers/nostr/torrents";
import NotificationIconEntry from "./components/notification-icon-entry";
import { getPubkeysMentionedInContent } from "../../helpers/nostr/post";
import useCurrentAccount from "../../../hooks/use-current-account";
import { NostrEvent, isATag, isETag } from "../../../types/nostr-event";
import { useRegisterIntersectionEntity } from "../../../providers/local/intersection-observer";
import { parseZapEvent } from "../../../helpers/nostr/zaps";
import { readablizeSats } from "../../../helpers/bolt11";
import { getEventUID, getThreadReferences, isMentionedInContent, parseCoordinate } from "../../../helpers/nostr/event";
import { EmbedEvent, EmbedEventPointer } from "../../../components/embed-event";
import EmbeddedUnknown from "../../../components/embed-event/event-types/embedded-unknown";
import { ErrorBoundary } from "../../../components/error-boundary";
import { TrustProvider } from "../../../providers/local/trust";
import Heart from "../../../components/icons/heart";
import UserAvatarLink from "../../../components/user/user-avatar-link";
import {
AtIcon,
ChevronDownIcon,
ChevronUpIcon,
LightningIcon,
ReplyIcon,
RepostIcon,
} from "../../../components/icons";
import useSingleEvent from "../../../hooks/use-single-event";
import { TORRENT_COMMENT_KIND } from "../../../helpers/nostr/torrents";
import NotificationIconEntry from "./notification-icon-entry";
import { getPubkeysMentionedInContent } from "../../../helpers/nostr/post";
export const ExpandableToggleButton = ({
toggle,

View File

@ -1,7 +1,9 @@
import { memo, useEffect, useMemo, useRef } from "react";
import { Button, ButtonGroup, Flex, useDisclosure } from "@chakra-ui/react";
import { Button, ButtonGroup, Flex, IconButton, Input, useDisclosure } from "@chakra-ui/react";
import { kinds } from "nostr-tools";
import { Link as RouterLink } from "react-router-dom";
import dayjs from "dayjs";
import { useThrottle } from "react-use";
import RequireCurrentAccount from "../../providers/route/require-current-account";
import TimelineActionAndStatus from "../../components/timeline-page/timeline-action-and-status";
@ -11,17 +13,20 @@ import IntersectionObserverProvider, {
import useSubject from "../../hooks/use-subject";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
import { useNotificationTimeline } from "../../providers/global/notification-timeline";
import { getEventUID, isReply, isRepost } from "../../helpers/nostr/event";
import { getEventUID, isReply } from "../../helpers/nostr/event";
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
import VerticalPageLayout from "../../components/vertical-page-layout";
import NotificationItem from "./notification-item";
import NotificationItem from "./components/notification-item";
import NotificationTypeToggles from "./notification-type-toggles";
import { NostrEvent } from "../../types/nostr-event";
import { groupByTime } from "../../helpers/notification";
import DayGroup from "./components/day-group";
import { useThrottle } from "react-use";
import TimelineLoader from "../../classes/timeline-loader";
import { ChevronLeftIcon, ChevronRightIcon } from "../../components/icons";
import useRouteSearchValue from "../../hooks/use-route-search-value";
const DATE_FORMAT = "YYYY-MM-DD";
const NotificationDay = memo(({ day, events }: { day: number; events: NostrEvent[] }) => {
const ref = useRef<HTMLDivElement | null>(null);
@ -39,6 +44,7 @@ const NotificationDay = memo(({ day, events }: { day: number; events: NostrEvent
const NotificationsTimeline = memo(
({
timeline,
day,
showReplies,
showMentions,
showZaps,
@ -46,6 +52,7 @@ const NotificationsTimeline = memo(
showReactions,
}: {
timeline: TimelineLoader;
day: string;
showReplies: boolean;
showMentions: boolean;
showZaps: boolean;
@ -54,6 +61,8 @@ const NotificationsTimeline = memo(
}) => {
const { people } = usePeopleListContext();
const peoplePubkeys = useMemo(() => people?.map((p) => p.pubkey), [people]);
const minTimestamp = dayjs(day, DATE_FORMAT).startOf("day").unix();
const maxTimestamp = dayjs(day, DATE_FORMAT).endOf("day").unix();
const events = useSubject(timeline.timeline);
@ -61,6 +70,7 @@ const NotificationsTimeline = memo(
const filteredEvents = useMemo(
() =>
throttledEvents.filter((e) => {
if (e.created_at < minTimestamp || e.created_at > maxTimestamp) return false;
if (peoplePubkeys && e.kind !== kinds.Zap && !peoplePubkeys.includes(e.pubkey)) return false;
if (e.kind === kinds.ShortTextNote) {
@ -73,17 +83,42 @@ const NotificationsTimeline = memo(
return true;
}),
[throttledEvents, peoplePubkeys, showReplies, showMentions, showReactions, showReposts, showZaps],
[
throttledEvents,
peoplePubkeys,
showReplies,
showMentions,
showReactions,
showReposts,
showZaps,
minTimestamp,
maxTimestamp,
],
);
const sortedDays = useMemo(() => groupByTime(filteredEvents), [filteredEvents]);
// const sortedDays = useMemo(() => groupByTime(filteredEvents), [filteredEvents]);
if (filteredEvents.length === 0)
return (
<Flex alignItems="center" justifyContent="center" minH="25vh" fontWeight="bold" fontSize="4xl">
Nothing...
</Flex>
);
return (
<>
{sortedDays.map(([day, events]) => (
<NotificationDay key={day} day={day} events={events} />
{filteredEvents.map((event) => (
<NotificationItem key={event.id} event={event} />
))}
</>
);
// return (
// <>
// {sortedDays.map(([day, events]) => (
// <NotificationDay key={day} day={day} events={events} />
// ))}
// </>
// );
},
);
@ -98,6 +133,24 @@ function NotificationsPage() {
defaultIsOpen: localStorage.getItem("notifications-show-reactions") !== "false",
});
const today = dayjs().format(DATE_FORMAT);
const { value: day, setValue: setDay } = useRouteSearchValue("date", dayjs().format(DATE_FORMAT));
const nextDay = () => {
setDay((date) =>
dayjs(date ?? today, DATE_FORMAT)
.add(1, "day")
.format(DATE_FORMAT),
);
};
const perviousDay = () => {
setDay((date) =>
dayjs(date ?? today, DATE_FORMAT)
.subtract(1, "day")
.format(DATE_FORMAT),
);
};
// save toggles to localStorage when changed
useEffect(() => {
localStorage.setItem("notifications-show-replies", String(showReplies.isOpen));
@ -111,9 +164,27 @@ function NotificationsPage() {
const callback = useTimelineCurserIntersectionCallback(timeline);
return (
<IntersectionObserverProvider callback={callback}>
<VerticalPageLayout>
<Flex gap="2" wrap="wrap">
<VerticalPageLayout>
<Flex direction={{ base: "column", lg: "row-reverse" }} gap="2" justifyContent="space-between">
<Flex gap="2" justifyContent="space-between">
<IconButton aria-label="Pervious" icon={<ChevronLeftIcon boxSize={6} />} onClick={perviousDay} />
<Input
maxW="xs"
minW="64"
type="date"
value={day}
onChange={(e) => e.target.value && setDay(e.target.value)}
max={today}
/>
<IconButton
aria-label="Next"
icon={<ChevronRightIcon boxSize={6} />}
onClick={nextDay}
isDisabled={day === today}
/>
</Flex>
<Flex gap="2" wrap="wrap" flex={1}>
<NotificationTypeToggles
showReplies={showReplies}
showMentions={showMentions}
@ -128,18 +199,33 @@ function NotificationsPage() {
</Button>
</ButtonGroup>
</Flex>
</Flex>
<IntersectionObserverProvider callback={callback}>
<NotificationsTimeline
timeline={timeline}
day={day}
showReplies={showReplies.isOpen}
showMentions={showMentions.isOpen}
showZaps={showZaps.isOpen}
showReposts={showReposts.isOpen}
showReactions={showReactions.isOpen}
/>
<TimelineActionAndStatus timeline={timeline} />
</VerticalPageLayout>
</IntersectionObserverProvider>
</IntersectionObserverProvider>
{/* <TimelineActionAndStatus timeline={timeline} /> */}
<ButtonGroup mx="auto" mt="4">
<Button leftIcon={<ChevronLeftIcon boxSize={6} />} onClick={perviousDay}>
Pervious
</Button>
{day !== today && (
<Button rightIcon={<ChevronRightIcon boxSize={6} />} onClick={nextDay}>
Next
</Button>
)}
</ButtonGroup>
</VerticalPageLayout>
);
}

View File

@ -1,3 +1,4 @@
import { useState } from "react";
import {
Box,
Button,
@ -19,12 +20,12 @@ import {
Text,
useDisclosure,
} from "@chakra-ui/react";
import { useState } from "react";
import { useDebounce } from "react-use";
import { useRelayInfo } from "../../../hooks/use-relay-info";
import UserAvatar from "../../../components/user/user-avatar";
import UserLink from "../../../components/user/user-link";
import { useDebounce } from "react-use";
import { UserDnsIdentityIcon } from "../../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../../components/user/user-dns-identity";
import { CodeIcon } from "../../../components/icons";
import { Metadata } from "./relay-card";
import { safeRelayUrl } from "../../../helpers/relay";
@ -43,7 +44,7 @@ function RelayDetails({ url, debug }: { url: string; debug?: boolean }) {
<Text fontWeight="bold">Owner: </Text>
<UserAvatar pubkey={info.pubkey} size="xs" />
<UserLink pubkey={info.pubkey} />
<UserDnsIdentityIcon pubkey={info.pubkey} onlyIcon />
<UserDnsIdentity pubkey={info.pubkey} onlyIcon />
</Flex>
)}
<Metadata name="NIPs">{info.supported_nips?.join(", ")}</Metadata>

View File

@ -27,9 +27,8 @@ import { RelayFavicon } from "../../../components/relay-favicon";
import { CodeIcon } from "../../../components/icons";
import UserLink from "../../../components/user/user-link";
import UserAvatar from "../../../components/user/user-avatar";
import { UserDnsIdentityIcon } from "../../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../../components/user/user-dns-identity";
import RawJson from "../../../components/debug-modal/raw-json";
import { RelayShareButton } from "./relay-share-button";
import useRelayStats from "../../../hooks/use-relay-stats";
import { getNetwork } from "../../../helpers/nostr/relay-stats";
@ -56,7 +55,7 @@ export function RelayMetadata({ url, extended }: { url: string; extended?: boole
<B>Owner:</B>
<UserAvatar pubkey={info.pubkey} size="xs" noProxy />
<UserLink pubkey={info.pubkey} />
<UserDnsIdentityIcon pubkey={info.pubkey} onlyIcon />
<UserDnsIdentity pubkey={info.pubkey} onlyIcon />
</Flex>
)}
{extended && (

View File

@ -4,7 +4,7 @@ import { Link as RouterLink } from "react-router-dom";
import UserAvatarLink from "../../../components/user/user-avatar-link";
import UserLink from "../../../components/user/user-link";
import { UserDnsIdentityIcon } from "../../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../../components/user/user-dns-identity";
import StarRating from "../../../components/star-rating";
import { safeJson } from "../../../helpers/parse";
import { NostrEvent } from "../../../types/nostr-event";
@ -28,7 +28,7 @@ export default function RelayReviewNote({ event, hideUrl }: { event: NostrEvent;
<CardHeader display="flex" gap="2" px="2" pt="2" pb="0">
<UserAvatarLink pubkey={event.pubkey} size="xs" />
<UserLink pubkey={event.pubkey} isTruncated fontWeight="bold" fontSize="lg" />
<UserDnsIdentityIcon pubkey={event.pubkey} onlyIcon />
<UserDnsIdentity pubkey={event.pubkey} onlyIcon />
<Timestamp ml="auto" timestamp={event.created_at} />
</CardHeader>
<CardBody p="2">

View File

@ -14,7 +14,7 @@ import useSubject from "../../../hooks/use-subject";
import { useTimelineCurserIntersectionCallback } from "../../../hooks/use-timeline-cursor-intersection-callback";
import TimelineActionAndStatus from "../../../components/timeline-page/timeline-action-and-status";
import UserAvatarLink from "../../../components/user/user-avatar-link";
import { UserDnsIdentityIcon } from "../../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../../components/user/user-dns-identity";
import HoverLinkOverlay from "../../../components/hover-link-overlay";
import UserLink from "../../../components/user/user-link";
import { getRelaysFromList } from "../../../helpers/nostr/lists";
@ -30,7 +30,7 @@ function UserCard({ list, pubkey }: { list: NostrEvent; pubkey: string }) {
<UserAvatarLink pubkey={pubkey} />
<Flex direction="column" overflow="hidden">
<HoverLinkOverlay as={UserLink} pubkey={pubkey} fontWeight="bold" isTruncated />
<UserDnsIdentityIcon pubkey={pubkey} />
<UserDnsIdentity pubkey={pubkey} />
</Flex>
</Card>
);

View File

@ -8,7 +8,7 @@ import { parseMetadataContent } from "../../helpers/nostr/user-metadata";
import { readablizeSats } from "../../helpers/bolt11";
import { EmbedableContent, embedUrls } from "../../helpers/embeds";
import UserAvatar from "../../components/user/user-avatar";
import { UserDnsIdentityIcon } from "../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../components/user/user-dns-identity";
import { embedNostrLinks, renderGenericUrl } from "../../components/embed-types";
import UserLink from "../../components/user/user-link";
import trustedUserStatsService, { NostrBandUserStats } from "../../services/trusted-user-stats";
@ -38,7 +38,7 @@ function ProfileResult({ profile }: { profile: NostrEvent }) {
<UserAvatar pubkey={profile.pubkey} noProxy mr="2" float="left" />
<UserLink pubkey={profile.pubkey} fontWeight="bold" fontSize="xl" isTruncated />
<br />
<UserDnsIdentityIcon pubkey={profile.pubkey} />
<UserDnsIdentity pubkey={profile.pubkey} />
<br />
<Box whiteSpace="pre" overflow="hidden" maxH="xs" isTruncated>
{aboutContent}

View File

@ -7,7 +7,7 @@ import UserLink from "../../components/user/user-link";
import { containerProps } from "./common";
import { UserFollowButton } from "../../components/user/user-follow-button";
import { Kind0ParsedContent } from "../../helpers/nostr/user-metadata";
import { UserDnsIdentityIcon } from "../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../components/user/user-dns-identity";
type TrendingApi = {
profiles: {
@ -45,7 +45,7 @@ export default function FinishedStep() {
<UserAvatarLink pubkey={pubkey} />
<Flex direction="column" overflow="hidden">
<UserLink pubkey={pubkey} fontWeight="bold" fontSize="lg" isTruncated />
<UserDnsIdentityIcon pubkey={pubkey} />
<UserDnsIdentity pubkey={pubkey} />
</Flex>
<UserFollowButton pubkey={pubkey} flexShrink={0} ml="auto" />
</Flex>

View File

@ -13,7 +13,7 @@ import Timestamp from "../../../components/timestamp";
import Expand01 from "../../../components/icons/expand-01";
import Minus from "../../../components/icons/minus";
import { useBreakpointValue } from "../../../providers/global/breakpoint-provider";
import { UserDnsIdentityIcon } from "../../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../../components/user/user-dns-identity";
import EventInteractionDetailsModal from "../../../components/event-interactions-modal";
import { getSharableEventAddress } from "../../../helpers/nip19";
import { useRegisterIntersectionEntity } from "../../../providers/local/intersection-observer";
@ -67,7 +67,7 @@ export const ThreadPost = memo(({ post, initShowReplies, focusId, level = -1 }:
<Flex gap="2" alignItems="center">
<UserAvatarLink pubkey={post.event.pubkey} size="sm" />
<UserLink pubkey={post.event.pubkey} fontWeight="bold" isTruncated />
<UserDnsIdentityIcon pubkey={post.event.pubkey} onlyIcon />
<UserDnsIdentity pubkey={post.event.pubkey} onlyIcon />
<POWIcon event={post.event} boxSize={5} />
<Link as={RouterLink} whiteSpace="nowrap" color="current" to={`/n/${getSharableEventAddress(post.event)}`}>
<Timestamp timestamp={post.event.created_at} />

View File

@ -5,7 +5,7 @@ import { NostrEvent } from "../../../types/nostr-event";
import useSingleEvent from "../../../hooks/use-single-event";
import { NoteTranslationsPage } from "./translation";
import UserAvatarLink from "../../../components/user/user-avatar-link";
import { UserDnsIdentityIcon } from "../../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../../components/user/user-dns-identity";
import UserLink from "../../../components/user/user-link";
import NoteTextToSpeechPage from "./text-to-speech";
import useRouteSearchValue from "../../../hooks/use-route-search-value";
@ -36,7 +36,7 @@ function TransformNotePage({ note }: { note: NostrEvent }) {
<Flex gap="2" alignItems="center">
<UserAvatarLink pubkey={note.pubkey} size={["xs", "sm"]} />
<UserLink pubkey={note.pubkey} isTruncated fontWeight="bold" fontSize="lg" />
<UserDnsIdentityIcon pubkey={note.pubkey} onlyIcon />
<UserDnsIdentity pubkey={note.pubkey} onlyIcon />
<Spacer />
<NoteMenu event={note} aria-label="Note Options" />
</Flex>

View File

@ -24,7 +24,7 @@ import {
import useClientSideMuteFilter from "../../../hooks/use-client-side-mute-filter";
import UserAvatarLink from "../../../components/user/user-avatar-link";
import UserLink from "../../../components/user/user-link";
import { UserDnsIdentityIcon } from "../../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../../components/user/user-dns-identity";
import Timestamp from "../../../components/timestamp";
import Minus from "../../../components/icons/minus";
import Expand01 from "../../../components/icons/expand-01";
@ -67,7 +67,7 @@ export const ThreadPost = memo(({ post, level = -1 }: { post: ThreadItem; level?
<Flex gap="2" alignItems="center">
<UserAvatarLink pubkey={post.event.pubkey} size="sm" />
<UserLink pubkey={post.event.pubkey} fontWeight="bold" isTruncated />
<UserDnsIdentityIcon pubkey={post.event.pubkey} onlyIcon />
<UserDnsIdentity pubkey={post.event.pubkey} onlyIcon />
<Timestamp timestamp={post.event.created_at} />
{replies.length > 0 ? (
<Button variant="ghost" onClick={expanded.onToggle} rightIcon={expanded.isOpen ? <Minus /> : <Expand01 />}>

View File

@ -13,7 +13,7 @@ import { ReplyIcon } from "../../../components/icons";
import TrackStemstrButton from "./track-stemstr-button";
import TrackDownloadButton from "./track-download-button";
import TrackPlayer from "./track-player";
import { UserDnsIdentityIcon } from "../../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../../components/user/user-dns-identity";
import TrackMenu from "./track-menu";
import QuoteRepostButton from "../../../components/note/quote-repost-button";
import NoteZapButton from "../../../components/note/note-zap-button";
@ -29,7 +29,7 @@ export default function TrackCard({ track, ...props }: { track: NostrEvent } & O
<CardHeader display="flex" alignItems="center" p="2" pb="0" gap="2">
<UserAvatarLink pubkey={track.pubkey} size="sm" />
<UserLink pubkey={track.pubkey} isTruncated fontWeight="bold" fontSize="lg" />
<UserDnsIdentityIcon pubkey={track.pubkey} onlyIcon />
<UserDnsIdentity pubkey={track.pubkey} onlyIcon />
<Timestamp ml="auto" timestamp={track.created_at} />
</CardHeader>
<CardBody p="2" display="flex" gap="2" flexDirection="column">

View File

@ -20,7 +20,7 @@ import {
} from "../../../components/icons";
import { CopyIconButton } from "../../../components/copy-icon-button";
import { QrIconButton } from "../components/share-qr-button";
import { UserDnsIdentityIcon } from "../../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../../components/user/user-dns-identity";
import UserAvatar from "../../../components/user/user-avatar";
import { ChatIcon } from "@chakra-ui/icons";
import { UserFollowButton } from "../../../components/user/user-follow-button";
@ -90,7 +90,7 @@ export default function UserAboutTab() {
<UserAvatar pubkey={pubkey} size={["lg", "lg", "xl"]} noProxy />
<Box overflow="hidden">
<Heading isTruncated>{getUserDisplayName(metadata, pubkey)}</Heading>
<UserDnsIdentityIcon pubkey={pubkey} />
<UserDnsIdentity pubkey={pubkey} />
</Box>
<Flex gap="2" ml="auto">
@ -136,7 +136,7 @@ export default function UserAboutTab() {
<Flex gap="2">
<AtIcon />
<Link href={`//${parsedNip05.domain}/.well-known/nostr.json?name=${parsedNip05.name}`} isExternal>
<UserDnsIdentityIcon pubkey={pubkey} />
<UserDnsIdentity pubkey={pubkey} />
</Link>
</Flex>
)}

View File

@ -3,7 +3,7 @@ import { useNavigate } from "react-router-dom";
import { EditIcon, GhostIcon } from "../../../components/icons";
import UserAvatar from "../../../components/user/user-avatar";
import { UserDnsIdentityIcon } from "../../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../../components/user/user-dns-identity";
import { getUserDisplayName } from "../../../helpers/nostr/user-metadata";
import useCurrentAccount from "../../../hooks/use-current-account";
import useUserMetadata from "../../../hooks/use-user-metadata";
@ -36,7 +36,7 @@ export default function Header({
<Heading size="md" isTruncated>
{getUserDisplayName(metadata, pubkey)}
</Heading>
<UserDnsIdentityIcon pubkey={pubkey} onlyIcon={showFullNip05} />
<UserDnsIdentity pubkey={pubkey} onlyIcon={showFullNip05} />
<Spacer />
{isSelf && !account.readonly && (
<IconButton

View File

@ -1,6 +1,6 @@
import { Flex, FlexProps } from "@chakra-ui/react";
import { UserDnsIdentityIcon } from "../../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../../components/user/user-dns-identity";
import { UserFollowButton } from "../../../components/user/user-follow-button";
import UserLink from "../../../components/user/user-link";
import UserAvatarLink from "../../../components/user/user-avatar-link";
@ -24,7 +24,7 @@ export const UserCard = ({ pubkey, relay, ...props }: UserCardProps) => {
<UserAvatarLink pubkey={pubkey} />
<Flex direction="column" flex={1} overflow="hidden">
<UserLink pubkey={pubkey} fontWeight="bold" />
<UserDnsIdentityIcon pubkey={pubkey} />
<UserDnsIdentity pubkey={pubkey} />
</Flex>
<UserFollowButton pubkey={pubkey} size="sm" variant="outline" flexShrink={0} />
</Flex>

View File

@ -13,7 +13,7 @@ import useParamsAddressPointer from "../../hooks/use-params-address-pointer";
import useReplaceableEvent from "../../hooks/use-replaceable-event";
import UserAvatarLink from "../../components/user/user-avatar-link";
import UserLink from "../../components/user/user-link";
import { UserDnsIdentityIcon } from "../../components/user/user-dns-identity-icon";
import UserDnsIdentity from "../../components/user/user-dns-identity";
import { UserFollowButton } from "../../components/user/user-follow-button";
import VideoMenu from "./components/video-menu";
import SimpleLikeButton from "../../components/event-reactions/simple-like-button";
@ -73,7 +73,7 @@ function VideoDetailsPage({ video }: { video: NostrEvent }) {
<Flex gap="2" alignItems="center">
<UserAvatarLink pubkey={video.pubkey} size="sm" />
<UserLink pubkey={video.pubkey} fontSize="lg" tab="videos" />
<UserDnsIdentityIcon pubkey={video.pubkey} onlyIcon />
<UserDnsIdentity pubkey={video.pubkey} onlyIcon />
<UserFollowButton pubkey={video.pubkey} size="sm" />
<ButtonGroup ml="auto" size="sm" variant="ghost">
<SimpleBookmarkButton event={video} aria-label="Bookmark video" title="Bookmark video" />