mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-09-21 22:13:11 +02:00
add tools link to nav
This commit is contained in:
@@ -28,7 +28,6 @@ import DirectMessageChatView from "./views/messages/chat";
|
|||||||
import NostrLinkView from "./views/link";
|
import NostrLinkView from "./views/link";
|
||||||
import UserReportsTab from "./views/user/reports";
|
import UserReportsTab from "./views/user/reports";
|
||||||
import ToolsHomeView from "./views/tools";
|
import ToolsHomeView from "./views/tools";
|
||||||
import Nip19ToolsView from "./views/tools/nip19";
|
|
||||||
import UserAboutTab from "./views/user/about";
|
import UserAboutTab from "./views/user/about";
|
||||||
import UserLikesTab from "./views/user/likes";
|
import UserLikesTab from "./views/user/likes";
|
||||||
import useSetColorMode from "./hooks/use-set-color-mode";
|
import useSetColorMode from "./hooks/use-set-color-mode";
|
||||||
@@ -116,10 +115,7 @@ const router = createHashRouter([
|
|||||||
{ path: "profile", element: <ProfileView /> },
|
{ path: "profile", element: <ProfileView /> },
|
||||||
{
|
{
|
||||||
path: "tools",
|
path: "tools",
|
||||||
children: [
|
children: [{ path: "", element: <ToolsHomeView /> }],
|
||||||
{ path: "", element: <ToolsHomeView /> },
|
|
||||||
{ path: "nip19", element: <Nip19ToolsView /> },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "streams",
|
path: "streams",
|
||||||
|
@@ -7,25 +7,25 @@ import DesktopSideNav from "./desktop-side-nav";
|
|||||||
import MobileBottomNav from "./mobile-bottom-nav";
|
import MobileBottomNav from "./mobile-bottom-nav";
|
||||||
|
|
||||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||||
const showSideNav = useBreakpointValue({ base: true, md: false });
|
const isMobile = useBreakpointValue({ base: true, md: false });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ReloadPrompt mb="2" />
|
<ReloadPrompt mb="2" />
|
||||||
<Container size="lg" display="flex" padding="0" gap="4" alignItems="flex-start">
|
<Container size="lg" display="flex" padding="0" gap="4" alignItems="flex-start">
|
||||||
{!showSideNav && <DesktopSideNav position="sticky" top="0" />}
|
{!isMobile && <DesktopSideNav position="sticky" top="0" />}
|
||||||
<Flex
|
<Flex
|
||||||
flexGrow={1}
|
flexGrow={1}
|
||||||
direction="column"
|
direction="column"
|
||||||
w="full"
|
w="full"
|
||||||
overflowX="hidden"
|
overflowX="hidden"
|
||||||
overflowY="visible"
|
overflowY="visible"
|
||||||
pb={showSideNav ? "14" : 0}
|
pb={isMobile ? "14" : 0}
|
||||||
minH="50vh"
|
minH="50vh"
|
||||||
>
|
>
|
||||||
<ErrorBoundary>{children}</ErrorBoundary>
|
<ErrorBoundary>{children}</ErrorBoundary>
|
||||||
</Flex>
|
</Flex>
|
||||||
{showSideNav && (
|
{isMobile && (
|
||||||
<MobileBottomNav
|
<MobileBottomNav
|
||||||
position="fixed"
|
position="fixed"
|
||||||
bottom="0"
|
bottom="0"
|
||||||
|
@@ -10,6 +10,7 @@ import {
|
|||||||
RelayIcon,
|
RelayIcon,
|
||||||
SearchIcon,
|
SearchIcon,
|
||||||
SettingsIcon,
|
SettingsIcon,
|
||||||
|
ToolsIcon,
|
||||||
} from "../icons";
|
} from "../icons";
|
||||||
|
|
||||||
export default function NavItems({ isInDrawer = false }: { isInDrawer?: boolean }) {
|
export default function NavItems({ isInDrawer = false }: { isInDrawer?: boolean }) {
|
||||||
@@ -47,6 +48,9 @@ export default function NavItems({ isInDrawer = false }: { isInDrawer?: boolean
|
|||||||
<Button onClick={() => navigate("/map")} leftIcon={<MapIcon />} justifyContent="flex-start">
|
<Button onClick={() => navigate("/map")} leftIcon={<MapIcon />} justifyContent="flex-start">
|
||||||
Map
|
Map
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button onClick={() => navigate("/tools")} leftIcon={<ToolsIcon />} justifyContent="flex-start">
|
||||||
|
Tools
|
||||||
|
</Button>
|
||||||
<Divider my="2" />
|
<Divider my="2" />
|
||||||
<Button onClick={() => navigate("/settings")} leftIcon={<SettingsIcon />} justifyContent="flex-start">
|
<Button onClick={() => navigate("/settings")} leftIcon={<SettingsIcon />} justifyContent="flex-start">
|
||||||
Settings
|
Settings
|
||||||
|
@@ -43,9 +43,6 @@ export default function SettingsView() {
|
|||||||
</Accordion>
|
</Accordion>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
<Flex gap="4" padding="4" alignItems="center">
|
<Flex gap="4" padding="4" alignItems="center">
|
||||||
<Button as={RouterLink} to="/tools" leftIcon={<ToolsIcon />}>
|
|
||||||
Tools
|
|
||||||
</Button>
|
|
||||||
<Link isExternal href="https://github.com/hzrd149/nostrudel">
|
<Link isExternal href="https://github.com/hzrd149/nostrudel">
|
||||||
<GithubIcon /> Github
|
<GithubIcon /> Github
|
||||||
</Link>
|
</Link>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { Avatar, Button, Flex, Heading, Image, Link } from "@chakra-ui/react";
|
import { Avatar, Button, Flex, Heading, Image, Link } from "@chakra-ui/react";
|
||||||
import { Link as RouterLink } from "react-router-dom";
|
import { Link as RouterLink } from "react-router-dom";
|
||||||
import { ToolsIcon } from "../../components/icons";
|
import { ToolsIcon } from "../../components/icons";
|
||||||
|
import OpenGraphCard from "../../components/open-graph-card";
|
||||||
|
|
||||||
export default function ToolsHomeView() {
|
export default function ToolsHomeView() {
|
||||||
return (
|
return (
|
||||||
@@ -9,6 +10,15 @@ export default function ToolsHomeView() {
|
|||||||
<ToolsIcon /> Tools
|
<ToolsIcon /> Tools
|
||||||
</Heading>
|
</Heading>
|
||||||
<Flex wrap="wrap" gap="4">
|
<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" />}
|
||||||
|
>
|
||||||
|
URL Shortener
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
as={Link}
|
as={Link}
|
||||||
href="https://nak.nostr.com/"
|
href="https://nak.nostr.com/"
|
||||||
@@ -18,8 +28,28 @@ export default function ToolsHomeView() {
|
|||||||
>
|
>
|
||||||
nostr army knife
|
nostr army knife
|
||||||
</Button>
|
</Button>
|
||||||
<Button as={RouterLink} to="./nip19">
|
<Button
|
||||||
Nip-19 encode/decode
|
as={Link}
|
||||||
|
href="https://nostrdebug.com/"
|
||||||
|
isExternal
|
||||||
|
target="_blank"
|
||||||
|
leftIcon={<Image src="https://nostrdebug.com/favicon.ico" h="1.5em" />}
|
||||||
|
>
|
||||||
|
Nostr Debug
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
as={Link}
|
||||||
|
href="https://www.nostrapps.com/"
|
||||||
|
isExternal
|
||||||
|
target="_blank"
|
||||||
|
leftIcon={
|
||||||
|
<Image
|
||||||
|
src="https://uploads-ssl.webflow.com/641d0d46d5c124ac928a6027/64b1dd06d59d8f1e530d2926_32x32.png"
|
||||||
|
h="1.5em"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Nostr Apps
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@@ -1,133 +0,0 @@
|
|||||||
import {
|
|
||||||
Button,
|
|
||||||
Card,
|
|
||||||
CardBody,
|
|
||||||
Code,
|
|
||||||
Flex,
|
|
||||||
FormControl,
|
|
||||||
FormErrorMessage,
|
|
||||||
FormLabel,
|
|
||||||
Heading,
|
|
||||||
Input,
|
|
||||||
useToast,
|
|
||||||
} from "@chakra-ui/react";
|
|
||||||
import { ToolsIcon } from "../../components/icons";
|
|
||||||
import { useForm } from "react-hook-form";
|
|
||||||
import { RelayUrlInput } from "../../components/relay-url-input";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { normalizeToHex } from "../../helpers/nip19";
|
|
||||||
import { nip19 } from "nostr-tools";
|
|
||||||
import { normalizeRelayUrl } from "../../helpers/url";
|
|
||||||
import RawValue from "../../components/debug-modals/raw-value";
|
|
||||||
|
|
||||||
function EncodeForm() {
|
|
||||||
const toast = useToast();
|
|
||||||
const { handleSubmit, register, formState, setValue } = useForm({
|
|
||||||
mode: "onBlur",
|
|
||||||
defaultValues: {
|
|
||||||
pubkey: "",
|
|
||||||
relay: "",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const [output, setOutput] = useState("");
|
|
||||||
|
|
||||||
const encode = handleSubmit((values) => {
|
|
||||||
try {
|
|
||||||
const pubkey = normalizeToHex(values.pubkey);
|
|
||||||
if (!pubkey) throw new Error("bad pubkey");
|
|
||||||
const relay = normalizeRelayUrl(values.relay);
|
|
||||||
|
|
||||||
const nprofile = nip19.nprofileEncode({
|
|
||||||
pubkey,
|
|
||||||
relays: [relay],
|
|
||||||
});
|
|
||||||
|
|
||||||
setOutput(nprofile);
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof Error) toast({ description: e.message, status: "error" });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card size="sm">
|
|
||||||
<CardBody>
|
|
||||||
<form onSubmit={encode}>
|
|
||||||
<FormControl isInvalid={!!formState.errors.pubkey}>
|
|
||||||
<FormLabel>Public key</FormLabel>
|
|
||||||
<Input {...register("pubkey", { minLength: 8, required: true })} placeholder="npub or hex" />
|
|
||||||
{formState.errors.pubkey && <FormErrorMessage>{formState.errors.pubkey.message}</FormErrorMessage>}
|
|
||||||
</FormControl>
|
|
||||||
<FormControl isInvalid={!!formState.errors.pubkey}>
|
|
||||||
<FormLabel>Relay url</FormLabel>
|
|
||||||
<RelayUrlInput
|
|
||||||
{...register("relay", { required: true })}
|
|
||||||
onChange={(v) => setValue("relay", v)}
|
|
||||||
placeholder="wss://relay.example.com"
|
|
||||||
/>
|
|
||||||
{formState.errors.pubkey && <FormErrorMessage>{formState.errors.pubkey.message}</FormErrorMessage>}
|
|
||||||
</FormControl>
|
|
||||||
<Button type="submit">Encode</Button>
|
|
||||||
</form>
|
|
||||||
{output && <RawValue heading="nprofile" value={output} />}
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function DecodeForm() {
|
|
||||||
const toast = useToast();
|
|
||||||
const { handleSubmit, register, formState, setValue } = useForm({
|
|
||||||
mode: "onBlur",
|
|
||||||
defaultValues: {
|
|
||||||
input: "",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const [output, setOutput] = useState<Object>();
|
|
||||||
|
|
||||||
const decode = handleSubmit((values) => {
|
|
||||||
try {
|
|
||||||
setOutput(nip19.decode(values.input));
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof Error) toast({ description: e.message, status: "error" });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card size="sm">
|
|
||||||
<CardBody>
|
|
||||||
<form onSubmit={decode}>
|
|
||||||
<FormControl isInvalid={!!formState.errors.input}>
|
|
||||||
<FormLabel>Encoded id</FormLabel>
|
|
||||||
<Input
|
|
||||||
{...register("input", { minLength: 8, required: true })}
|
|
||||||
placeholder="npub, note1, nevent, nprofile..."
|
|
||||||
/>
|
|
||||||
{formState.errors.input && <FormErrorMessage>{formState.errors.input.message}</FormErrorMessage>}
|
|
||||||
</FormControl>
|
|
||||||
<Button type="submit">Decode</Button>
|
|
||||||
</form>
|
|
||||||
{output && (
|
|
||||||
<Code whiteSpace="pre" overflowX="auto" width="100%">
|
|
||||||
{JSON.stringify(output, null, 2)}
|
|
||||||
</Code>
|
|
||||||
)}
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Nip19ToolsView() {
|
|
||||||
return (
|
|
||||||
<Flex direction="column" gap="4" p="4">
|
|
||||||
<Heading>
|
|
||||||
<ToolsIcon /> Nip-19 Tools
|
|
||||||
</Heading>
|
|
||||||
<Heading size="sm">Encode</Heading>
|
|
||||||
<EncodeForm />
|
|
||||||
<Heading size="sm">Decode</Heading>
|
|
||||||
<DecodeForm />
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
Reference in New Issue
Block a user