mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-09-27 03:56:44 +02:00
improve relay icons
This commit is contained in:
@@ -89,7 +89,7 @@ export const Note = React.memo(({ event, maxHeight, variant = "outline" }: NoteP
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<NoteRelays event={event} size="sm" variant="link" />
|
<NoteRelays event={event} />
|
||||||
<NoteMenu event={event} size="sm" variant="link" aria-label="More Options" />
|
<NoteMenu event={event} size="sm" variant="link" aria-label="More Options" />
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
|
@@ -1,28 +1,18 @@
|
|||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
import { IconButton, IconButtonProps, Flex, useDisclosure } from "@chakra-ui/react";
|
import { IconButtonProps } from "@chakra-ui/react";
|
||||||
import { getEventRelays } from "../../services/event-relays";
|
import { getEventRelays } from "../../services/event-relays";
|
||||||
import { NostrEvent } from "../../types/nostr-event";
|
import { NostrEvent } from "../../types/nostr-event";
|
||||||
import { RelayIcon } from "../icons";
|
|
||||||
import { RelayFavicon } from "../relay-favicon";
|
|
||||||
import useSubject from "../../hooks/use-subject";
|
import useSubject from "../../hooks/use-subject";
|
||||||
|
import { RelayIconStack } from "../relay-icon-stack";
|
||||||
import { useIsMobile } from "../../hooks/use-is-mobile";
|
import { useIsMobile } from "../../hooks/use-is-mobile";
|
||||||
|
|
||||||
export type NoteRelaysProps = Omit<IconButtonProps, "icon" | "aria-label"> & {
|
export type NoteRelaysProps = {
|
||||||
event: NostrEvent;
|
event: NostrEvent;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NoteRelays = memo(({ event, ...props }: NoteRelaysProps) => {
|
export const NoteRelays = memo(({ event }: NoteRelaysProps) => {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
const eventRelays = useSubject(getEventRelays(event.id));
|
const eventRelays = useSubject(getEventRelays(event.id));
|
||||||
const { isOpen, onOpen } = useDisclosure();
|
|
||||||
|
|
||||||
return isOpen || !isMobile ? (
|
return <RelayIconStack relays={eventRelays} direction="row-reverse" maxRelays={isMobile ? 4 : undefined} />;
|
||||||
<Flex alignItems="center" gap="-4">
|
|
||||||
{eventRelays.map((url) => (
|
|
||||||
<RelayFavicon key={url} relay={url} size="2xs" title={url} />
|
|
||||||
))}
|
|
||||||
</Flex>
|
|
||||||
) : (
|
|
||||||
<IconButton icon={<RelayIcon />} size="xs" aria-label="Relays" onClick={onOpen} variant="link" />
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
56
src/components/relay-icon-stack.tsx
Normal file
56
src/components/relay-icon-stack.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
Box,
|
||||||
|
Flex,
|
||||||
|
FlexProps,
|
||||||
|
IconButton,
|
||||||
|
Modal,
|
||||||
|
ModalBody,
|
||||||
|
ModalCloseButton,
|
||||||
|
ModalContent,
|
||||||
|
ModalHeader,
|
||||||
|
ModalOverlay,
|
||||||
|
Text,
|
||||||
|
useDisclosure,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
|
import { RelayFavicon } from "./relay-favicon";
|
||||||
|
import relayScoreboardService from "../services/relay-scoreboard";
|
||||||
|
|
||||||
|
export function RelayIconStack({ relays, maxRelays, ...props }: { relays: string[]; maxRelays?: number } & FlexProps) {
|
||||||
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
|
|
||||||
|
const topRelays = relayScoreboardService.getRankedRelays(relays);
|
||||||
|
const clamped = maxRelays ? topRelays.slice(0, maxRelays) : topRelays;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex alignItems="center" gap="-4" overflow="hidden" cursor="pointer" onClick={onOpen} {...props}>
|
||||||
|
{clamped.map((url) => (
|
||||||
|
<RelayFavicon key={url} relay={url} size="2xs" title={url} />
|
||||||
|
))}
|
||||||
|
{clamped.length !== topRelays.length && (
|
||||||
|
<Text mx="1" fontSize="sm" lineHeight={0}>
|
||||||
|
+{topRelays.length - clamped.length}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
<Modal isOpen={isOpen} onClose={onClose}>
|
||||||
|
<ModalOverlay />
|
||||||
|
<ModalContent>
|
||||||
|
<ModalHeader>Relays</ModalHeader>
|
||||||
|
<ModalCloseButton />
|
||||||
|
<ModalBody>
|
||||||
|
<Flex direction="column" gap="1">
|
||||||
|
{topRelays.map((url) => (
|
||||||
|
<Flex key={url}>
|
||||||
|
<RelayFavicon relay={url} size="2xs" mr="2" />
|
||||||
|
<Text>{url}</Text>
|
||||||
|
</Flex>
|
||||||
|
))}
|
||||||
|
</Flex>
|
||||||
|
</ModalBody>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
import { Box, Button, Flex, FormControl, FormLabel, Spinner, Switch, useDisclosure } from "@chakra-ui/react";
|
import { Button, Flex, FormControl, FormLabel, Spinner, Switch, useDisclosure } from "@chakra-ui/react";
|
||||||
import { useOutletContext } from "react-router-dom";
|
import { useOutletContext } from "react-router-dom";
|
||||||
import { Note } from "../../components/note";
|
import { Note } from "../../components/note";
|
||||||
import RepostNote from "../../components/repost-note";
|
import RepostNote from "../../components/repost-note";
|
||||||
@@ -8,10 +8,11 @@ import userTimelineService from "../../services/user-timeline";
|
|||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import useSubject from "../../hooks/use-subject";
|
import useSubject from "../../hooks/use-subject";
|
||||||
import { useMount, useUnmount } from "react-use";
|
import { useMount, useUnmount } from "react-use";
|
||||||
|
import { RelayIconStack } from "../../components/relay-icon-stack";
|
||||||
|
|
||||||
const UserNotesTab = () => {
|
const UserNotesTab = () => {
|
||||||
const { pubkey } = useOutletContext() as { pubkey: string };
|
const { pubkey } = useOutletContext() as { pubkey: string };
|
||||||
const contextRelays = useAdditionalRelayContext();
|
const readRelays = useAdditionalRelayContext();
|
||||||
|
|
||||||
const { isOpen: showReplies, onToggle: toggleReplies } = useDisclosure();
|
const { isOpen: showReplies, onToggle: toggleReplies } = useDisclosure();
|
||||||
const { isOpen: hideReposts, onToggle: toggleReposts } = useDisclosure();
|
const { isOpen: hideReposts, onToggle: toggleReposts } = useDisclosure();
|
||||||
@@ -22,8 +23,8 @@ const UserNotesTab = () => {
|
|||||||
const loading = useSubject(timeline.loading);
|
const loading = useSubject(timeline.loading);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
timeline.setRelays(contextRelays);
|
timeline.setRelays(readRelays);
|
||||||
}, [timeline, contextRelays.join("|")]);
|
}, [timeline, readRelays.join("|")]);
|
||||||
|
|
||||||
useMount(() => timeline.open());
|
useMount(() => timeline.open());
|
||||||
useUnmount(() => timeline.close());
|
useUnmount(() => timeline.close());
|
||||||
@@ -45,7 +46,7 @@ const UserNotesTab = () => {
|
|||||||
<FormLabel htmlFor="reposts" mb="0">
|
<FormLabel htmlFor="reposts" mb="0">
|
||||||
Reposts
|
Reposts
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Box flexGrow={1} />
|
<RelayIconStack ml="auto" relays={readRelays} direction="row-reverse" mr="4" maxRelays={4} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
{filteredEvents.map((event) =>
|
{filteredEvents.map((event) =>
|
||||||
event.kind === 6 ? (
|
event.kind === 6 ? (
|
||||||
|
Reference in New Issue
Block a user