mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-03-31 16:08:52 +02:00
build better community post component
cleanup exports on common components
This commit is contained in:
parent
144a249317
commit
62a729a805
@ -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";
|
||||
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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 }) {
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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">) {
|
||||
|
@ -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";
|
||||
|
@ -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;
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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) {
|
||||
|
@ -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"> & {
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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 }) {
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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));
|
||||
|
@ -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";
|
||||
|
@ -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 }) {
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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} />;
|
||||
}
|
||||
|
||||
|
@ -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} />}
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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} />
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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,
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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) {
|
||||
|
@ -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";
|
||||
|
@ -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 }) {
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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);
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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>
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
Loading…
x
Reference in New Issue
Block a user