mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-09-21 14:09:17 +02:00
remove relay selection button
This commit is contained in:
10
src/app.tsx
10
src/app.tsx
@@ -106,9 +106,6 @@ const StreamView = lazy(() => import("./views/streams/stream"));
|
||||
const SearchView = lazy(() => import("./views/search"));
|
||||
const MapView = lazy(() => import("./views/map"));
|
||||
|
||||
const ThingsView = lazy(() => import("./views/things/index"));
|
||||
const ThingUploadView = lazy(() => import("./views/things/upload"));
|
||||
|
||||
const ChannelsHomeView = lazy(() => import("./views/channels"));
|
||||
const ChannelView = lazy(() => import("./views/channels/channel"));
|
||||
|
||||
@@ -322,13 +319,6 @@ const router = createHashRouter([
|
||||
{ path: "unknown", element: <UnknownTimelineView /> },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "things",
|
||||
children: [
|
||||
{ path: "", element: <ThingsView /> },
|
||||
{ path: "upload", element: <ThingUploadView /> },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "lists",
|
||||
children: [
|
||||
|
@@ -84,7 +84,7 @@ export default function EventDebugModal({ event, ...props }: { event: NostrEvent
|
||||
<ModalHeader p="4">{event.id}</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody p="0">
|
||||
<Accordion>
|
||||
<Accordion allowToggle>
|
||||
<Section label="IDs">
|
||||
<RawValue heading="Event Id" value={event.id} />
|
||||
<RawValue heading="NIP-19 Encoded Id" value={nip19.noteEncode(event.id)} />
|
||||
|
@@ -36,7 +36,7 @@ async function getPayRequestForPubkey(
|
||||
event: NostrEvent | undefined,
|
||||
amount: number,
|
||||
comment?: string,
|
||||
additionalRelays?: string[],
|
||||
additionalRelays?: Iterable<string>,
|
||||
): Promise<PayRequest> {
|
||||
const metadata = userMetadataService.getSubject(pubkey).value;
|
||||
const address = metadata?.lud16 || metadata?.lud06;
|
||||
@@ -106,7 +106,7 @@ async function getPayRequestsForEvent(
|
||||
amount: number,
|
||||
comment?: string,
|
||||
fallbackPubkey?: string,
|
||||
additionalRelays?: string[],
|
||||
additionalRelays?: Iterable<string>,
|
||||
) {
|
||||
const splits = getZapSplits(event, fallbackPubkey);
|
||||
|
||||
@@ -133,7 +133,7 @@ export type ZapModalProps = Omit<ModalProps, "children"> & {
|
||||
allowComment?: boolean;
|
||||
showEmbed?: boolean;
|
||||
embedProps?: EmbedProps;
|
||||
additionalRelays?: string[];
|
||||
additionalRelays?: Iterable<string>;
|
||||
onZapped: () => void;
|
||||
};
|
||||
|
||||
|
@@ -58,7 +58,6 @@ export default function NavItems() {
|
||||
else if (location.pathname.startsWith("/goals")) active = "goals";
|
||||
else if (location.pathname.startsWith("/badges")) active = "badges";
|
||||
else if (location.pathname.startsWith("/emojis")) active = "emojis";
|
||||
else if (location.pathname.startsWith("/things")) active = "things";
|
||||
else if (location.pathname.startsWith("/settings")) active = "settings";
|
||||
else if (location.pathname.startsWith("/tools")) active = "tools";
|
||||
else if (location.pathname.startsWith("/search")) active = "search";
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import React, { LegacyRef, forwardRef } from "react";
|
||||
import { Image, InputProps, Textarea, TextareaProps } from "@chakra-ui/react";
|
||||
// NOTE: Do not remove Textarea or Input from the imports. they are used
|
||||
import { Image, InputProps, Textarea, Input, TextareaProps } from "@chakra-ui/react";
|
||||
import ReactTextareaAutocomplete, {
|
||||
ItemComponentProps,
|
||||
TextareaProps as ReactTextareaAutocompleteProps,
|
||||
|
@@ -3,6 +3,7 @@ import { ButtonProps, IconButton, useDisclosure } from "@chakra-ui/react";
|
||||
import { RelayIcon } from "../icons";
|
||||
import RelayManagementDrawer from "../relay-management-drawer";
|
||||
|
||||
/** @deprecated */
|
||||
export default function RelaySelectionButton({ ...props }: ButtonProps) {
|
||||
const relaysModal = useDisclosure();
|
||||
return (
|
||||
|
@@ -1,61 +0,0 @@
|
||||
import { PropsWithChildren, createContext, useCallback, useContext, useMemo } from "react";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
|
||||
import { useReadRelays } from "../../hooks/use-client-relays";
|
||||
import { unique } from "../../helpers/array";
|
||||
|
||||
type RelaySelectionContextType = {
|
||||
relays: string[];
|
||||
setSelected: (relays: string[]) => void;
|
||||
};
|
||||
|
||||
export const RelaySelectionContext = createContext<RelaySelectionContextType>({
|
||||
relays: [],
|
||||
setSelected: () => {},
|
||||
});
|
||||
|
||||
export function useRelaySelectionContext() {
|
||||
return useContext(RelaySelectionContext);
|
||||
}
|
||||
export function useRelaySelectionRelays() {
|
||||
return useContext(RelaySelectionContext).relays;
|
||||
}
|
||||
|
||||
export type RelaySelectionProviderProps = PropsWithChildren & {
|
||||
overrideDefault?: Iterable<string>;
|
||||
additionalDefaults?: Iterable<string>;
|
||||
};
|
||||
|
||||
export default function RelaySelectionProvider({
|
||||
children,
|
||||
overrideDefault,
|
||||
additionalDefaults,
|
||||
}: RelaySelectionProviderProps) {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const readRelays = useReadRelays();
|
||||
const relays = useMemo(() => {
|
||||
if (location.state?.relays) return location.state.relays as string[];
|
||||
if (overrideDefault) return Array.from(overrideDefault);
|
||||
if (additionalDefaults) return unique([...readRelays, ...additionalDefaults]);
|
||||
return readRelays.urls;
|
||||
}, [location.state?.relays, overrideDefault, readRelays.urls.join("|"), additionalDefaults]);
|
||||
|
||||
const setSelected = useCallback(
|
||||
(relays: string[]) => {
|
||||
navigate(location.pathname + location.search, { state: { relays }, replace: true });
|
||||
},
|
||||
[navigate, location],
|
||||
);
|
||||
|
||||
const context = useMemo(
|
||||
() => ({
|
||||
relays,
|
||||
setSelected,
|
||||
}),
|
||||
[relays.join("|"), setSelected],
|
||||
);
|
||||
|
||||
return <RelaySelectionContext.Provider value={context}>{children}</RelaySelectionContext.Provider>;
|
||||
}
|
@@ -7,9 +7,7 @@ import useSingleEvent from "../../hooks/use-single-event";
|
||||
import { ErrorBoundary } from "../../components/error-boundary";
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import useChannelMetadata from "../../hooks/use-channel-metadata";
|
||||
import RelaySelectionProvider, { useRelaySelectionContext } from "../../providers/local/relay-selection-provider";
|
||||
import { ChevronLeftIcon } from "../../components/icons";
|
||||
import RelaySelectionButton from "../../components/relay-selection/relay-selection-button";
|
||||
import ChannelMetadataDrawer from "./components/channel-metadata-drawer";
|
||||
import ChannelJoinButton from "./components/channel-join-button";
|
||||
import ChannelMenu from "./components/channel-menu";
|
||||
@@ -25,6 +23,7 @@ import ChannelMessageBlock from "./components/channel-message-block";
|
||||
import TimelineActionAndStatus from "../../components/timeline-page/timeline-action-and-status";
|
||||
import ChannelMessageForm from "./components/send-message-form";
|
||||
import useParamsEventPointer from "../../hooks/use-params-event-pointer";
|
||||
import { useReadRelays } from "../../hooks/use-client-relays";
|
||||
|
||||
const ChannelChatLog = memo(({ timeline, channel }: { timeline: TimelineLoader; channel: NostrEvent }) => {
|
||||
const messages = useSubject(timeline.timeline);
|
||||
@@ -45,7 +44,7 @@ const ChannelChatLog = memo(({ timeline, channel }: { timeline: TimelineLoader;
|
||||
|
||||
function ChannelPage({ channel }: { channel: NostrEvent }) {
|
||||
const navigate = useNavigate();
|
||||
const { relays } = useRelaySelectionContext();
|
||||
const relays = useReadRelays();
|
||||
const { metadata } = useChannelMetadata(channel.id, relays);
|
||||
const drawer = useDisclosure();
|
||||
|
||||
@@ -76,7 +75,6 @@ function ChannelPage({ channel }: { channel: NostrEvent }) {
|
||||
<Button leftIcon={<ChevronLeftIcon />} onClick={() => navigate(-1)}>
|
||||
Back
|
||||
</Button>
|
||||
<RelaySelectionButton hideBelow="lg" />
|
||||
<Heading hideBelow="lg" size="lg">
|
||||
{metadata?.name}
|
||||
</Heading>
|
||||
@@ -116,9 +114,7 @@ export default function ChannelView() {
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<RelaySelectionProvider>
|
||||
<ChannelPage channel={channel} />
|
||||
</RelaySelectionProvider>
|
||||
<ChannelPage channel={channel} />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
@@ -30,7 +30,7 @@ export default function ChannelCard({
|
||||
channel,
|
||||
additionalRelays,
|
||||
...props
|
||||
}: Omit<CardProps, "children"> & { channel: NostrEvent; additionalRelays?: string[] }) {
|
||||
}: Omit<CardProps, "children"> & { channel: NostrEvent; additionalRelays?: Iterable<string> }) {
|
||||
const readRelays = useReadRelays(additionalRelays);
|
||||
const { metadata } = useChannelMetadata(channel.id, readRelays);
|
||||
|
||||
|
@@ -25,11 +25,12 @@ import IntersectionObserverProvider from "../../../providers/local/intersection-
|
||||
import UserLink from "../../../components/user-link";
|
||||
import HoverLinkOverlay from "../../../components/hover-link-overlay";
|
||||
import UserAvatar from "../../../components/user-avatar";
|
||||
import { useRelaySelectionContext } from "../../../providers/local/relay-selection-provider";
|
||||
import { UserDnsIdentityIcon } from "../../../components/user-dns-identity-icon";
|
||||
import ChannelJoinButton from "./channel-join-button";
|
||||
import { ExternalLinkIcon } from "../../../components/icons";
|
||||
import { CHANNELS_LIST_KIND } from "../../../helpers/nostr/lists";
|
||||
import { useReadRelays } from "../../../hooks/use-client-relays";
|
||||
import { useAdditionalRelayContext } from "../../../providers/local/additional-relay-context";
|
||||
|
||||
function UserCard({ pubkey }: { pubkey: string }) {
|
||||
return (
|
||||
@@ -40,7 +41,7 @@ function UserCard({ pubkey }: { pubkey: string }) {
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
function ChannelMembers({ channel, relays }: { channel: NostrEvent; relays: string[] }) {
|
||||
function ChannelMembers({ channel, relays }: { channel: NostrEvent; relays: Iterable<string> }) {
|
||||
const timeline = useTimelineLoader(`${channel.id}-members`, relays, {
|
||||
kinds: [CHANNELS_LIST_KIND],
|
||||
"#e": [channel.id],
|
||||
@@ -67,7 +68,7 @@ export default function ChannelMetadataDrawer({
|
||||
...props
|
||||
}: Omit<DrawerProps, "children"> & { channel: NostrEvent }) {
|
||||
const { metadata } = useChannelMetadata(channel.id);
|
||||
const { relays } = useRelaySelectionContext();
|
||||
const relays = useReadRelays(useAdditionalRelayContext());
|
||||
|
||||
return (
|
||||
<Drawer isOpen={isOpen} placement="right" onClose={onClose} {...props}>
|
||||
|
@@ -3,21 +3,20 @@ import { kinds } from "nostr-tools";
|
||||
import { Flex } from "@chakra-ui/react";
|
||||
|
||||
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||
import RelaySelectionProvider, { useRelaySelectionContext } from "../../providers/local/relay-selection-provider";
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
|
||||
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import { ErrorBoundary } from "../../components/error-boundary";
|
||||
import RelaySelectionButton from "../../components/relay-selection/relay-selection-button";
|
||||
import useClientSideMuteFilter from "../../hooks/use-client-side-mute-filter";
|
||||
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
|
||||
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
|
||||
import ChannelCard from "./components/channel-card";
|
||||
import { useReadRelays } from "../../hooks/use-client-relays";
|
||||
|
||||
function ChannelsHomePage() {
|
||||
const { relays } = useRelaySelectionContext();
|
||||
const relays = useReadRelays();
|
||||
const { filter, listId } = usePeopleListContext();
|
||||
|
||||
const clientMuteFilter = useClientSideMuteFilter();
|
||||
@@ -42,7 +41,6 @@ function ChannelsHomePage() {
|
||||
<VerticalPageLayout>
|
||||
<Flex gap="2">
|
||||
<PeopleListSelection />
|
||||
<RelaySelectionButton />
|
||||
</Flex>
|
||||
<IntersectionObserverProvider callback={callback}>
|
||||
{channels.map((channel) => (
|
||||
@@ -57,10 +55,8 @@ function ChannelsHomePage() {
|
||||
|
||||
export default function ChannelsHomeView() {
|
||||
return (
|
||||
<RelaySelectionProvider>
|
||||
<PeopleListProvider>
|
||||
<ChannelsHomePage />
|
||||
</PeopleListProvider>
|
||||
</RelaySelectionProvider>
|
||||
<PeopleListProvider>
|
||||
<ChannelsHomePage />
|
||||
</PeopleListProvider>
|
||||
);
|
||||
}
|
||||
|
@@ -10,9 +10,7 @@ import useAppSettings from "../../hooks/use-app-settings";
|
||||
import { TrustProvider, useTrusted } from "../../providers/local/trust";
|
||||
import BlurredImage from "../../components/blured-image";
|
||||
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
|
||||
import RelaySelectionProvider, { useRelaySelectionContext } from "../../providers/local/relay-selection-provider";
|
||||
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
|
||||
import RelaySelectionButton from "../../components/relay-selection/relay-selection-button";
|
||||
import { UserAvatarLink } from "../../components/user-avatar-link";
|
||||
import UserLink from "../../components/user-link";
|
||||
import MimeTypePicker from "./mime-type-picker";
|
||||
@@ -24,6 +22,7 @@ import IntersectionObserverProvider, {
|
||||
useRegisterIntersectionEntity,
|
||||
} from "../../providers/local/intersection-observer";
|
||||
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
|
||||
import { useReadRelays } from "../../hooks/use-client-relays";
|
||||
|
||||
function ImageFile({ event }: { event: NostrEvent }) {
|
||||
const parsed = parseImageFile(event);
|
||||
@@ -112,7 +111,7 @@ function FileType({ event }: { event: NostrEvent }) {
|
||||
|
||||
function FilesPage() {
|
||||
const { listId, filter } = usePeopleListContext();
|
||||
const { relays } = useRelaySelectionContext();
|
||||
const relays = useReadRelays();
|
||||
|
||||
const [selectedTypes, setSelectedTypes] = useState<string[]>(IMAGE_TYPES);
|
||||
|
||||
@@ -131,7 +130,6 @@ function FilesPage() {
|
||||
<Flex gap="2">
|
||||
<PeopleListSelection />
|
||||
<MimeTypePicker selected={selectedTypes} onChange={(v) => setSelectedTypes(v)} />
|
||||
<RelaySelectionButton ml="auto" />
|
||||
</Flex>
|
||||
|
||||
<IntersectionObserverProvider callback={callback}>
|
||||
@@ -151,9 +149,7 @@ function FilesPage() {
|
||||
export default function FilesView() {
|
||||
return (
|
||||
<PeopleListProvider>
|
||||
<RelaySelectionProvider>
|
||||
<FilesPage />
|
||||
</RelaySelectionProvider>
|
||||
<FilesPage />
|
||||
</PeopleListProvider>
|
||||
);
|
||||
}
|
||||
|
@@ -18,8 +18,6 @@ import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||
import { isReply, isRepost } from "../../helpers/nostr/events";
|
||||
import { CheckIcon, EditIcon } from "../../components/icons";
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import RelaySelectionButton from "../../components/relay-selection/relay-selection-button";
|
||||
import RelaySelectionProvider, { useRelaySelectionRelays } from "../../providers/local/relay-selection-provider";
|
||||
import useRelaysChanged from "../../hooks/use-relays-changed";
|
||||
import TimelinePage, { useTimelinePageEventFilter } from "../../components/timeline-page";
|
||||
import TimelineViewTypeButtons from "../../components/timeline-page/timeline-view-type";
|
||||
@@ -28,6 +26,7 @@ import PeopleListProvider, { usePeopleListContext } from "../../providers/local/
|
||||
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
|
||||
import NoteFilterTypeButtons from "../../components/note-filter-type-buttons";
|
||||
import { useRouteStateBoolean } from "../../hooks/use-route-state-value";
|
||||
import { useReadRelays } from "../../hooks/use-client-relays";
|
||||
|
||||
function EditableControls() {
|
||||
const { isEditing, getSubmitButtonProps, getCancelButtonProps, getEditButtonProps } = useEditableControls();
|
||||
@@ -54,7 +53,7 @@ function HashTagPage() {
|
||||
const showReplies = useRouteStateBoolean("show-replies", true);
|
||||
const showReposts = useRouteStateBoolean("show-reposts", true);
|
||||
|
||||
const readRelays = useRelaySelectionRelays();
|
||||
const readRelays = useReadRelays().urls;
|
||||
|
||||
const { listId, filter } = usePeopleListContext();
|
||||
const timelinePageEventFilter = useTimelinePageEventFilter();
|
||||
@@ -98,7 +97,6 @@ function HashTagPage() {
|
||||
<EditableControls />
|
||||
</Editable>
|
||||
<PeopleListSelection />
|
||||
<RelaySelectionButton />
|
||||
<NoteFilterTypeButtons showReplies={showReplies} showReposts={showReposts} />
|
||||
<Spacer />
|
||||
<TimelineViewTypeButtons />
|
||||
@@ -110,10 +108,8 @@ function HashTagPage() {
|
||||
|
||||
export default function HashTagView() {
|
||||
return (
|
||||
<RelaySelectionProvider>
|
||||
<PeopleListProvider initList="global">
|
||||
<HashTagPage />
|
||||
</PeopleListProvider>
|
||||
</RelaySelectionProvider>
|
||||
<PeopleListProvider initList="global">
|
||||
<HashTagPage />
|
||||
</PeopleListProvider>
|
||||
);
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useEffect, useMemo } from "react";
|
||||
import { Flex, useDisclosure } from "@chakra-ui/react";
|
||||
import { useCallback, useEffect } from "react";
|
||||
import { Flex, Spacer, useDisclosure } from "@chakra-ui/react";
|
||||
import { kinds } from "nostr-tools";
|
||||
|
||||
import { isReply, isRepost } from "../../helpers/nostr/events";
|
||||
@@ -8,12 +8,11 @@ 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 RelaySelectionButton from "../../components/relay-selection/relay-selection-button";
|
||||
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
|
||||
import RelaySelectionProvider, { useRelaySelectionContext } from "../../providers/local/relay-selection-provider";
|
||||
import useClientSideMuteFilter from "../../hooks/use-client-side-mute-filter";
|
||||
import NoteFilterTypeButtons from "../../components/note-filter-type-buttons";
|
||||
import KindSelectionProvider, { useKindSelectionContext } from "../../providers/local/kind-selection-provider";
|
||||
import { useReadRelays } from "../../hooks/use-client-relays";
|
||||
|
||||
const defaultKinds = [
|
||||
kinds.ShortTextNote,
|
||||
@@ -46,7 +45,7 @@ function HomePage() {
|
||||
[timelinePageEventFilter, showReplies.isOpen, showReposts.isOpen, muteFilter],
|
||||
);
|
||||
|
||||
const { relays } = useRelaySelectionContext();
|
||||
const relays = useReadRelays();
|
||||
const { listId, filter } = usePeopleListContext();
|
||||
const { kinds } = useKindSelectionContext();
|
||||
|
||||
@@ -58,7 +57,7 @@ function HomePage() {
|
||||
<Flex gap="2" wrap="wrap" alignItems="center">
|
||||
<PeopleListSelection />
|
||||
<NoteFilterTypeButtons showReplies={showReplies} showReposts={showReposts} />
|
||||
<RelaySelectionButton ml="auto" />
|
||||
<Spacer />
|
||||
<TimelineViewTypeButtons />
|
||||
</Flex>
|
||||
);
|
||||
@@ -69,11 +68,9 @@ function HomePage() {
|
||||
export default function HomeView() {
|
||||
return (
|
||||
<PeopleListProvider>
|
||||
<RelaySelectionProvider>
|
||||
<KindSelectionProvider initKinds={defaultKinds}>
|
||||
<HomePage />
|
||||
</KindSelectionProvider>
|
||||
</RelaySelectionProvider>
|
||||
<KindSelectionProvider initKinds={defaultKinds}>
|
||||
<HomePage />
|
||||
</KindSelectionProvider>
|
||||
</PeopleListProvider>
|
||||
);
|
||||
}
|
||||
|
@@ -42,7 +42,6 @@ export const internalApps: App[] = [
|
||||
{ title: "Lists", description: "Browse and create lists", icon: ListsIcon, id: "lists", to: "/lists" },
|
||||
{ title: "Tracks", description: "Browse stemstr tracks", icon: TrackIcon, id: "tracks", to: "/tracks" },
|
||||
{ title: "Videos", description: "Browse flare videos", icon: Film02, id: "videos", to: "/videos" },
|
||||
// { title: "Things", icon: ThingsIcon, id: "things", to: "/things" },
|
||||
];
|
||||
|
||||
export const internalTools: App[] = [
|
||||
|
@@ -1,72 +1,19 @@
|
||||
import { useMemo } from "react";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
import { CloseIcon } from "@chakra-ui/icons";
|
||||
|
||||
import { Button, Flex, Heading, IconButton, Link } from "@chakra-ui/react";
|
||||
import { Button, Flex, Heading } from "@chakra-ui/react";
|
||||
import useSubject from "../../../hooks/use-subject";
|
||||
import { offlineMode } from "../../../services/offline-mode";
|
||||
import WifiOff from "../../../components/icons/wifi-off";
|
||||
import Wifi from "../../../components/icons/wifi";
|
||||
import BackButton from "../../../components/back-button";
|
||||
import AddRelayForm from "./add-relay-form";
|
||||
import relayPoolService from "../../../services/relay-pool";
|
||||
import clientRelaysService from "../../../services/client-relays";
|
||||
import { RelayMode } from "../../../classes/relay";
|
||||
import { RelayFavicon } from "../../../components/relay-favicon";
|
||||
import UploadCloud01 from "../../../components/icons/upload-cloud-01";
|
||||
import RelaySet from "../../../classes/relay-set";
|
||||
import { useReadRelays, useWriteRelays } from "../../../hooks/use-client-relays";
|
||||
import useCurrentAccount from "../../../hooks/use-current-account";
|
||||
|
||||
function RelayControl({ url }: { url: string }) {
|
||||
const relay = useMemo(() => relayPoolService.requestRelay(url, false), [url]);
|
||||
const status = useSubject(relay.status);
|
||||
const writeRelays = useSubject(clientRelaysService.writeRelays);
|
||||
|
||||
let color = "gray";
|
||||
switch (status) {
|
||||
case WebSocket.OPEN:
|
||||
color = "green";
|
||||
break;
|
||||
case WebSocket.CONNECTING:
|
||||
color = "yellow";
|
||||
break;
|
||||
case WebSocket.CLOSED:
|
||||
color = "red";
|
||||
break;
|
||||
}
|
||||
|
||||
const onChange = () => {
|
||||
if (writeRelays.has(url)) clientRelaysService.removeRelay(url, RelayMode.WRITE);
|
||||
else clientRelaysService.addRelay(url, RelayMode.WRITE);
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex gap="2" alignItems="center" pl="2">
|
||||
<RelayFavicon relay={url} size="xs" outline="2px solid" outlineColor={color} />
|
||||
<Link as={RouterLink} to={`/r/${encodeURIComponent(url)}`} isTruncated>
|
||||
{url}
|
||||
</Link>
|
||||
<IconButton
|
||||
ml="auto"
|
||||
aria-label="Toggle Write"
|
||||
icon={<UploadCloud01 />}
|
||||
size="sm"
|
||||
variant={writeRelays.has(url) ? "solid" : "ghost"}
|
||||
colorScheme={writeRelays.has(url) ? "green" : "gray"}
|
||||
onClick={onChange}
|
||||
title="Toggle Write"
|
||||
/>
|
||||
<IconButton
|
||||
aria-label="Remove Relay"
|
||||
icon={<CloseIcon />}
|
||||
size="sm"
|
||||
colorScheme="red"
|
||||
onClick={() => clientRelaysService.removeRelay(url, RelayMode.ALL)}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
import RelayControl from "./relay-control";
|
||||
import SelectRelaySet from "./select-relay-set";
|
||||
|
||||
export default function AppRelays() {
|
||||
const account = useCurrentAccount();
|
||||
@@ -99,6 +46,15 @@ export default function AppRelays() {
|
||||
clientRelaysService.addRelay(url, RelayMode.ALL);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* {account && (
|
||||
<>
|
||||
<Heading size="md" mt="2">
|
||||
Use relay set
|
||||
</Heading>
|
||||
<SelectRelaySet onChange={(cord, set) => set && clientRelaysService.setRelaysFromRelaySet(set)} />
|
||||
</>
|
||||
)} */}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
61
src/views/relays/app/relay-control.tsx
Normal file
61
src/views/relays/app/relay-control.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { useMemo } from "react";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
import { CloseIcon } from "@chakra-ui/icons";
|
||||
|
||||
import { Flex, IconButton, Link } from "@chakra-ui/react";
|
||||
import useSubject from "../../../hooks/use-subject";
|
||||
import relayPoolService from "../../../services/relay-pool";
|
||||
import clientRelaysService from "../../../services/client-relays";
|
||||
import { RelayMode } from "../../../classes/relay";
|
||||
import { RelayFavicon } from "../../../components/relay-favicon";
|
||||
import UploadCloud01 from "../../../components/icons/upload-cloud-01";
|
||||
|
||||
export default function RelayControl({ url }: { url: string }) {
|
||||
const relay = useMemo(() => relayPoolService.requestRelay(url, false), [url]);
|
||||
const status = useSubject(relay.status);
|
||||
const writeRelays = useSubject(clientRelaysService.writeRelays);
|
||||
|
||||
let color = "gray";
|
||||
switch (status) {
|
||||
case WebSocket.OPEN:
|
||||
color = "green";
|
||||
break;
|
||||
case WebSocket.CONNECTING:
|
||||
color = "yellow";
|
||||
break;
|
||||
case WebSocket.CLOSED:
|
||||
color = "red";
|
||||
break;
|
||||
}
|
||||
|
||||
const onChange = () => {
|
||||
if (writeRelays.has(url)) clientRelaysService.removeRelay(url, RelayMode.WRITE);
|
||||
else clientRelaysService.addRelay(url, RelayMode.WRITE);
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex gap="2" alignItems="center" pl="2">
|
||||
<RelayFavicon relay={url} size="xs" outline="2px solid" outlineColor={color} />
|
||||
<Link as={RouterLink} to={`/r/${encodeURIComponent(url)}`} isTruncated>
|
||||
{url}
|
||||
</Link>
|
||||
<IconButton
|
||||
ml="auto"
|
||||
aria-label="Toggle Write"
|
||||
icon={<UploadCloud01 />}
|
||||
size="sm"
|
||||
variant={writeRelays.has(url) ? "solid" : "ghost"}
|
||||
colorScheme={writeRelays.has(url) ? "green" : "gray"}
|
||||
onClick={onChange}
|
||||
title="Toggle Write"
|
||||
/>
|
||||
<IconButton
|
||||
aria-label="Remove Relay"
|
||||
icon={<CloseIcon />}
|
||||
size="sm"
|
||||
colorScheme="red"
|
||||
onClick={() => clientRelaysService.removeRelay(url, RelayMode.ALL)}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
}
|
41
src/views/relays/app/select-relay-set.tsx
Normal file
41
src/views/relays/app/select-relay-set.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Select } from "@chakra-ui/react";
|
||||
import { NostrEvent } from "nostr-tools";
|
||||
|
||||
import useCurrentAccount from "../../../hooks/use-current-account";
|
||||
import useUserRelaySets from "../../../hooks/use-user-relay-sets";
|
||||
import { getEventCoordinate } from "../../../helpers/nostr/events";
|
||||
import { getListName } from "../../../helpers/nostr/lists";
|
||||
|
||||
export default function SelectRelaySet({
|
||||
value,
|
||||
onChange,
|
||||
pubkey,
|
||||
}: {
|
||||
value?: string;
|
||||
onChange: (cord: string, set?: NostrEvent) => void;
|
||||
pubkey?: string;
|
||||
}) {
|
||||
const account = useCurrentAccount();
|
||||
const relaySets = useUserRelaySets(pubkey || account?.pubkey);
|
||||
|
||||
return (
|
||||
<Select
|
||||
size="sm"
|
||||
borderRadius="md"
|
||||
placeholder="Select set"
|
||||
value={value}
|
||||
onChange={(e) =>
|
||||
onChange(
|
||||
e.target.value,
|
||||
relaySets.find((set) => getEventCoordinate(set) === e.target.value),
|
||||
)
|
||||
}
|
||||
>
|
||||
{relaySets.map((set) => (
|
||||
<option key={set.id} value={getEventCoordinate(set)}>
|
||||
{getListName(set)}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
}
|
@@ -4,8 +4,6 @@ import { getEventUID } from "nostr-idb";
|
||||
|
||||
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
|
||||
import RelaySelectionProvider, { useRelaySelectionRelays } from "../../providers/local/relay-selection-provider";
|
||||
import RelaySelectionButton from "../../components/relay-selection/relay-selection-button";
|
||||
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
|
||||
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
|
||||
@@ -15,6 +13,7 @@ import { NostrEvent } from "../../types/nostr-event";
|
||||
import { getListName, getRelaysFromList } from "../../helpers/nostr/lists";
|
||||
import { RelayFavicon } from "../../components/relay-favicon";
|
||||
import TimelineActionAndStatus from "../../components/timeline-page/timeline-action-and-status";
|
||||
import { useReadRelays } from "../../hooks/use-client-relays";
|
||||
|
||||
function RelaySetCard({ set }: { set: NostrEvent }) {
|
||||
const name = getListName(set);
|
||||
@@ -35,7 +34,7 @@ function RelaySetCard({ set }: { set: NostrEvent }) {
|
||||
}
|
||||
|
||||
function BrowseRelaySetsPage() {
|
||||
const relays = useRelaySelectionRelays();
|
||||
const relays = useReadRelays();
|
||||
const { filter } = usePeopleListContext();
|
||||
const timeline = useTimelineLoader("relay-sets", relays, filter && { kinds: [kinds.Relaysets], ...filter }, {
|
||||
eventFilter: (e) => getRelaysFromList(e).length > 0,
|
||||
@@ -47,7 +46,6 @@ function BrowseRelaySetsPage() {
|
||||
return (
|
||||
<VerticalPageLayout>
|
||||
<Flex gap="2" wrap="wrap">
|
||||
<RelaySelectionButton />
|
||||
<PeopleListSelection />
|
||||
</Flex>
|
||||
<IntersectionObserverProvider callback={callback}>
|
||||
@@ -62,10 +60,8 @@ function BrowseRelaySetsPage() {
|
||||
|
||||
export default function BrowseRelaySetsView() {
|
||||
return (
|
||||
<RelaySelectionProvider>
|
||||
<PeopleListProvider>
|
||||
<BrowseRelaySetsPage />
|
||||
</PeopleListProvider>
|
||||
</RelaySelectionProvider>
|
||||
<PeopleListProvider>
|
||||
<BrowseRelaySetsPage />
|
||||
</PeopleListProvider>
|
||||
);
|
||||
}
|
||||
|
@@ -54,7 +54,7 @@ export default function RelaysView() {
|
||||
Mailboxes
|
||||
</Button>
|
||||
)}
|
||||
{account && (
|
||||
{/* {account && (
|
||||
<>
|
||||
<Heading size="sm" mt="2">
|
||||
Relay Sets
|
||||
@@ -70,7 +70,7 @@ export default function RelaysView() {
|
||||
</Button>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
)} */}
|
||||
</Flex>
|
||||
);
|
||||
if (vertical) {
|
||||
|
@@ -1,14 +1,14 @@
|
||||
import { kinds } from "nostr-tools";
|
||||
|
||||
import { useRelaySelectionRelays } from "../../providers/local/relay-selection-provider";
|
||||
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
|
||||
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
|
||||
import GenericNoteTimeline from "../../components/timeline-page/generic-note-timeline";
|
||||
import { usePeopleListContext } from "../../providers/local/people-list-provider";
|
||||
import { useAdditionalRelayContext } from "../../providers/local/additional-relay-context";
|
||||
|
||||
export default function ArticleSearchResults({ search }: { search: string }) {
|
||||
const searchRelays = useRelaySelectionRelays();
|
||||
const searchRelays = useAdditionalRelayContext();
|
||||
const { listId, filter } = usePeopleListContext();
|
||||
|
||||
const timeline = useTimelineLoader(
|
||||
|
@@ -1,16 +1,17 @@
|
||||
import { useRelaySelectionRelays } from "../../providers/local/relay-selection-provider";
|
||||
import { useRef } from "react";
|
||||
|
||||
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
|
||||
import IntersectionObserverProvider, {
|
||||
useRegisterIntersectionEntity,
|
||||
} from "../../providers/local/intersection-observer";
|
||||
import { COMMUNITY_DEFINITION_KIND } from "../../helpers/nostr/communities";
|
||||
import { useRef } from "react";
|
||||
import { getEventUID } from "../../helpers/nostr/events";
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import CommunityCard from "../communities/components/community-card";
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
import { usePeopleListContext } from "../../providers/local/people-list-provider";
|
||||
import { useAdditionalRelayContext } from "../../providers/local/additional-relay-context";
|
||||
|
||||
function CommunityResult({ community }: { community: NostrEvent }) {
|
||||
const ref = useRef<HTMLDivElement | null>(null);
|
||||
@@ -24,7 +25,7 @@ function CommunityResult({ community }: { community: NostrEvent }) {
|
||||
}
|
||||
|
||||
export default function CommunitySearchResults({ search }: { search: string }) {
|
||||
const searchRelays = useRelaySelectionRelays();
|
||||
const searchRelays = useAdditionalRelayContext();
|
||||
const { listId, filter } = usePeopleListContext();
|
||||
|
||||
const timeline = useTimelineLoader(
|
||||
|
@@ -6,7 +6,6 @@ import { SEARCH_RELAYS } from "../../const";
|
||||
import { safeDecode } from "../../helpers/nip19";
|
||||
import { getMatchHashtag } from "../../helpers/regexp";
|
||||
import { CommunityIcon, CopyToClipboardIcon, NotesIcon } from "../../components/icons";
|
||||
import RelaySelectionProvider from "../../providers/local/relay-selection-provider";
|
||||
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||
import User01 from "../../components/icons/user-01";
|
||||
import Feather from "../../components/icons/feather";
|
||||
@@ -19,6 +18,7 @@ import PeopleListSelection from "../../components/people-list-selection/people-l
|
||||
import useRouteSearchValue from "../../hooks/use-route-search-value";
|
||||
import { useBreakpointValue } from "../../providers/global/breakpoint-provider";
|
||||
import QRCodeScannerButton from "../../components/qr-code-scanner-button";
|
||||
import { AdditionalRelayProvider } from "../../providers/local/additional-relay-context";
|
||||
|
||||
export function SearchPage() {
|
||||
const navigate = useNavigate();
|
||||
@@ -147,10 +147,10 @@ export function SearchPage() {
|
||||
|
||||
export default function SearchView() {
|
||||
return (
|
||||
<RelaySelectionProvider overrideDefault={SEARCH_RELAYS}>
|
||||
<AdditionalRelayProvider relays={SEARCH_RELAYS}>
|
||||
<PeopleListProvider initList="global">
|
||||
<SearchPage />
|
||||
</PeopleListProvider>
|
||||
</RelaySelectionProvider>
|
||||
</AdditionalRelayProvider>
|
||||
);
|
||||
}
|
||||
|
@@ -1,14 +1,14 @@
|
||||
import { kinds } from "nostr-tools";
|
||||
|
||||
import { useRelaySelectionRelays } from "../../providers/local/relay-selection-provider";
|
||||
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
|
||||
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
|
||||
import GenericNoteTimeline from "../../components/timeline-page/generic-note-timeline";
|
||||
import { usePeopleListContext } from "../../providers/local/people-list-provider";
|
||||
import { useAdditionalRelayContext } from "../../providers/local/additional-relay-context";
|
||||
|
||||
export default function NoteSearchResults({ search }: { search: string }) {
|
||||
const searchRelays = useRelaySelectionRelays();
|
||||
const searchRelays = useAdditionalRelayContext();
|
||||
const { listId, filter } = usePeopleListContext();
|
||||
|
||||
const timeline = useTimelineLoader(
|
||||
|
@@ -12,13 +12,13 @@ import { UserDnsIdentityIcon } from "../../components/user-dns-identity-icon";
|
||||
import { embedNostrLinks, renderGenericUrl } from "../../components/embed-types";
|
||||
import UserLink from "../../components/user-link";
|
||||
import trustedUserStatsService, { NostrBandUserStats } from "../../services/trusted-user-stats";
|
||||
import { useRelaySelectionRelays } from "../../providers/local/relay-selection-provider";
|
||||
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/local/intersection-observer";
|
||||
import TimelineActionAndStatus from "../../components/timeline-page/timeline-action-and-status";
|
||||
import { usePeopleListContext } from "../../providers/local/people-list-provider";
|
||||
import { useAdditionalRelayContext } from "../../providers/local/additional-relay-context";
|
||||
|
||||
function ProfileResult({ profile }: { profile: NostrEvent }) {
|
||||
const metadata = parseKind0Event(profile);
|
||||
@@ -51,7 +51,7 @@ function ProfileResult({ profile }: { profile: NostrEvent }) {
|
||||
}
|
||||
|
||||
export default function ProfileSearchResults({ search }: { search: string }) {
|
||||
const searchRelays = useRelaySelectionRelays();
|
||||
const searchRelays = useAdditionalRelayContext();
|
||||
|
||||
const { listId, filter } = usePeopleListContext();
|
||||
const timeline = useTimelineLoader(
|
||||
|
@@ -1,47 +0,0 @@
|
||||
import {
|
||||
Flex,
|
||||
IconButton,
|
||||
IconButtonProps,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
useDisclosure,
|
||||
} from "@chakra-ui/react";
|
||||
import { CodeIcon } from "../../../components/icons";
|
||||
import RawValue from "../../../components/debug-modal/raw-value";
|
||||
import RawJson from "../../../components/debug-modal/raw-json";
|
||||
import { ParsedStream } from "../../../helpers/nostr/stream";
|
||||
import useEventNaddr from "../../../hooks/use-event-naddr";
|
||||
|
||||
export default function StreamDebugButton({
|
||||
stream,
|
||||
...props
|
||||
}: { stream: ParsedStream } & Omit<IconButtonProps, "icon" | "aria-label">) {
|
||||
const debugModal = useDisclosure();
|
||||
const naddr = useEventNaddr(stream.event);
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton icon={<CodeIcon />} aria-label="Show raw event" onClick={debugModal.onOpen} {...props} />
|
||||
|
||||
<Modal isOpen={debugModal.isOpen} onClose={debugModal.onClose} size="6xl">
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Raw event</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody p="4">
|
||||
<Flex gap="2" direction="column">
|
||||
<RawValue heading="Event Id" value={stream.event.id} />
|
||||
<RawValue heading="naddr" value={naddr} />
|
||||
<RawJson heading="Parsed" json={{ ...stream, event: "Omitted, see JSON below" }} />
|
||||
<RawJson heading="JSON" json={stream.event} />
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
@@ -3,8 +3,9 @@ import { ParsedStream } from "../../../helpers/nostr/stream";
|
||||
import { LightningIcon } from "../../../components/icons";
|
||||
import useUserLNURLMetadata from "../../../hooks/use-user-lnurl-metadata";
|
||||
import ZapModal from "../../../components/event-zap-modal";
|
||||
import { useRelaySelectionRelays } from "../../../providers/local/relay-selection-provider";
|
||||
import useStreamGoal from "../../../hooks/use-stream-goal";
|
||||
import { useReadRelays } from "../../../hooks/use-client-relays";
|
||||
import { useAdditionalRelayContext } from "../../../providers/local/additional-relay-context";
|
||||
|
||||
export default function StreamZapButton({
|
||||
stream,
|
||||
@@ -19,7 +20,7 @@ export default function StreamZapButton({
|
||||
}) {
|
||||
const zapModal = useDisclosure();
|
||||
const zapMetadata = useUserLNURLMetadata(stream.host);
|
||||
const relays = useRelaySelectionRelays();
|
||||
const relays = useReadRelays(useAdditionalRelayContext());
|
||||
const goal = useStreamGoal(stream);
|
||||
|
||||
const commonProps = {
|
||||
|
@@ -2,13 +2,13 @@ import { useMemo } from "react";
|
||||
import { Card, CardBody, CardHeader, CardProps, Heading, Image, LinkBox, LinkOverlay } from "@chakra-ui/react";
|
||||
|
||||
import { useReadRelays } from "../../../hooks/use-client-relays";
|
||||
import { useRelaySelectionRelays } from "../../../providers/local/relay-selection-provider";
|
||||
import replaceableEventLoaderService from "../../../services/replaceable-event-requester";
|
||||
import useSubject from "../../../hooks/use-subject";
|
||||
import { NoteContents } from "../../../components/note/text-note-contents";
|
||||
import { isATag } from "../../../types/nostr-event";
|
||||
import useReplaceableEvent from "../../../hooks/use-replaceable-event";
|
||||
import OpenGraphCard from "../../../components/open-graph-card";
|
||||
import { useAdditionalRelayContext } from "../../../providers/local/additional-relay-context";
|
||||
|
||||
export const STREAMER_CARDS_TYPE = 17777;
|
||||
export const STREAMER_CARD_TYPE = 37777;
|
||||
@@ -24,7 +24,7 @@ function useStreamerCardsCords(pubkey: string, relays: Iterable<string>) {
|
||||
}
|
||||
|
||||
function StreamerCard({ cord, relay, ...props }: { cord: string; relay?: string } & CardProps) {
|
||||
const contextRelays = useRelaySelectionRelays();
|
||||
const contextRelays = useAdditionalRelayContext();
|
||||
const readRelays = useReadRelays(relay ? [...contextRelays, relay] : contextRelays);
|
||||
|
||||
const card = useReplaceableEvent(cord, readRelays);
|
||||
@@ -61,7 +61,7 @@ function StreamerCard({ cord, relay, ...props }: { cord: string; relay?: string
|
||||
}
|
||||
|
||||
export default function StreamerCards({ pubkey, ...props }: Omit<CardProps, "children"> & { pubkey: string }) {
|
||||
const contextRelays = useRelaySelectionRelays();
|
||||
const contextRelays = useAdditionalRelayContext();
|
||||
const readRelays = useReadRelays(contextRelays);
|
||||
|
||||
const cardCords = useStreamerCardsCords(pubkey, readRelays);
|
||||
|
@@ -15,7 +15,7 @@ import useCurrentAccount from "../../../hooks/use-current-account";
|
||||
import { getEventUID } from "../../../helpers/nostr/events";
|
||||
import { useReadRelays } from "../../../hooks/use-client-relays";
|
||||
import { ChevronLeftIcon } from "../../../components/icons";
|
||||
import RelaySelectionProvider from "../../../providers/local/relay-selection-provider";
|
||||
import { AdditionalRelayProvider } from "../../../providers/local/additional-relay-context";
|
||||
import UsersCard from "./users-card";
|
||||
import ZapsCard from "./zaps-card";
|
||||
import ChatCard from "./chat-card";
|
||||
@@ -105,9 +105,9 @@ function StreamModerationPage() {
|
||||
</Select>
|
||||
</Flex>
|
||||
{selected && (
|
||||
<RelaySelectionProvider additionalDefaults={selected.relays ?? []}>
|
||||
<AdditionalRelayProvider relays={selected.relays ?? []}>
|
||||
<StreamModerationDashboard stream={selected} />
|
||||
</RelaySelectionProvider>
|
||||
</AdditionalRelayProvider>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { Divider, Flex, Heading, SimpleGrid, Switch } from "@chakra-ui/react";
|
||||
import { Flex, Heading, SimpleGrid, Switch } from "@chakra-ui/react";
|
||||
|
||||
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
|
||||
@@ -7,8 +7,6 @@ import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
import StreamCard from "./components/stream-card";
|
||||
import { STREAM_KIND } from "../../helpers/nostr/stream";
|
||||
import RelaySelectionButton from "../../components/relay-selection/relay-selection-button";
|
||||
import RelaySelectionProvider, { useRelaySelectionRelays } from "../../providers/local/relay-selection-provider";
|
||||
import useRelaysChanged from "../../hooks/use-relays-changed";
|
||||
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
|
||||
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
|
||||
@@ -20,10 +18,12 @@ import { NostrEvent } from "../../types/nostr-event";
|
||||
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||
import useClientSideMuteFilter from "../../hooks/use-client-side-mute-filter";
|
||||
import { useRouteStateBoolean } from "../../hooks/use-route-state-value";
|
||||
import { useReadRelays } from "../../hooks/use-client-relays";
|
||||
import { AdditionalRelayProvider, useAdditionalRelayContext } from "../../providers/local/additional-relay-context";
|
||||
|
||||
function StreamsPage() {
|
||||
useAppTitle("Streams");
|
||||
const relays = useRelaySelectionRelays();
|
||||
const relays = useReadRelays(useAdditionalRelayContext()).urls;
|
||||
const userMuteFilter = useClientSideMuteFilter();
|
||||
const showEnded = useRouteStateBoolean("ended", false);
|
||||
|
||||
@@ -63,7 +63,6 @@ function StreamsPage() {
|
||||
<Switch checked={showEnded.isOpen} onChange={showEnded.onToggle}>
|
||||
Show Ended
|
||||
</Switch>
|
||||
<RelaySelectionButton ml="auto" />
|
||||
</Flex>
|
||||
<IntersectionObserverProvider callback={callback}>
|
||||
<Heading size="lg" mt="2">
|
||||
@@ -93,12 +92,12 @@ function StreamsPage() {
|
||||
}
|
||||
export default function StreamsView() {
|
||||
return (
|
||||
<RelaySelectionProvider
|
||||
additionalDefaults={["wss://nos.lol", "wss://relay.damus.io", "wss://relay.snort.social", "wss://nostr.wine"]}
|
||||
<AdditionalRelayProvider
|
||||
relays={["wss://nos.lol", "wss://relay.damus.io", "wss://relay.snort.social", "wss://nostr.wine"]}
|
||||
>
|
||||
<PeopleListProvider>
|
||||
<StreamsPage />
|
||||
</PeopleListProvider>
|
||||
</RelaySelectionProvider>
|
||||
</AdditionalRelayProvider>
|
||||
);
|
||||
}
|
||||
|
@@ -31,11 +31,8 @@ import StreamSummaryContent from "../components/stream-summary-content";
|
||||
import { ChevronLeftIcon, ExternalLinkIcon } from "../../../components/icons";
|
||||
import useSetColorMode from "../../../hooks/use-set-color-mode";
|
||||
import { CopyIconButton } from "../../../components/copy-icon-button";
|
||||
import StreamDebugButton from "../components/stream-debug-button";
|
||||
import replaceableEventLoaderService from "../../../services/replaceable-event-requester";
|
||||
import useSubject from "../../../hooks/use-subject";
|
||||
import RelaySelectionButton from "../../../components/relay-selection/relay-selection-button";
|
||||
import RelaySelectionProvider from "../../../providers/local/relay-selection-provider";
|
||||
import StreamerCards from "../components/streamer-cards";
|
||||
import { useAppTitle } from "../../../hooks/use-app-title";
|
||||
import StreamSatsPerMinute from "../components/stream-sats-per-minute";
|
||||
@@ -50,6 +47,8 @@ import StreamGoal from "../components/stream-goal";
|
||||
import StreamShareButton from "../components/stream-share-button";
|
||||
import VerticalPageLayout from "../../../components/vertical-page-layout";
|
||||
import { useBreakpointValue } from "../../../providers/global/breakpoint-provider";
|
||||
import { AdditionalRelayProvider } from "../../../providers/local/additional-relay-context";
|
||||
import DebugEventButton from "../../../components/debug-modal/debug-event-button";
|
||||
|
||||
function DesktopStreamPage({ stream }: { stream: ParsedStream }) {
|
||||
useAppTitle(stream.title);
|
||||
@@ -96,8 +95,7 @@ function DesktopStreamPage({ stream }: { stream: ParsedStream }) {
|
||||
<StreamStatusBadge stream={stream} fontSize="lg" />
|
||||
<Spacer />
|
||||
<StreamShareButton stream={stream} title="Share stream" />
|
||||
<RelaySelectionButton display={{ base: "none", md: "block" }} />
|
||||
<StreamDebugButton stream={stream} variant="ghost" />
|
||||
<DebugEventButton event={stream.event} variant="ghost" />
|
||||
<Button onClick={() => setShowChat((v) => !v)}>{showChat ? "Hide" : "Show"} Chat</Button>
|
||||
</Flex>
|
||||
<Flex gap="2" maxH="calc(100vh - 4rem)" overflow="hidden">
|
||||
@@ -278,10 +276,10 @@ export default function StreamView() {
|
||||
if (!stream) return <Spinner />;
|
||||
return (
|
||||
// add snort and damus relays so zap.stream will always see zaps
|
||||
<RelaySelectionProvider additionalDefaults={streamRelays}>
|
||||
<AdditionalRelayProvider relays={streamRelays}>
|
||||
<UserEmojiProvider pubkey={stream.host}>
|
||||
{displayMode ? <ChatWidget stream={stream} displayMode={displayMode} /> : <StreamPage stream={stream} />}
|
||||
</UserEmojiProvider>
|
||||
</RelaySelectionProvider>
|
||||
</AdditionalRelayProvider>
|
||||
);
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@ import { Box, Button, Flex, useToast } from "@chakra-ui/react";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
import { ParsedStream, buildChatMessage } from "../../../../helpers/nostr/stream";
|
||||
import { useRelaySelectionRelays } from "../../../../providers/local/relay-selection-provider";
|
||||
import { unique } from "../../../../helpers/array";
|
||||
import { useSigningContext } from "../../../../providers/global/signing-provider";
|
||||
import { createEmojiTags, ensureNotifyContentMentions } from "../../../../helpers/nostr/post";
|
||||
@@ -13,12 +12,14 @@ import StreamZapButton from "../../components/stream-zap-button";
|
||||
import { nostrBuildUploadImage } from "../../../../helpers/nostr-build";
|
||||
import { useUserInbox } from "../../../../hooks/use-user-mailboxes";
|
||||
import { usePublishEvent } from "../../../../providers/global/publish-provider";
|
||||
import { useReadRelays } from "../../../../hooks/use-client-relays";
|
||||
import { useAdditionalRelayContext } from "../../../../providers/local/additional-relay-context";
|
||||
|
||||
export default function ChatMessageForm({ stream, hideZapButton }: { stream: ParsedStream; hideZapButton?: boolean }) {
|
||||
const toast = useToast();
|
||||
const publish = usePublishEvent();
|
||||
const emojis = useContextEmojis();
|
||||
const streamRelays = useRelaySelectionRelays();
|
||||
const streamRelays = useReadRelays(useAdditionalRelayContext());
|
||||
const hostReadRelays = useUserInbox(stream.host);
|
||||
|
||||
const relays = useMemo(() => unique([...streamRelays, ...hostReadRelays]), [hostReadRelays, streamRelays]);
|
||||
|
@@ -5,14 +5,15 @@ import { getEventUID } from "../../../../helpers/nostr/events";
|
||||
import { ParsedStream, STREAM_CHAT_MESSAGE_KIND, getATag } from "../../../../helpers/nostr/stream";
|
||||
import useTimelineLoader from "../../../../hooks/use-timeline-loader";
|
||||
import { NostrEvent } from "../../../../types/nostr-event";
|
||||
import { useRelaySelectionRelays } from "../../../../providers/local/relay-selection-provider";
|
||||
import useStreamGoal from "../../../../hooks/use-stream-goal";
|
||||
import { NostrQuery } from "../../../../types/nostr-query";
|
||||
import useUserMuteFilter from "../../../../hooks/use-user-mute-filter";
|
||||
import useClientSideMuteFilter from "../../../../hooks/use-client-side-mute-filter";
|
||||
import { useReadRelays } from "../../../../hooks/use-client-relays";
|
||||
import { useAdditionalRelayContext } from "../../../../providers/local/additional-relay-context";
|
||||
|
||||
export default function useStreamChatTimeline(stream: ParsedStream) {
|
||||
const streamRelays = useRelaySelectionRelays();
|
||||
const streamRelays = useReadRelays(useAdditionalRelayContext());
|
||||
|
||||
const hostMuteFilter = useUserMuteFilter(stream.host, [], { alwaysRequest: true });
|
||||
const muteFilter = useClientSideMuteFilter();
|
||||
|
@@ -1,56 +0,0 @@
|
||||
import { Button, Flex, SimpleGrid } from "@chakra-ui/react";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
|
||||
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
import { FILE_KIND } from "../../helpers/nostr/files";
|
||||
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
|
||||
import RelaySelectionProvider, { useRelaySelectionContext } from "../../providers/local/relay-selection-provider";
|
||||
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
|
||||
import RelaySelectionButton from "../../components/relay-selection/relay-selection-button";
|
||||
import TimelineActionAndStatus from "../../components/timeline-page/timeline-action-and-status";
|
||||
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
|
||||
import Upload01 from "../../components/icons/upload-01";
|
||||
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
|
||||
|
||||
function FilesPage() {
|
||||
const { listId, filter } = usePeopleListContext();
|
||||
const { relays } = useRelaySelectionContext();
|
||||
|
||||
const timeline = useTimelineLoader(
|
||||
`${listId}-files`,
|
||||
relays,
|
||||
filter && { kinds: [FILE_KIND], "#m": ["model/stl"], ...filter },
|
||||
);
|
||||
|
||||
const files = useSubject(timeline.timeline);
|
||||
const callback = useTimelineCurserIntersectionCallback(timeline);
|
||||
|
||||
return (
|
||||
<VerticalPageLayout>
|
||||
<Flex gap="2">
|
||||
<PeopleListSelection />
|
||||
<RelaySelectionButton />
|
||||
<Button as={RouterLink} colorScheme="primary" ml="auto" leftIcon={<Upload01 />} to="/things/upload">
|
||||
New Thing
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
<IntersectionObserverProvider callback={callback}>
|
||||
<SimpleGrid minChildWidth="20rem" spacing="2"></SimpleGrid>
|
||||
</IntersectionObserverProvider>
|
||||
<TimelineActionAndStatus timeline={timeline} />
|
||||
</VerticalPageLayout>
|
||||
);
|
||||
}
|
||||
|
||||
export default function FilesView() {
|
||||
return (
|
||||
<PeopleListProvider initList="global">
|
||||
<RelaySelectionProvider>
|
||||
<FilesPage />
|
||||
</RelaySelectionProvider>
|
||||
</PeopleListProvider>
|
||||
);
|
||||
}
|
@@ -1,43 +0,0 @@
|
||||
import { useState } from "react";
|
||||
import { Button, ButtonGroup, Flex, Heading, Image, Text } from "@chakra-ui/react";
|
||||
|
||||
import useObjectURL from "../../../hooks/use-object-url";
|
||||
import BackButton from "../../../components/back-button";
|
||||
|
||||
export default function ConfirmStep({
|
||||
screenshot,
|
||||
name,
|
||||
hash,
|
||||
summary,
|
||||
onConfirm,
|
||||
}: {
|
||||
screenshot: Blob;
|
||||
name: string;
|
||||
summary: string;
|
||||
hash: string;
|
||||
onConfirm: () => Promise<void>;
|
||||
}) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const objectURL = useObjectURL(screenshot);
|
||||
|
||||
const confirm = async () => {
|
||||
setLoading(true);
|
||||
await onConfirm();
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex gap="2" direction="column" maxW="40rem" w="full" mx="auto">
|
||||
<Image src={objectURL} maxW="2xl" />
|
||||
<Heading size="md">{name}</Heading>
|
||||
<Text>File Hash: {hash}</Text>
|
||||
<Text whiteSpace="pre-line">{summary}</Text>
|
||||
<ButtonGroup ml="auto">
|
||||
<BackButton />
|
||||
<Button onClick={confirm} isLoading={loading} colorScheme="primary">
|
||||
Upload
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
);
|
||||
}
|
@@ -1,46 +0,0 @@
|
||||
import { Button, ButtonGroup, Flex, FormControl, FormLabel, Input, Textarea } from "@chakra-ui/react";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
import BackButton from "../../../components/back-button";
|
||||
|
||||
type FormValues = {
|
||||
name: string;
|
||||
summary: string;
|
||||
};
|
||||
|
||||
export default function DetailsStep({ onSubmit }: { onSubmit: (values: FormValues) => void }) {
|
||||
const { register, getValues, setValue, handleSubmit, watch } = useForm<FormValues>({
|
||||
defaultValues: {
|
||||
name: "",
|
||||
summary: "",
|
||||
},
|
||||
mode: "all",
|
||||
});
|
||||
|
||||
const submit = handleSubmit(onSubmit);
|
||||
|
||||
return (
|
||||
<Flex as="form" onSubmit={submit} gap="2" direction="column" maxW="40rem" w="full" mx="auto">
|
||||
<FormControl isRequired>
|
||||
<FormLabel>Name</FormLabel>
|
||||
<Input {...register("name", { required: true })} placeholder="Thing name" autoComplete="off" />
|
||||
</FormControl>
|
||||
<FormControl isRequired>
|
||||
<FormLabel>Summary</FormLabel>
|
||||
<Textarea
|
||||
rows={3}
|
||||
isRequired
|
||||
placeholder="A short summary of the thing"
|
||||
autoComplete="off"
|
||||
{...register("summary", { required: true })}
|
||||
/>
|
||||
</FormControl>
|
||||
<ButtonGroup ml="auto">
|
||||
<BackButton />
|
||||
<Button type="submit" colorScheme="primary">
|
||||
Next
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
);
|
||||
}
|
@@ -1,99 +0,0 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Box,
|
||||
Step,
|
||||
StepIcon,
|
||||
StepIndicator,
|
||||
StepNumber,
|
||||
StepSeparator,
|
||||
StepStatus,
|
||||
StepTitle,
|
||||
Stepper,
|
||||
} from "@chakra-ui/react";
|
||||
|
||||
import VerticalPageLayout from "../../../components/vertical-page-layout";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import useRouteStateValue from "../../../hooks/use-route-state-value";
|
||||
import SelectFileStep from "./select-file-step";
|
||||
import DetailsStep from "./details-step";
|
||||
import PreviewStep from "./preview-step";
|
||||
import ConfirmStep from "./confirm-step";
|
||||
|
||||
const steps = [{ title: "Select File" }, { title: "Details" }, { title: "Upload" }];
|
||||
|
||||
export default function ThingUploadView() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const step = useRouteStateValue("step", 0);
|
||||
|
||||
const [file, setFile] = useState<Blob>();
|
||||
const [fileURL, setFileURL] = useState<string>();
|
||||
const [hash, setHash] = useState<string>();
|
||||
const [name, setName] = useState<string>();
|
||||
const [summary, setSummary] = useState<string>();
|
||||
const [screenshot, setScreenshot] = useState<Blob>();
|
||||
|
||||
const upload = async () => {
|
||||
// await publish("Post", draft);
|
||||
};
|
||||
|
||||
const renderContent = () => {
|
||||
switch (step.value) {
|
||||
case 0:
|
||||
return (
|
||||
<SelectFileStep
|
||||
onSubmit={(values) => {
|
||||
setFile(values.file);
|
||||
if (values.fileURL) setFileURL(values.fileURL);
|
||||
setHash(values.hash);
|
||||
step.setValue(1, false);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case 1:
|
||||
return (
|
||||
<DetailsStep
|
||||
onSubmit={(values) => {
|
||||
setName(values.name);
|
||||
setSummary(values.summary);
|
||||
step.setValue(2, false);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case 2:
|
||||
return (
|
||||
<PreviewStep
|
||||
file={file!}
|
||||
onSubmit={(values) => {
|
||||
setScreenshot(values.screenshot);
|
||||
step.setValue(3, false);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case 3:
|
||||
return <ConfirmStep name={name!} hash={hash!} summary={summary!} screenshot={screenshot!} onConfirm={upload} />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<VerticalPageLayout>
|
||||
<Stepper index={step.value}>
|
||||
{steps.map((step, index) => (
|
||||
<Step key={index}>
|
||||
<StepIndicator>
|
||||
<StepStatus complete={<StepIcon />} incomplete={<StepNumber />} active={<StepNumber />} />
|
||||
</StepIndicator>
|
||||
|
||||
<Box flexShrink="0">
|
||||
<StepTitle>{step.title}</StepTitle>
|
||||
{/* {step.description && <StepDescription>{step.description}</StepDescription>} */}
|
||||
</Box>
|
||||
|
||||
<StepSeparator />
|
||||
</Step>
|
||||
))}
|
||||
</Stepper>
|
||||
{renderContent()}
|
||||
</VerticalPageLayout>
|
||||
);
|
||||
}
|
@@ -1,54 +0,0 @@
|
||||
import { useRef } from "react";
|
||||
import { Button, ButtonGroup, Flex, useToast } from "@chakra-ui/react";
|
||||
|
||||
import STLViewer from "../../../components/stl-viewer";
|
||||
import useObjectURL from "../../../hooks/use-object-url";
|
||||
import BackButton from "../../../components/back-button";
|
||||
|
||||
type FormValues = {
|
||||
screenshot: Blob;
|
||||
};
|
||||
|
||||
function canvasToBlob(canvas: HTMLCanvasElement, type?: string): Promise<Blob> {
|
||||
return new Promise((res, rej) => {
|
||||
canvas.toBlob((blob) => {
|
||||
if (blob) res(blob);
|
||||
else rej(new Error("Failed to get blob"));
|
||||
}, type);
|
||||
});
|
||||
}
|
||||
|
||||
export default function PreviewStep({ file, onSubmit }: { file: Blob; onSubmit: (values: FormValues) => void }) {
|
||||
const toast = useToast();
|
||||
|
||||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||
const takeScreenshot = async () => {
|
||||
if (!canvasRef.current) return;
|
||||
try {
|
||||
const blob = await canvasToBlob(canvasRef.current, "image/jpeg");
|
||||
onSubmit({ screenshot: blob });
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
toast({ description: e.message, status: "error" });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const previewURL = useObjectURL(file);
|
||||
|
||||
return (
|
||||
<Flex gap="2" direction="column">
|
||||
{previewURL && (
|
||||
<>
|
||||
<STLViewer aspectRatio={16 / 10} url={previewURL} ref={canvasRef} />
|
||||
<ButtonGroup ml="auto">
|
||||
<BackButton />
|
||||
<Button onClick={takeScreenshot} colorScheme="primary">
|
||||
Take Screenshot
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
@@ -1,79 +0,0 @@
|
||||
import { ChangeEventHandler, useCallback } from "react";
|
||||
import { Button, ButtonGroup, Divider, Flex, FormControl, FormLabel, Heading, Input } from "@chakra-ui/react";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
import useObjectURL from "../../../hooks/use-object-url";
|
||||
import { arrayBufferToHex } from "../../../helpers/array";
|
||||
import BackButton from "../../../components/back-button";
|
||||
|
||||
type FormValues = {
|
||||
file: Blob;
|
||||
fileURL?: string;
|
||||
hash: string;
|
||||
};
|
||||
|
||||
// example file https://tonybox.net/objects/keystone/keystone.stl
|
||||
|
||||
export default function SelectFileStep({ onSubmit }: { onSubmit: (values: FormValues) => void }) {
|
||||
const { register, getValues, setValue, handleSubmit, watch, resetField } = useForm<FormValues>({
|
||||
defaultValues: {
|
||||
fileURL: "",
|
||||
},
|
||||
mode: "all",
|
||||
});
|
||||
watch("file");
|
||||
watch("fileURL");
|
||||
|
||||
const handleFileChange = useCallback<ChangeEventHandler<HTMLInputElement>>(
|
||||
(e) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) setValue("file", file);
|
||||
else resetField("file");
|
||||
},
|
||||
[setValue, resetField],
|
||||
);
|
||||
|
||||
const submit = handleSubmit(async (values) => {
|
||||
let file: Blob | undefined = values.file;
|
||||
if (!file && values.fileURL) file = await fetch(values.fileURL).then((res) => res.blob());
|
||||
if (!file) throw new Error("Cant access file");
|
||||
|
||||
// get file hash
|
||||
const buffer = await file.arrayBuffer();
|
||||
const hash = await window.crypto.subtle.digest("SHA-256", buffer);
|
||||
onSubmit({ hash: arrayBufferToHex(hash), file, fileURL: values.fileURL });
|
||||
|
||||
// const pub = await publish("Post", draft);
|
||||
});
|
||||
|
||||
const fileObjectURL = useObjectURL(getValues().file);
|
||||
const stlURL = fileObjectURL || getValues().fileURL;
|
||||
let step = 0;
|
||||
if (getValues().file) step = 1;
|
||||
|
||||
return (
|
||||
<Flex as="form" onSubmit={submit} direction="column" gap="2" maxW="40rem" w="full" mx="auto">
|
||||
<Heading size="md">Upload File</Heading>
|
||||
<Input type="file" accept="model/stl" placeholder="Select STL file" onChange={handleFileChange} isDisabled />
|
||||
<Flex gap="4" alignItems="center" my="4">
|
||||
<Divider />
|
||||
<Heading size="sm">OR</Heading>
|
||||
<Divider />
|
||||
</Flex>
|
||||
<Heading size="md">Use Remote File</Heading>
|
||||
<Input
|
||||
type="url"
|
||||
{...register("fileURL", { validate: (str) => str && str.endsWith(".stl"), required: true })}
|
||||
placeholder="https://example.com/files/things/cube.stl"
|
||||
onChange={handleFileChange}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<ButtonGroup ml="auto">
|
||||
<BackButton />
|
||||
<Button type="submit" colorScheme="primary">
|
||||
Next
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
);
|
||||
}
|
@@ -25,18 +25,17 @@ import { useUserMetadata } from "../../hooks/use-user-metadata";
|
||||
import EventStore from "../../classes/event-store";
|
||||
import NostrRequest from "../../classes/nostr-request";
|
||||
import { isPTag } from "../../types/nostr-event";
|
||||
import RelaySelectionProvider, { useRelaySelectionContext } from "../../providers/local/relay-selection-provider";
|
||||
import RelaySelectionButton from "../../components/relay-selection/relay-selection-button";
|
||||
import { useDebounce } from "react-use";
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
import { ChevronLeftIcon } from "../../components/icons";
|
||||
import { useReadRelays } from "../../hooks/use-client-relays";
|
||||
|
||||
type NodeType = { id: string; image?: string; name?: string };
|
||||
|
||||
function NetworkDMGraphPage() {
|
||||
const navigate = useNavigate();
|
||||
const account = useCurrentAccount()!;
|
||||
const { relays } = useRelaySelectionContext();
|
||||
const relays = useReadRelays();
|
||||
|
||||
const contacts = useUserContactList(account.pubkey);
|
||||
const contactsPubkeys = useMemo(
|
||||
@@ -123,7 +122,6 @@ function NetworkDMGraphPage() {
|
||||
onChange={(e) => setSince(dayjs(e.target.value).unix())}
|
||||
/>
|
||||
<Text>Showing all direct messages between contacts in the last {dayjs.unix(since).fromNow(true)}</Text>
|
||||
<RelaySelectionButton ml="auto" />
|
||||
</Flex>
|
||||
<Box overflow="hidden" flex={1}>
|
||||
<AutoSizer>
|
||||
@@ -170,9 +168,7 @@ function NetworkDMGraphPage() {
|
||||
export default function NetworkDMGraphView() {
|
||||
return (
|
||||
<RequireCurrentAccount>
|
||||
<RelaySelectionProvider>
|
||||
<NetworkDMGraphPage />
|
||||
</RelaySelectionProvider>
|
||||
<NetworkDMGraphPage />
|
||||
</RequireCurrentAccount>
|
||||
);
|
||||
}
|
||||
|
@@ -6,9 +6,7 @@ import { bytesToHex } from "@noble/hashes/utils";
|
||||
|
||||
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
|
||||
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||
import RelaySelectionProvider, { useRelaySelectionContext } from "../../providers/local/relay-selection-provider";
|
||||
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
|
||||
import RelaySelectionButton from "../../components/relay-selection/relay-selection-button";
|
||||
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||
import useClientSideMuteFilter from "../../hooks/use-client-side-mute-filter";
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
@@ -23,6 +21,7 @@ import accountService from "../../services/account";
|
||||
import signingService from "../../services/signing";
|
||||
import CategorySelect from "./components/category-select";
|
||||
import useRouteSearchValue from "../../hooks/use-route-search-value";
|
||||
import { useReadRelays } from "../../hooks/use-client-relays";
|
||||
|
||||
function Warning() {
|
||||
const navigate = useNavigate();
|
||||
@@ -60,7 +59,7 @@ function Warning() {
|
||||
|
||||
function TorrentsPage() {
|
||||
const { filter, listId } = usePeopleListContext();
|
||||
const { relays } = useRelaySelectionContext();
|
||||
const relays = useReadRelays();
|
||||
const tagsParam = useRouteSearchValue("tags");
|
||||
const tags = tagsParam.value?.split(",") ?? [];
|
||||
|
||||
@@ -100,7 +99,6 @@ function TorrentsPage() {
|
||||
<VerticalPageLayout>
|
||||
{!!account && <Warning />}
|
||||
<Flex gap="2">
|
||||
<RelaySelectionButton />
|
||||
<PeopleListSelection />
|
||||
<CategorySelect maxW="xs" value={tags.join(",")} onChange={handleTagsChange} />
|
||||
<Spacer />
|
||||
@@ -135,10 +133,8 @@ function TorrentsPage() {
|
||||
|
||||
export default function TorrentsView() {
|
||||
return (
|
||||
<RelaySelectionProvider>
|
||||
<PeopleListProvider>
|
||||
<TorrentsPage />
|
||||
</PeopleListProvider>
|
||||
</RelaySelectionProvider>
|
||||
<PeopleListProvider>
|
||||
<TorrentsPage />
|
||||
</PeopleListProvider>
|
||||
);
|
||||
}
|
||||
|
@@ -6,17 +6,18 @@ import { STEMSTR_RELAY, STEMSTR_TRACK_KIND } from "../../helpers/nostr/stemstr";
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
|
||||
import RelaySelectionProvider, { useRelaySelectionContext } from "../../providers/local/relay-selection-provider";
|
||||
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
|
||||
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
|
||||
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
|
||||
import TrackCard from "./components/track-card";
|
||||
import useClientSideMuteFilter from "../../hooks/use-client-side-mute-filter";
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import { AdditionalRelayProvider, useAdditionalRelayContext } from "../../providers/local/additional-relay-context";
|
||||
import { useReadRelays } from "../../hooks/use-client-relays";
|
||||
|
||||
function TracksPage() {
|
||||
const { listId, filter } = usePeopleListContext();
|
||||
const { relays } = useRelaySelectionContext();
|
||||
const relays = useReadRelays(useAdditionalRelayContext());
|
||||
|
||||
const clientMuteFilter = useClientSideMuteFilter();
|
||||
const eventFilter = useCallback(
|
||||
@@ -50,9 +51,9 @@ function TracksPage() {
|
||||
export default function TracksView() {
|
||||
return (
|
||||
<PeopleListProvider>
|
||||
<RelaySelectionProvider additionalDefaults={[STEMSTR_RELAY]}>
|
||||
<AdditionalRelayProvider relays={[STEMSTR_RELAY]}>
|
||||
<TracksPage />
|
||||
</RelaySelectionProvider>
|
||||
</AdditionalRelayProvider>
|
||||
</PeopleListProvider>
|
||||
);
|
||||
}
|
||||
|
@@ -4,9 +4,7 @@ import { Link as RouterLink } from "react-router-dom";
|
||||
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
|
||||
import RelaySelectionProvider, { useRelaySelectionContext } from "../../providers/local/relay-selection-provider";
|
||||
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
|
||||
import RelaySelectionButton from "../../components/relay-selection/relay-selection-button";
|
||||
import TimelineActionAndStatus from "../../components/timeline-page/timeline-action-and-status";
|
||||
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
|
||||
@@ -16,10 +14,11 @@ import { FLARE_VIDEO_KIND } from "../../helpers/nostr/flare";
|
||||
import VideoCard from "./components/video-card";
|
||||
import { getEventUID } from "../../helpers/nostr/events";
|
||||
import { ErrorBoundary } from "../../components/error-boundary";
|
||||
import { useReadRelays } from "../../hooks/use-client-relays";
|
||||
|
||||
function VideosPage() {
|
||||
const { listId, filter } = usePeopleListContext();
|
||||
const { relays } = useRelaySelectionContext();
|
||||
const relays = useReadRelays();
|
||||
|
||||
const timeline = useTimelineLoader(`${listId}-videos`, relays, filter && { kinds: [FLARE_VIDEO_KIND], ...filter });
|
||||
|
||||
@@ -30,10 +29,6 @@ function VideosPage() {
|
||||
<VerticalPageLayout>
|
||||
<Flex gap="2">
|
||||
<PeopleListSelection />
|
||||
<RelaySelectionButton />
|
||||
<Button as={RouterLink} colorScheme="primary" ml="auto" leftIcon={<Upload01 />} to="/things/upload">
|
||||
New Thing
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
<IntersectionObserverProvider callback={callback}>
|
||||
@@ -53,9 +48,7 @@ function VideosPage() {
|
||||
export default function VideosView() {
|
||||
return (
|
||||
<PeopleListProvider initList="following">
|
||||
<RelaySelectionProvider>
|
||||
<VideosPage />
|
||||
</RelaySelectionProvider>
|
||||
<VideosPage />
|
||||
</PeopleListProvider>
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user