add share option to event console

This commit is contained in:
hzrd149
2024-03-18 15:53:32 -05:00
parent 44ac1e8631
commit 8dbf7c3405
3 changed files with 50 additions and 25 deletions

View File

@@ -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}
/> />

View File

@@ -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}

View File

@@ -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"