mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-04-05 02:20:26 +02:00
More list features
This commit is contained in:
parent
0af6c2cfcd
commit
8ea8c88c52
5
.changeset/fifty-lamps-itch.md
Normal file
5
.changeset/fifty-lamps-itch.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"nostrudel": minor
|
||||
---
|
||||
|
||||
Add more details to publish details modal
|
5
.changeset/old-news-lie.md
Normal file
5
.changeset/old-news-lie.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"nostrudel": minor
|
||||
---
|
||||
|
||||
Filter relay reviews by list
|
@ -27,7 +27,6 @@ export default function PeopleListSelection({
|
||||
const { list, setList, listEvent } = usePeopleListContext();
|
||||
|
||||
const handleSelect = (value: string | string[]) => {
|
||||
console.log(value);
|
||||
if (typeof value === "string") {
|
||||
setList(value);
|
||||
}
|
||||
@ -36,7 +35,7 @@ export default function PeopleListSelection({
|
||||
return (
|
||||
<Menu>
|
||||
<MenuButton as={Button} {...props}>
|
||||
{listEvent ? getListName(listEvent) : list === "global" ? "Global" : "Following"}
|
||||
{listEvent ? getListName(listEvent) : list === "global" ? "Global" : "Loading..."}
|
||||
</MenuButton>
|
||||
<MenuList zIndex={100}>
|
||||
<MenuOptionGroup value={list} onChange={handleSelect} type="radio">
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Alert, AlertDescription, AlertIcon, AlertTitle, Box, Flex, FlexProps, Progress } from "@chakra-ui/react";
|
||||
import { Alert, AlertDescription, AlertIcon, AlertTitle, Box, Flex, FlexProps, Link, Progress } from "@chakra-ui/react";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
import NostrPublishAction from "../classes/nostr-publish-action";
|
||||
import useSubject from "../hooks/use-subject";
|
||||
import { RelayPaidTag } from "../views/relays/components/relay-card";
|
||||
|
||||
export type PostResultsProps = {
|
||||
pub: NostrPublishAction;
|
||||
@ -16,7 +18,12 @@ export const PublishDetails = ({ pub }: PostResultsProps & Omit<FlexProps, "chil
|
||||
<Alert key={result.relay.url} status={result.status ? "success" : "warning"}>
|
||||
<AlertIcon />
|
||||
<Box>
|
||||
<AlertTitle>{result.relay.url}</AlertTitle>
|
||||
<AlertTitle>
|
||||
<Link as={RouterLink} to={`/r/${encodeURIComponent(result.relay.url)}`}>
|
||||
{result.relay.url}
|
||||
</Link>
|
||||
<RelayPaidTag url={result.relay.url} />
|
||||
</AlertTitle>
|
||||
{result.message && <AlertDescription>{result.message}</AlertDescription>}
|
||||
</Box>
|
||||
</Alert>
|
||||
|
@ -1,18 +1,24 @@
|
||||
import { useCallback } from "react";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
import { ButtonGroup, ButtonGroupProps, IconButton } from "@chakra-ui/react";
|
||||
|
||||
import { ImageGridTimelineIcon, TextTimelineIcon, TimelineHealthIcon } from "../icons";
|
||||
import { TimelineViewType } from "./index";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
import { searchParamsToJson } from "../../helpers/url";
|
||||
|
||||
export default function TimelineViewTypeButtons(props: ButtonGroupProps) {
|
||||
const [params, setParams] = useSearchParams();
|
||||
const mode = (params.get("view") as TimelineViewType) ?? "timeline";
|
||||
|
||||
const onChange = (type: TimelineViewType) => {
|
||||
setParams({ view: type }, { replace: true });
|
||||
};
|
||||
const onChange = useCallback(
|
||||
(type: TimelineViewType) => {
|
||||
setParams((p) => ({ ...searchParamsToJson(p), view: type }), { replace: true });
|
||||
},
|
||||
[setParams],
|
||||
);
|
||||
|
||||
return (
|
||||
<ButtonGroup>
|
||||
<ButtonGroup {...props}>
|
||||
<IconButton
|
||||
aria-label="Health"
|
||||
icon={<TimelineHealthIcon />}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import type { URLSearchParamsInit } from "react-router-dom";
|
||||
|
||||
export const convertToUrl = (url: string | URL) => (url instanceof URL ? url : new URL(url));
|
||||
|
||||
export const IMAGE_EXT = [".svg", ".gif", ".png", ".jpg", ".jpeg", ".webp", ".avif"];
|
||||
@ -52,3 +54,13 @@ export function replaceDomain(url: string | URL, replacementUrl: string | URL) {
|
||||
if (replacementUrl.password) newUrl.password = replacementUrl.password;
|
||||
return newUrl;
|
||||
}
|
||||
|
||||
export function searchParamsToJson(params: URLSearchParams) {
|
||||
const json: URLSearchParamsInit = {};
|
||||
|
||||
for (const [key, value] of params.entries()) {
|
||||
json[key] = value;
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
@ -1,37 +1,70 @@
|
||||
import { PropsWithChildren, createContext, useContext, useMemo, useState } from "react";
|
||||
import { PropsWithChildren, createContext, useCallback, useContext, useMemo } from "react";
|
||||
import { Kind } from "nostr-tools";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
|
||||
import { useCurrentAccount } from "../hooks/use-current-account";
|
||||
import { getPubkeysFromList } from "../helpers/nostr/lists";
|
||||
import useReplaceableEvent from "../hooks/use-replaceable-event";
|
||||
import { NostrEvent } from "../types/nostr-event";
|
||||
import { NostrQuery } from "../types/nostr-query";
|
||||
import { searchParamsToJson } from "../helpers/url";
|
||||
|
||||
export type ListId = "global" | string;
|
||||
export type Person = { pubkey: string; relay?: string };
|
||||
|
||||
export type PeopleListContextType = {
|
||||
list?: string;
|
||||
list: ListId;
|
||||
listEvent?: NostrEvent;
|
||||
people: { pubkey: string; relay?: string }[] | undefined;
|
||||
setList: (list: string | undefined) => void;
|
||||
people: Person[] | undefined;
|
||||
setList: (list: ListId) => void;
|
||||
filter: NostrQuery | undefined;
|
||||
};
|
||||
const PeopleListContext = createContext<PeopleListContextType>({ list: "following", setList: () => {}, people: [] });
|
||||
const PeopleListContext = createContext<PeopleListContextType>({
|
||||
setList: () => {},
|
||||
people: undefined,
|
||||
list: "global",
|
||||
filter: undefined,
|
||||
});
|
||||
|
||||
export function usePeopleListContext() {
|
||||
return useContext(PeopleListContext);
|
||||
}
|
||||
|
||||
export default function PeopleListProvider({ children }: PropsWithChildren) {
|
||||
export type PeopleListProviderProps = PropsWithChildren & {
|
||||
initList?: "following" | "global";
|
||||
};
|
||||
export default function PeopleListProvider({ children, initList = "following" }: PeopleListProviderProps) {
|
||||
const account = useCurrentAccount();
|
||||
const [listCord, setList] = useState(account ? `${Kind.Contacts}:${account.pubkey}` : undefined);
|
||||
const listEvent = useReplaceableEvent(listCord);
|
||||
const [params, setParams] = useSearchParams({
|
||||
people: account && initList === "following" ? `${Kind.Contacts}:${account.pubkey}` : "global",
|
||||
});
|
||||
|
||||
const list = params.get("people") as ListId;
|
||||
const setList = useCallback(
|
||||
(value: ListId) => {
|
||||
setParams((p) => ({ ...searchParamsToJson(p), people: value }));
|
||||
},
|
||||
[setParams],
|
||||
);
|
||||
|
||||
const listEvent = useReplaceableEvent(list !== "global" ? list : undefined, [], true);
|
||||
|
||||
const people = listEvent && getPubkeysFromList(listEvent);
|
||||
|
||||
const filter = useMemo<NostrQuery | undefined>(() => {
|
||||
if (list === "global") return {};
|
||||
return people && { authors: people.map((p) => p.pubkey) };
|
||||
}, [people, list]);
|
||||
|
||||
const context = useMemo(
|
||||
() => ({
|
||||
people,
|
||||
list: listCord,
|
||||
list,
|
||||
listEvent,
|
||||
setList,
|
||||
filter,
|
||||
}),
|
||||
[listCord, setList, people, listEvent],
|
||||
[list, setList, people, listEvent],
|
||||
);
|
||||
|
||||
return <PeopleListContext.Provider value={context}>{children}</PeopleListContext.Provider>;
|
||||
|
@ -1,10 +1,9 @@
|
||||
import { useCallback } from "react";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { Flex } from "@chakra-ui/react";
|
||||
import { Kind } from "nostr-tools";
|
||||
|
||||
import { isReply, truncatedId } from "../../helpers/nostr/events";
|
||||
import { isReply } from "../../helpers/nostr/events";
|
||||
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||
import { useCurrentAccount } from "../../hooks/use-current-account";
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import TimelinePage, { useTimelinePageEventFilter } from "../../components/timeline-page";
|
||||
import TimelineViewTypeButtons from "../../components/timeline-page/timeline-view-type";
|
||||
@ -12,6 +11,7 @@ import PeopleListSelection from "../../components/people-list-selection/people-l
|
||||
import RelaySelectionButton from "../../components/relay-selection/relay-selection-button";
|
||||
import PeopleListProvider, { usePeopleListContext } from "../../providers/people-list-provider";
|
||||
import RelaySelectionProvider, { useRelaySelectionContext } from "../../providers/relay-selection-provider";
|
||||
import { NostrRequestFilter } from "../../types/nostr-query";
|
||||
|
||||
function HomePage() {
|
||||
const timelinePageEventFilter = useTimelinePageEventFilter();
|
||||
@ -24,12 +24,16 @@ function HomePage() {
|
||||
);
|
||||
|
||||
const { relays } = useRelaySelectionContext();
|
||||
const { people, list } = usePeopleListContext();
|
||||
const { list, filter } = usePeopleListContext();
|
||||
|
||||
const kinds = [Kind.Text, Kind.Repost, 2];
|
||||
const query = people && people.length > 0 ? { authors: people.map((p) => p.pubkey), kinds } : { kinds };
|
||||
const query = useMemo<NostrRequestFilter>(() => {
|
||||
if (filter === undefined) return { kinds };
|
||||
return { ...filter, kinds };
|
||||
}, [filter]);
|
||||
|
||||
const timeline = useTimelineLoader(`${list}-home-feed`, relays, query, {
|
||||
enabled: !!people && people.length > 0,
|
||||
enabled: !!filter,
|
||||
eventFilter,
|
||||
});
|
||||
|
||||
|
@ -1,23 +1,10 @@
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
import {
|
||||
AvatarGroup,
|
||||
Box,
|
||||
Card,
|
||||
CardBody,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
Flex,
|
||||
Heading,
|
||||
Link,
|
||||
Spacer,
|
||||
Text,
|
||||
} from "@chakra-ui/react";
|
||||
import { AvatarGroup, Box, Card, CardBody, CardFooter, CardHeader, Flex, Heading, Link, Text } from "@chakra-ui/react";
|
||||
import { Kind } from "nostr-tools";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
import { UserAvatarLink } from "../../../components/user-avatar-link";
|
||||
import { UserLink } from "../../../components/user-link";
|
||||
import EventVerificationIcon from "../../../components/event-verification-icon";
|
||||
import { getEventsFromList, getListName, getPubkeysFromList } from "../../../helpers/nostr/lists";
|
||||
import { getSharableEventNaddr } from "../../../helpers/nip19";
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
@ -44,13 +31,13 @@ export default function ListCard({ cord, event: maybeEvent }: { cord?: string; e
|
||||
{getListName(event)}
|
||||
</Link>
|
||||
</Heading>
|
||||
<Flex gap="2">
|
||||
<Text>Created by:</Text>
|
||||
<UserAvatarLink pubkey={event.pubkey} size="xs" />
|
||||
<UserLink pubkey={event.pubkey} isTruncated fontWeight="bold" fontSize="lg" />
|
||||
</Flex>
|
||||
<Text>Updated: {dayjs.unix(event.created_at).fromNow()}</Text>
|
||||
</Box>
|
||||
<Spacer />
|
||||
<Text>Created by:</Text>
|
||||
<UserAvatarLink pubkey={event.pubkey} size="xs" />
|
||||
<UserLink pubkey={event.pubkey} isTruncated fontWeight="bold" fontSize="lg" />
|
||||
<EventVerificationIcon event={event} />
|
||||
</CardHeader>
|
||||
<CardBody p="2">
|
||||
{people.length > 0 && (
|
||||
|
@ -184,6 +184,18 @@ export function RelayShareButton({
|
||||
);
|
||||
}
|
||||
|
||||
export function RelayPaidTag({ url }: { url: string }) {
|
||||
const { info } = useRelayInfo(url);
|
||||
|
||||
return (
|
||||
info?.payments_url && (
|
||||
<Tag as="a" variant="solid" colorScheme="green" size="sm" ml="2" target="_blank" href={info.payments_url}>
|
||||
Paid relay
|
||||
</Tag>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default function RelayCard({ url, ...props }: { url: string } & Omit<CardProps, "children">) {
|
||||
const { info } = useRelayInfo(url);
|
||||
return (
|
||||
@ -193,11 +205,7 @@ export default function RelayCard({ url, ...props }: { url: string } & Omit<Card
|
||||
<RelayFavicon relay={url} size="xs" />
|
||||
<Heading size="md" isTruncated>
|
||||
<RouterLink to={`/r/${encodeURIComponent(url)}`}>{url}</RouterLink>
|
||||
{info?.payments_url && (
|
||||
<Tag as="a" variant="solid" colorScheme="green" size="sm" ml="2" target="_blank" href={info.payments_url}>
|
||||
Paid
|
||||
</Tag>
|
||||
)}
|
||||
<RelayPaidTag url={url} />
|
||||
</Heading>
|
||||
</CardHeader>
|
||||
<CardBody px="2" py="0" display="flex" flexDirection="column" gap="2">
|
||||
|
@ -21,6 +21,8 @@ import { ExternalLinkIcon } from "../../../components/icons";
|
||||
import RelayReviewForm from "./relay-review-form";
|
||||
import RelayReviews from "./relay-reviews";
|
||||
import RelayNotes from "./relay-notes";
|
||||
import PeopleListProvider from "../../../providers/people-list-provider";
|
||||
import PeopleListSelection from "../../../components/people-list-selection/people-list-selection";
|
||||
|
||||
function RelayPage({ relay }: { relay: string }) {
|
||||
const { info } = useRelayInfo(relay);
|
||||
@ -68,13 +70,15 @@ function RelayPage({ relay }: { relay: string }) {
|
||||
|
||||
<TabPanels>
|
||||
<TabPanel py="2" px="0">
|
||||
{showReviewForm.isOpen ? (
|
||||
<RelayReviewForm onClose={showReviewForm.onClose} relay={relay} />
|
||||
) : (
|
||||
<Button colorScheme="brand" ml="aut" mb="2" onClick={showReviewForm.onOpen}>
|
||||
Write review
|
||||
</Button>
|
||||
)}
|
||||
<Flex gap="2">
|
||||
<PeopleListSelection />
|
||||
{!showReviewForm.isOpen && (
|
||||
<Button colorScheme="brand" ml="auto" mb="2" onClick={showReviewForm.onOpen}>
|
||||
Write review
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
{showReviewForm.isOpen && <RelayReviewForm onClose={showReviewForm.onClose} relay={relay} my="4" />}
|
||||
<RelayReviews relay={relay} />
|
||||
</TabPanel>
|
||||
<TabPanel py="2" px="0">
|
||||
@ -94,5 +98,9 @@ export default function RelayView() {
|
||||
|
||||
if (!safeUrl) return <>Bad relay url</>;
|
||||
|
||||
return <RelayPage relay={safeUrl} />;
|
||||
return (
|
||||
<PeopleListProvider initList="global">
|
||||
<RelayPage relay={safeUrl} />
|
||||
</PeopleListProvider>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { useCallback } from "react";
|
||||
import { Flex, Switch, useDisclosure } from "@chakra-ui/react";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { Flex, Spacer } from "@chakra-ui/react";
|
||||
import { Kind } from "nostr-tools";
|
||||
|
||||
import { isReply } from "../../../helpers/nostr/events";
|
||||
import { useAppTitle } from "../../../hooks/use-app-title";
|
||||
@ -7,27 +8,34 @@ import useTimelineLoader from "../../../hooks/use-timeline-loader";
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
import TimelinePage, { useTimelinePageEventFilter } from "../../../components/timeline-page";
|
||||
import TimelineViewTypeButtons from "../../../components/timeline-page/timeline-view-type";
|
||||
import PeopleListSelection from "../../../components/people-list-selection/people-list-selection";
|
||||
import { usePeopleListContext } from "../../../providers/people-list-provider";
|
||||
import { NostrRequestFilter } from "../../../types/nostr-query";
|
||||
|
||||
export default function RelayNotes({ relay }: { relay: string }) {
|
||||
useAppTitle(`${relay} - Notes`);
|
||||
|
||||
const showReplies = useDisclosure();
|
||||
const { filter } = usePeopleListContext();
|
||||
const kinds = [Kind.Text];
|
||||
const query = useMemo<NostrRequestFilter>(() => {
|
||||
if (filter === undefined) return { kinds };
|
||||
return { ...filter, kinds };
|
||||
}, [filter]);
|
||||
|
||||
const timelineEventFilter = useTimelinePageEventFilter();
|
||||
const eventFilter = useCallback(
|
||||
(event: NostrEvent) => {
|
||||
if (!showReplies.isOpen && isReply(event)) return false;
|
||||
if (!isReply(event)) return false;
|
||||
return timelineEventFilter(event);
|
||||
},
|
||||
[showReplies.isOpen, timelineEventFilter],
|
||||
[timelineEventFilter],
|
||||
);
|
||||
const timeline = useTimelineLoader(`${relay}-notes`, [relay], { kinds: [1] }, { eventFilter });
|
||||
const timeline = useTimelineLoader(`${relay}-notes`, [relay], query, { eventFilter, enabled: !!filter });
|
||||
|
||||
const header = (
|
||||
<Flex gap="2" pr="2" justifyContent="space-between" alignItems="center">
|
||||
<Switch isChecked={showReplies.isOpen} onChange={showReplies.onToggle} size="sm">
|
||||
Show Replies
|
||||
</Switch>
|
||||
<Flex gap="2" wrap="wrap" px={["2", 0]}>
|
||||
<PeopleListSelection />
|
||||
<Spacer />
|
||||
<TimelineViewTypeButtons />
|
||||
</Flex>
|
||||
);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Button, Flex, Heading, Textarea, useToast } from "@chakra-ui/react";
|
||||
import { Button, Flex, FlexProps, Heading, Textarea, useToast } from "@chakra-ui/react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
@ -9,7 +9,11 @@ import { RELAY_REVIEW_LABEL, RELAY_REVIEW_LABEL_NAMESPACE, REVIEW_KIND } from ".
|
||||
import { useSigningContext } from "../../../providers/signing-provider";
|
||||
import NostrPublishAction from "../../../classes/nostr-publish-action";
|
||||
|
||||
export default function RelayReviewForm({ onClose, relay }: { onClose: () => void; relay: string }) {
|
||||
export default function RelayReviewForm({
|
||||
onClose,
|
||||
relay,
|
||||
...props
|
||||
}: { onClose: () => void; relay: string } & Omit<FlexProps, "children">) {
|
||||
const toast = useToast();
|
||||
const { requestSignature } = useSigningContext();
|
||||
const writeRelays = useWriteRelayUrls();
|
||||
@ -44,7 +48,7 @@ export default function RelayReviewForm({ onClose, relay }: { onClose: () => voi
|
||||
});
|
||||
|
||||
return (
|
||||
<Flex as="form" direction="column" onSubmit={onSubmit} gap="2" mb="2">
|
||||
<Flex as="form" direction="column" onSubmit={onSubmit} gap="2" mb="2" {...props}>
|
||||
<Flex gap="2">
|
||||
<Heading size="md">Write review</Heading>
|
||||
<StarRating quality={getValues().quality} fontSize="1.5rem" onChange={(q) => setValue("quality", q)} />
|
||||
|
@ -6,15 +6,24 @@ import useSubject from "../../../hooks/use-subject";
|
||||
import useTimelineLoader from "../../../hooks/use-timeline-loader";
|
||||
import RelayReviewNote from "../components/relay-review-note";
|
||||
import { useAppTitle } from "../../../hooks/use-app-title";
|
||||
import { usePeopleListContext } from "../../../providers/people-list-provider";
|
||||
|
||||
export default function RelayReviews({ relay }: { relay: string }) {
|
||||
useAppTitle(`${relay} - Reviews`);
|
||||
const readRelays = useReadRelayUrls();
|
||||
const timeline = useTimelineLoader(`${relay}-reviews`, readRelays, {
|
||||
kinds: [1985],
|
||||
"#r": [relay],
|
||||
"#l": [RELAY_REVIEW_LABEL],
|
||||
});
|
||||
|
||||
const { filter } = usePeopleListContext();
|
||||
const timeline = useTimelineLoader(
|
||||
`${relay}-reviews`,
|
||||
readRelays,
|
||||
{
|
||||
...filter,
|
||||
kinds: [1985],
|
||||
"#r": [relay],
|
||||
"#l": [RELAY_REVIEW_LABEL],
|
||||
},
|
||||
{ enabled: !!filter },
|
||||
);
|
||||
|
||||
const events = useSubject(timeline.timeline);
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Button, Flex } from "@chakra-ui/react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { useReadRelayUrls } from "../../hooks/use-client-relays";
|
||||
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||
@ -6,15 +7,24 @@ import useSubject from "../../hooks/use-subject";
|
||||
import RelayReviewNote from "./components/relay-review-note";
|
||||
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
|
||||
import IntersectionObserverProvider from "../../providers/intersection-observer";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import PeopleListProvider, { usePeopleListContext } from "../../providers/people-list-provider";
|
||||
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
|
||||
|
||||
export default function RelayReviewsView() {
|
||||
function RelayReviewsPage() {
|
||||
const navigate = useNavigate();
|
||||
const readRelays = useReadRelayUrls();
|
||||
const timeline = useTimelineLoader("relay-reviews", readRelays, {
|
||||
kinds: [1985],
|
||||
"#l": ["review/relay"],
|
||||
});
|
||||
|
||||
const { filter } = usePeopleListContext();
|
||||
const timeline = useTimelineLoader(
|
||||
"relay-reviews",
|
||||
readRelays,
|
||||
{
|
||||
...filter,
|
||||
kinds: [1985],
|
||||
"#l": ["review/relay"],
|
||||
},
|
||||
{ enabled: !!filter },
|
||||
);
|
||||
|
||||
const reviews = useSubject(timeline.timeline);
|
||||
|
||||
@ -23,8 +33,9 @@ export default function RelayReviewsView() {
|
||||
return (
|
||||
<IntersectionObserverProvider<string> callback={callback}>
|
||||
<Flex direction="column" gap="2" py="2">
|
||||
<Flex>
|
||||
<Flex gap="2">
|
||||
<Button onClick={() => navigate(-1)}>Back</Button>
|
||||
<PeopleListSelection />
|
||||
</Flex>
|
||||
{reviews.map((event) => (
|
||||
<RelayReviewNote key={event.id} event={event} />
|
||||
@ -33,3 +44,11 @@ export default function RelayReviewsView() {
|
||||
</IntersectionObserverProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default function RelayReviewsView() {
|
||||
return (
|
||||
<PeopleListProvider initList="global">
|
||||
<RelayReviewsPage />
|
||||
</PeopleListProvider>
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Divider, Flex, Heading, Select, SimpleGrid } from "@chakra-ui/react";
|
||||
import { useMemo } from "react";
|
||||
import { Divider, Flex, Heading, SimpleGrid } from "@chakra-ui/react";
|
||||
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||
import IntersectionObserverProvider from "../../providers/intersection-observer";
|
||||
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
|
||||
@ -12,20 +13,23 @@ import PeopleListSelection from "../../components/people-list-selection/people-l
|
||||
import PeopleListProvider, { usePeopleListContext } from "../../providers/people-list-provider";
|
||||
import TimelineActionAndStatus from "../../components/timeline-page/timeline-action-and-status";
|
||||
import useParsedStreams from "../../hooks/use-parsed-streams";
|
||||
import { NostrRequestFilter } from "../../types/nostr-query";
|
||||
import { useAppTitle } from "../../hooks/use-app-title";
|
||||
|
||||
function StreamsPage() {
|
||||
useAppTitle("Streams");
|
||||
const relays = useRelaySelectionRelays();
|
||||
|
||||
const { people, list } = usePeopleListContext();
|
||||
const query =
|
||||
people && people.length > 0
|
||||
? [
|
||||
{ authors: people.map((p) => p.pubkey), kinds: [STREAM_KIND] },
|
||||
{ "#p": people.map((p) => p.pubkey), kinds: [STREAM_KIND] },
|
||||
]
|
||||
: { kinds: [STREAM_KIND] };
|
||||
const { filter, list } = usePeopleListContext();
|
||||
const query = useMemo<NostrRequestFilter>(() => {
|
||||
if (list === "global" || !filter) return { kinds: [STREAM_KIND] };
|
||||
return [
|
||||
{ authors: filter.authors, kinds: [STREAM_KIND] },
|
||||
{ "#p": filter.authors, kinds: [STREAM_KIND] },
|
||||
];
|
||||
}, [filter, list]);
|
||||
|
||||
const timeline = useTimelineLoader(`${list}-streams`, relays, query);
|
||||
const timeline = useTimelineLoader(`${list}-streams`, relays, query, { enabled: !!filter });
|
||||
|
||||
useRelaysChanged(relays, () => timeline.reset());
|
||||
|
||||
@ -40,7 +44,7 @@ function StreamsPage() {
|
||||
return (
|
||||
<Flex p="2" gap="2" overflow="hidden" direction="column">
|
||||
<Flex gap="2" wrap="wrap">
|
||||
<PeopleListSelection w={["full", "xs"]} />
|
||||
<PeopleListSelection />
|
||||
<RelaySelectionButton ml="auto" />
|
||||
</Flex>
|
||||
<IntersectionObserverProvider callback={callback}>
|
||||
|
@ -33,8 +33,10 @@ import useSubject from "../../../hooks/use-subject";
|
||||
import RelaySelectionButton from "../../../components/relay-selection/relay-selection-button";
|
||||
import RelaySelectionProvider from "../../../providers/relay-selection-provider";
|
||||
import StreamerCards from "../components/streamer-cards";
|
||||
import { useAppTitle } from "../../../hooks/use-app-title";
|
||||
|
||||
function StreamPage({ stream, displayMode }: { stream: ParsedStream; displayMode?: ChatDisplayMode }) {
|
||||
useAppTitle(stream.title);
|
||||
const vertical = useBreakpointValue({ base: true, lg: false });
|
||||
const scrollBox = useRef<HTMLDivElement | null>(null);
|
||||
const scrollState = useScroll(scrollBox);
|
||||
|
Loading…
x
Reference in New Issue
Block a user