mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-09-26 11:37:40 +02:00
add share option to event console
This commit is contained in:
@@ -3,19 +3,26 @@ import { IconButton, IconButtonProps, useToast } from "@chakra-ui/react";
|
|||||||
|
|
||||||
import { CheckIcon, CopyToClipboardIcon } from "./icons";
|
import { CheckIcon, CopyToClipboardIcon } from "./icons";
|
||||||
|
|
||||||
export const CopyIconButton = ({ value, ...props }: { value?: string } & Omit<IconButtonProps, "icon">) => {
|
type CopyIconButtonProps = Omit<IconButtonProps, "icon" | "value"> & {
|
||||||
|
value: string | undefined | (() => string);
|
||||||
|
icon?: IconButtonProps["icon"];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CopyIconButton = ({ value, icon, ...props }: CopyIconButtonProps) => {
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={copied ? <CheckIcon /> : <CopyToClipboardIcon />}
|
icon={copied ? <CheckIcon /> : icon || <CopyToClipboardIcon />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (value && navigator.clipboard && !copied) {
|
const v: string | undefined = typeof value === "function" ? value() : value;
|
||||||
navigator.clipboard.writeText(value);
|
|
||||||
|
if (v && navigator.clipboard && !copied) {
|
||||||
|
navigator.clipboard.writeText(v);
|
||||||
setCopied(true);
|
setCopied(true);
|
||||||
setTimeout(() => setCopied(false), 2000);
|
setTimeout(() => setCopied(false), 2000);
|
||||||
} else toast({ description: value, isClosable: true, duration: null });
|
} else toast({ description: v, isClosable: true, duration: null });
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
@@ -1,17 +1,5 @@
|
|||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
import {
|
import { Box, Card, CardBody, CardProps, Flex, Heading, Image, LinkBox, Tag, Text, useToast } from "@chakra-ui/react";
|
||||||
Box,
|
|
||||||
Card,
|
|
||||||
CardBody,
|
|
||||||
CardProps,
|
|
||||||
Flex,
|
|
||||||
Heading,
|
|
||||||
Image,
|
|
||||||
LinkBox,
|
|
||||||
LinkOverlay,
|
|
||||||
Tag,
|
|
||||||
Text,
|
|
||||||
} from "@chakra-ui/react";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getArticleImage,
|
getArticleImage,
|
||||||
@@ -27,15 +15,21 @@ import Timestamp from "../../timestamp";
|
|||||||
import { AppHandlerContext } from "../../../providers/route/app-handler-provider";
|
import { AppHandlerContext } from "../../../providers/route/app-handler-provider";
|
||||||
|
|
||||||
export default function EmbeddedArticle({ article, ...props }: Omit<CardProps, "children"> & { article: NostrEvent }) {
|
export default function EmbeddedArticle({ article, ...props }: Omit<CardProps, "children"> & { article: NostrEvent }) {
|
||||||
|
const toast = useToast();
|
||||||
const title = getArticleTitle(article);
|
const title = getArticleTitle(article);
|
||||||
const image = getArticleImage(article);
|
const image = getArticleImage(article);
|
||||||
const summary = getArticleSummary(article);
|
const summary = getArticleSummary(article);
|
||||||
|
|
||||||
const naddr = getSharableEventAddress(article);
|
|
||||||
const { openAddress } = useContext(AppHandlerContext);
|
const { openAddress } = useContext(AppHandlerContext);
|
||||||
|
|
||||||
|
const open = () => {
|
||||||
|
const naddr = getSharableEventAddress(article);
|
||||||
|
if (naddr) openAddress(naddr);
|
||||||
|
else toast({ status: "error", description: "Failed to get address" });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card as={LinkBox} size="sm" onClick={() => naddr && openAddress(naddr)} cursor="pointer" {...props}>
|
<Card as={LinkBox} size="sm" onClick={open} cursor="pointer" {...props}>
|
||||||
{image && (
|
{image && (
|
||||||
<Box
|
<Box
|
||||||
backgroundImage={image}
|
backgroundImage={image}
|
||||||
|
@@ -19,6 +19,8 @@ import { NostrEvent, Relay, Subscription } from "nostr-tools";
|
|||||||
import { useLocalStorage } from "react-use";
|
import { useLocalStorage } from "react-use";
|
||||||
import { Subscription as IDBSubscription, CacheRelay } from "nostr-idb";
|
import { Subscription as IDBSubscription, CacheRelay } from "nostr-idb";
|
||||||
import _throttle from "lodash.throttle";
|
import _throttle from "lodash.throttle";
|
||||||
|
import stringify from "json-stringify-deterministic";
|
||||||
|
import { useSearchParams } from "react-router-dom";
|
||||||
|
|
||||||
import VerticalPageLayout from "../../../components/vertical-page-layout";
|
import VerticalPageLayout from "../../../components/vertical-page-layout";
|
||||||
import BackButton from "../../../components/router/back-button";
|
import BackButton from "../../../components/router/back-button";
|
||||||
@@ -30,11 +32,12 @@ import EventRow from "./event-row";
|
|||||||
import { processFilter } from "./process";
|
import { processFilter } from "./process";
|
||||||
import HelpModal from "./help-modal";
|
import HelpModal from "./help-modal";
|
||||||
import HelpCircle from "../../../components/icons/help-circle";
|
import HelpCircle from "../../../components/icons/help-circle";
|
||||||
import stringify from "json-stringify-deterministic";
|
import { DownloadIcon, ShareIcon } from "../../../components/icons";
|
||||||
import { DownloadIcon } from "../../../components/icons";
|
|
||||||
import { RelayUrlInput } from "../../../components/relay-url-input";
|
import { RelayUrlInput } from "../../../components/relay-url-input";
|
||||||
import { validateRelayURL } from "../../../helpers/relay";
|
import { validateRelayURL } from "../../../helpers/relay";
|
||||||
import FilterEditor from "./filter-editor";
|
import FilterEditor from "./filter-editor";
|
||||||
|
import { CopyIconButton } from "../../../components/copy-icon-button";
|
||||||
|
import { safeJson } from "../../../helpers/parse";
|
||||||
|
|
||||||
const EventTimeline = memo(({ events }: { events: NostrEvent[] }) => {
|
const EventTimeline = memo(({ events }: { events: NostrEvent[] }) => {
|
||||||
return (
|
return (
|
||||||
@@ -47,16 +50,27 @@ const EventTimeline = memo(({ events }: { events: NostrEvent[] }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default function EventConsoleView() {
|
export default function EventConsoleView() {
|
||||||
|
const [params, setParams] = useSearchParams();
|
||||||
const historyDrawer = useDisclosure();
|
const historyDrawer = useDisclosure();
|
||||||
const [history, setHistory] = useLocalStorage<string[]>("console-history", []);
|
const [history, setHistory] = useLocalStorage<string[]>("console-history", []);
|
||||||
const helpModal = useDisclosure();
|
const helpModal = useDisclosure();
|
||||||
const queryRelay = useDisclosure();
|
const queryRelay = useDisclosure({ defaultIsOpen: params.has("relay") });
|
||||||
const [relayURL, setRelayURL] = useState("");
|
const [relayURL, setRelayURL] = useState(params.get("relay") || "");
|
||||||
const [relay, setRelay] = useState<Relay | null>(null);
|
const [relay, setRelay] = useState<Relay | null>(null);
|
||||||
|
|
||||||
const [sub, setSub] = useState<Subscription | IDBSubscription | null>(null);
|
const [sub, setSub] = useState<Subscription | IDBSubscription | null>(null);
|
||||||
|
|
||||||
const [query, setQuery] = useState(() => history?.[0] || JSON.stringify({ kinds: [1], limit: 20 }, null, 2));
|
const [query, setQuery] = useState(() => {
|
||||||
|
if (params.has("filter")) {
|
||||||
|
const str = params.get("filter");
|
||||||
|
if (str) {
|
||||||
|
const f = safeJson(str, null);
|
||||||
|
if (f) return JSON.stringify(f, null, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (history?.[0]) return history?.[0];
|
||||||
|
return JSON.stringify({ kinds: [1], limit: 20 }, null, 2);
|
||||||
|
});
|
||||||
|
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -122,6 +136,13 @@ export default function EventConsoleView() {
|
|||||||
window.open(url, "_blank");
|
window.open(url, "_blank");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateSharedURL = () => {
|
||||||
|
const p = new URLSearchParams(params);
|
||||||
|
p.set("filter", query);
|
||||||
|
p.set("relay", relayURL);
|
||||||
|
setParams(p, { replace: true });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VerticalPageLayout>
|
<VerticalPageLayout>
|
||||||
<Flex gap="2" alignItems="center" wrap="wrap">
|
<Flex gap="2" alignItems="center" wrap="wrap">
|
||||||
@@ -141,6 +162,9 @@ export default function EventConsoleView() {
|
|||||||
)}
|
)}
|
||||||
<ButtonGroup ml="auto">
|
<ButtonGroup ml="auto">
|
||||||
<IconButton icon={<HelpCircle />} aria-label="Help" title="Help" size="sm" onClick={helpModal.onOpen} />
|
<IconButton icon={<HelpCircle />} aria-label="Help" title="Help" size="sm" onClick={helpModal.onOpen} />
|
||||||
|
{queryRelay.isOpen && (
|
||||||
|
<IconButton icon={<ShareIcon />} aria-label="Share" size="sm" onClick={updateSharedURL} />
|
||||||
|
)}
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={<ClockRewind />}
|
icon={<ClockRewind />}
|
||||||
aria-label="History"
|
aria-label="History"
|
||||||
|
Reference in New Issue
Block a user