mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-09-26 11:37:40 +02:00
rebuild tools view
This commit is contained in:
5
.changeset/short-plants-hide.md
Normal file
5
.changeset/short-plants-hide.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"nostrudel": minor
|
||||
---
|
||||
|
||||
Rebuild tools view
|
@@ -1,11 +1,4 @@
|
||||
import {
|
||||
MouseEventHandler,
|
||||
MutableRefObject,
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useRef,
|
||||
} from "react";
|
||||
import { MouseEventHandler, MutableRefObject, forwardRef, useCallback, useMemo, useRef } from "react";
|
||||
import { Image, ImageProps, Link, LinkProps } from "@chakra-ui/react";
|
||||
|
||||
import appSettings from "../../services/settings/app-settings";
|
||||
|
@@ -22,56 +22,57 @@ export default function LightningSettings() {
|
||||
|
||||
return (
|
||||
<AccordionItem>
|
||||
{({ isExpanded }) => (<>
|
||||
<h2>
|
||||
<AccordionButton fontSize="xl">
|
||||
<LightningIcon mr="2" />
|
||||
<Box as="span" flex="1" textAlign="left">
|
||||
Lightning
|
||||
</Box>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
</h2>
|
||||
<AccordionPanel>
|
||||
<Flex direction="column" gap="4">
|
||||
{isExpanded && <Button />}
|
||||
<FormControl>
|
||||
<Flex alignItems="center">
|
||||
<FormLabel htmlFor="autoPayWithWebLN" mb="0">
|
||||
Auto pay with WebLN
|
||||
</FormLabel>
|
||||
<Switch id="autoPayWithWebLN" {...register("autoPayWithWebLN")} />
|
||||
{({ isExpanded }) => (
|
||||
<>
|
||||
<h2>
|
||||
<AccordionButton fontSize="xl">
|
||||
<LightningIcon mr="2" />
|
||||
<Box as="span" flex="1" textAlign="left">
|
||||
Lightning
|
||||
</Box>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
</h2>
|
||||
<AccordionPanel>
|
||||
<Flex direction="column" gap="4">
|
||||
{isExpanded && <Button />}
|
||||
<FormControl>
|
||||
<Flex alignItems="center">
|
||||
<FormLabel htmlFor="autoPayWithWebLN" mb="0">
|
||||
Auto pay with WebLN
|
||||
</FormLabel>
|
||||
<Switch id="autoPayWithWebLN" {...register("autoPayWithWebLN")} />
|
||||
</Flex>
|
||||
|
||||
<FormHelperText>
|
||||
<span>Enabled: Attempt to automatically pay with WebLN if its available</span>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<FormLabel htmlFor="customZapAmounts" mb="0">
|
||||
Zap Amounts
|
||||
</FormLabel>
|
||||
<Input
|
||||
id="customZapAmounts"
|
||||
autoComplete="off"
|
||||
{...register("customZapAmounts", {
|
||||
validate: (v) => {
|
||||
if (!/^[\d,]*$/.test(v)) return "Must be a list of comma separated numbers";
|
||||
return true;
|
||||
},
|
||||
})}
|
||||
/>
|
||||
{formState.errors.customZapAmounts && (
|
||||
<FormErrorMessage>{formState.errors.customZapAmounts.message}</FormErrorMessage>
|
||||
)}
|
||||
<FormHelperText>
|
||||
<span>Comma separated list of custom zap amounts</span>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</Flex>
|
||||
|
||||
<FormHelperText>
|
||||
<span>Enabled: Attempt to automatically pay with WebLN if its available</span>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<FormLabel htmlFor="customZapAmounts" mb="0">
|
||||
Zap Amounts
|
||||
</FormLabel>
|
||||
<Input
|
||||
id="customZapAmounts"
|
||||
autoComplete="off"
|
||||
{...register("customZapAmounts", {
|
||||
validate: (v) => {
|
||||
if (!/^[\d,]*$/.test(v)) return "Must be a list of comma separated numbers";
|
||||
return true;
|
||||
},
|
||||
})}
|
||||
/>
|
||||
{formState.errors.customZapAmounts && (
|
||||
<FormErrorMessage>{formState.errors.customZapAmounts.message}</FormErrorMessage>
|
||||
)}
|
||||
<FormHelperText>
|
||||
<span>Comma separated list of custom zap amounts</span>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</Flex>
|
||||
</AccordionPanel>
|
||||
</>
|
||||
</AccordionPanel>
|
||||
</>
|
||||
)}
|
||||
</AccordionItem>
|
||||
);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Flex } from "@chakra-ui/react";
|
||||
import { Button, Flex } from "@chakra-ui/react";
|
||||
import { memo, useRef } from "react";
|
||||
import { Kind } from "nostr-tools";
|
||||
|
||||
@@ -12,6 +12,8 @@ import IntersectionObserverProvider, { useRegisterIntersectionEntity } from "../
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
import EmbeddedDM from "../../components/embed-event/event-types/embedded-dm";
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import { ChevronLeftIcon } from "../../components/icons";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const DirectMessage = memo(({ dm }: { dm: NostrEvent }) => {
|
||||
const ref = useRef<HTMLDivElement | null>(null);
|
||||
@@ -25,6 +27,7 @@ const DirectMessage = memo(({ dm }: { dm: NostrEvent }) => {
|
||||
});
|
||||
|
||||
export function DMFeedPage() {
|
||||
const navigate = useNavigate();
|
||||
const { listId, filter } = usePeopleListContext();
|
||||
|
||||
const readRelays = useReadRelayUrls();
|
||||
@@ -48,6 +51,9 @@ export function DMFeedPage() {
|
||||
return (
|
||||
<VerticalPageLayout>
|
||||
<Flex gap="2">
|
||||
<Button leftIcon={<ChevronLeftIcon />} onClick={() => navigate(-1)}>
|
||||
Back
|
||||
</Button>
|
||||
<PeopleListSelection />
|
||||
</Flex>
|
||||
<IntersectionObserverProvider callback={callback}>
|
||||
|
@@ -1,105 +1,114 @@
|
||||
import { Button, Divider, Flex, Heading, Image, Link } from "@chakra-ui/react";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
CardHeader,
|
||||
ComponentWithAs,
|
||||
Flex,
|
||||
Heading,
|
||||
IconProps,
|
||||
Image,
|
||||
Link,
|
||||
LinkBox,
|
||||
} from "@chakra-ui/react";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
import { ExternalLinkIcon, LiveStreamIcon, MapIcon, ToolsIcon } from "../../components/icons";
|
||||
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||
import { ConnectedRelays } from "../../components/connected-relays";
|
||||
|
||||
import { DirectMessagesIcon, ExternalLinkIcon, LiveStreamIcon, MapIcon, MuteIcon } from "../../components/icons";
|
||||
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||
import ShieldOff from "../../components/icons/shield-off";
|
||||
import HoverLinkOverlay from "../../components/hover-link-overlay";
|
||||
import Users01 from "../../components/icons/users-01";
|
||||
import PackageSearch from "../../components/icons/package-search";
|
||||
|
||||
function InternalLink({
|
||||
to,
|
||||
icon: Icon,
|
||||
children,
|
||||
}: {
|
||||
to?: string;
|
||||
icon: ComponentWithAs<"svg", IconProps>;
|
||||
children: string;
|
||||
}) {
|
||||
return (
|
||||
<Card as={LinkBox} alignItems="center" p="4" gap="4" minW="40">
|
||||
<Icon boxSize={10} />
|
||||
<CardHeader p="0">
|
||||
<Heading size="md">
|
||||
<HoverLinkOverlay as={RouterLink} to={to}>
|
||||
{children}
|
||||
</HoverLinkOverlay>
|
||||
</Heading>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
function ExternalLink({ href, image, children }: { href?: string; image: string; children: string }) {
|
||||
return (
|
||||
<Card as={LinkBox} alignItems="center" p="4" gap="4" minW="40">
|
||||
<Image src={image} h="10" />
|
||||
<CardHeader p="0">
|
||||
<Heading size="md">
|
||||
<HoverLinkOverlay as={Link} href={href} isExternal>
|
||||
{children}
|
||||
</HoverLinkOverlay>
|
||||
</Heading>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
export default function ToolsHomeView() {
|
||||
return (
|
||||
<VerticalPageLayout>
|
||||
<Heading>
|
||||
<ToolsIcon /> Tools
|
||||
</Heading>
|
||||
<Divider />
|
||||
<Heading>Tools</Heading>
|
||||
<Flex wrap="wrap" gap="4">
|
||||
<Button as={RouterLink} to="/tools/content-discovery">
|
||||
Content Discovery DVM
|
||||
</Button>
|
||||
<Button as={RouterLink} to="/tools/network">
|
||||
Contact network
|
||||
</Button>
|
||||
<Button as={RouterLink} to="/tools/network-mute-graph">
|
||||
Contacts Mute Graph
|
||||
</Button>
|
||||
<Button as={RouterLink} to="/tools/network-dm-graph">
|
||||
Contacts DM Graph
|
||||
</Button>
|
||||
<Button as={RouterLink} to="/tools/dm-feed">
|
||||
<InternalLink to="/tools/content-discovery" icon={PackageSearch}>
|
||||
Discovery DVM
|
||||
</InternalLink>
|
||||
<InternalLink to="/tools/network" icon={Users01}>
|
||||
User Network
|
||||
</InternalLink>
|
||||
<InternalLink to="/tools/network-mute-graph" icon={MuteIcon}>
|
||||
Mute Graph
|
||||
</InternalLink>
|
||||
<InternalLink to="/tools/network-dm-graph" icon={DirectMessagesIcon}>
|
||||
DM Graph
|
||||
</InternalLink>
|
||||
<InternalLink to="/tools/dm-feed" icon={ShieldOff}>
|
||||
DM Feed
|
||||
</Button>
|
||||
<Button as={RouterLink} to="/map" leftIcon={<MapIcon />}>
|
||||
</InternalLink>
|
||||
<InternalLink to="/map" icon={MapIcon}>
|
||||
Map
|
||||
</Button>
|
||||
<Button as={RouterLink} to="/tools/stream-moderation" leftIcon={<LiveStreamIcon />}>
|
||||
</InternalLink>
|
||||
<InternalLink to="/tools/stream-moderation" icon={LiveStreamIcon}>
|
||||
Stream Moderation
|
||||
</Button>
|
||||
<ConnectedRelays />
|
||||
</InternalLink>
|
||||
</Flex>
|
||||
|
||||
<Heading size="lg" mt="4">
|
||||
Third party tools
|
||||
</Heading>
|
||||
<Divider />
|
||||
<Flex wrap="wrap" gap="4">
|
||||
<Button
|
||||
as={Link}
|
||||
href="https://w3.do/"
|
||||
isExternal
|
||||
target="_blank"
|
||||
leftIcon={<Image src="https://w3.do/favicon.ico" h="1.5em" />}
|
||||
>
|
||||
<ExternalLink href="https://w3.do/" image="https://w3.do/favicon.ico">
|
||||
URL Shortener
|
||||
</Button>
|
||||
<Button
|
||||
as={Link}
|
||||
href="https://nak.nostr.com/"
|
||||
isExternal
|
||||
target="_blank"
|
||||
leftIcon={<Image src="https://nak.nostr.com/favicon.ico" h="1.5em" />}
|
||||
>
|
||||
</ExternalLink>
|
||||
<ExternalLink href="https://nak.nostr.com/" image="https://nak.nostr.com/favicon.ico">
|
||||
nostr army knife
|
||||
</Button>
|
||||
<Button
|
||||
as={Link}
|
||||
href="https://nostr-delete.vercel.app/"
|
||||
isExternal
|
||||
target="_blank"
|
||||
leftIcon={<ExternalLinkIcon />}
|
||||
>
|
||||
</ExternalLink>
|
||||
<ExternalLink href="https://nostr-delete.vercel.app/" image="https://nostr-delete.vercel.app/favicon.png">
|
||||
Nostr Event Deletion
|
||||
</Button>
|
||||
<Button
|
||||
as={Link}
|
||||
href="https://nostrdebug.com/"
|
||||
isExternal
|
||||
target="_blank"
|
||||
leftIcon={<Image src="https://nostrdebug.com/favicon.ico" h="1.5em" />}
|
||||
>
|
||||
</ExternalLink>
|
||||
<ExternalLink href="https://nostrdebug.com/" image="https://nostrdebug.com/favicon.ico">
|
||||
Nostr Debug
|
||||
</Button>
|
||||
<Button
|
||||
as={Link}
|
||||
href="https://metadata.nostr.com/"
|
||||
isExternal
|
||||
target="_blank"
|
||||
leftIcon={<Image src="https://metadata.nostr.com/img/git.png" h="1.5em" />}
|
||||
>
|
||||
</ExternalLink>
|
||||
<ExternalLink href="https://metadata.nostr.com/" image="https://metadata.nostr.com/img/git.png">
|
||||
Nostr Profile Manager
|
||||
</Button>
|
||||
<Button
|
||||
as={Link}
|
||||
</ExternalLink>
|
||||
<ExternalLink
|
||||
href="https://www.nostrapps.com/"
|
||||
isExternal
|
||||
target="_blank"
|
||||
leftIcon={
|
||||
<Image
|
||||
src="https://uploads-ssl.webflow.com/641d0d46d5c124ac928a6027/64b1dd06d59d8f1e530d2926_32x32.png"
|
||||
h="1.5em"
|
||||
/>
|
||||
}
|
||||
image="https://uploads-ssl.webflow.com/641d0d46d5c124ac928a6027/64b1dd06d59d8f1e530d2926_32x32.png"
|
||||
>
|
||||
Nostr Apps
|
||||
</Button>
|
||||
</ExternalLink>
|
||||
</Flex>
|
||||
</VerticalPageLayout>
|
||||
);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { Box, Flex, Input, Text } from "@chakra-ui/react";
|
||||
import { Box, Button, Flex, Input, Text } from "@chakra-ui/react";
|
||||
import AutoSizer from "react-virtualized-auto-sizer";
|
||||
import ForceGraph, { LinkObject, NodeObject } from "react-force-graph-3d";
|
||||
import { Kind } from "nostr-tools";
|
||||
@@ -28,10 +28,13 @@ import RelaySelectionProvider, { useRelaySelectionContext } from "../../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 { useNavigate } from "react-router-dom";
|
||||
|
||||
type NodeType = { id: string; image?: string; name?: string };
|
||||
|
||||
function NetworkDMGraphPage() {
|
||||
const navigate = useNavigate();
|
||||
const account = useCurrentAccount()!;
|
||||
const { relays } = useRelaySelectionContext();
|
||||
|
||||
@@ -110,6 +113,9 @@ function NetworkDMGraphPage() {
|
||||
return (
|
||||
<Flex direction="column" gap="2" h="full" pt="2">
|
||||
<Flex gap="2" alignItems="center">
|
||||
<Button leftIcon={<ChevronLeftIcon />} onClick={() => navigate(-1)}>
|
||||
Back
|
||||
</Button>
|
||||
<Input
|
||||
type="datetime-local"
|
||||
maxW="sm"
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { useMemo, useRef } from "react";
|
||||
import { Box } from "@chakra-ui/react";
|
||||
import { Box, Button, Flex, Text } from "@chakra-ui/react";
|
||||
import AutoSizer from "react-virtualized-auto-sizer";
|
||||
import ForceGraph, { LinkObject, NodeObject } from "react-force-graph-3d";
|
||||
import {
|
||||
@@ -22,6 +22,8 @@ import { useReadRelayUrls } from "../../hooks/use-client-relays";
|
||||
import replaceableEventLoaderService from "../../services/replaceable-event-requester";
|
||||
import useSubjects from "../../hooks/use-subjects";
|
||||
import { useUserMetadata } from "../../hooks/use-user-metadata";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { ChevronLeftIcon } from "../../components/icons";
|
||||
|
||||
export function useUsersMuteLists(pubkeys: string[], additionalRelays: string[] = []) {
|
||||
const readRelays = useReadRelayUrls(additionalRelays);
|
||||
@@ -34,6 +36,7 @@ export function useUsersMuteLists(pubkeys: string[], additionalRelays: string[]
|
||||
type NodeType = { id: string; image?: string; name?: string };
|
||||
|
||||
function NetworkGraphPage() {
|
||||
const navigate = useNavigate();
|
||||
const account = useCurrentAccount()!;
|
||||
|
||||
const selfMetadata = useUserMetadata(account.pubkey);
|
||||
@@ -82,44 +85,52 @@ function NetworkGraphPage() {
|
||||
}, [contacts, usersMuteLists, usersMetadata, selfMetadata]);
|
||||
|
||||
return (
|
||||
<Box overflow="hidden" flex={1}>
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<ForceGraph<NodeType>
|
||||
graphData={graphData}
|
||||
enableNodeDrag={false}
|
||||
width={width}
|
||||
height={height}
|
||||
linkDirectionalArrowLength={3.5}
|
||||
linkDirectionalArrowRelPos={1}
|
||||
linkCurvature={0.25}
|
||||
nodeThreeObject={(node: NodeType) => {
|
||||
if (!node.image) {
|
||||
return new Mesh(new SphereGeometry(5, 12, 6), new MeshBasicMaterial({ color: 0xaa0f0f }));
|
||||
}
|
||||
<Flex direction="column" gap="2" h="full" pt="2">
|
||||
<Flex gap="2" alignItems="center">
|
||||
<Button leftIcon={<ChevronLeftIcon />} onClick={() => navigate(-1)}>
|
||||
Back
|
||||
</Button>
|
||||
<Text>Showing how many of your contacts are muting each other</Text>
|
||||
</Flex>
|
||||
<Box overflow="hidden" flex={1}>
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<ForceGraph<NodeType>
|
||||
graphData={graphData}
|
||||
enableNodeDrag={false}
|
||||
width={width}
|
||||
height={height}
|
||||
linkDirectionalArrowLength={3.5}
|
||||
linkDirectionalArrowRelPos={1}
|
||||
linkCurvature={0.25}
|
||||
nodeThreeObject={(node: NodeType) => {
|
||||
if (!node.image) {
|
||||
return new Mesh(new SphereGeometry(5, 12, 6), new MeshBasicMaterial({ color: 0xaa0f0f }));
|
||||
}
|
||||
|
||||
const group = new Group();
|
||||
const group = new Group();
|
||||
|
||||
const imgTexture = new TextureLoader().load(node.image);
|
||||
imgTexture.colorSpace = SRGBColorSpace;
|
||||
const material = new SpriteMaterial({ map: imgTexture });
|
||||
const sprite = new Sprite(material);
|
||||
sprite.scale.set(10, 10, 10);
|
||||
const imgTexture = new TextureLoader().load(node.image);
|
||||
imgTexture.colorSpace = SRGBColorSpace;
|
||||
const material = new SpriteMaterial({ map: imgTexture });
|
||||
const sprite = new Sprite(material);
|
||||
sprite.scale.set(10, 10, 10);
|
||||
|
||||
group.children.push(sprite);
|
||||
group.children.push(sprite);
|
||||
|
||||
// if (node.name) {
|
||||
// const text = new SpriteText(node.name, 8, "ffffff");
|
||||
// text.position.set(0, 0, 16);
|
||||
// group.children.push(text);
|
||||
// }
|
||||
// if (node.name) {
|
||||
// const text = new SpriteText(node.name, 8, "ffffff");
|
||||
// text.position.set(0, 0, 16);
|
||||
// group.children.push(text);
|
||||
// }
|
||||
|
||||
return sprite;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</Box>
|
||||
return sprite;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</Box>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user