From d7832cb893f7f53c5c1af9172bf9e46757a31d3a Mon Sep 17 00:00:00 2001 From: hzrd149 Date: Tue, 7 Feb 2023 17:04:18 -0600 Subject: [PATCH] UI improvements --- src/app.jsx | 58 +++++++++++++++++++++++++------- src/components/page.jsx | 23 +++++++++++++ src/components/post-modal.jsx | 30 +++++++++++++++++ src/components/post.jsx | 42 +++++++++++++++++++++++ src/index.jsx | 7 +++- src/providers/index.jsx | 13 +++++++ src/providers/relay-provider.jsx | 52 ++++++++++++++++++++++++++++ src/providers/styles.jsx | 0 src/theme/container.js | 17 ++++++++++ src/theme/index.js | 10 ++++++ src/views/global/index.jsx | 39 +++++++++++++++++++++ src/views/home.jsx | 7 ++-- src/views/settings.jsx | 28 +++++++-------- src/views/user/index.jsx | 13 ++++--- src/views/user/posts.jsx | 12 ++----- 15 files changed, 301 insertions(+), 50 deletions(-) create mode 100644 src/components/page.jsx create mode 100644 src/components/post-modal.jsx create mode 100644 src/components/post.jsx create mode 100644 src/providers/index.jsx create mode 100644 src/providers/relay-provider.jsx create mode 100644 src/providers/styles.jsx create mode 100644 src/theme/container.js create mode 100644 src/theme/index.js create mode 100644 src/views/global/index.jsx diff --git a/src/app.jsx b/src/app.jsx index 5bcc362e9..317bc32f7 100644 --- a/src/app.jsx +++ b/src/app.jsx @@ -1,23 +1,55 @@ import React from "react"; -import { ChakraProvider } from "@chakra-ui/react"; -import { HashRouter, Route, Routes } from "react-router-dom"; +import { Spinner } from "@chakra-ui/react"; +import { Route, Routes } from "react-router-dom"; import { HomeView } from "./views/home"; import { UserView } from "./views/user"; import { ErrorBoundary } from "./components/error-boundary"; import { SettingsView } from "./views/settings"; +import { GlobalView } from "./views/global"; +import { useRelays } from "./providers/relay-provider"; +import { Page } from "./components/page"; export const App = () => { + const { loading } = useRelays(); + + if (loading) return ; + return ( - - - - - } /> - } /> - } /> - - - - + + + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + ); }; diff --git a/src/components/page.jsx b/src/components/page.jsx new file mode 100644 index 000000000..0d66a8228 --- /dev/null +++ b/src/components/page.jsx @@ -0,0 +1,23 @@ +import React from "react"; +import { Box, Button, Container, HStack, VStack } from "@chakra-ui/react"; +import { useNavigate } from "react-router-dom"; +import { ErrorBoundary } from "./error-boundary"; + +export const Page = ({ children }) => { + const navigate = useNavigate(); + + return ( + + + + + + + + + {children} + + + + ); +}; diff --git a/src/components/post-modal.jsx b/src/components/post-modal.jsx new file mode 100644 index 000000000..47abb8ad5 --- /dev/null +++ b/src/components/post-modal.jsx @@ -0,0 +1,30 @@ +import React from "react"; +import { + Button, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, +} from "@chakra-ui/react"; +import ReactMarkdown from "react-markdown"; + +export const PostModal = ({ event, onClose, isOpen }) => ( + + + + {event.pubkey} + + + {event.content} + + + + + + +); diff --git a/src/components/post.jsx b/src/components/post.jsx new file mode 100644 index 000000000..5fde68df1 --- /dev/null +++ b/src/components/post.jsx @@ -0,0 +1,42 @@ +import React from "react"; +import { + Box, + Button, + Card, + CardBody, + Heading, + useDisclosure, + VStack, +} from "@chakra-ui/react"; +import ReactMarkdown from "react-markdown"; +import { Link } from "react-router-dom"; +import { PostModal } from "./post-modal"; + +export const Post = ({ event }) => { + const { isOpen, onClose, onOpen } = useDisclosure(); + + const isLong = event.content.length > 800; + + return ( + + + + + {event.pubkey} + + + {event.content} + + {isLong && ( + <> + + + + )} + + + + ); +}; diff --git a/src/index.jsx b/src/index.jsx index 155cd8d75..166719b2f 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -1,6 +1,11 @@ import React from "react"; import { createRoot } from "react-dom/client"; import { App } from "./app"; +import { Providers } from "./providers"; const root = createRoot(document.getElementById("root")); -root.render(); +root.render( + + + +); diff --git a/src/providers/index.jsx b/src/providers/index.jsx new file mode 100644 index 000000000..a3be27f6f --- /dev/null +++ b/src/providers/index.jsx @@ -0,0 +1,13 @@ +import React from "react"; +import { ChakraProvider } from "@chakra-ui/react"; +import { RelaysProvider } from "./relay-provider"; +import { HashRouter } from "react-router-dom"; +import theme from "../theme"; + +export const Providers = ({ children }) => ( + + + {children} + + +); diff --git a/src/providers/relay-provider.jsx b/src/providers/relay-provider.jsx new file mode 100644 index 000000000..0acefff45 --- /dev/null +++ b/src/providers/relay-provider.jsx @@ -0,0 +1,52 @@ +import React, { + createContext, + useCallback, + useContext, + useEffect, + useState, +} from "react"; +import settingsService from "../services/settings"; + +const relaysContext = createContext({ + relays: [], + loading: true, + overwriteRelays: () => {}, +}); + +export function useRelays() { + return useContext(relaysContext); +} + +export const RelaysProvider = ({ children }) => { + const [relays, setRelays] = useState([]); + const [loading, setLoading] = useState(true); + + const update = useCallback(async () => { + setRelays(await settingsService.getRelays()); + setLoading(false); + }, [setRelays]); + + const overwriteRelays = useCallback( + async (urls) => { + if (urls) await settingsService.setRelays(urls); + await update(); + }, + [update] + ); + + useEffect(() => { + update(); + }, [update]); + + return ( + + {children} + + ); +}; diff --git a/src/providers/styles.jsx b/src/providers/styles.jsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/theme/container.js b/src/theme/container.js new file mode 100644 index 000000000..a269bd1e9 --- /dev/null +++ b/src/theme/container.js @@ -0,0 +1,17 @@ +import { defineStyle, defineStyleConfig } from "@chakra-ui/react"; + +// define custom sizes +const sizes = { + sm: defineStyle({ + maxW: "10rem", + }), + md: defineStyle({ + maxW: "50rem", + }), + lg: defineStyle({ + maxW: "100rem", + }), +}; + +// export the component theme +export const containerTheme = defineStyleConfig({ sizes }); diff --git a/src/theme/index.js b/src/theme/index.js new file mode 100644 index 000000000..d2f17c9f9 --- /dev/null +++ b/src/theme/index.js @@ -0,0 +1,10 @@ +import { extendTheme } from "@chakra-ui/react"; +import { containerTheme } from "./container"; + +const theme = extendTheme({ + components: { + Container: containerTheme, + }, +}); + +export default theme; diff --git a/src/views/global/index.jsx b/src/views/global/index.jsx new file mode 100644 index 000000000..f28e8243d --- /dev/null +++ b/src/views/global/index.jsx @@ -0,0 +1,39 @@ +import React, { useState } from "react"; +import { SkeletonText } from "@chakra-ui/react"; +import { useSubscription } from "../../helpers/use-subscription"; +import { useSignal } from "../../hooks/use-signal"; +import { useRelays } from "../../providers/relay-provider"; +import { Post } from "../../components/post"; + +export const GlobalView = () => { + const { relays } = useRelays(); + const [events, setEvents] = useState({}); + + const sub = useSubscription(relays, { kinds: [1], limit: 10 }, []); + + useSignal( + sub?.onEvent, + (event) => { + if (event.kind === 1) { + setEvents((dir) => ({ [event.id]: event, ...dir })); + } + }, + [setEvents] + ); + + const timeline = Object.values(events).sort( + (a, b) => a.created_at - b.created_at + ); + + if (timeline.length === 0) { + return ; + } + + return ( + <> + {timeline.map((event) => ( + + ))} + + ); +}; diff --git a/src/views/home.jsx b/src/views/home.jsx index 28a321d31..f370d2510 100644 --- a/src/views/home.jsx +++ b/src/views/home.jsx @@ -1,11 +1,9 @@ import React from "react"; -import { VStack, Heading } from "@chakra-ui/react"; import { Link } from "react-router-dom"; export const HomeView = () => { return ( - - personal nostr client + <> There are many benefits to a joint design and development system. Not only does it bring benefits to the design team, but it also brings @@ -13,13 +11,12 @@ export const HomeView = () => { consistent look and feel, not just in our design specs, but in production. - Settings self gigi - + ); }; diff --git a/src/views/settings.jsx b/src/views/settings.jsx index 0951832bd..429aa833e 100644 --- a/src/views/settings.jsx +++ b/src/views/settings.jsx @@ -6,30 +6,28 @@ import { Stack, Textarea, } from "@chakra-ui/react"; -import React, { useEffect, useState } from "react"; -import * as settings from "../services/settings"; +import React, { useState } from "react"; +import { useRelays } from "../providers/relay-provider"; export const SettingsView = () => { - const [relayUrls, setRelayUrls] = useState(""); + const { relays, overwriteRelays } = useRelays(); + const [relayUrls, setRelayUrls] = useState(relays.join("\n")); - const handleSubmit = (event) => { + const handleSubmit = async (event) => { event.preventDefault(); - const relays = relayUrls + const newRelays = relayUrls .split("\n") .filter(Boolean) .map((url) => url.trim()); - if (relays.length > 0) { - settings.setRelays(relays); + + if (newRelays.length > 0) { + await overwriteRelays(newRelays); } }; - const resetForm = async () => { - const urls = await settings.getRelays(); - setRelayUrls(urls.join("\n")); - }; - useEffect(() => { - resetForm(); - }, []); + const resetForm = async () => { + setRelayUrls(relays.join("\n")); + }; return (
@@ -41,7 +39,7 @@ export const SettingsView = () => { required size="md" rows={10} - resize="horizontal" + resize="vertical" /> One relay per line diff --git a/src/views/user/index.jsx b/src/views/user/index.jsx index 4b544990b..7101df8b4 100644 --- a/src/views/user/index.jsx +++ b/src/views/user/index.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React from "react"; import { Heading, Tab, @@ -8,7 +8,6 @@ import { Tabs, } from "@chakra-ui/react"; import { useParams } from "react-router-dom"; -import { ErrorBoundary } from "../../components/error-boundary"; import { UserPostsTab } from "./posts"; export const UserView = () => { @@ -16,15 +15,15 @@ export const UserView = () => { if (!params.pubkey) { // TODO: better 404 - return "no pubkey"; + throw new Error("No pubkey"); } return ( - - user {params.pubkey} + <> + {params.pubkey} - Notes + Posts Other Relays @@ -41,6 +40,6 @@ export const UserView = () => { - + ); }; diff --git a/src/views/user/posts.jsx b/src/views/user/posts.jsx index a8c0aac36..8fd8179fc 100644 --- a/src/views/user/posts.jsx +++ b/src/views/user/posts.jsx @@ -1,9 +1,9 @@ import React, { useState } from "react"; -import { Card, CardBody, SkeletonText } from "@chakra-ui/react"; -import ReactMarkdown from "react-markdown"; +import { SkeletonText } from "@chakra-ui/react"; import settingsService from "../../services/settings"; import { useSignal } from "../../hooks/use-signal"; import { useSubscription } from "../../helpers/use-subscription"; +import { Post } from "../../components/post"; const relayUrls = await settingsService.getRelays(); @@ -30,11 +30,5 @@ export const UserPostsTab = ({ pubkey }) => { return ; } - return timeline.map((event) => ( - - - {event.content} - - - )); + return timeline.map((event) => ); };