mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-04-09 20:29:17 +02:00
Dont require login for profile and note views
This commit is contained in:
parent
780491aa6c
commit
2aa6ec5678
5
.changeset/nervous-bats-teach.md
Normal file
5
.changeset/nervous-bats-teach.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"nostrudel": minor
|
||||
---
|
||||
|
||||
Dont require login for profile and note views
|
35
src/app.tsx
35
src/app.tsx
@ -37,37 +37,12 @@ import UserMediaTab from "./views/user/media";
|
||||
// code split search view because QrScanner library is 400kB
|
||||
const SearchView = React.lazy(() => import("./views/search"));
|
||||
|
||||
const RequireCurrentAccount = ({ children }: { children: JSX.Element }) => {
|
||||
let location = useLocation();
|
||||
const loading = useSubject(accountService.loading);
|
||||
const account = useSubject(accountService.current);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Flex alignItems="center" height="100%" gap="4" direction="column">
|
||||
<Flex gap="4" grow="1" alignItems="center">
|
||||
<Spinner />
|
||||
<Text>Loading Accounts</Text>
|
||||
</Flex>
|
||||
<Button variant="link" margin="4" onClick={() => deleteDatabase()}>
|
||||
Stuck loading? clear cache
|
||||
</Button>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
if (!account) return <Navigate to="/login" state={{ from: location.pathname }} replace />;
|
||||
|
||||
return children;
|
||||
};
|
||||
|
||||
const RootPage = () => (
|
||||
<RequireCurrentAccount>
|
||||
<Page>
|
||||
<Suspense fallback={<Spinner />}>
|
||||
<Outlet />
|
||||
</Suspense>
|
||||
</Page>
|
||||
</RequireCurrentAccount>
|
||||
<Page>
|
||||
<Suspense fallback={<Spinner />}>
|
||||
<Outlet />
|
||||
</Suspense>
|
||||
</Page>
|
||||
);
|
||||
|
||||
const router = createBrowserRouter([
|
||||
|
@ -18,7 +18,7 @@ export function QuoteRepostButton({ event }: { event: NostrEvent }) {
|
||||
onClick={handleClick}
|
||||
aria-label="Quote repost"
|
||||
title="Quote repost"
|
||||
isDisabled={account.readonly}
|
||||
isDisabled={account?.readonly ?? true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ export default function ReactionButton({ note, ...props }: { note: NostrEvent }
|
||||
handleClick(input);
|
||||
};
|
||||
|
||||
const isLiked = reactions.some((event) => event.pubkey === account.pubkey);
|
||||
const isLiked = !!account && reactions.some((event) => event.pubkey === account.pubkey);
|
||||
|
||||
return (
|
||||
// <Popover placement="bottom" trigger="hover" openDelay={500}>
|
||||
|
@ -13,6 +13,12 @@ export function ReplyButton({ event }: { event: NostrEvent }) {
|
||||
const reply = () => openModal(buildReply(event));
|
||||
|
||||
return (
|
||||
<IconButton icon={<ReplyIcon />} title="Reply" aria-label="Reply" onClick={reply} isDisabled={account.readonly} />
|
||||
<IconButton
|
||||
icon={<ReplyIcon />}
|
||||
title="Reply"
|
||||
aria-label="Reply"
|
||||
onClick={reply}
|
||||
isDisabled={account?.readonly ?? true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ export function RepostButton({ event }: { event: NostrEvent }) {
|
||||
|
||||
const handleClick = async () => {
|
||||
try {
|
||||
if (!account) throw new Error("not logged in");
|
||||
setLoading(true);
|
||||
const draftRepost = buildRepost(event);
|
||||
const repost = await signingService.requestSignature(draftRepost, account);
|
||||
@ -50,7 +51,7 @@ export function RepostButton({ event }: { event: NostrEvent }) {
|
||||
onClick={onOpen}
|
||||
aria-label="Repost Note"
|
||||
title="Repost Note"
|
||||
isDisabled={account.readonly}
|
||||
isDisabled={account?.readonly ?? true}
|
||||
isLoading={loading}
|
||||
/>
|
||||
{isOpen && (
|
||||
|
@ -43,6 +43,7 @@ export const NoteMenu = ({ event, ...props }: { event: NostrEvent } & Omit<MenuI
|
||||
|
||||
const deleteNote = useCallback(async () => {
|
||||
try {
|
||||
if (!account) throw new Error("not logged in");
|
||||
setDeleting(true);
|
||||
const deleteEvent = buildDeleteEvent([event.id], reason);
|
||||
const signed = await signingService.requestSignature(deleteEvent, account);
|
||||
@ -75,7 +76,7 @@ export const NoteMenu = ({ event, ...props }: { event: NostrEvent } & Omit<MenuI
|
||||
Copy Note ID
|
||||
</MenuItem>
|
||||
)}
|
||||
{account.pubkey === event.pubkey && (
|
||||
{account?.pubkey === event.pubkey && (
|
||||
<MenuItem icon={<TrashIcon />} color="red.500" onClick={deleteModal.onOpen}>
|
||||
Delete Note
|
||||
</MenuItem>
|
||||
|
@ -26,7 +26,7 @@ export default function NoteZapButton({ note, ...props }: { note: NostrEvent } &
|
||||
}, [zaps]);
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
const hasZapped = parsedZaps.some((zapRequest) => zapRequest.request.pubkey === account.pubkey);
|
||||
const hasZapped = !!account && parsedZaps.some((zapRequest) => zapRequest.request.pubkey === account.pubkey);
|
||||
const tipAddress = metadata?.lud06 || metadata?.lud16;
|
||||
|
||||
const invoicePaid = () => eventZapsService.requestZaps(note.id, clientRelaysService.getReadUrls(), true);
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React, { PropsWithChildren, useContext } from "react";
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import { useReadRelayUrls } from "../../hooks/use-client-relays";
|
||||
import { useUserContacts } from "../../hooks/use-user-contacts";
|
||||
import { useCurrentAccount } from "../../hooks/use-current-account";
|
||||
import clientFollowingService from "../../services/client-following";
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
|
||||
const TrustContext = React.createContext<boolean>(false);
|
||||
|
||||
@ -18,11 +18,9 @@ export function TrustProvider({
|
||||
const parentTrust = useContext(TrustContext);
|
||||
|
||||
const account = useCurrentAccount();
|
||||
const readRelays = useReadRelayUrls();
|
||||
const contacts = useUserContacts(account.pubkey, readRelays);
|
||||
const following = contacts?.contacts || [];
|
||||
const following = useSubject(clientFollowingService.following).map((p) => p[1]);
|
||||
|
||||
const isEventTrusted = trust || (!!event && (event.pubkey === account.pubkey || following.includes(event.pubkey)));
|
||||
const isEventTrusted = trust || (!!event && (event.pubkey === account?.pubkey || following.includes(event.pubkey)));
|
||||
|
||||
return <TrustContext.Provider value={parentTrust || isEventTrusted}>{children}</TrustContext.Provider>;
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import { useCurrentAccount } from "../../hooks/use-current-account";
|
||||
import accountService from "../../services/account";
|
||||
import { ConnectedRelays } from "../connected-relays";
|
||||
import { ChatIcon, FeedIcon, LogoutIcon, NotificationIcon, ProfileIcon, RelayIcon, SearchIcon } from "../icons";
|
||||
import { ProfileButton } from "../profile-button";
|
||||
import ProfileLink from "./profile-link";
|
||||
import AccountSwitcher from "./account-switcher";
|
||||
|
||||
export default function DesktopSideNav() {
|
||||
@ -19,7 +19,7 @@ export default function DesktopSideNav() {
|
||||
<Avatar src="/apple-touch-icon.png" size="sm" />
|
||||
<Heading size="md">noStrudel</Heading>
|
||||
</Flex>
|
||||
<ProfileButton />
|
||||
<ProfileLink />
|
||||
<AccountSwitcher />
|
||||
<Button onClick={() => navigate("/")} leftIcon={<FeedIcon />}>
|
||||
Home
|
||||
@ -42,10 +42,12 @@ export default function DesktopSideNav() {
|
||||
<Button onClick={() => navigate("/settings")} leftIcon={<SettingsIcon />}>
|
||||
Settings
|
||||
</Button>
|
||||
<Button onClick={() => accountService.logout()} leftIcon={<LogoutIcon />}>
|
||||
Logout
|
||||
</Button>
|
||||
{account.readonly && (
|
||||
{account && (
|
||||
<Button onClick={() => accountService.logout()} leftIcon={<LogoutIcon />}>
|
||||
Logout
|
||||
</Button>
|
||||
)}
|
||||
{account?.readonly && (
|
||||
<Text color="red.200" textAlign="center">
|
||||
Readonly Mode
|
||||
</Text>
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { Flex, IconButton, useDisclosure } from "@chakra-ui/react";
|
||||
import { Avatar, Flex, IconButton, useDisclosure } from "@chakra-ui/react";
|
||||
import { useContext, useEffect } from "react";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { useCurrentAccount } from "../../hooks/use-current-account";
|
||||
import { PostModalContext } from "../../providers/post-modal-provider";
|
||||
import { ChatIcon, HomeIcon, NotificationIcon, PlusCircleIcon, SearchIcon } from "../icons";
|
||||
import { ChatIcon, FeedIcon, HomeIcon, NotificationIcon, PlusCircleIcon, SearchIcon } from "../icons";
|
||||
import { UserAvatar } from "../user-avatar";
|
||||
import MobileSideDrawer from "./mobile-side-drawer";
|
||||
|
||||
@ -19,7 +19,11 @@ export default function MobileBottomNav() {
|
||||
return (
|
||||
<>
|
||||
<Flex flexShrink={0} gap="2" padding="2" alignItems="center">
|
||||
<UserAvatar pubkey={account.pubkey} size="sm" onClick={onOpen} />
|
||||
{account ? (
|
||||
<UserAvatar pubkey={account.pubkey} size="sm" onClick={onOpen} noProxy />
|
||||
) : (
|
||||
<Avatar size="sm" src="/apple-touch-icon.png" onClick={onOpen} cursor="pointer" />
|
||||
)}
|
||||
<IconButton icon={<HomeIcon />} aria-label="Home" onClick={() => navigate("/")} flexGrow="1" size="md" />
|
||||
<IconButton
|
||||
icon={<SearchIcon />}
|
||||
@ -36,7 +40,7 @@ export default function MobileBottomNav() {
|
||||
}}
|
||||
variant="solid"
|
||||
colorScheme="brand"
|
||||
isDisabled={account.readonly}
|
||||
isDisabled={account?.readonly ?? true}
|
||||
/>
|
||||
<IconButton icon={<ChatIcon />} aria-label="Messages" onClick={() => navigate(`/dm`)} flexGrow="1" size="md" />
|
||||
<IconButton
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
Drawer,
|
||||
DrawerBody,
|
||||
@ -10,32 +11,36 @@ import {
|
||||
Flex,
|
||||
Text,
|
||||
} from "@chakra-ui/react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { getUserDisplayName } from "../../helpers/user-metadata";
|
||||
import { useCurrentAccount } from "../../hooks/use-current-account";
|
||||
import { useUserMetadata } from "../../hooks/use-user-metadata";
|
||||
import accountService from "../../services/account";
|
||||
import { Link as RouterLink, useNavigate } from "react-router-dom";
|
||||
import { ConnectedRelays } from "../connected-relays";
|
||||
import { HomeIcon, LogoutIcon, ProfileIcon, RelayIcon, SearchIcon, SettingsIcon } from "../icons";
|
||||
import { UserAvatar } from "../user-avatar";
|
||||
import { UserLink } from "../user-link";
|
||||
import AccountSwitcher from "./account-switcher";
|
||||
import { useCurrentAccount } from "../../hooks/use-current-account";
|
||||
import accountService from "../../services/account";
|
||||
|
||||
export default function MobileSideDrawer({ ...props }: Omit<DrawerProps, "children">) {
|
||||
const navigate = useNavigate();
|
||||
const account = useCurrentAccount();
|
||||
const metadata = useUserMetadata(account.pubkey);
|
||||
|
||||
return (
|
||||
<Drawer placement="left" {...props}>
|
||||
<DrawerOverlay />
|
||||
<DrawerContent>
|
||||
<DrawerCloseButton />
|
||||
<DrawerHeader>
|
||||
<Flex gap="2">
|
||||
<UserAvatar pubkey={account.pubkey} size="sm" />
|
||||
<UserLink pubkey={account.pubkey} />
|
||||
</Flex>
|
||||
<DrawerHeader px="4" py="4">
|
||||
{account ? (
|
||||
<Flex gap="2">
|
||||
<UserAvatar pubkey={account.pubkey} size="sm" noProxy />
|
||||
<UserLink pubkey={account.pubkey} />
|
||||
</Flex>
|
||||
) : (
|
||||
<Flex gap="2">
|
||||
<Avatar src="/apple-touch-icon.png" size="sm" />
|
||||
<Text m={0}>Nostrudel</Text>
|
||||
</Flex>
|
||||
)}
|
||||
</DrawerHeader>
|
||||
<DrawerBody padding={0} overflowY="auto" overflowX="hidden">
|
||||
<AccountSwitcher />
|
||||
@ -55,9 +60,15 @@ export default function MobileSideDrawer({ ...props }: Omit<DrawerProps, "childr
|
||||
<Button onClick={() => navigate("/settings")} leftIcon={<SettingsIcon />}>
|
||||
Settings
|
||||
</Button>
|
||||
<Button onClick={() => accountService.logout()} leftIcon={<LogoutIcon />}>
|
||||
Logout
|
||||
</Button>
|
||||
{account ? (
|
||||
<Button onClick={() => accountService.logout()} leftIcon={<LogoutIcon />}>
|
||||
Logout
|
||||
</Button>
|
||||
) : (
|
||||
<Button as={RouterLink} to="/login" colorScheme="brand">
|
||||
Login
|
||||
</Button>
|
||||
)}
|
||||
<ConnectedRelays />
|
||||
</Flex>
|
||||
</DrawerBody>
|
||||
|
40
src/components/page/profile-link.tsx
Normal file
40
src/components/page/profile-link.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { Box, Button, LinkBox, Text } from "@chakra-ui/react";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
import { UserAvatar } from "../user-avatar";
|
||||
import { useUserMetadata } from "../../hooks/use-user-metadata";
|
||||
import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip19";
|
||||
import { truncatedId } from "../../helpers/nostr-event";
|
||||
import { useCurrentAccount } from "../../hooks/use-current-account";
|
||||
|
||||
function ProfileButton() {
|
||||
const account = useCurrentAccount()!;
|
||||
const metadata = useUserMetadata(account.pubkey);
|
||||
|
||||
return (
|
||||
<LinkBox
|
||||
as={RouterLink}
|
||||
to={`/u/${normalizeToBech32(account.pubkey, Bech32Prefix.Pubkey)}`}
|
||||
display="flex"
|
||||
gap="2"
|
||||
overflow="hidden"
|
||||
>
|
||||
<UserAvatar pubkey={account.pubkey} noProxy />
|
||||
<Box>
|
||||
<Text fontWeight="bold">{metadata?.name}</Text>
|
||||
<Text>{truncatedId(normalizeToBech32(account.pubkey) ?? "")}</Text>
|
||||
</Box>
|
||||
</LinkBox>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ProfileLink() {
|
||||
const account = useCurrentAccount();
|
||||
|
||||
if (account) return <ProfileButton />;
|
||||
else
|
||||
return (
|
||||
<Button as={RouterLink} to="/login" state={{ from: location.pathname }} colorScheme="brand">
|
||||
Login
|
||||
</Button>
|
||||
);
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
import { Box, LinkBox, Text } from "@chakra-ui/react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { UserAvatar } from "./user-avatar";
|
||||
import { useUserMetadata } from "../hooks/use-user-metadata";
|
||||
import { Bech32Prefix, normalizeToBech32 } from "../helpers/nip19";
|
||||
import { truncatedId } from "../helpers/nostr-event";
|
||||
import { useCurrentAccount } from "../hooks/use-current-account";
|
||||
|
||||
export const ProfileButton = () => {
|
||||
const { pubkey } = useCurrentAccount();
|
||||
const metadata = useUserMetadata(pubkey);
|
||||
|
||||
return (
|
||||
<LinkBox
|
||||
as={Link}
|
||||
to={`/u/${normalizeToBech32(pubkey, Bech32Prefix.Pubkey)}`}
|
||||
display="flex"
|
||||
gap="2"
|
||||
overflow="hidden"
|
||||
>
|
||||
<UserAvatar pubkey={pubkey} noProxy />
|
||||
<Box>
|
||||
<Text fontWeight="bold">{metadata?.name}</Text>
|
||||
<Text>{truncatedId(normalizeToBech32(pubkey) ?? "")}</Text>
|
||||
</Box>
|
||||
</LinkBox>
|
||||
);
|
||||
};
|
@ -2,8 +2,6 @@ import { Button, ButtonProps } from "@chakra-ui/react";
|
||||
import { useCurrentAccount } from "../hooks/use-current-account";
|
||||
import useSubject from "../hooks/use-subject";
|
||||
import clientFollowingService from "../services/client-following";
|
||||
import { useAsync } from "react-use";
|
||||
import { NostrRequest } from "../classes/nostr-request";
|
||||
import clientRelaysService from "../services/client-relays";
|
||||
import { useUserContacts } from "../hooks/use-user-contacts";
|
||||
|
||||
@ -30,8 +28,14 @@ export const UserFollowButton = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<Button colorScheme="brand" {...props} isLoading={savingDraft} onClick={toggleFollow} isDisabled={account.readonly}>
|
||||
{isFollowing ? "Unfollow" : userContacts?.contacts.includes(account.pubkey) ? "Follow Back" : "Follow"}
|
||||
<Button
|
||||
colorScheme="brand"
|
||||
{...props}
|
||||
isLoading={savingDraft}
|
||||
onClick={toggleFollow}
|
||||
isDisabled={account?.readonly ?? true}
|
||||
>
|
||||
{isFollowing ? "Unfollow" : account && userContacts?.contacts.includes(account.pubkey) ? "Follow Back" : "Follow"}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
@ -2,7 +2,5 @@ import accountService from "../services/account";
|
||||
import useSubject from "./use-subject";
|
||||
|
||||
export function useCurrentAccount() {
|
||||
const account = useSubject(accountService.current);
|
||||
if (!account) throw Error("no account");
|
||||
return account;
|
||||
return useSubject(accountService.current);
|
||||
}
|
||||
|
44
src/providers/require-current-account.tsx
Normal file
44
src/providers/require-current-account.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import useSubject from "../hooks/use-subject";
|
||||
import accountService from "../services/account";
|
||||
import { Button, Flex, Heading, Spinner, Text } from "@chakra-ui/react";
|
||||
import { deleteDatabase } from "../services/db";
|
||||
import { ExternalLinkIcon } from "../components/icons";
|
||||
|
||||
export default function RequireCurrentAccount({ children }: { children: JSX.Element }) {
|
||||
let location = useLocation();
|
||||
const loading = useSubject(accountService.loading);
|
||||
const account = useSubject(accountService.current);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Flex alignItems="center" height="100%" gap="4" direction="column">
|
||||
<Flex gap="4" grow="1" alignItems="center">
|
||||
<Spinner />
|
||||
<Text>Loading Accounts</Text>
|
||||
</Flex>
|
||||
<Button variant="link" margin="4" onClick={() => deleteDatabase()}>
|
||||
Stuck loading? clear cache
|
||||
</Button>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
if (!account)
|
||||
return (
|
||||
<Flex direction="column" w="full" h="full" alignItems="center" justifyContent="center" gap="4">
|
||||
<Heading size="md">You must be logged in to use this view</Heading>
|
||||
<Button
|
||||
as={Link}
|
||||
to="/login"
|
||||
state={{ from: location.pathname }}
|
||||
colorScheme="brand"
|
||||
rightIcon={<ExternalLinkIcon />}
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
return children;
|
||||
}
|
@ -4,7 +4,7 @@ import { AppSettings } from "./user-app-settings";
|
||||
|
||||
export type Account = {
|
||||
pubkey: string;
|
||||
readonly?: boolean;
|
||||
readonly: boolean;
|
||||
relays?: string[];
|
||||
secKey?: ArrayBuffer;
|
||||
iv?: Uint8Array;
|
||||
|
@ -10,6 +10,14 @@ import signingService from "./signing";
|
||||
|
||||
export type RelayDirectory = Record<string, { read: boolean; write: boolean }>;
|
||||
|
||||
const DEFAULT_RELAYS = [
|
||||
{ url: "wss://relay.damus.io", mode: RelayMode.READ },
|
||||
{ url: "wss://nostr.wine", mode: RelayMode.READ },
|
||||
{ url: "wss://relay.snort.social", mode: RelayMode.READ },
|
||||
{ url: "wss://eden.nostr.land", mode: RelayMode.READ },
|
||||
{ url: "wss://nos.lol", mode: RelayMode.READ },
|
||||
];
|
||||
|
||||
class ClientRelayService {
|
||||
bootstrapRelays = new Set<string>();
|
||||
relays = new PersistentSubject<RelayConfig[]>([]);
|
||||
@ -19,9 +27,10 @@ class ClientRelayService {
|
||||
constructor() {
|
||||
let lastSubject: Subject<ParsedUserRelays> | undefined;
|
||||
accountService.current.subscribe((account) => {
|
||||
this.relays.next([]);
|
||||
|
||||
if (!account) return;
|
||||
if (!account) {
|
||||
this.relays.next(DEFAULT_RELAYS);
|
||||
return;
|
||||
} else this.relays.next([]);
|
||||
|
||||
if (account.relays) {
|
||||
this.bootstrapRelays.clear();
|
||||
|
@ -19,6 +19,7 @@ import { DraftNostrEvent, NostrEvent } from "../../types/nostr-event";
|
||||
import DecryptPlaceholder from "./decrypt-placeholder";
|
||||
import { EmbedableContent } from "../../helpers/embeds";
|
||||
import { embedImages, embedLinks, embedNostrLinks, embedVideos } from "../../components/embed-types";
|
||||
import RequireCurrentAccount from "../../providers/require-current-account";
|
||||
|
||||
function MessageContent({ event, text }: { event: NostrEvent; text: string }) {
|
||||
let content: EmbedableContent = [text];
|
||||
@ -33,7 +34,7 @@ function MessageContent({ event, text }: { event: NostrEvent; text: string }) {
|
||||
}
|
||||
|
||||
function Message({ event }: { event: NostrEvent } & Omit<CardProps, "children">) {
|
||||
const account = useCurrentAccount();
|
||||
const account = useCurrentAccount()!;
|
||||
const isOwnMessage = account.pubkey === event.pubkey;
|
||||
|
||||
return (
|
||||
@ -55,7 +56,7 @@ function Message({ event }: { event: NostrEvent } & Omit<CardProps, "children">)
|
||||
);
|
||||
}
|
||||
|
||||
export default function DirectMessageChatView() {
|
||||
function DirectMessageChatPage() {
|
||||
const { key } = useParams();
|
||||
if (!key) return <Navigate to="/" />;
|
||||
const pubkey = normalizeToHex(key);
|
||||
@ -131,3 +132,10 @@ export default function DirectMessageChatView() {
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
export default function DirectMessageChatView() {
|
||||
return (
|
||||
<RequireCurrentAccount>
|
||||
<DirectMessageChatPage />
|
||||
</RequireCurrentAccount>
|
||||
);
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import { useUserMetadata } from "../../hooks/use-user-metadata";
|
||||
import directMessagesService from "../../services/direct-messages";
|
||||
import { ExternalLinkIcon } from "../../components/icons";
|
||||
import { useIsMobile } from "../../hooks/use-is-mobile";
|
||||
import RequireCurrentAccount from "../../providers/require-current-account";
|
||||
|
||||
function ContactCard({ pubkey }: { pubkey: string }) {
|
||||
const subject = useMemo(() => directMessagesService.getUserMessages(pubkey), [pubkey]);
|
||||
@ -48,7 +49,7 @@ function ContactCard({ pubkey }: { pubkey: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
function DirectMessagesView() {
|
||||
function DirectMessagesPage() {
|
||||
const isMobile = useIsMobile();
|
||||
const [from, setFrom] = useState(moment().subtract(2, "days"));
|
||||
const conversations = useSubject(directMessagesService.conversations);
|
||||
@ -121,4 +122,10 @@ function DirectMessagesView() {
|
||||
);
|
||||
}
|
||||
|
||||
export default DirectMessagesView;
|
||||
export default function DirectMessagesView() {
|
||||
return (
|
||||
<RequireCurrentAccount>
|
||||
<DirectMessagesPage />
|
||||
</RequireCurrentAccount>
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { useMemo } from "react";
|
||||
import { Button, Flex, Spinner } from "@chakra-ui/react";
|
||||
import moment from "moment";
|
||||
import { Note } from "../../components/note";
|
||||
@ -11,6 +11,7 @@ import userContactsService, { UserContacts } from "../../services/user-contacts"
|
||||
import { PersistentSubject } from "../../classes/subject";
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
import { useThrottle } from "react-use";
|
||||
import RequireCurrentAccount from "../../providers/require-current-account";
|
||||
|
||||
class DiscoverContacts {
|
||||
pubkey: string;
|
||||
@ -59,9 +60,9 @@ class DiscoverContacts {
|
||||
}
|
||||
}
|
||||
|
||||
export default function DiscoverTab() {
|
||||
function DiscoverTabBody() {
|
||||
useAppTitle("discover");
|
||||
const account = useCurrentAccount();
|
||||
const account = useCurrentAccount()!;
|
||||
const relays = useReadRelayUrls();
|
||||
|
||||
const discover = useMemo(() => new DiscoverContacts(account.pubkey, relays), [account.pubkey, relays.join("|")]);
|
||||
@ -86,3 +87,11 @@ export default function DiscoverTab() {
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
export default function DiscoverTab() {
|
||||
return (
|
||||
<RequireCurrentAccount>
|
||||
<DiscoverTabBody />
|
||||
</RequireCurrentAccount>
|
||||
);
|
||||
}
|
||||
|
@ -11,9 +11,10 @@ import { PostModalContext } from "../../providers/post-modal-provider";
|
||||
import { useReadRelayUrls } from "../../hooks/use-client-relays";
|
||||
import { useCurrentAccount } from "../../hooks/use-current-account";
|
||||
import RepostNote from "../../components/note/repost-note";
|
||||
import RequireCurrentAccount from "../../providers/require-current-account";
|
||||
|
||||
export default function FollowingTab() {
|
||||
const account = useCurrentAccount();
|
||||
function FollowingTabBody() {
|
||||
const account = useCurrentAccount()!;
|
||||
const readRelays = useReadRelayUrls();
|
||||
const { openModal } = useContext(PostModalContext);
|
||||
const contacts = useUserContacts(account.pubkey, readRelays);
|
||||
@ -55,3 +56,11 @@ export default function FollowingTab() {
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
export default function FollowingTab() {
|
||||
return (
|
||||
<RequireCurrentAccount>
|
||||
<FollowingTabBody />
|
||||
</RequireCurrentAccount>
|
||||
);
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ export default function LoginNsecView() {
|
||||
const pubkey = getPublicKey(hexKey);
|
||||
|
||||
const encrypted = await signingService.encryptSecKey(hexKey);
|
||||
accountService.addAccount({ pubkey, relays: [relayUrl], ...encrypted });
|
||||
accountService.addAccount({ pubkey, relays: [relayUrl], ...encrypted, readonly: false });
|
||||
clientRelaysService.bootstrapRelays.add(relayUrl);
|
||||
accountService.switchAccount(pubkey);
|
||||
};
|
||||
|
@ -42,7 +42,7 @@ export default function LoginStartView() {
|
||||
relays = ["wss://relay.damus.io", "wss://relay.snort.social", "wss://nostr.wine"];
|
||||
}
|
||||
|
||||
accountService.addAccount({ pubkey, relays, useExtension: true });
|
||||
accountService.addAccount({ pubkey, relays, useExtension: true, readonly: false });
|
||||
}
|
||||
|
||||
accountService.switchAccount(pubkey);
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { Button, Card, CardBody, CardHeader, Flex, Spinner, Text } from "@chakra-ui/react";
|
||||
import moment from "moment";
|
||||
import { memo } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { UserAvatar } from "../../components/user-avatar";
|
||||
import { UserLink } from "../../components/user-link";
|
||||
import { convertTimestampToDate } from "../../helpers/date";
|
||||
@ -10,6 +9,7 @@ import { useCurrentAccount } from "../../hooks/use-current-account";
|
||||
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import { NoteLink } from "../../components/note-link";
|
||||
import RequireCurrentAccount from "../../providers/require-current-account";
|
||||
|
||||
const Kind1Notification = ({ event }: { event: NostrEvent }) => (
|
||||
<Card size="sm" variant="outline">
|
||||
@ -35,9 +35,9 @@ const NotificationItem = memo(({ event }: { event: NostrEvent }) => {
|
||||
return <>Unknown event type {event.kind}</>;
|
||||
});
|
||||
|
||||
const NotificationsView = () => {
|
||||
function NotificationsPage() {
|
||||
const readRelays = useReadRelayUrls();
|
||||
const account = useCurrentAccount();
|
||||
const account = useCurrentAccount()!;
|
||||
const { events, loading, loadMore } = useTimelineLoader(
|
||||
"notifications",
|
||||
readRelays,
|
||||
@ -60,6 +60,12 @@ const NotificationsView = () => {
|
||||
{loading ? <Spinner ml="auto" mr="auto" mt="8" mb="8" /> : <Button onClick={() => loadMore()}>Load More</Button>}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default NotificationsView;
|
||||
export default function NotificationsView() {
|
||||
return (
|
||||
<RequireCurrentAccount>
|
||||
<NotificationsPage />
|
||||
</RequireCurrentAccount>
|
||||
);
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ type MetadataFormProps = {
|
||||
};
|
||||
|
||||
const MetadataForm = ({ defaultValues, onSubmit }: MetadataFormProps) => {
|
||||
const account = useCurrentAccount();
|
||||
const account = useCurrentAccount()!;
|
||||
const isMobile = useIsMobile();
|
||||
const {
|
||||
register,
|
||||
@ -189,7 +189,7 @@ export const ProfileEditView = () => {
|
||||
const writeRelays = useWriteRelayUrls();
|
||||
const readRelays = useReadRelayUrls();
|
||||
const toast = useToast();
|
||||
const account = useCurrentAccount();
|
||||
const account = useCurrentAccount()!;
|
||||
const metadata = useUserMetadata(account.pubkey, readRelays, true);
|
||||
|
||||
const defaultValues = useMemo<FormData>(
|
||||
|
@ -1,5 +1,10 @@
|
||||
import RequireCurrentAccount from "../../providers/require-current-account";
|
||||
import { ProfileEditView } from "./edit";
|
||||
|
||||
export default function ProfileView() {
|
||||
return <ProfileEditView />;
|
||||
return (
|
||||
<RequireCurrentAccount>
|
||||
<ProfileEditView />
|
||||
</RequireCurrentAccount>
|
||||
);
|
||||
}
|
||||
|
@ -26,8 +26,9 @@ import useSubject from "../../hooks/use-subject";
|
||||
import { RelayStatus } from "../../components/relay-status";
|
||||
import { normalizeRelayUrl } from "../../helpers/url";
|
||||
import { RelayScoreBreakdown } from "../../components/relay-score-breakdown";
|
||||
import RequireCurrentAccount from "../../providers/require-current-account";
|
||||
|
||||
export default function RelaysView() {
|
||||
function RelaysPage() {
|
||||
const relays = useSubject(clientRelaysService.relays);
|
||||
const toast = useToast();
|
||||
|
||||
@ -152,3 +153,11 @@ export default function RelaysView() {
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
export default function RelaysView() {
|
||||
return (
|
||||
<RequireCurrentAccount>
|
||||
<RelaysPage />
|
||||
</RequireCurrentAccount>
|
||||
);
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ export default function Header({
|
||||
const npub = normalizeToBech32(pubkey, Bech32Prefix.Pubkey);
|
||||
|
||||
const account = useCurrentAccount();
|
||||
const isSelf = pubkey === account.pubkey;
|
||||
const isSelf = pubkey === account?.pubkey;
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap="2" px="2" pt="2">
|
||||
|
Loading…
x
Reference in New Issue
Block a user