build better community post component

cleanup exports on common components
This commit is contained in:
hzrd149 2023-10-19 13:13:57 -05:00
parent 144a249317
commit 62a729a805
76 changed files with 214 additions and 165 deletions

View File

@ -15,7 +15,7 @@ import {
import { Link as RouterLink } from "react-router-dom";
import { nip19 } from "nostr-tools";
import { UserAvatar } from "./user-avatar";
import UserAvatar from "./user-avatar";
import { getUserDisplayName } from "../helpers/user-metadata";
import { useUserMetadata } from "../hooks/use-user-metadata";

View File

@ -12,7 +12,7 @@ import { useRegisterIntersectionEntity } from "../../../providers/intersection-o
import { getEventUID } from "../../../helpers/nostr/events";
import { buildAppSelectUrl } from "../../../helpers/nostr/apps";
import { getSharableEventAddress } from "../../../helpers/nip19";
import { UserAvatarLink } from "../../user-avatar-link";
import UserAvatarLink from "../../user-avatar-link";
import { UserLink } from "../../user-link";
import Timestamp from "../../timestamp";

View File

@ -12,7 +12,7 @@ import {
Text,
} from "@chakra-ui/react";
import { UserAvatarLink } from "../../../components/user-avatar-link";
import UserAvatarLink from "../../../components/user-avatar-link";
import { UserLink } from "../../../components/user-link";
import { getSharableEventAddress } from "../../../helpers/nip19";
import { NostrEvent } from "../../../types/nostr-event";

View File

@ -2,7 +2,7 @@ import { Link as RouterLink } from "react-router-dom";
import { Card, CardFooter, CardHeader, CardProps, Heading, LinkBox, LinkOverlay, Text } from "@chakra-ui/react";
import { nip19 } from "nostr-tools";
import { UserAvatarLink } from "../../../components/user-avatar-link";
import UserAvatarLink from "../../../components/user-avatar-link";
import { UserLink } from "../../../components/user-link";
import { NostrEvent } from "../../../types/nostr-event";
import { getCommunityImage, getCommunityName } from "../../../helpers/nostr/communities";

View File

@ -15,7 +15,7 @@ import { Link as RouterLink } from "react-router-dom";
import { getSharableEventAddress } from "../../../helpers/nip19";
import { getEmojisFromPack, getPackName } from "../../../helpers/nostr/emoji-packs";
import { UserAvatarLink } from "../../user-avatar-link";
import UserAvatarLink from "../../user-avatar-link";
import { UserLink } from "../../user-link";
import EmojiPackFavoriteButton from "../../../views/emoji-packs/components/emoji-pack-favorite-button";
import EmojiPackMenu from "../../../views/emoji-packs/components/emoji-pack-menu";

View File

@ -4,7 +4,7 @@ import { Link as RouterLink } from "react-router-dom";
import { getSharableEventAddress } from "../../../helpers/nip19";
import { NostrEvent } from "../../../types/nostr-event";
import { getGoalName } from "../../../helpers/nostr/goal";
import { UserAvatarLink } from "../../user-avatar-link";
import UserAvatarLink from "../../user-avatar-link";
import { UserLink } from "../../user-link";
import GoalProgress from "../../../views/goals/components/goal-progress";
import GoalZapButton from "../../../views/goals/components/goal-zap-button";

View File

@ -5,7 +5,7 @@ import { NostrEvent } from "../../../types/nostr-event";
import { getListName, isSpecialListKind } from "../../../helpers/nostr/lists";
import { createCoordinate } from "../../../services/replaceable-event-requester";
import { getSharableEventAddress } from "../../../helpers/nip19";
import { UserAvatarLink } from "../../user-avatar-link";
import UserAvatarLink from "../../user-avatar-link";
import { UserLink } from "../../user-link";
import ListFeedButton from "../../../views/lists/components/list-feed-button";
import { ListCardContent } from "../../../views/lists/components/list-card";

View File

@ -1,9 +1,9 @@
import { MouseEventHandler, useCallback } from "react";
import { Card, CardProps, Flex, LinkBox, LinkOverlay, Spacer, cssVar } from "@chakra-ui/react";
import { Card, CardProps, Flex, LinkBox, Spacer } from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom";
import { NostrEvent } from "../../../types/nostr-event";
import { UserAvatarLink } from "../../user-avatar-link";
import UserAvatarLink from "../../user-avatar-link";
import { UserLink } from "../../user-link";
import { UserDnsIdentityIcon } from "../../user-dns-identity-icon";
import useSubject from "../../../hooks/use-subject";

View File

@ -16,7 +16,7 @@ import {
} from "@chakra-ui/react";
import { NostrEvent } from "../../../types/nostr-event";
import { UserAvatarLink } from "../../user-avatar-link";
import UserAvatarLink from "../../user-avatar-link";
import { UserLink } from "../../user-link";
import { InlineNoteContent } from "../../note/inline-note-content";
import { getDownloadURL, getHashtags, getStreamURL } from "../../../helpers/nostr/stemstr";

View File

@ -3,7 +3,7 @@ import { Link as RouterLink } from "react-router-dom";
import { NostrEvent, isATag } from "../../../types/nostr-event";
import { UserLink } from "../../user-link";
import { UserAvatar } from "../../user-avatar";
import UserAvatar from "../../user-avatar";
import ChatMessageContent from "../../../views/streams/stream/stream-chat/chat-message-content";
import useReplaceableEvent from "../../../hooks/use-replaceable-event";
import { parseStreamEvent } from "../../../helpers/nostr/stream";

View File

@ -5,7 +5,7 @@ import { parseStreamEvent } from "../../../helpers/nostr/stream";
import { NostrEvent } from "../../../types/nostr-event";
import StreamStatusBadge from "../../../views/streams/components/status-badge";
import { UserLink } from "../../user-link";
import { UserAvatar } from "../../user-avatar";
import UserAvatar from "../../user-avatar";
import useEventNaddr from "../../../hooks/use-event-naddr";
import Timestamp from "../../timestamp";
import { useBreakpointValue } from "../../../providers/breakpoint-provider";

View File

@ -2,7 +2,7 @@ import { Box, Card, CardBody, CardHeader, CardProps, Flex, Link, Text } from "@c
import { getSharableEventAddress } from "../../../helpers/nip19";
import { NostrEvent } from "../../../types/nostr-event";
import { UserAvatarLink } from "../../user-avatar-link";
import UserAvatarLink from "../../user-avatar-link";
import { UserLink } from "../../user-link";
import { truncatedId } from "../../../helpers/nostr/events";
import { buildAppSelectUrl } from "../../../helpers/nostr/apps";

View File

@ -9,7 +9,7 @@ import { getZapSplits } from "../../helpers/nostr/zaps";
import { EmbedEvent, EmbedProps } from "../embed-event";
import useAppSettings from "../../hooks/use-app-settings";
import CustomZapAmountOptions from "./zap-options";
import { UserAvatar } from "../user-avatar";
import UserAvatar from "../user-avatar";
import { UserLink } from "../user-link";
function UserCard({ pubkey, percent }: { pubkey: string; percent?: number }) {

View File

@ -2,7 +2,7 @@ import { useMount } from "react-use";
import { Alert, Box, Button, ButtonGroup, Flex, IconButton, Spacer, useDisclosure, useToast } from "@chakra-ui/react";
import { PayRequest } from ".";
import { UserAvatar } from "../user-avatar";
import UserAvatar from "../user-avatar";
import { UserLink } from "../user-link";
import { ChevronDownIcon, ChevronUpIcon, CheckIcon, ErrorIcon, LightningIcon } from "../icons";
import { InvoiceModalContent } from "../invoice-modal";

View File

@ -7,7 +7,7 @@ import useSubject from "../../hooks/use-subject";
import { useUserMetadata } from "../../hooks/use-user-metadata";
import accountService, { Account } from "../../services/account";
import { AddIcon, ChevronDownIcon, ChevronUpIcon } from "../icons";
import { UserAvatar } from "../user-avatar";
import UserAvatar from "../user-avatar";
import AccountInfoBadge from "../account-info-badge";
import { useCurrentAccount } from "../../hooks/use-current-account";

View File

@ -8,7 +8,7 @@ import dayjs from "dayjs";
import { useCurrentAccount } from "../../hooks/use-current-account";
import useSubject from "../../hooks/use-subject";
import accountService from "../../services/account";
import { UserAvatar } from "../user-avatar";
import UserAvatar from "../user-avatar";
import { UserLink } from "../user-link";
import { GhostIcon } from "../icons";
import useTimelineLoader from "../../hooks/use-timeline-loader";

View File

@ -5,7 +5,7 @@ import { useLocation, useNavigate } from "react-router-dom";
import { useCurrentAccount } from "../../hooks/use-current-account";
import { PostModalContext } from "../../providers/post-modal-provider";
import { DirectMessagesIcon, NotesIcon, NotificationsIcon, PlusCircleIcon, SearchIcon } from "../icons";
import { UserAvatar } from "../user-avatar";
import UserAvatar from "../user-avatar";
import MobileSideDrawer from "./mobile-side-drawer";
export default function MobileBottomNav(props: Omit<FlexProps, "children">) {

View File

@ -30,7 +30,7 @@ declare module "yet-another-react-lightbox" {
}
import { NostrEvent } from "../types/nostr-event";
import { UserAvatarLink } from "./user-avatar-link";
import UserAvatarLink from "./user-avatar-link";
import { UserLink } from "./user-link";
import { UserDnsIdentityIcon } from "./user-dns-identity-icon";
import styled from "@emotion/styled";

View File

@ -11,7 +11,7 @@ import { matchSorter } from "match-sorter/dist/match-sorter.esm.js";
import { Emoji, useContextEmojis } from "../providers/emoji-provider";
import { useUserSearchDirectoryContext } from "../providers/user-directory-provider";
import { UserAvatar } from "./user-avatar";
import UserAvatar from "./user-avatar";
export type PeopleToken = { pubkey: string; names: string[] };
type Token = Emoji | PeopleToken;

View File

@ -15,7 +15,7 @@ import {
useDisclosure,
} from "@chakra-ui/react";
import { NostrEvent, isATag } from "../../types/nostr-event";
import { UserAvatarLink } from "../user-avatar-link";
import UserAvatarLink from "../user-avatar-link";
import { Link as RouterLink } from "react-router-dom";
import NoteMenu from "./note-menu";

View File

@ -14,7 +14,7 @@ import {
} from "@chakra-ui/react";
import { NostrEvent } from "../../types/nostr-event";
import { UserAvatarLink } from "../user-avatar-link";
import UserAvatarLink from "../user-avatar-link";
import { UserLink } from "../user-link";
import { DislikeIcon, LightningIcon, LikeIcon } from "../icons";
import { ParsedZap } from "../../helpers/nostr/zaps";

View File

@ -22,7 +22,7 @@ import { useAsync } from "react-use";
import { getUserDisplayName } from "../../helpers/user-metadata";
import userMetadataService from "../../services/user-metadata";
import { normalizeToHex } from "../../helpers/nip19";
import { UserAvatar } from "../user-avatar";
import UserAvatar from "../user-avatar";
import { UserLink } from "../user-link";
function getRemainingPercent(split: EventSplit) {

View File

@ -19,7 +19,7 @@ import { useMemo } from "react";
import { NostrEvent } from "../types/nostr-event";
import { groupReactions } from "../helpers/nostr/reactions";
import { ReactionIcon } from "./event-reactions";
import { UserAvatarLink } from "./user-avatar-link";
import UserAvatarLink from "./user-avatar-link";
import { UserLink } from "./user-link";
export type ReactionDetailsModalProps = Omit<ModalProps, "children"> & {

View File

@ -6,7 +6,7 @@ import { matchSorter } from "match-sorter";
import { nip19 } from "nostr-tools";
import { useUserSearchDirectoryContext } from "../../providers/user-directory-provider";
import { UserAvatar } from "../user-avatar";
import UserAvatar from "../user-avatar";
import { useUserMetadata } from "../../hooks/use-user-metadata";
import { getUserDisplayName } from "../../helpers/user-metadata";

View File

@ -2,7 +2,7 @@ import { useRef } from "react";
import { Flex, Text } from "@chakra-ui/react";
import { NostrEvent } from "../../../types/nostr-event";
import { UserAvatar } from "../../user-avatar";
import UserAvatar from "../../user-avatar";
import { UserLink } from "../../user-link";
import RelayCard from "../../../views/relays/components/relay-card";
import { safeRelayUrl } from "../../../helpers/url";

View File

@ -7,7 +7,7 @@ import { NostrEvent } from "../../../types/nostr-event";
import { EmbedEvent } from "../../embed-event";
import { useRegisterIntersectionEntity } from "../../../providers/intersection-observer";
import Note from "../../note";
import { UserAvatar } from "../../user-avatar";
import UserAvatar from "../../user-avatar";
import { UserLink } from "../../user-link";
function ReplyNote({ event }: { event: NostrEvent }) {

View File

@ -5,7 +5,7 @@ import { Kind, validateEvent } from "nostr-tools";
import { isETag, NostrEvent } from "../../../types/nostr-event";
import { Note } from "../../note";
import NoteMenu from "../../note/note-menu";
import { UserAvatar } from "../../user-avatar";
import UserAvatar from "../../user-avatar";
import { UserDnsIdentityIcon } from "../../user-dns-identity-icon";
import { UserLink } from "../../user-link";
import { TrustProvider } from "../../../providers/trust";

View File

@ -20,7 +20,7 @@ import { NostrEvent } from "../../../types/nostr-event";
import { parseStreamEvent } from "../../../helpers/nostr/stream";
import useEventNaddr from "../../../hooks/use-event-naddr";
import { useRegisterIntersectionEntity } from "../../../providers/intersection-observer";
import { UserAvatar } from "../../user-avatar";
import UserAvatar from "../../user-avatar";
import { UserLink } from "../../user-link";
import StreamStatusBadge from "../../../views/streams/components/status-badge";
import { EventRelays } from "../../note/note-relays";

View File

@ -1,11 +1,13 @@
import React from "react";
import { forwardRef, memo } from "react";
import { Link } from "react-router-dom";
import { nip19 } from "nostr-tools";
import { UserAvatar, UserAvatarProps } from "./user-avatar";
export const UserAvatarLink = React.memo(({ pubkey, ...props }: UserAvatarProps) => (
<Link to={`/u/${nip19.npubEncode(pubkey)}`}>
export const UserAvatarLink = forwardRef<HTMLAnchorElement, UserAvatarProps>(({ pubkey, ...props }, ref) => (
<Link to={`/u/${nip19.npubEncode(pubkey)}`} ref={ref}>
<UserAvatar pubkey={pubkey} {...props} />
</Link>
));
export default memo(UserAvatarLink);

View File

@ -1,4 +1,4 @@
import React, { useMemo } from "react";
import { forwardRef, memo, useMemo } from "react";
import { Avatar, AvatarProps } from "@chakra-ui/react";
import { useUserMetadata } from "../hooks/use-user-metadata";
import { useAsync } from "react-use";
@ -9,7 +9,7 @@ import appSettings from "../services/settings/app-settings";
import useSubject from "../hooks/use-subject";
import { getUserDisplayName } from "../helpers/user-metadata";
export const UserIdenticon = React.memo(({ pubkey }: { pubkey: string }) => {
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;
@ -20,7 +20,7 @@ export type UserAvatarProps = Omit<AvatarProps, "src"> & {
relay?: string;
noProxy?: boolean;
};
export const UserAvatar = React.memo(({ pubkey, noProxy, relay, ...props }: UserAvatarProps) => {
export const UserAvatar = forwardRef<HTMLDivElement, UserAvatarProps>(({ pubkey, noProxy, relay, ...props }, ref) => {
const { imageProxy, proxyUserMedia } = useSubject(appSettings);
const metadata = useUserMetadata(pubkey, relay ? [relay] : undefined);
const picture = useMemo(() => {
@ -44,8 +44,11 @@ export const UserAvatar = React.memo(({ pubkey, noProxy, relay, ...props }: User
icon={<UserIdenticon pubkey={pubkey} />}
overflow="hidden"
title={getUserDisplayName(metadata, pubkey)}
ref={ref}
{...props}
/>
);
});
UserAvatar.displayName = "UserAvatar";
export default memo(UserAvatar);

View File

@ -1,5 +1,5 @@
import { validateEvent } from "nostr-tools";
import { NostrEvent, isDTag, isPTag } from "../../types/nostr-event";
import { NostrEvent, isDTag, isETag, isPTag } from "../../types/nostr-event";
export const SUBSCRIBED_COMMUNITIES_LIST_IDENTIFIER = "communities";
export const COMMUNITY_DEFINITION_KIND = 34550;
@ -47,3 +47,19 @@ export function validateCommunity(community: NostrEvent) {
return false;
}
}
export function buildApprovalMap(events: Iterable<NostrEvent>) {
const approvals = new Map<string, NostrEvent[]>();
for (const event of events) {
if (event.kind === COMMUNITY_APPROVAL_KIND) {
for (const tag of event.tags) {
if (isETag(tag)) {
const arr = approvals.get(tag[1]);
if (!arr) approvals.set(tag[1], [event]);
else arr.push(event);
}
}
}
}
return approvals;
}

View File

@ -38,7 +38,7 @@ import clientRelaysService from "../services/client-relays";
import replaceableEventLoaderService from "../services/replaceable-event-requester";
import useUserMuteList from "../hooks/use-user-mute-list";
import { DraftNostrEvent } from "../types/nostr-event";
import { UserAvatar } from "../components/user-avatar";
import UserAvatar from "../components/user-avatar";
import { UserLink } from "../components/user-link";
import { ChevronDownIcon } from "../components/icons";

View File

@ -28,7 +28,7 @@ import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-
import useSubject from "../../hooks/use-subject";
import { NostrEvent } from "../../types/nostr-event";
import { getEventCoordinate } from "../../helpers/nostr/events";
import { UserAvatarLink } from "../../components/user-avatar-link";
import UserAvatarLink from "../../components/user-avatar-link";
import { UserLink } from "../../components/user-link";
import Timestamp from "../../components/timestamp";
import VerticalPageLayout from "../../components/vertical-page-layout";

View File

@ -10,7 +10,7 @@ import { getEventUID } from "../../../helpers/nostr/events";
import { getSharableEventAddress } from "../../../helpers/nip19";
import { UserLink } from "../../../components/user-link";
import Timestamp from "../../../components/timestamp";
import { UserAvatarLink } from "../../../components/user-avatar-link";
import UserAvatarLink from "../../../components/user-avatar-link";
export default function BadgeAwardCard({ award, showImage = true }: { award: NostrEvent; showImage?: boolean }) {
const badge = useReplaceableEvent(getBadgeAwardBadge(award));

View File

@ -2,7 +2,7 @@ import { memo, useRef } from "react";
import { Link as RouterLink, useNavigate } from "react-router-dom";
import { ButtonGroup, Card, CardBody, CardHeader, CardProps, Flex, Heading, Image, Link, Text } from "@chakra-ui/react";
import { UserAvatarLink } from "../../../components/user-avatar-link";
import UserAvatarLink from "../../../components/user-avatar-link";
import { UserLink } from "../../../components/user-link";
import { getSharableEventAddress } from "../../../helpers/nip19";
import { NostrEvent } from "../../../types/nostr-event";

View File

@ -19,7 +19,7 @@ import { getEventUID } from "../../../helpers/nostr/events";
import { getCommunityImage, getCommunityName } from "../../../helpers/nostr/communities";
import CommunityDescription from "./community-description";
import useCountCommunityPosts from "../hooks/use-count-community-post";
import { UserAvatarLink } from "../../../components/user-avatar-link";
import UserAvatarLink from "../../../components/user-avatar-link";
import { UserLink } from "../../../components/user-link";
function CommunityCard({ community, ...props }: Omit<CardProps, "children"> & { community: NostrEvent }) {

View File

@ -1,6 +1,6 @@
import { AvatarGroup, AvatarGroupProps } from "@chakra-ui/react";
import { UserAvatarLink } from "../../../components/user-avatar-link";
import UserAvatarLink from "../../../components/user-avatar-link";
import { NostrEvent } from "../../../types/nostr-event";
import { getCommunityMods } from "../../../helpers/nostr/communities";

View File

@ -14,7 +14,7 @@ import CommunityCard from "./components/community-card";
function LoadCommunityCard({ pointer }: { pointer: AddressPointer }) {
const community = useReplaceableEvent(pointer);
if (!community) return <span>{nip19.naddrEncode(pointer)}</span>;
if (!community) return <span>Loading {pointer.identifier}</span>;
return <CommunityCard community={community} />;
}

View File

@ -1,22 +1,18 @@
import { Box, Button, ButtonGroup, Card, Flex, Heading, Text } from "@chakra-ui/react";
import { Button, ButtonGroup, Flex, Heading, Text } from "@chakra-ui/react";
import { Outlet, Link as RouterLink, useLocation } from "react-router-dom";
import { nip19 } from "nostr-tools";
import { Kind, nip19 } from "nostr-tools";
import {
getCommunityRelays as getCommunityRelays,
getCommunityImage,
getCommunityMods,
getCommunityName,
getCommunityDescription,
COMMUNITY_APPROVAL_KIND,
} from "../../helpers/nostr/communities";
import { NostrEvent } from "../../types/nostr-event";
import VerticalPageLayout from "../../components/vertical-page-layout";
import { UserAvatarLink } from "../../components/user-avatar-link";
import UserAvatarLink from "../../components/user-avatar-link";
import { UserLink } from "../../components/user-link";
import CommunityDescription from "../communities/components/community-description";
import CommunityJoinButton from "../communities/components/community-subscribe-button";
import { AdditionalRelayProvider } from "../../providers/additional-relay-context";
import { RelayIconStack } from "../../components/relay-icon-stack";
import TrendUp01 from "../../components/icons/trend-up-01";
import Clock from "../../components/icons/clock";
@ -24,6 +20,9 @@ import Hourglass03 from "../../components/icons/hourglass-03";
import VerticalCommunityDetails from "./components/vertical-community-details";
import { useBreakpointValue } from "../../providers/breakpoint-provider";
import HorizontalCommunityDetails from "./components/horizonal-community-details";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { getEventCoordinate, getEventUID } from "../../helpers/nostr/events";
function getCommunityPath(community: NostrEvent) {
return `/c/${encodeURIComponent(getCommunityName(community))}/${nip19.npubEncode(community.pubkey)}`;
@ -36,6 +35,11 @@ export default function CommunityHomePage({ community }: { community: NostrEvent
const verticalLayout = useBreakpointValue({ base: true, xl: false });
const communityRelays = getCommunityRelays(community);
const readRelays = useReadRelayUrls(communityRelays);
const timeline = useTimelineLoader(`${getEventUID(community)}-timeline`, readRelays, {
kinds: [Kind.Text, COMMUNITY_APPROVAL_KIND],
"#a": [getEventCoordinate(community)],
});
let active = "new";
if (location.pathname.endsWith("/pending")) active = "pending";
@ -76,6 +80,7 @@ export default function CommunityHomePage({ community }: { community: NostrEvent
leftIcon={<Clock />}
as={RouterLink}
to={getCommunityPath(community)}
replace
colorScheme={active === "new" ? "primary" : "gray"}
>
New
@ -84,13 +89,14 @@ export default function CommunityHomePage({ community }: { community: NostrEvent
leftIcon={<Hourglass03 />}
as={RouterLink}
to={getCommunityPath(community) + "/pending"}
replace
colorScheme={active == "pending" ? "primary" : "gray"}
>
Pending
</Button>
</ButtonGroup>
<Outlet context={{ community }} />
<Outlet context={{ community, timeline }} />
</Flex>
{!verticalLayout && <VerticalCommunityDetails community={community} w="full" maxW="xs" flexShrink={0} />}

View File

@ -18,7 +18,7 @@ import {
getCommunityRules,
} from "../../../helpers/nostr/communities";
import CommunityDescription from "../../communities/components/community-description";
import { UserAvatarLink } from "../../../components/user-avatar-link";
import UserAvatarLink from "../../../components/user-avatar-link";
import { UserLink } from "../../../components/user-link";
import { RelayIconStack } from "../../../components/relay-icon-stack";
import { NostrEvent } from "../../../types/nostr-event";

View File

@ -6,7 +6,7 @@ import {
getCommunityRules,
} from "../../../helpers/nostr/communities";
import CommunityDescription from "../../communities/components/community-description";
import { UserAvatarLink } from "../../../components/user-avatar-link";
import UserAvatarLink from "../../../components/user-avatar-link";
import { UserLink } from "../../../components/user-link";
import { RelayIconStack } from "../../../components/relay-icon-stack";
import { NostrEvent } from "../../../types/nostr-event";

View File

@ -1,68 +1,111 @@
import { useRef } from "react";
import { Flex } from "@chakra-ui/react";
import { useOutletContext } from "react-router-dom";
import { MouseEventHandler, memo, useCallback, useRef } from "react";
import { AvatarGroup, Card, CardBody, CardFooter, CardHeader, Flex, Heading, LinkBox, Text } from "@chakra-ui/react";
import { useOutletContext, Link as RouterLink } from "react-router-dom";
import dayjs from "dayjs";
import { unique } from "../../../helpers/array";
import {
COMMUNITY_APPROVAL_KIND,
getApprovedEmbeddedNote,
getCommunityMods,
getCommunityRelays,
} from "../../../helpers/nostr/communities";
import { getEventCoordinate, getEventUID } from "../../../helpers/nostr/events";
import { useReadRelayUrls } from "../../../hooks/use-client-relays";
import { COMMUNITY_APPROVAL_KIND, buildApprovalMap, getCommunityMods } from "../../../helpers/nostr/communities";
import { getEventUID } from "../../../helpers/nostr/events";
import useSubject from "../../../hooks/use-subject";
import { useTimelineCurserIntersectionCallback } from "../../../hooks/use-timeline-cursor-intersection-callback";
import useTimelineLoader from "../../../hooks/use-timeline-loader";
import { NostrEvent, isETag } from "../../../types/nostr-event";
import { EmbedEvent } from "../../../components/embed-event";
import useSingleEvent from "../../../hooks/use-single-event";
import { useAdditionalRelayContext } from "../../../providers/additional-relay-context";
import { NostrEvent } from "../../../types/nostr-event";
import IntersectionObserverProvider, { useRegisterIntersectionEntity } from "../../../providers/intersection-observer";
import TimelineActionAndStatus from "../../../components/timeline-page/timeline-action-and-status";
import PostVoteButtons from "../components/post-vote-buttions";
import TimelineLoader from "../../../classes/timeline-loader";
import UserAvatarLink from "../../../components/user-avatar-link";
import { useNavigateInDrawer } from "../../../providers/drawer-sub-view-provider";
import { getSharableEventAddress } from "../../../helpers/nip19";
import { InlineNoteContent } from "../../../components/note/inline-note-content";
import HoverLinkOverlay from "../../../components/hover-link-overlay";
import { UserLink } from "../../../components/user-link";
function ApprovedEvent({ approval, community }: { approval: NostrEvent; community: NostrEvent }) {
const ref = useRef<HTMLDivElement | null>(null);
function ApprovalIcon({ approval }: { approval: NostrEvent }) {
const ref = useRef<HTMLAnchorElement | null>(null);
useRegisterIntersectionEntity(ref, getEventUID(approval));
const additionalRelays = useAdditionalRelayContext();
const embeddedEvent = getApprovedEmbeddedNote(approval);
const eventTag = approval.tags.find(isETag);
const loadEvent = useSingleEvent(
eventTag?.[1],
eventTag?.[2] ? [eventTag[2], ...additionalRelays] : additionalRelays,
);
const event = loadEvent || embeddedEvent;
if (!event) return;
return (
<Flex ref={ref} gap="2" alignItems="flex-start" overflow="hidden">
<PostVoteButtons event={event} community={community} />
<EmbedEvent event={event} flex={1} />
</Flex>
);
return <UserAvatarLink pubkey={approval.pubkey} ref={ref} size="xs" />;
}
const ApprovedEvent = memo(
({ event, approvals, community }: { event: NostrEvent; approvals: NostrEvent[]; community: NostrEvent }) => {
const ref = useRef<HTMLDivElement | null>(null);
useRegisterIntersectionEntity(ref, getEventUID(event));
// const additionalRelays = useAdditionalRelayContext();
// const embeddedEvent = getApprovedEmbeddedNote(approval);
// const eventTag = approval.tags.find(isETag);
// const loadEvent = useSingleEvent(
// eventTag?.[1],
// eventTag?.[2] ? [eventTag[2], ...additionalRelays] : additionalRelays,
// );
// const event = loadEvent || embeddedEvent;
// if (!event) return;
const navigate = useNavigateInDrawer();
const to = `/n/${getSharableEventAddress(event)}`;
const handleClick = useCallback<MouseEventHandler>(
(e) => {
e.preventDefault();
navigate(to);
},
[navigate, to],
);
return (
<Flex ref={ref} gap="2" alignItems="flex-start" overflow="hidden">
<PostVoteButtons event={event} community={community} />
<Flex gap="2" direction="column" flex={1}>
<Card as={LinkBox}>
<CardHeader px="2" pt="4" pb="0">
<Heading size="md">
<HoverLinkOverlay as={RouterLink} to={to} onClick={handleClick}>
{event.content.match(/^[^\n\t]+/)}
</HoverLinkOverlay>
</Heading>
</CardHeader>
<CardBody p="2">
<InlineNoteContent event={event} maxLength={96} />
</CardBody>
<CardFooter display="flex" gap="2" alignItems="center" p="2">
<Text>
Posted {dayjs.unix(event.created_at).fromNow()} by <UserLink pubkey={event.pubkey} fontWeight="bold" />
</Text>
<Text fontSize="sm" ml="auto">
Approved by
</Text>
<AvatarGroup>
{approvals.map((approval) => (
<ApprovalIcon key={approval.id} approval={approval} />
))}
</AvatarGroup>
</CardFooter>
</Card>
</Flex>
</Flex>
);
},
);
export default function CommunityNewestView() {
const { community } = useOutletContext() as { community: NostrEvent };
const { community, timeline } = useOutletContext() as { community: NostrEvent; timeline: TimelineLoader };
const mods = getCommunityMods(community);
const readRelays = useReadRelayUrls(getCommunityRelays(community));
const timeline = useTimelineLoader(`${getEventUID(community)}-approved-posts`, readRelays, {
authors: unique([community.pubkey, ...mods]),
kinds: [COMMUNITY_APPROVAL_KIND],
"#a": [getEventCoordinate(community)],
});
const events = useSubject(timeline.timeline);
const approvalMap = buildApprovalMap(events);
const approvals = useSubject(timeline.timeline);
const approved = events
.filter((e) => approvalMap.has(e.id))
.map((event) => ({ event, approvals: approvalMap.get(event.id) }));
const approvals = events.filter((e) => e.kind === COMMUNITY_APPROVAL_KIND && mods.includes(e.pubkey));
const callback = useTimelineCurserIntersectionCallback(timeline);
return (
<>
<IntersectionObserverProvider callback={callback}>
{approvals.map((approval) => (
<ApprovedEvent key={getEventUID(approval)} approval={approval} community={community} />
{approved.map(({ event, approvals }) => (
<ApprovedEvent key={event.id} event={event} approvals={approvals ?? []} community={community} />
))}
</IntersectionObserverProvider>
<TimelineActionAndStatus timeline={timeline} />

View File

@ -1,19 +1,16 @@
import { useCallback, useRef } from "react";
import { useRef } from "react";
import { Box } from "@chakra-ui/react";
import { useOutletContext } from "react-router-dom";
import { Kind } from "nostr-tools";
import { NostrEvent, isETag } from "../../../types/nostr-event";
import { getEventCoordinate, getEventUID } from "../../../helpers/nostr/events";
import { COMMUNITY_APPROVAL_KIND, getCommunityRelays } from "../../../helpers/nostr/communities";
import { useReadRelayUrls } from "../../../hooks/use-client-relays";
import useTimelineLoader from "../../../hooks/use-timeline-loader";
import { NostrEvent } from "../../../types/nostr-event";
import { getEventUID } from "../../../helpers/nostr/events";
import { COMMUNITY_APPROVAL_KIND, buildApprovalMap } from "../../../helpers/nostr/communities";
import useSubject from "../../../hooks/use-subject";
import IntersectionObserverProvider, { useRegisterIntersectionEntity } from "../../../providers/intersection-observer";
import { EmbedEvent } from "../../../components/embed-event";
import { useTimelineCurserIntersectionCallback } from "../../../hooks/use-timeline-cursor-intersection-callback";
import TimelineActionAndStatus from "../../../components/timeline-page/timeline-action-and-status";
import EventStore from "../../../classes/event-store";
import TimelineLoader from "../../../classes/timeline-loader";
function PendingPost({ event }: { event: NostrEvent }) {
const ref = useRef<HTMLDivElement | null>(null);
@ -27,32 +24,12 @@ function PendingPost({ event }: { event: NostrEvent }) {
}
export default function CommunityPendingView() {
const { community } = useOutletContext() as { community: NostrEvent };
const readRelays = useReadRelayUrls(getCommunityRelays(community));
const eventFilter = useCallback((event: NostrEvent, store: EventStore) => event.kind !== COMMUNITY_APPROVAL_KIND, []);
const timeline = useTimelineLoader(
`${getEventUID(community)}-pending-posts`,
readRelays,
{
kinds: [Kind.Text, COMMUNITY_APPROVAL_KIND],
"#a": [getEventCoordinate(community)],
},
{ eventFilter },
);
const { community, timeline } = useOutletContext() as { community: NostrEvent; timeline: TimelineLoader };
const events = useSubject(timeline.timeline);
const approvals = new Set<string>();
for (const [_, event] of timeline.events.events) {
if (event.kind === COMMUNITY_APPROVAL_KIND) {
for (const tag of event.tags) {
if (isETag(tag)) approvals.add(tag[1]);
}
}
}
const pending = events.filter((e) => !approvals.has(e.id));
const approvals = buildApprovalMap(events);
const pending = events.filter((e) => e.kind !== COMMUNITY_APPROVAL_KIND && !approvals.has(e.id));
const callback = useTimelineCurserIntersectionCallback(timeline);

View File

@ -14,7 +14,7 @@ import {
Text,
} from "@chakra-ui/react";
import { UserAvatarLink } from "../../../components/user-avatar-link";
import UserAvatarLink from "../../../components/user-avatar-link";
import { UserLink } from "../../../components/user-link";
import { getSharableEventAddress } from "../../../helpers/nip19";
import { NostrEvent } from "../../../types/nostr-event";

View File

@ -2,7 +2,7 @@ import { memo, useRef } from "react";
import { Link as RouterLink } from "react-router-dom";
import { ButtonGroup, Card, CardBody, CardHeader, CardProps, Flex, Heading, Link, Text } from "@chakra-ui/react";
import { UserAvatarLink } from "../../../components/user-avatar-link";
import UserAvatarLink from "../../../components/user-avatar-link";
import { UserLink } from "../../../components/user-link";
import { getSharableEventAddress } from "../../../helpers/nip19";
import { NostrEvent } from "../../../types/nostr-event";

View File

@ -4,7 +4,7 @@ import { getEventUID } from "../../../helpers/nostr/events";
import { getGoalRelays } from "../../../helpers/nostr/goal";
import useEventZaps from "../../../hooks/use-event-zaps";
import { NostrEvent } from "../../../types/nostr-event";
import { UserAvatarLink } from "../../../components/user-avatar-link";
import UserAvatarLink from "../../../components/user-avatar-link";
import { UserLink } from "../../../components/user-link";
import { readablizeSats } from "../../../helpers/bolt11";
import { LightningIcon } from "../../../components/icons";

View File

@ -3,7 +3,7 @@ import { getEventUID } from "../../../helpers/nostr/events";
import { getGoalRelays } from "../../../helpers/nostr/goal";
import useEventZaps from "../../../hooks/use-event-zaps";
import { NostrEvent } from "../../../types/nostr-event";
import { UserAvatarLink } from "../../../components/user-avatar-link";
import UserAvatarLink from "../../../components/user-avatar-link";
import { UserLink } from "../../../components/user-link";
import { readablizeSats } from "../../../helpers/bolt11";
import { LightningIcon } from "../../../components/icons";

View File

@ -9,7 +9,7 @@ import GoalProgress from "./components/goal-progress";
import useSingleEvent from "../../hooks/use-single-event";
import { isHexKey } from "../../helpers/nip19";
import { EventPointer } from "nostr-tools/lib/types/nip19";
import { UserAvatar } from "../../components/user-avatar";
import UserAvatar from "../../components/user-avatar";
import { UserLink } from "../../components/user-link";
import GoalContents from "./components/goal-contents";
import GoalZapList from "./components/goal-zap-list";

View File

@ -15,7 +15,7 @@ import {
} from "@chakra-ui/react";
import { Kind, nip19 } from "nostr-tools";
import { UserAvatarLink } from "../../../components/user-avatar-link";
import UserAvatarLink from "../../../components/user-avatar-link";
import { UserLink } from "../../../components/user-link";
import {
getEventsFromList,

View File

@ -4,7 +4,7 @@ import { nip19 } from "nostr-tools";
import { useUserMetadata } from "../../../hooks/use-user-metadata";
import { getUserDisplayName } from "../../../helpers/user-metadata";
import { UserAvatar } from "../../../components/user-avatar";
import UserAvatar from "../../../components/user-avatar";
import { UserDnsIdentityIcon } from "../../../components/user-dns-identity-icon";
import { NostrEvent } from "../../../types/nostr-event";
import useAsyncErrorHandler from "../../../hooks/use-async-error-handler";

View File

@ -5,7 +5,7 @@ import { Kind } from "nostr-tools";
import { Navigate, useNavigate, useParams } from "react-router-dom";
import { ChevronLeftIcon } from "../../components/icons";
import { UserAvatar } from "../../components/user-avatar";
import UserAvatar from "../../components/user-avatar";
import { UserLink } from "../../components/user-link";
import { normalizeToHex } from "../../helpers/nip19";
import useSubject from "../../hooks/use-subject";

View File

@ -16,7 +16,7 @@ import {
} from "@chakra-ui/react";
import dayjs from "dayjs";
import { Link as RouterLink } from "react-router-dom";
import { UserAvatar } from "../../components/user-avatar";
import UserAvatar from "../../components/user-avatar";
import { getUserDisplayName } from "../../helpers/user-metadata";
import useSubject from "../../hooks/use-subject";
import { useUserMetadata } from "../../hooks/use-user-metadata";

View File

@ -14,7 +14,7 @@ import {
renderVideoUrl,
} from "../../components/embed-types";
import { useRegisterIntersectionEntity } from "../../providers/intersection-observer";
import { UserAvatar } from "../../components/user-avatar";
import UserAvatar from "../../components/user-avatar";
import { UserLink } from "../../components/user-link";
import { getEventUID } from "../../helpers/nostr/events";
import Timestamp from "../../components/timestamp";

View File

@ -2,7 +2,7 @@ import { ReactNode, forwardRef, memo, useMemo, useRef } from "react";
import { Box, Card, Flex, Text } from "@chakra-ui/react";
import { Kind, nip18, nip25 } from "nostr-tools";
import { UserAvatar } from "../../components/user-avatar";
import UserAvatar from "../../components/user-avatar";
import { UserLink } from "../../components/user-link";
import { useCurrentAccount } from "../../hooks/use-current-account";
import { NostrEvent, isATag, isETag } from "../../types/nostr-event";

View File

@ -21,7 +21,7 @@ import {
} from "@chakra-ui/react";
import { useState } from "react";
import { useRelayInfo } from "../../../hooks/use-relay-info";
import { UserAvatar } from "../../../components/user-avatar";
import UserAvatar from "../../../components/user-avatar";
import { UserLink } from "../../../components/user-link";
import { safeRelayUrl } from "../../../helpers/url";
import { useDebounce } from "react-use";

View File

@ -30,7 +30,7 @@ import { useRelayInfo } from "../../../hooks/use-relay-info";
import { RelayFavicon } from "../../../components/relay-favicon";
import { CodeIcon } from "../../../components/icons";
import { UserLink } from "../../../components/user-link";
import { UserAvatar } from "../../../components/user-avatar";
import UserAvatar from "../../../components/user-avatar";
import { useClientRelays } from "../../../hooks/use-client-relays";
import clientRelaysService from "../../../services/client-relays";
import { RelayMode } from "../../../classes/relay";

View File

@ -2,7 +2,7 @@ import { useRef } from "react";
import { Card, CardBody, CardHeader, Link } from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom";
import { UserAvatarLink } from "../../../components/user-avatar-link";
import UserAvatarLink from "../../../components/user-avatar-link";
import { UserLink } from "../../../components/user-link";
import { UserDnsIdentityIcon } from "../../../components/user-dns-identity-icon";
import StarRating from "../../../components/star-rating";

View File

@ -25,7 +25,7 @@ import userRelaysService from "../../services/user-relays";
import { NostrEvent } from "../../types/nostr-event";
import { RelayFavicon } from "../../components/relay-favicon";
import { ChevronLeftIcon } from "../../components/icons";
import { UserAvatar } from "../../components/user-avatar";
import UserAvatar from "../../components/user-avatar";
import { RelayMetadata, RelayPaidTag } from "./components/relay-card";
function usePopularContactsRelays(list?: NostrEvent) {

View File

@ -20,7 +20,7 @@ import useTimelineLoader from "../../hooks/use-timeline-loader";
import useSubject from "../../hooks/use-subject";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
import IntersectionObserverProvider from "../../providers/intersection-observer";
import { UserAvatar } from "../../components/user-avatar";
import UserAvatar from "../../components/user-avatar";
import TimelineActionAndStatus from "../../components/timeline-page/timeline-action-and-status";
import { UserDnsIdentityIcon } from "../../components/user-dns-identity-icon";
import { embedNostrLinks, renderGenericUrl } from "../../components/embed-types";

View File

@ -3,7 +3,7 @@ import { Box, IconButton, Text } from "@chakra-ui/react";
import { getUserDisplayName } from "../../../helpers/user-metadata";
import { useUserMetadata } from "../../../hooks/use-user-metadata";
import accountService, { Account } from "../../../services/account";
import { UserAvatar } from "../../../components/user-avatar";
import UserAvatar from "../../../components/user-avatar";
import AccountInfoBadge from "../../../components/account-info-badge";
export default function AccountCard({ account }: { account: Account }) {

View File

@ -2,7 +2,7 @@ import { Box, Button, Card, Flex, Heading, Text } from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom";
import { useAsync } from "react-use";
import { UserAvatarLink } from "../../components/user-avatar-link";
import UserAvatarLink from "../../components/user-avatar-link";
import { UserLink } from "../../components/user-link";
import { containerProps } from "./common";
import { UserFollowButton } from "../../components/user-follow-button";

View File

@ -3,7 +3,7 @@ import { Box, Card, CardBody, CardProps, Flex, Heading, LinkBox, LinkOverlay, Te
import { ParsedStream } from "../../../helpers/nostr/stream";
import { Link as RouterLink } from "react-router-dom";
import { UserAvatar } from "../../../components/user-avatar";
import UserAvatar from "../../../components/user-avatar";
import { UserLink } from "../../../components/user-link";
import StreamStatusBadge from "./status-badge";
import { useRegisterIntersectionEntity } from "../../../providers/intersection-observer";

View File

@ -8,7 +8,7 @@ import { readablizeSats } from "../../../helpers/bolt11";
import useStreamChatTimeline from "../stream/stream-chat/use-stream-chat-timeline";
import { ParsedStream } from "../../../helpers/nostr/stream";
import useSubject from "../../../hooks/use-subject";
import { UserAvatarLink } from "../../../components/user-avatar-link";
import UserAvatarLink from "../../../components/user-avatar-link";
export default function TopZappers({ stream, ...props }: FlexProps & { stream: ParsedStream }) {
const timeline = useStreamChatTimeline(stream);

View File

@ -25,7 +25,7 @@ import { useReadRelayUrls } from "../../../hooks/use-client-relays";
import { unique } from "../../../helpers/array";
import { LiveVideoPlayer } from "../../../components/live-video-player";
import StreamChat, { ChatDisplayMode } from "./stream-chat";
import { UserAvatarLink } from "../../../components/user-avatar-link";
import UserAvatarLink from "../../../components/user-avatar-link";
import { UserLink } from "../../../components/user-link";
import StreamSummaryContent from "../components/stream-summary-content";
import { ChevronLeftIcon, ExternalLinkIcon } from "../../../components/icons";

View File

@ -2,7 +2,7 @@ import React, { useRef } from "react";
import { Box, Text } from "@chakra-ui/react";
import { ParsedStream } from "../../../../helpers/nostr/stream";
import { UserAvatar } from "../../../../components/user-avatar";
import UserAvatar from "../../../../components/user-avatar";
import { UserLink } from "../../../../components/user-link";
import { NostrEvent } from "../../../../types/nostr-event";
import { useRegisterIntersectionEntity } from "../../../../providers/intersection-observer";

View File

@ -2,7 +2,7 @@ import React, { useMemo, useRef } from "react";
import { Box, Flex, Text } from "@chakra-ui/react";
import { ParsedStream } from "../../../../helpers/nostr/stream";
import { UserAvatar } from "../../../../components/user-avatar";
import UserAvatar from "../../../../components/user-avatar";
import { UserLink } from "../../../../components/user-link";
import { NostrEvent } from "../../../../types/nostr-event";
import { useRegisterIntersectionEntity } from "../../../../providers/intersection-observer";

View File

@ -4,7 +4,7 @@ import { memo, useMemo, useState } from "react";
import { useCurrentAccount } from "../../hooks/use-current-account";
import RequireCurrentAccount from "../../providers/require-current-account";
import { useNetworkConnectionCount } from "../../hooks/use-user-network";
import { UserAvatarLink } from "../../components/user-avatar-link";
import UserAvatarLink from "../../components/user-avatar-link";
import { UserLink } from "../../components/user-link";
import { ChevronLeftIcon } from "../../components/icons";
import { useNavigate } from "react-router-dom";

View File

@ -5,7 +5,7 @@ import dayjs from "dayjs";
import useSubject from "../../../hooks/use-subject";
import { useCurrentAccount } from "../../../hooks/use-current-account";
import useStreamChatTimeline from "../../streams/stream/stream-chat/use-stream-chat-timeline";
import { UserAvatar } from "../../../components/user-avatar";
import UserAvatar from "../../../components/user-avatar";
import { UserLink } from "../../../components/user-link";
import useUserMuteFunctions from "../../../hooks/use-user-mute-functions";
import { useMuteModalContext } from "../../../providers/mute-modal-provider";

View File

@ -21,7 +21,7 @@ import {
useDisclosure,
} from "@chakra-ui/react";
import { useAsync } from "react-use";
import { nip19 } from "nostr-tools";
import { Kind, nip19 } from "nostr-tools";
import { readablizeSats } from "../../../helpers/bolt11";
import { getUserDisplayName } from "../../../helpers/user-metadata";
@ -44,7 +44,7 @@ import {
import { CopyIconButton } from "../../../components/copy-icon-button";
import { QrIconButton } from "../components/share-qr-button";
import { UserDnsIdentityIcon } from "../../../components/user-dns-identity-icon";
import { UserAvatar } from "../../../components/user-avatar";
import UserAvatar from "../../../components/user-avatar";
import { ChatIcon } from "@chakra-ui/icons";
import { UserFollowButton } from "../../../components/user-follow-button";
import UserZapButton from "../components/user-zap-button";
@ -54,6 +54,7 @@ import useUserContactList from "../../../hooks/use-user-contact-list";
import { getPubkeysFromList } from "../../../helpers/nostr/lists";
import Timestamp from "../../../components/timestamp";
import UserProfileBadges from "./user-profile-badges";
import useEventCount from "../../../hooks/use-event-count";
function buildDescriptionContent(description: string) {
let content: EmbedableContent = [description.trim()];
@ -75,6 +76,7 @@ export default function UserAboutTab() {
const nprofile = useSharableProfileId(pubkey);
const { value: stats } = useAsync(() => trustedUserStatsService.getUserStats(pubkey), [pubkey]);
const followerCount = useEventCount({ "#p": [pubkey], kinds: [Kind.Contacts] });
const aboutContent = metadata?.about && buildDescriptionContent(metadata?.about);
const parsedNip05 = metadata?.nip05 ? parseAddress(metadata.nip05) : undefined;
@ -211,7 +213,7 @@ export default function UserAboutTab() {
<>
<Stat>
<StatLabel>Followers</StatLabel>
<StatNumber>{readablizeSats(stats.followers_pubkey_count) || 0}</StatNumber>
<StatNumber>{readablizeSats(followerCount ?? 0) || 0}</StatNumber>
</Stat>
<Stat>

View File

@ -23,7 +23,7 @@ import { getBadgeDescription, getBadgeImage, getBadgeName } from "../../../helpe
import { getEventCoordinate } from "../../../helpers/nostr/events";
import { NostrEvent } from "../../../types/nostr-event";
import { getSharableEventAddress } from "../../../helpers/nip19";
import { UserAvatarLink } from "../../../components/user-avatar-link";
import UserAvatarLink from "../../../components/user-avatar-link";
import { UserLink } from "../../../components/user-link";
import Timestamp from "../../../components/timestamp";

View File

@ -2,7 +2,7 @@ import { Flex, Heading, IconButton, Spacer } from "@chakra-ui/react";
import { useNavigate } from "react-router-dom";
import { EditIcon, GhostIcon } from "../../../components/icons";
import { UserAvatar } from "../../../components/user-avatar";
import UserAvatar from "../../../components/user-avatar";
import { UserDnsIdentityIcon } from "../../../components/user-dns-identity-icon";
import { getUserDisplayName } from "../../../helpers/user-metadata";
import { useCurrentAccount } from "../../../hooks/use-current-account";

View File

@ -4,7 +4,7 @@ import { nip19 } from "nostr-tools";
import { useUserMetadata } from "../../../hooks/use-user-metadata";
import { getUserDisplayName } from "../../../helpers/user-metadata";
import { UserAvatar } from "../../../components/user-avatar";
import UserAvatar from "../../../components/user-avatar";
import { UserDnsIdentityIcon } from "../../../components/user-dns-identity-icon";
import { UserFollowButton } from "../../../components/user-follow-button";

View File

@ -11,7 +11,7 @@ import TimelineActionAndStatus from "../../components/timeline-page/timeline-act
import { useMemo, useRef } from "react";
import { getEventUID } from "../../helpers/nostr/events";
import { UserLink } from "../../components/user-link";
import { UserAvatarLink } from "../../components/user-avatar-link";
import UserAvatarLink from "../../components/user-avatar-link";
function FollowerItem({ event }: { event: Event }) {
const ref = useRef<HTMLDivElement | null>(null);

View File

@ -2,7 +2,7 @@ import { memo, useMemo, useRef } from "react";
import { Flex, Heading, SimpleGrid } from "@chakra-ui/react";
import { useOutletContext } from "react-router-dom";
import { UserAvatarLink } from "../../components/user-avatar-link";
import UserAvatarLink from "../../components/user-avatar-link";
import { UserLink } from "../../components/user-link";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { useReadRelayUrls } from "../../hooks/use-client-relays";

View File

@ -12,7 +12,7 @@ import useSubject from "../../hooks/use-subject";
import IntersectionObserverProvider, { useRegisterIntersectionEntity } from "../../providers/intersection-observer";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
import { TrustProvider } from "../../providers/trust";
import { UserAvatar } from "../../components/user-avatar";
import UserAvatar from "../../components/user-avatar";
import { UserLink } from "../../components/user-link";
import NoteMenu from "../../components/note/note-menu";
import { EmbedEventPointer } from "../../components/embed-event";

View File

@ -6,7 +6,7 @@ import { useOutletContext } from "react-router-dom";
import { ErrorBoundary, ErrorFallback } from "../../components/error-boundary";
import { LightningIcon } from "../../components/icons";
import { NoteLink } from "../../components/note-link";
import { UserAvatarLink } from "../../components/user-avatar-link";
import UserAvatarLink from "../../components/user-avatar-link";
import { UserLink } from "../../components/user-link";
import { readablizeSats } from "../../helpers/bolt11";
import { isProfileZap, isNoteZap, parseZapEvent, totalZaps } from "../../helpers/nostr/zaps";