mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-06-11 01:10:57 +02:00
make login more fancy
This commit is contained in:
parent
2c3c45991d
commit
f69a120c22
@ -20,9 +20,9 @@ export const GlobalIcon = createIcon({
|
|||||||
defaultProps,
|
defaultProps,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const HomeIcon = createIcon({
|
export const FeedIcon = createIcon({
|
||||||
displayName: "home-line",
|
displayName: "feed-icon",
|
||||||
d: "M21 20a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9.49a1 1 0 0 1 .386-.79l8-6.222a1 1 0 0 1 1.228 0l8 6.222a1 1 0 0 1 .386.79V20zm-2-1V9.978l-7-5.444-7 5.444V19h14z",
|
d: "M16 18v2H5v-2h11zm5-7v2H3v-2h18zm-2-7v2H8V4h11z",
|
||||||
defaultProps,
|
defaultProps,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -117,13 +117,19 @@ export const RelayIcon = createIcon({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const ExternalLinkIcon = createIcon({
|
export const ExternalLinkIcon = createIcon({
|
||||||
displayName: "eternal-link-icon",
|
displayName: "external-link-icon",
|
||||||
d: "M10 6v2H5v11h11v-5h2v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h6zm11-3v8h-2V6.413l-7.793 7.794-1.414-1.414L17.585 5H13V3h8z",
|
d: "M10 6v2H5v11h11v-5h2v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h6zm11-3v8h-2V6.413l-7.793 7.794-1.414-1.414L17.585 5H13V3h8z",
|
||||||
defaultProps,
|
defaultProps,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const SearchIcon = createIcon({
|
export const SearchIcon = createIcon({
|
||||||
displayName: "eternal-link-icon",
|
displayName: "search-icon",
|
||||||
d: "M18.031 16.617l4.283 4.282-1.415 1.415-4.282-4.283A8.96 8.96 0 0 1 11 20c-4.968 0-9-4.032-9-9s4.032-9 9-9 9 4.032 9 9a8.96 8.96 0 0 1-1.969 5.617zm-2.006-.742A6.977 6.977 0 0 0 18 11c0-3.868-3.133-7-7-7-3.868 0-7 3.132-7 7 0 3.867 3.132 7 7 7a6.977 6.977 0 0 0 4.875-1.975l.15-.15z",
|
d: "M18.031 16.617l4.283 4.282-1.415 1.415-4.282-4.283A8.96 8.96 0 0 1 11 20c-4.968 0-9-4.032-9-9s4.032-9 9-9 9 4.032 9 9a8.96 8.96 0 0 1-1.969 5.617zm-2.006-.742A6.977 6.977 0 0 0 18 11c0-3.868-3.133-7-7-7-3.868 0-7 3.132-7 7 0 3.867 3.132 7 7 7a6.977 6.977 0 0 0 4.875-1.975l.15-.15z",
|
||||||
defaultProps,
|
defaultProps,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const ShareIcon = createIcon({
|
||||||
|
displayName: "share-icon",
|
||||||
|
d: "M13.12 17.023l-4.199-2.29a4 4 0 1 1 0-5.465l4.2-2.29a4 4 0 1 1 .959 1.755l-4.2 2.29a4.008 4.008 0 0 1 0 1.954l4.199 2.29a4 4 0 1 1-.959 1.755zM6 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm11-6a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm0 12a2 2 0 1 0 0-4 2 2 0 0 0 0 4z",
|
||||||
|
defaultProps,
|
||||||
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Link as RouterLink } from "react-router-dom";
|
import { Link as RouterLink } from "react-router-dom";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { Box, Card, CardBody, CardHeader, Flex, Heading, Link } from "@chakra-ui/react";
|
import { Box, Card, CardBody, CardFooter, CardHeader, Flex, Heading, Link } from "@chakra-ui/react";
|
||||||
import { NostrEvent } from "../../types/nostr-event";
|
import { NostrEvent } from "../../types/nostr-event";
|
||||||
import { UserAvatarLink } from "../user-avatar-link";
|
import { UserAvatarLink } from "../user-avatar-link";
|
||||||
import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip-19";
|
import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip-19";
|
||||||
@ -29,23 +29,15 @@ export const Note = React.memo(({ event }: NoteProps) => {
|
|||||||
return (
|
return (
|
||||||
<Card padding="2" variant="outline">
|
<Card padding="2" variant="outline">
|
||||||
<CardHeader padding="0" mb="2">
|
<CardHeader padding="0" mb="2">
|
||||||
<Flex gap="2">
|
<Flex flex="1" gap="2" alignItems="center" wrap="wrap">
|
||||||
<Flex flex="1" gap="2">
|
<UserAvatarLink pubkey={event.pubkey} size={isMobile ? "xs" : "sm"} />
|
||||||
<UserAvatarLink pubkey={event.pubkey} size={isMobile ? "xs" : "sm"} />
|
|
||||||
|
|
||||||
<Box>
|
<Heading size="sm" display="inline">
|
||||||
<Heading size="sm" display="inline">
|
<UserLink pubkey={event.pubkey} />
|
||||||
<UserLink pubkey={event.pubkey} />
|
</Heading>
|
||||||
</Heading>
|
<Link as={RouterLink} to={`/n/${normalizeToBech32(event.id, Bech32Prefix.Note)}`} whiteSpace="nowrap">
|
||||||
<span> </span>
|
{moment(event.created_at * 1000).fromNow()}
|
||||||
<Link as={RouterLink} to={`/n/${normalizeToBech32(event.id, Bech32Prefix.Note)}`} whiteSpace="nowrap">
|
</Link>
|
||||||
{moment(event.created_at * 1000).fromNow()}
|
|
||||||
</Link>
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
<UserTipButton pubkey={event.pubkey} size="xs" />
|
|
||||||
<NoteRelays event={event} size="xs" />
|
|
||||||
<NoteMenu event={event} />
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardBody padding="0">
|
<CardBody padding="0">
|
||||||
@ -53,6 +45,11 @@ export const Note = React.memo(({ event }: NoteProps) => {
|
|||||||
<NoteContents event={event} trusted={following.includes(event.pubkey)} />
|
<NoteContents event={event} trusted={following.includes(event.pubkey)} />
|
||||||
</Box>
|
</Box>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
|
<CardFooter padding="0" display="flex" gap="2" justifyContent="flex-end">
|
||||||
|
<UserTipButton pubkey={event.pubkey} size="xs" />
|
||||||
|
<NoteRelays event={event} size="xs" />
|
||||||
|
<NoteMenu event={event} />
|
||||||
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -15,7 +15,7 @@ import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip-19";
|
|||||||
import { NostrEvent } from "../../types/nostr-event";
|
import { NostrEvent } from "../../types/nostr-event";
|
||||||
import { MenuIconButton } from "../menu-icon-button";
|
import { MenuIconButton } from "../menu-icon-button";
|
||||||
|
|
||||||
import { ClipboardIcon, CodeIcon, IMAGE_ICONS } from "../icons";
|
import { ClipboardIcon, CodeIcon, IMAGE_ICONS, ShareIcon } from "../icons";
|
||||||
import { getReferences } from "../../helpers/nostr-event";
|
import { getReferences } from "../../helpers/nostr-event";
|
||||||
|
|
||||||
export const NoteMenu = ({ event }: { event: NostrEvent }) => {
|
export const NoteMenu = ({ event }: { event: NostrEvent }) => {
|
||||||
@ -26,6 +26,10 @@ export const NoteMenu = ({ event }: { event: NostrEvent }) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MenuIconButton>
|
<MenuIconButton>
|
||||||
|
{/* TODO: should open the post modal and mention the note/relay */}
|
||||||
|
{/* <MenuItem icon={<ShareIcon />} onClick={() => console.log("Share Note", event)}>
|
||||||
|
Repost
|
||||||
|
</MenuItem> */}
|
||||||
<MenuItem
|
<MenuItem
|
||||||
as="a"
|
as="a"
|
||||||
icon={<Avatar src={IMAGE_ICONS.nostrGuruIcon} size="xs" />}
|
icon={<Avatar src={IMAGE_ICONS.nostrGuruIcon} size="xs" />}
|
||||||
@ -73,8 +77,10 @@ export const NoteMenu = ({ event }: { event: NostrEvent }) => {
|
|||||||
<ModalContent>
|
<ModalContent>
|
||||||
<ModalHeader>Raw Event</ModalHeader>
|
<ModalHeader>Raw Event</ModalHeader>
|
||||||
<ModalCloseButton />
|
<ModalCloseButton />
|
||||||
<ModalBody overflow="auto">
|
<ModalBody overflow="auto" fontSize="sm" padding="2">
|
||||||
|
Raw JSON:
|
||||||
<pre>{JSON.stringify(event, null, 2)}</pre>
|
<pre>{JSON.stringify(event, null, 2)}</pre>
|
||||||
|
Parsed Refs:
|
||||||
<pre>{JSON.stringify(getReferences(event), null, 2)}</pre>
|
<pre>{JSON.stringify(getReferences(event), null, 2)}</pre>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
@ -10,7 +10,6 @@ import {
|
|||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
Text,
|
Text,
|
||||||
Flex,
|
Flex,
|
||||||
Portal,
|
|
||||||
PopoverFooter,
|
PopoverFooter,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { nostrPostAction } from "../../classes/nostr-post-action";
|
import { nostrPostAction } from "../../classes/nostr-post-action";
|
||||||
@ -68,26 +67,24 @@ export const NoteRelays = memo(({ event, ...props }: NoteRelaysProps) => {
|
|||||||
<PopoverTrigger>
|
<PopoverTrigger>
|
||||||
<IconButton title="Note Relays" icon={<RelayIcon />} size={props.size ?? "sm"} aria-label="Note Relays" />
|
<IconButton title="Note Relays" icon={<RelayIcon />} size={props.size ?? "sm"} aria-label="Note Relays" />
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<Portal>
|
<PopoverContent>
|
||||||
<PopoverContent>
|
<PopoverArrow />
|
||||||
<PopoverArrow />
|
<PopoverBody>
|
||||||
<PopoverBody>
|
{relays.map((url) => (
|
||||||
{relays.map((url) => (
|
<Text key={url}>{url}</Text>
|
||||||
<Text key={url}>{url}</Text>
|
))}
|
||||||
))}
|
</PopoverBody>
|
||||||
</PopoverBody>
|
<PopoverFooter>
|
||||||
<PopoverFooter>
|
<Flex gap="2">
|
||||||
<Flex gap="2">
|
<Button size="xs" onClick={queryRelays} isLoading={querying} leftIcon={<SearchIcon />}>
|
||||||
<Button size="xs" onClick={queryRelays} isLoading={querying} leftIcon={<SearchIcon />}>
|
Search
|
||||||
Search
|
</Button>
|
||||||
</Button>
|
<Button size="xs" onClick={broadcast} isLoading={broadcasting} leftIcon={<RelayIcon />}>
|
||||||
<Button size="xs" onClick={broadcast} isLoading={broadcasting} leftIcon={<RelayIcon />}>
|
Broadcast
|
||||||
Broadcast
|
</Button>
|
||||||
</Button>
|
</Flex>
|
||||||
</Flex>
|
</PopoverFooter>
|
||||||
</PopoverFooter>
|
</PopoverContent>
|
||||||
</PopoverContent>
|
|
||||||
</Portal>
|
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Avatar, Button, Container, Flex, Heading, IconButton, LinkOverlay, VStack } from "@chakra-ui/react";
|
import { Avatar, Button, Container, Flex, Heading, IconButton, LinkOverlay, VStack } from "@chakra-ui/react";
|
||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
import { HomeIcon, LogoutIcon, ProfileIcon, SettingsIcon } from "./icons";
|
import { FeedIcon, LogoutIcon, ProfileIcon, SettingsIcon } from "./icons";
|
||||||
import { ErrorBoundary } from "./error-boundary";
|
import { ErrorBoundary } from "./error-boundary";
|
||||||
import { ConnectedRelays } from "./connected-relays";
|
import { ConnectedRelays } from "./connected-relays";
|
||||||
|
|
||||||
@ -10,84 +10,75 @@ import identity from "../services/identity";
|
|||||||
import { FollowingList } from "./following-list";
|
import { FollowingList } from "./following-list";
|
||||||
import { ReloadPrompt } from "./reload-prompt";
|
import { ReloadPrompt } from "./reload-prompt";
|
||||||
|
|
||||||
const MobileLayout = ({ children }: { children: React.ReactNode }) => {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex direction="column" height="100%">
|
|
||||||
<ReloadPrompt />
|
|
||||||
<Flex flexGrow={1} direction="column" overflow="hidden">
|
|
||||||
{children}
|
|
||||||
</Flex>
|
|
||||||
<Flex flexShrink={0} gap="2" padding="2">
|
|
||||||
<IconButton icon={<HomeIcon />} aria-label="Home" onClick={() => navigate("/")} flexGrow="1" size="lg" />
|
|
||||||
<IconButton
|
|
||||||
icon={<ProfileIcon />}
|
|
||||||
aria-label="Profile"
|
|
||||||
onClick={() => navigate(`/profile`)}
|
|
||||||
flexGrow="1"
|
|
||||||
size="lg"
|
|
||||||
/>
|
|
||||||
<IconButton
|
|
||||||
icon={<SettingsIcon />}
|
|
||||||
aria-label="Settings"
|
|
||||||
onClick={() => navigate("/settings")}
|
|
||||||
flexGrow="1"
|
|
||||||
size="lg"
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const DesktopLayout = ({ children }: { children: React.ReactNode }) => {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Container
|
|
||||||
size="lg"
|
|
||||||
display="flex"
|
|
||||||
gap="2"
|
|
||||||
flexDirection="column"
|
|
||||||
height="100vh"
|
|
||||||
overflow="hidden"
|
|
||||||
position="relative"
|
|
||||||
>
|
|
||||||
<ReloadPrompt />
|
|
||||||
<Flex gap="4" grow={1} overflow="hidden">
|
|
||||||
<VStack width="15rem" pt="2" alignItems="stretch" flexShrink={0}>
|
|
||||||
<Flex gap="2" alignItems="center" position="relative">
|
|
||||||
<LinkOverlay as={Link} to="/" />
|
|
||||||
<Avatar src="/apple-touch-icon.png" size="sm" />
|
|
||||||
<Heading size="md">noStrudel</Heading>
|
|
||||||
</Flex>
|
|
||||||
<Button onClick={() => navigate("/")} leftIcon={<HomeIcon />}>
|
|
||||||
Home
|
|
||||||
</Button>
|
|
||||||
<Button onClick={() => navigate("/settings")} leftIcon={<SettingsIcon />}>
|
|
||||||
Settings
|
|
||||||
</Button>
|
|
||||||
<Button onClick={() => identity.logout()} leftIcon={<LogoutIcon />}>
|
|
||||||
Logout
|
|
||||||
</Button>
|
|
||||||
<ConnectedRelays />
|
|
||||||
</VStack>
|
|
||||||
<Flex flexGrow={1} direction="column" overflow="hidden">
|
|
||||||
<ErrorBoundary>{children}</ErrorBoundary>
|
|
||||||
</Flex>
|
|
||||||
<VStack width="15rem" pt="2" alignItems="stretch" flexShrink={0}>
|
|
||||||
<Heading size="md">Following</Heading>
|
|
||||||
<FollowingList />
|
|
||||||
</VStack>
|
|
||||||
</Flex>
|
|
||||||
</Container>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Page = ({ children }: { children: React.ReactNode }) => {
|
export const Page = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
const Layout = isMobile ? MobileLayout : DesktopLayout;
|
|
||||||
|
|
||||||
return <Layout>{children}</Layout>;
|
if (isMobile) {
|
||||||
|
return (
|
||||||
|
<Flex direction="column" height="100%">
|
||||||
|
<ReloadPrompt />
|
||||||
|
<Flex flexGrow={1} direction="column" overflow="hidden">
|
||||||
|
{children}
|
||||||
|
</Flex>
|
||||||
|
<Flex flexShrink={0} gap="2" padding="2">
|
||||||
|
<IconButton icon={<FeedIcon />} aria-label="Home" onClick={() => navigate("/")} flexGrow="1" size="lg" />
|
||||||
|
<IconButton
|
||||||
|
icon={<ProfileIcon />}
|
||||||
|
aria-label="Profile"
|
||||||
|
onClick={() => navigate(`/profile`)}
|
||||||
|
flexGrow="1"
|
||||||
|
size="lg"
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
icon={<SettingsIcon />}
|
||||||
|
aria-label="Settings"
|
||||||
|
onClick={() => navigate("/settings")}
|
||||||
|
flexGrow="1"
|
||||||
|
size="lg"
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container
|
||||||
|
size="lg"
|
||||||
|
display="flex"
|
||||||
|
gap="2"
|
||||||
|
flexDirection="column"
|
||||||
|
height="100vh"
|
||||||
|
overflow="hidden"
|
||||||
|
position="relative"
|
||||||
|
>
|
||||||
|
<ReloadPrompt />
|
||||||
|
<Flex gap="4" grow={1} overflow="hidden">
|
||||||
|
<VStack width="15rem" pt="2" alignItems="stretch" flexShrink={0}>
|
||||||
|
<Flex gap="2" alignItems="center" position="relative">
|
||||||
|
<LinkOverlay as={Link} to="/" />
|
||||||
|
<Avatar src="/apple-touch-icon.png" size="sm" />
|
||||||
|
<Heading size="md">noStrudel</Heading>
|
||||||
|
</Flex>
|
||||||
|
<Button onClick={() => navigate("/")} leftIcon={<FeedIcon />}>
|
||||||
|
Home
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => navigate("/settings")} leftIcon={<SettingsIcon />}>
|
||||||
|
Settings
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => identity.logout()} leftIcon={<LogoutIcon />}>
|
||||||
|
Logout
|
||||||
|
</Button>
|
||||||
|
<ConnectedRelays />
|
||||||
|
</VStack>
|
||||||
|
<Flex flexGrow={1} direction="column" overflow="hidden">
|
||||||
|
<ErrorBoundary>{children}</ErrorBoundary>
|
||||||
|
</Flex>
|
||||||
|
<VStack width="15rem" pt="2" alignItems="stretch" flexShrink={0}>
|
||||||
|
<Heading size="md">Following</Heading>
|
||||||
|
<FollowingList />
|
||||||
|
</VStack>
|
||||||
|
</Flex>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Button, Flex, Heading, Spinner } from "@chakra-ui/react";
|
import { Avatar, Button, Flex, Heading, Spinner } from "@chakra-ui/react";
|
||||||
import { Navigate, useLocation } from "react-router-dom";
|
import { Navigate, useLocation } from "react-router-dom";
|
||||||
import useSubject from "../hooks/use-subject";
|
import useSubject from "../hooks/use-subject";
|
||||||
import identity from "../services/identity";
|
import identity from "../services/identity";
|
||||||
@ -12,8 +12,9 @@ export const LoginView = () => {
|
|||||||
if (setup) return <Navigate to={location.state?.from ?? "/"} replace />;
|
if (setup) return <Navigate to={location.state?.from ?? "/"} replace />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" alignItems="center" justifyContent="center">
|
<Flex direction="column" alignItems="center" justifyContent="center" gap="4" height="80%">
|
||||||
<Heading>Login</Heading>
|
<Avatar src="/apple-touch-icon.png" size="lg" />
|
||||||
|
<Heading>noStrudel</Heading>
|
||||||
<Button onClick={() => identity.loginWithExtension()}>Use browser extension</Button>
|
<Button onClick={() => identity.loginWithExtension()}>Use browser extension</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user