layout cleanup

This commit is contained in:
hzrd149 2025-01-14 21:45:51 -06:00
parent 0214b69bc7
commit 788dd9bcce
49 changed files with 555 additions and 715 deletions

View File

@ -4,7 +4,6 @@ import { Spinner } from "@chakra-ui/react";
import { ErrorBoundary } from "./components/error-boundary";
import AppLayout from "./components/layout";
import Layout from "./components/legacy-layout";
import DrawerSubViewProvider from "./providers/drawer-sub-view-provider";
import useSetColorMode from "./hooks/use-set-color-mode";
import { RouteProviders } from "./providers/route";
@ -78,7 +77,6 @@ import CacheRelayView from "./views/relays/cache";
import RelaySetView from "./views/relays/relay-set";
import AppRelays from "./views/relays/app";
import MailboxesView from "./views/relays/mailboxes";
import MediaServersView from "./views/settings/media-servers";
import NIP05RelaysView from "./views/relays/nip05";
import DatabaseView from "./views/relays/cache/database";
import ContactListRelaysView from "./views/relays/contact-list";
@ -99,6 +97,7 @@ import PerformanceSettings from "./views/settings/performance";
import PrivacySettings from "./views/settings/privacy";
import PostSettings from "./views/settings/post";
import AccountSettings from "./views/settings/accounts";
import MediaServersView from "./views/settings/media-servers";
import ArticlesHomeView from "./views/articles";
import ArticleView from "./views/articles/article";
import WalletView from "./views/wallet";
@ -107,6 +106,7 @@ import UserMediaPostsTab from "./views/user/media-posts";
import NewView from "./views/new";
import NewNoteView from "./views/new/note";
import NewMediaPostView from "./views/new/media";
import ConnectionStatus from "./components/bakery/connection-status";
const TracksView = lazy(() => import("./views/tracks"));
const UserTracksTab = lazy(() => import("./views/user/tracks"));
const UserVideosTab = lazy(() => import("./views/user/videos"));
@ -159,23 +159,17 @@ const RequireBakery = lazy(() => import("./components/router/require-bakery"));
const BakerySetupView = lazy(() => import("./views/bakery/setup"));
const BakeryAuthView = lazy(() => import("./views/bakery/connect/auth"));
const RequireBakeryAuth = lazy(() => import("./components/router/require-bakery-auth"));
const DisplaySettingsView = lazy(() => import("./views/bakery/settings/tabs/display-settings"));
const NotificationSettingsView = lazy(() => import("./views/bakery/settings/tabs/notifications"));
const BakeryGeneralSettingsView = lazy(() => import("./views/bakery/settings/tabs/general-settings"));
const BakeryNetworkSettingsView = lazy(() => import("./views/bakery/settings/tabs/network"));
const BakeryServiceLogsView = lazy(() => import("./views/bakery/settings/tabs/service-logs"));
const NotificationSettingsView = lazy(() => import("./views/settings/bakery/notifications"));
const BakeryGeneralSettingsView = lazy(() => import("./views/settings/bakery/general-settings"));
const BakeryNetworkSettingsView = lazy(() => import("./views/settings/bakery/network"));
const BakeryServiceLogsView = lazy(() => import("./views/settings/bakery/service-logs"));
const RootPage = () => {
useSetColorMode();
return (
<RouteProviders>
<Layout>
<ScrollRestoration />
<Suspense fallback={<Spinner />}>
<Outlet />
</Suspense>
</Layout>
<AppLayout />
</RouteProviders>
);
};
@ -222,50 +216,6 @@ const router = createHashRouter([
},
],
},
{
path: "streams/moderation",
element: (
<RouteProviders>
<StreamModerationView />
</RouteProviders>
),
},
{
path: "streams/:naddr",
element: (
<RouteProviders>
<StreamView />
</RouteProviders>
),
},
{
path: "map",
element: <MapView />,
},
{
path: "launchpad",
element: (
<RouteProviders>
<LaunchpadView />
</RouteProviders>
),
},
{
path: "/discovery/relays",
element: (
<RouteProviders>
<RelayDiscoveryView />
</RouteProviders>
),
},
{
path: "/tools/publisher",
element: (
<RouteProviders>
<EventPublisherView />
</RouteProviders>
),
},
{
path: "/",
element: <RootPage />,
@ -283,6 +233,14 @@ const router = createHashRouter([
{ path: "media", element: <NewMediaPostView /> },
],
},
{
path: "launchpad",
element: <LaunchpadView />,
},
{
path: "map",
element: <MapView />,
},
{
path: "/u/:pubkey",
element: <UserView />,
@ -334,6 +292,22 @@ const router = createHashRouter([
{ path: "lightning", element: <LightningSettings /> },
{ path: "performance", element: <PerformanceSettings /> },
{ path: "media-servers", element: <MediaServersView /> },
{
path: "bakery",
children: [
{ path: "", element: <BakeryGeneralSettingsView /> },
{ path: "notifications", element: <NotificationSettingsView /> },
{
path: "network",
element: (
<RequireBakeryAuth>
<BakeryNetworkSettingsView />
</RequireBakeryAuth>
),
},
{ path: "logs", element: <BakeryServiceLogsView /> },
],
},
],
},
{
@ -420,6 +394,14 @@ const router = createHashRouter([
{ path: ":pointer", element: <MediaPostView /> },
],
},
{
path: "streams/moderation",
element: <StreamModerationView />,
},
{
path: "streams/:naddr",
element: <StreamView />,
},
{
path: "files",
children: [
@ -466,7 +448,7 @@ const router = createHashRouter([
},
{ path: "search", element: <SearchView /> },
{
path: "dm",
path: "messages",
element: <DirectMessagesView />,
children: [{ path: ":pubkey", element: <DirectMessageChatView /> }],
},
@ -483,8 +465,16 @@ const router = createHashRouter([
{ path: "console", element: <EventConsoleView /> },
{ path: "corrections", element: <CorrectionsFeedView /> },
{ path: "nostrudel-users", element: <NoStrudelUsersView /> },
{
path: "publisher",
element: <EventPublisherView />,
},
],
},
{
path: "/discovery/relays",
element: <RelayDiscoveryView />,
},
{
path: "lists",
children: [
@ -577,7 +567,7 @@ const router = createHashRouter([
path: "auth",
element: (
<RequireBakery>
{/* <ConnectionStatus /> */}
<ConnectionStatus />
<BakeryAuthView />
</RequireBakery>
),
@ -588,28 +578,6 @@ const router = createHashRouter([
path: "setup",
element: <BakerySetupView />,
},
// {
// path: "network",
// element: (
// <RequireBakery>
// <RequireBakeryAuth>
// <AppLayout />
// </RequireBakeryAuth>
// </RequireBakery>
// ),
// children: [
// {
// path: "",
// element: <NetworkView />,
// children: [
// {
// path: "",
// element: <OverviewList />,
// },
// ],
// },
// ],
// },
{
path: "",
element: (
@ -622,57 +590,10 @@ const router = createHashRouter([
</RequireBakery>
),
children: [
// {
// path: "messages",
// element: <MessagesView />,
// children: [
// {
// path: "p/:pubkey",
// element: <DirectMessageConversationView />,
// },
// ],
// },
// {
// path: "profile/:pubkey",
// element: <UserProfileView />,
// children: [
// { path: "", element: <UserSummaryView /> },
// { path: "summary", element: <UserSummaryView /> },
// { path: "messages", element: <DirectMessageConversationView /> },
// { path: "notes", element: <UserNotesView /> },
// { path: "articles", element: <UserArticlesView /> },
// ],
// },
{
path: "search",
element: <SearchView />,
},
{
path: "settings",
element: <SettingsView />,
children: [
{ path: "", element: <DisplaySettingsView /> },
{ path: "display", element: <DisplaySettingsView /> },
{ path: "notifications", element: <NotificationSettingsView /> },
{
path: "general",
element: (
<RequireBakeryAuth>
<BakeryGeneralSettingsView />
</RequireBakeryAuth>
),
},
{
path: "network",
element: (
<RequireBakeryAuth>
<BakeryNetworkSettingsView />
</RequireBakeryAuth>
),
},
{ path: "logs", element: <BakeryServiceLogsView /> },
],
},
{
path: "",
element: <HomeView />,

View File

@ -1,28 +1,20 @@
import { Flex } from "@chakra-ui/react";
import { Suspense } from "react";
import { Flex, Spinner } from "@chakra-ui/react";
import { Outlet, ScrollRestoration } from "react-router-dom";
import DesktopSideNav from "./side-nav";
import ConnectionStatus from "../connection-status";
import { ErrorBoundary } from "../../error-boundary";
export default function DesktopLayout() {
return (
<>
<ScrollRestoration />
<ConnectionStatus />
<Flex
direction={{
base: "column",
md: "row",
}}
overflow="hidden"
h="full"
>
<DesktopSideNav />
<DesktopSideNav />
<Suspense fallback={<Spinner />}>
<ErrorBoundary>
<Outlet />
</ErrorBoundary>
</Flex>
</Suspense>
</>
);
}

View File

@ -1,13 +1,28 @@
import { Flex, IconButton, Menu, MenuButton, MenuDivider, MenuItem, MenuList } from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom";
import { createContext, useState } from "react";
import {
Flex,
FlexProps,
IconButton,
Menu,
MenuButton,
MenuDivider,
MenuItem,
MenuList,
Spacer,
} from "@chakra-ui/react";
import UserAvatar from "../../user/user-avatar";
import useCurrentAccount from "../../../hooks/use-current-account";
import accountService from "../../../services/account";
import UserName from "../../user/user-name";
import UserDnsIdentity from "../../user/user-dns-identity";
import { DirectMessagesIcon, SettingsIcon, SearchIcon, RelayIcon } from "../../icons";
import Home05 from "../../icons/home-05";
import { ChevronLeftIcon, ChevronRightIcon, SettingsIcon } from "../../icons";
import Plus from "../../icons/plus";
import NavItem from "../nav-items/nav-item";
import NavItems from "../nav-items";
import useRootPadding from "../../../hooks/use-root-padding";
export const ExpandedContext = createContext(false);
function UserAccount() {
const account = useCurrentAccount()!;
@ -37,76 +52,49 @@ function UserAccount() {
);
}
export default function DesktopSideNav() {
export default function DesktopSideNav({ ...props }: Omit<FlexProps, "children">) {
const account = useCurrentAccount();
const [expanded, setExpanded] = useState(true);
useRootPadding({ left: expanded ? "var(--chakra-sizes-64)" : "var(--chakra-sizes-16)" });
return (
<Flex
direction="column"
gap="2"
px="2"
py="2"
shrink={0}
borderRightWidth={1}
pt="calc(var(--chakra-space-2) + var(--safe-top))"
pb="calc(var(--chakra-space-2) + var(--safe-bottom))"
>
{account && <UserAccount />}
<IconButton
as={RouterLink}
aria-label="Search"
title="Search"
icon={<Home05 boxSize={5} />}
w="12"
h="12"
fontSize="24"
variant="outline"
to="/"
/>
<IconButton
as={RouterLink}
aria-label="Search"
title="Search"
icon={<SearchIcon boxSize={5} />}
w="12"
h="12"
fontSize="24"
variant="outline"
to="/search"
/>
<IconButton
as={RouterLink}
aria-label="Messages"
title="Messages"
icon={<DirectMessagesIcon boxSize={5} />}
w="12"
h="12"
fontSize="24"
variant="outline"
to="/messages"
/>
<IconButton
w="12"
h="12"
as={RouterLink}
aria-label="Network"
title="Network"
icon={<RelayIcon boxSize={6} />}
variant="outline"
to="/network"
/>
<IconButton
as={RouterLink}
w="12"
h="12"
aria-label="Settings"
title="Settings"
mt="auto"
variant="outline"
icon={<SettingsIcon boxSize={5} />}
to="/settings"
/>
{/* {explore.isOpen && <ExploreCommunitiesModal isOpen onClose={explore.onClose} />} */}
</Flex>
<ExpandedContext.Provider value={expanded}>
<Flex
direction="column"
gap="2"
px="2"
py="2"
shrink={0}
borderRightWidth={1}
pt="calc(var(--chakra-space-2) + var(--safe-top))"
pb="calc(var(--chakra-space-2) + var(--safe-bottom))"
w={expanded ? "64" : "16"}
position="fixed"
left="0"
bottom="0"
top="0"
{...props}
>
<IconButton
aria-label={expanded ? "Close" : "Open"}
title={expanded ? "Close" : "Open"}
size="sm"
variant="ghost"
onClick={() => setExpanded(!expanded)}
icon={expanded ? <ChevronLeftIcon boxSize={5} /> : <ChevronRightIcon boxSize={5} />}
position="absolute"
bottom="4"
right="-4"
/>
{account && <UserAccount />}
<NavItem icon={Plus} label="Create new" colorScheme="primary" to="/new" variant="solid" />
<NavItems />
<Spacer />
<NavItem label="Settings" icon={SettingsIcon} to="/settings" />
</Flex>
</ExpandedContext.Provider>
);
}

View File

@ -1,29 +1,75 @@
import { Flex, IconButton } from "@chakra-ui/react";
import { Avatar, Flex, IconButton, useDisclosure } from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom";
import { DirectMessagesIcon, RelayIcon, SearchIcon } from "../../icons";
import Home05 from "../../icons/home-05";
import { DirectMessagesIcon, NotesIcon, NotificationsIcon, PlusCircleIcon, SearchIcon } from "../../icons";
import useRootPadding from "../../../hooks/use-root-padding";
import Rocket02 from "../../icons/rocket-02";
import UserAvatar from "../../user/user-avatar";
import useCurrentAccount from "../../../hooks/use-current-account";
import NavDrawer from "./nav-drawer";
export default function MobileBottomNav() {
useRootPadding({ bottom: "var(--chakra-sizes-14)" });
const account = useCurrentAccount();
const drawer = useDisclosure();
return (
<Flex
gap="2"
p="2"
borderTopWidth={1}
hideFrom="md"
bg="var(--chakra-colors-chakra-body-bg)"
mb="var(--safe-bottom)"
>
<IconButton as={RouterLink} to="/" icon={<Home05 boxSize={5} />} aria-label="Search" flex={1} />
<IconButton as={RouterLink} to="/search" icon={<SearchIcon boxSize={5} />} aria-label="Search" flex={1} />
<IconButton
as={RouterLink}
to="/messages"
icon={<DirectMessagesIcon boxSize={5} />}
aria-label="Messages"
flex={1}
/>
<IconButton as={RouterLink} to="/network" icon={<RelayIcon boxSize={6} />} aria-label="Network" flex={1} />
</Flex>
<>
<Flex
gap="2"
p="2"
borderTopWidth={1}
hideFrom="md"
bg="var(--chakra-colors-chakra-body-bg)"
mb="var(--safe-bottom)"
position="fixed"
bottom="0"
left="0"
right="0"
zIndex="modal"
>
{account ? (
<UserAvatar pubkey={account.pubkey} size="sm" onClick={drawer.onOpen} noProxy />
) : (
<Avatar size="sm" src="/apple-touch-icon.png" onClick={drawer.onOpen} cursor="pointer" />
)}
<IconButton as={RouterLink} icon={<NotesIcon boxSize={6} />} aria-label="Home" flexGrow="1" size="md" to="/" />
<IconButton
as={RouterLink}
icon={<SearchIcon boxSize={6} />}
aria-label="Search"
flexGrow="1"
size="md"
to="/search"
/>
<IconButton
as={RouterLink}
icon={<PlusCircleIcon boxSize={6} />}
aria-label="Create new"
title="Create new"
variant="solid"
colorScheme="primary"
to="/new"
/>
<IconButton
as={RouterLink}
icon={<DirectMessagesIcon boxSize={6} />}
aria-label="Messages"
flexGrow="1"
size="md"
to="/messages"
/>
<IconButton
as={RouterLink}
icon={<NotificationsIcon boxSize={6} />}
aria-label="Notifications"
flexGrow="1"
size="md"
to="/notifications"
/>
<IconButton as={RouterLink} icon={<Rocket02 boxSize={6} />} aria-label="Launchpad" to="/launchpad" />
</Flex>
<NavDrawer isOpen={drawer.isOpen} onClose={drawer.onClose} />
</>
);
}

View File

@ -2,23 +2,17 @@ import { BehaviorSubject } from "rxjs";
import { Outlet, ScrollRestoration } from "react-router-dom";
import { useObservable } from "applesauce-react/hooks";
import ConnectionStatus from "../connection-status";
import MobileBottomNav from "./bottom-nav";
import { ErrorBoundary } from "../../error-boundary";
export const showMobileNav = new BehaviorSubject(true);
export default function MobileLayout() {
const showNav = useObservable(showMobileNav);
return (
<>
<ScrollRestoration />
<ConnectionStatus />
<ErrorBoundary>
<Outlet />
</ErrorBoundary>
{showNav && <MobileBottomNav />}
<MobileBottomNav />
</>
);
}

View File

@ -0,0 +1,51 @@
import {
Avatar,
Box,
Button,
Drawer,
DrawerBody,
DrawerContent,
DrawerOverlay,
DrawerProps,
Flex,
Text,
} from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom";
import AccountSwitcher from "../../legacy-layout/account-switcher";
import useCurrentAccount from "../../../hooks/use-current-account";
import NavItems from "../nav-items";
import TaskManagerButtons from "../../legacy-layout/task-manager-buttons";
import { ExpandedContext } from "../desktop/side-nav";
export default function NavDrawer({ ...props }: Omit<DrawerProps, "children">) {
const account = useCurrentAccount();
return (
<Drawer placement="left" {...props}>
<DrawerOverlay />
<DrawerContent>
<ExpandedContext.Provider value={true}>
<DrawerBody display="flex" flexDirection="column" px="4" pt="4" overflowY="auto" overflowX="hidden" gap="2">
{account ? (
<AccountSwitcher />
) : (
<Flex gap="2" my="2" alignItems="center">
<Avatar src="/apple-touch-icon.png" size="md" />
<Text m={0}>Nostrudel</Text>
</Flex>
)}
<NavItems />
<Box h="2" />
{!account && (
<Button as={RouterLink} to="/signin" colorScheme="primary" flexShrink={0}>
Sign in
</Button>
)}
<TaskManagerButtons mt="auto" flexShrink={0} />
</DrawerBody>
</ExpandedContext.Provider>
</DrawerContent>
</Drawer>
);
}

View File

@ -0,0 +1,113 @@
import { useMemo } from "react";
import { ButtonProps } from "@chakra-ui/react";
import { useLocation } from "react-router-dom";
import { nip19 } from "nostr-tools";
import {
DirectMessagesIcon,
NotificationsIcon,
ProfileIcon,
RelayIcon,
SearchIcon,
NotesIcon,
LightningIcon,
} from "../../icons";
import useCurrentAccount from "../../../hooks/use-current-account";
import PuzzlePiece01 from "../../icons/puzzle-piece-01";
import Package from "../../icons/package";
import Rocket02 from "../../icons/rocket-02";
import { useBreakpointValue } from "../../../providers/global/breakpoint-provider";
import useRecentIds from "../../../hooks/use-recent-ids";
import { internalApps, internalTools } from "../../../views/other-stuff/apps";
import { App } from "../../../views/other-stuff/component/app-card";
import NavItem from "./nav-item";
import { QuestionIcon } from "@chakra-ui/icons";
export default function NavItems() {
const location = useLocation();
const account = useCurrentAccount();
const showShortcuts = useBreakpointValue({ base: false, md: true });
const buttonProps: ButtonProps = {
py: "2",
justifyContent: "flex-start",
variant: "link",
};
let active = "";
if (location.pathname.startsWith("/n/")) active = "notes";
else if (location.pathname === "/") active = "notes";
else if (location.pathname.startsWith("/notifications")) active = "notifications";
else if (location.pathname.startsWith("/launchpad")) active = "launchpad";
else if (location.pathname.startsWith("/discovery")) active = "discovery";
else if (location.pathname.startsWith("/wallet")) active = "wallet";
else if (location.pathname.startsWith("/dm")) active = "dm";
else if (location.pathname.startsWith("/streams")) active = "streams";
else if (location.pathname.startsWith("/relays")) active = "relays";
else if (location.pathname.startsWith("/r/")) active = "relays";
else if (location.pathname.startsWith("/lists")) active = "lists";
else if (location.pathname.startsWith("/channels")) active = "channels";
else if (location.pathname.startsWith("/goals")) active = "goals";
else if (location.pathname.startsWith("/badges")) active = "badges";
else if (location.pathname.startsWith("/emojis")) active = "emojis";
else if (location.pathname.startsWith("/settings")) active = "settings";
else if (location.pathname.startsWith("/tools")) active = "tools";
else if (location.pathname.startsWith("/search")) active = "search";
else if (location.pathname.startsWith("/tracks")) active = "tracks";
else if (location.pathname.startsWith("/t/")) active = "search";
else if (location.pathname.startsWith("/torrents")) active = "tools";
else if (location.pathname.startsWith("/map")) active = "tools";
else if (location.pathname.startsWith("/profile")) active = "profile";
else if (location.pathname.startsWith("/support")) active = "support";
else if (location.pathname.startsWith("/other-stuff")) active = "other-stuff";
else if (
account &&
(location.pathname.startsWith("/u/" + nip19.npubEncode(account.pubkey)) ||
location.pathname.startsWith("/u/" + account.pubkey))
) {
active = "profile";
}
const { recent: recentApps } = useRecentIds("apps");
const otherStuff = useMemo(() => {
const internal = [...internalApps, ...internalTools];
const apps = recentApps.map((id) => internal.find((app) => app.id === id)).filter(Boolean) as App[];
if (apps.length > 3) {
apps.length = 3;
} else {
if (apps.length < 3 && !apps.some((a) => a.id === "streams")) {
apps.push(internal.find((app) => app.id === "streams")!);
}
if (apps.length < 3 && !apps.some((a) => a.id === "articles")) {
apps.push(internal.find((app) => app.id === "articles")!);
}
if (apps.length < 3 && !apps.some((a) => a.id === "channels")) {
apps.push(internal.find((app) => app.id === "channels")!);
}
}
return apps;
}, [recentApps]);
return (
<>
<NavItem to="/launchpad" icon={Rocket02} label="Launchpad" />
<NavItem to="/" icon={NotesIcon} colorScheme={location.pathname === "/" ? "primary" : "gray"} label="Notes" />
<NavItem label="Discover" to="/discovery" icon={PuzzlePiece01} />
{account && (
<>
<NavItem to="/notifications" icon={NotificationsIcon} label="Notifications" />
<NavItem to="/messages" icon={DirectMessagesIcon} label="Messages" />
</>
)}
<NavItem to="/search" icon={SearchIcon} label="Search" />
{account?.pubkey && <NavItem to={"/u/" + nip19.npubEncode(account.pubkey)} icon={ProfileIcon} label="Profile" />}
<NavItem to="/relays" icon={RelayIcon} label="Relays" />
{otherStuff.map((app) => (
<NavItem key={app.id} to={app.to} icon={app.icon || QuestionIcon} label={app.title} />
))}
<NavItem to="/other-stuff" icon={Package} label="More" />
<NavItem to="/support" icon={LightningIcon} label="Support" />
</>
);
}

View File

@ -0,0 +1,52 @@
import { useContext } from "react";
import { Button, ComponentWithAs, IconButton, IconButtonProps, IconProps } from "@chakra-ui/react";
import { To, Link as RouterLink, useLocation } from "react-router-dom";
import { ExpandedContext } from "../desktop/side-nav";
export default function NavItem({
to,
icon: Icon,
label,
colorScheme,
variant,
}: {
to: string;
icon: ComponentWithAs<"svg", IconProps>;
label: string;
colorScheme?: IconButtonProps["colorScheme"];
variant?: IconButtonProps["variant"];
}) {
const expanded = useContext(ExpandedContext);
const location = useLocation();
if (expanded)
return (
<Button
as={RouterLink}
aria-label={label}
title={label}
leftIcon={<Icon boxSize={5} />}
variant={variant || "link"}
py="2"
justifyContent="flex-start"
colorScheme={colorScheme || location.pathname.startsWith(to) ? "primary" : undefined}
to={to}
>
{label}
</Button>
);
else
return (
<IconButton
as={RouterLink}
aria-label={label}
title={label}
icon={<Icon boxSize={5} />}
fontSize="24"
variant={variant || "ghost"}
to={to}
colorScheme={colorScheme || location.pathname.startsWith(to) ? "primary" : undefined}
/>
);
}

View File

@ -1,23 +1,23 @@
import { Button, ButtonProps } from '@chakra-ui/react';
import { useMatch, Link as RouterLink } from 'react-router-dom';
import { Button, ButtonProps } from "@chakra-ui/react";
import { useMatch, Link as RouterLink } from "react-router-dom";
export default function SimpleNavItem({
children,
to,
...props
}: Omit<ButtonProps, 'variant' | 'colorScheme'> & { to: string }) {
const match = useMatch(to);
children,
to,
...props
}: Omit<ButtonProps, "variant" | "colorScheme"> & { to: string }) {
const match = useMatch(to);
return (
<Button
as={RouterLink}
to={to}
justifyContent="flex-start"
{...props}
variant="ghost"
colorScheme={match ? 'brand' : undefined}
>
{children}
</Button>
);
return (
<Button
as={RouterLink}
to={to}
justifyContent="flex-start"
{...props}
variant="outline"
colorScheme={match ? "brand" : undefined}
>
{children}
</Button>
);
}

View File

@ -10,6 +10,7 @@ export default function SimpleView({
as,
flush,
gap,
maxW,
...props
}: FlexProps & { flush?: boolean; actions?: ReactNode }) {
return (
@ -24,6 +25,8 @@ export default function SimpleView({
pb={flush ? 0 : "max(1rem, var(--safe-bottom))"}
gap={gap || "2"}
flexGrow={1}
maxW={maxW}
w={maxW ? "full" : "initial"}
>
{children}
</Flex>

View File

@ -7,7 +7,7 @@ import { useObservable } from "applesauce-react/hooks";
import Plus from "../icons/plus";
import useCurrentAccount from "../../hooks/use-current-account";
import AccountSwitcher from "./account-switcher";
import NavItems from "./nav-items";
import NavItems from "../layout/nav-items";
import { PostModalContext } from "../../providers/route/post-modal-provider";
import { offlineMode } from "../../services/offline-mode";
import WifiOff from "../icons/wifi-off";

View File

@ -5,7 +5,7 @@ import { useLocation, Link as RouterLink } from "react-router-dom";
import useCurrentAccount from "../../hooks/use-current-account";
import { DirectMessagesIcon, NotesIcon, NotificationsIcon, PlusCircleIcon, SearchIcon } from "../icons";
import UserAvatar from "../user/user-avatar";
import MobileSideDrawer from "./mobile-side-drawer";
import MobileSideDrawer from "../layout/mobile/nav-drawer";
import Rocket02 from "../icons/rocket-02";
export default function MobileBottomNav(props: Omit<FlexProps, "children">) {

View File

@ -1,48 +0,0 @@
import {
Avatar,
Box,
Button,
Drawer,
DrawerBody,
DrawerContent,
DrawerOverlay,
DrawerProps,
Flex,
Text,
} from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom";
import AccountSwitcher from "./account-switcher";
import useCurrentAccount from "../../hooks/use-current-account";
import NavItems from "./nav-items";
import TaskManagerButtons from "./task-manager-buttons";
export default function MobileSideDrawer({ ...props }: Omit<DrawerProps, "children">) {
const account = useCurrentAccount();
return (
<Drawer placement="left" {...props}>
<DrawerOverlay />
<DrawerContent>
<DrawerBody display="flex" flexDirection="column" px="4" pt="4" overflowY="auto" overflowX="hidden" gap="2">
{account ? (
<AccountSwitcher />
) : (
<Flex gap="2" my="2" alignItems="center">
<Avatar src="/apple-touch-icon.png" size="md" />
<Text m={0}>Nostrudel</Text>
</Flex>
)}
<NavItems />
<Box h="2" />
{!account && (
<Button as={RouterLink} to="/signin" colorScheme="primary" flexShrink={0}>
Sign in
</Button>
)}
<TaskManagerButtons mt="auto" flexShrink={0} />
</DrawerBody>
</DrawerContent>
</Drawer>
);
}

View File

@ -1,238 +0,0 @@
import { useMemo } from "react";
import { Box, Button, ButtonProps, Text } from "@chakra-ui/react";
import { Link as RouterLink, useLocation } from "react-router-dom";
import { nip19 } from "nostr-tools";
import {
DirectMessagesIcon,
NotificationsIcon,
ProfileIcon,
RelayIcon,
SearchIcon,
SettingsIcon,
LogoutIcon,
NotesIcon,
LightningIcon,
} from "../icons";
import useCurrentAccount from "../../hooks/use-current-account";
import accountService from "../../services/account";
import PuzzlePiece01 from "../icons/puzzle-piece-01";
import Package from "../icons/package";
import Rocket02 from "../icons/rocket-02";
import { useBreakpointValue } from "../../providers/global/breakpoint-provider";
import KeyboardShortcut from "../keyboard-shortcut";
import useRecentIds from "../../hooks/use-recent-ids";
import { internalApps, internalTools } from "../../views/other-stuff/apps";
import { App, AppIcon } from "../../views/other-stuff/component/app-card";
export default function NavItems() {
const location = useLocation();
const account = useCurrentAccount();
const showShortcuts = useBreakpointValue({ base: false, md: true });
const buttonProps: ButtonProps = {
py: "2",
justifyContent: "flex-start",
variant: "link",
};
let active = "";
if (location.pathname.startsWith("/n/")) active = "notes";
else if (location.pathname === "/") active = "notes";
else if (location.pathname.startsWith("/notifications")) active = "notifications";
else if (location.pathname.startsWith("/launchpad")) active = "launchpad";
else if (location.pathname.startsWith("/discovery")) active = "discovery";
else if (location.pathname.startsWith("/wallet")) active = "wallet";
else if (location.pathname.startsWith("/dm")) active = "dm";
else if (location.pathname.startsWith("/streams")) active = "streams";
else if (location.pathname.startsWith("/relays")) active = "relays";
else if (location.pathname.startsWith("/r/")) active = "relays";
else if (location.pathname.startsWith("/lists")) active = "lists";
else if (location.pathname.startsWith("/channels")) active = "channels";
else if (location.pathname.startsWith("/goals")) active = "goals";
else if (location.pathname.startsWith("/badges")) active = "badges";
else if (location.pathname.startsWith("/emojis")) active = "emojis";
else if (location.pathname.startsWith("/settings")) active = "settings";
else if (location.pathname.startsWith("/tools")) active = "tools";
else if (location.pathname.startsWith("/search")) active = "search";
else if (location.pathname.startsWith("/tracks")) active = "tracks";
else if (location.pathname.startsWith("/t/")) active = "search";
else if (location.pathname.startsWith("/torrents")) active = "tools";
else if (location.pathname.startsWith("/map")) active = "tools";
else if (location.pathname.startsWith("/profile")) active = "profile";
else if (location.pathname.startsWith("/support")) active = "support";
else if (location.pathname.startsWith("/other-stuff")) active = "other-stuff";
else if (
account &&
(location.pathname.startsWith("/u/" + nip19.npubEncode(account.pubkey)) ||
location.pathname.startsWith("/u/" + account.pubkey))
) {
active = "profile";
}
const { recent: recentApps } = useRecentIds("apps");
const otherStuff = useMemo(() => {
const internal = [...internalApps, ...internalTools];
const apps = recentApps.map((id) => internal.find((app) => app.id === id)).filter(Boolean) as App[];
if (apps.length > 3) {
apps.length = 3;
} else {
if (apps.length < 3 && !apps.some((a) => a.id === "streams")) {
apps.push(internal.find((app) => app.id === "streams")!);
}
if (apps.length < 3 && !apps.some((a) => a.id === "articles")) {
apps.push(internal.find((app) => app.id === "articles")!);
}
if (apps.length < 3 && !apps.some((a) => a.id === "channels")) {
apps.push(internal.find((app) => app.id === "channels")!);
}
}
return apps;
}, [recentApps]);
return (
<>
<Button
as={RouterLink}
to="/launchpad"
leftIcon={<Rocket02 boxSize={6} />}
colorScheme={active === "launchpad" ? "primary" : undefined}
{...buttonProps}
>
Launchpad
{showShortcuts && <KeyboardShortcut letter="l" requireMeta ml="auto" />}
</Button>
<Button
as={RouterLink}
to="/"
leftIcon={<NotesIcon boxSize={6} />}
colorScheme={active === "notes" ? "primary" : undefined}
{...buttonProps}
>
Notes
</Button>
<Button
as={RouterLink}
to="/discovery"
leftIcon={<PuzzlePiece01 boxSize={6} />}
colorScheme={active === "discovery" ? "primary" : undefined}
{...buttonProps}
>
Discover
</Button>
{account && (
<>
<Button
as={RouterLink}
to="/notifications"
leftIcon={<NotificationsIcon boxSize={6} />}
colorScheme={active === "notifications" ? "primary" : undefined}
{...buttonProps}
>
Notifications
{showShortcuts && <KeyboardShortcut letter="i" requireMeta ml="auto" />}
</Button>
<Button
as={RouterLink}
to={"/dm"}
leftIcon={<DirectMessagesIcon boxSize={6} />}
colorScheme={active === "dm" ? "primary" : undefined}
{...buttonProps}
>
Messages
{showShortcuts && <KeyboardShortcut letter="m" requireMeta ml="auto" />}
</Button>
{/* <Button
as={RouterLink}
to="/wallet"
leftIcon={<Wallet02 boxSize={6} />}
colorScheme={active === "wallet" ? "primary" : undefined}
{...buttonProps}
>
Wallet
</Button> */}
</>
)}
<Button
as={RouterLink}
to="/search"
leftIcon={<SearchIcon boxSize={6} />}
colorScheme={active === "search" ? "primary" : undefined}
{...buttonProps}
>
Search
{showShortcuts && <KeyboardShortcut letter="k" requireMeta ml="auto" />}
</Button>
{account?.pubkey && (
<Button
as={RouterLink}
to={"/u/" + nip19.npubEncode(account.pubkey)}
leftIcon={<ProfileIcon boxSize={6} />}
colorScheme={active === "profile" ? "primary" : undefined}
{...buttonProps}
>
Profile
</Button>
)}
<Button
as={RouterLink}
to="/relays"
leftIcon={<RelayIcon boxSize={6} />}
colorScheme={active === "relays" ? "primary" : undefined}
{...buttonProps}
>
Relays
</Button>
<Text position="relative" py="2" color="GrayText">
Other Stuff
</Text>
{otherStuff.map((app) => (
<Button
key={app.id}
as={RouterLink}
to={app.to}
leftIcon={<AppIcon size="6" app={app} />}
colorScheme={typeof app.to === "string" && location.pathname.startsWith(app.to) ? "primary" : undefined}
{...buttonProps}
>
{app.title}
</Button>
))}
<Button
as={RouterLink}
to="/other-stuff"
leftIcon={<Package boxSize={6} />}
colorScheme={active === "other-stuff" ? "primary" : undefined}
{...buttonProps}
>
More
{showShortcuts && <KeyboardShortcut letter="o" requireMeta ml="auto" />}
</Button>
<Box h="4" />
<Button
as={RouterLink}
to="/settings"
leftIcon={<SettingsIcon boxSize={6} />}
colorScheme={active === "settings" ? "primary" : undefined}
{...buttonProps}
>
Settings
</Button>
<Button
as={RouterLink}
to="/support"
leftIcon={<LightningIcon boxSize={6} color="yellow.400" />}
colorScheme={active === "support" ? "primary" : undefined}
{...buttonProps}
>
Support
</Button>
{account && (
<Button onClick={() => accountService.logout()} leftIcon={<LogoutIcon boxSize={6} />} {...buttonProps}>
Logout
</Button>
)}
</>
);
}

View File

@ -1,23 +0,0 @@
import { Button, ButtonProps } from "@chakra-ui/react";
import { useMatch, Link as RouterLink } from "react-router-dom";
export default function SimpleNavItem({
children,
to,
...props
}: Omit<ButtonProps, "variant" | "colorScheme"> & { to: string }) {
const match = useMatch(to);
return (
<Button
as={RouterLink}
to={to}
justifyContent="flex-start"
{...props}
variant="outline"
colorScheme={match ? "primary" : undefined}
>
{children}
</Button>
);
}

View File

@ -1,9 +1,8 @@
import { ComponentWithAs, Flex, FlexProps } from "@chakra-ui/react";
/** @deprecated */
const VerticalPageLayout: ComponentWithAs<"div", FlexProps> = ({ children, ...props }: FlexProps) => {
return (
<Flex direction="column" pt="2" pb="12" gap="2" px="2" {...props}>
<Flex direction="column" pt="2" pb="12" gap="2" px="2" overflowX="hidden" {...props}>
{children}
</Flex>
);

View File

@ -0,0 +1,20 @@
import { useEffect } from "react";
export default function useRootPadding(padding?: { left?: string; top?: string; right?: string; bottom?: string }) {
useEffect(() => {
const root = document.getElementById("root");
if (!root) return;
if (padding?.top) root.style.paddingTop = padding.top;
if (padding?.left) root.style.paddingLeft = padding.left;
if (padding?.right) root.style.paddingRight = padding.right;
if (padding?.bottom) root.style.paddingBottom = padding.bottom;
return () => {
root.style.removeProperty("padding-top");
root.style.removeProperty("padding-left");
root.style.removeProperty("padding-right");
root.style.removeProperty("padding-bottom");
};
}, [padding?.left, padding?.top, padding?.right, padding?.bottom]);
}

View File

@ -1,14 +1,16 @@
html,
body,
#root {
body {
margin: 0;
height: 100%;
width: 100%;
}
#root {
width: 100%;
}
body,
#root {
overflow: hidden;
display: flex;
flex-direction: column;

View File

@ -1,46 +0,0 @@
import {
Button,
Flex,
FormControl,
FormHelperText,
FormLabel,
Select,
useColorMode,
useColorModePreference,
} from "@chakra-ui/react";
import SimpleView from "../../../../components/layout/presets/simple-view";
export default function DisplaySettingsView() {
const colorPreference = useColorModePreference();
const { colorMode, setColorMode } = useColorMode();
return (
<SimpleView title="Display Settings">
<FormControl maxW="sm">
<Flex justifyContent="space-between">
<FormLabel htmlFor="colorMode" mb="0">
Color Mode
</FormLabel>
{colorPreference && colorMode !== colorPreference && (
<Button
variant="link"
colorScheme="brand"
fontWeight="normal"
fontSize="sm"
ml="auto"
onClick={() => setColorMode(colorPreference)}
>
Use system default
</Button>
)}
</Flex>
<Select id="colorMode" maxW="sm" value={colorMode} onChange={(e) => setColorMode(e.target.value)}>
<option value="light">Light</option>
<option value="dark">Dark</option>
</Select>
<FormHelperText></FormHelperText>
</FormControl>
</SimpleView>
);
}

View File

@ -54,7 +54,7 @@ function ConversationCard({ conversation }: { conversation: KnownConversation })
{lastReceived && <MessagePreview message={lastReceived} pubkey={lastReceived.pubkey} />}
</Flex>
</CardBody>
<LinkOverlay as={RouterLink} to={`/dm/${nip19.npubEncode(conversation.correspondent)}` + location.search} />
<LinkOverlay as={RouterLink} to={`/messages/${nip19.npubEncode(conversation.correspondent)}` + location.search} />
</LinkBox>
);
}

View File

@ -36,7 +36,7 @@ function Conversation({ conversation }: { conversation: KnownConversation }) {
<UserAvatar pubkey={conversation.correspondent} />
<Flex direction="column" overflow="hidden">
<Flex gap="2">
<HoverLinkOverlay as={RouterLink} to={`/dm/${nip19.npubEncode(conversation.correspondent)}`}>
<HoverLinkOverlay as={RouterLink} to={`/messages/${nip19.npubEncode(conversation.correspondent)}`}>
<UserName pubkey={conversation.correspondent} />
</HoverLinkOverlay>
<UserDnsIdentity pubkey={conversation.correspondent} onlyIcon />

View File

@ -1,47 +1,18 @@
import { Button, Container, Flex, IconButton } from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom";
import { Container } from "@chakra-ui/react";
import VerticalPageLayout from "../../components/vertical-page-layout";
import RequireCurrentAccount from "../../components/router/require-current-account";
import AccountSwitcher from "../../components/legacy-layout/account-switcher";
import { SettingsIcon } from "../../components/icons";
import { ErrorBoundary } from "../../components/error-boundary";
import FeedsCard from "./components/feeds-card";
import SearchForm from "./components/search-form";
import KeyboardShortcut from "../../components/keyboard-shortcut";
import DMsCard from "./components/dms-card";
import NotificationsCard from "./components/notifications-card";
import ToolsCard from "./components/tools-card";
import StreamsCard from "./components/streams-card";
import Plus from "../../components/icons/plus";
function LaunchpadPage() {
return (
<VerticalPageLayout gap="4" direction="row" wrap="wrap">
<Flex justifyContent="space-between" w="full">
<Flex gap="2">
<AccountSwitcher />
<Button
as={RouterLink}
colorScheme="primary"
size="lg"
to="/new"
variant="outline"
leftIcon={<Plus boxSize={6} />}
>
New
<KeyboardShortcut letter="n" ml="2" />
</Button>
</Flex>
<IconButton
as={RouterLink}
icon={<SettingsIcon boxSize={6} />}
aria-label="Settings"
title="Settings"
size="lg"
to="/settings"
/>
</Flex>
<SearchForm flex={1} />
<ErrorBoundary>

View File

@ -10,7 +10,7 @@ export type App = {
description: string;
id: string;
isExternal?: boolean;
to: To;
to: string;
};
export function AppIcon({ app, size }: { app: App; size: string }) {

View File

@ -1,3 +1,4 @@
import { Suspense } from "react";
import { Outlet, useLocation } from "react-router-dom";
import { Flex, Spinner } from "@chakra-ui/react";
@ -13,8 +14,7 @@ import useUserContactRelays from "../../hooks/use-user-contact-relays";
import UserSquare from "../../components/icons/user-square";
import Image01 from "../../components/icons/image-01";
import Server05 from "../../components/icons/server-05";
import { Suspense } from "react";
import SimpleNavItem from "../../components/simple-nav-item";
import SimpleNavItem from "../../components/layout/presets/simple-nav-item";
export default function RelaysView() {
const account = useCurrentAccount();

View File

@ -38,6 +38,7 @@ export default function AccountSettings() {
return (
<SimpleView
title="Account settings"
maxW="6xl"
actions={
<Button
colorScheme="primary"

View File

@ -38,7 +38,7 @@ function NodeGeneralSettingsPage() {
return (
<SimpleView title="Node Settings">
<FormControl>
<FormLabel>Node URL</FormLabel>
<FormLabel>Bakery URL</FormLabel>
<Flex gap="2">
<Input readOnly value={personalNode!.url} maxW="xs" />
<Button isDisabled>Change</Button>

View File

@ -4,9 +4,9 @@ import { useForm } from "react-hook-form";
import { safeRelayUrl } from "applesauce-core/helpers";
import { useObservable } from "applesauce-react/hooks";
import useAsyncErrorHandler from "../../../../../hooks/use-async-error-handler";
import { controlApi } from "../../../../../services/bakery";
import { RelayFavicon } from "../../../../../components/relay-favicon";
import useAsyncErrorHandler from "../../../../hooks/use-async-error-handler";
import { controlApi } from "../../../../services/bakery";
import { RelayFavicon } from "../../../../components/relay-favicon";
function BroadcastRelay({ relay }: { relay: string }) {
const config = useObservable(controlApi?.config);

View File

@ -1,7 +1,7 @@
import { Alert, AlertIcon } from "@chakra-ui/react";
import useNetworkOverviewReport from "../../../../../hooks/reports/use-network-status-report";
import PanelItemString from "../../../../../components/dashboard/panel-item-string";
import useNetworkOverviewReport from "../../../../hooks/reports/use-network-status-report";
import PanelItemString from "../../../../components/dashboard/panel-item-string";
export default function HyperInboundStatus() {
const status = useNetworkOverviewReport();

View File

@ -2,8 +2,8 @@ import { ReactNode } from "react";
import { Alert, AlertIcon, FormControl, FormHelperText, Switch } from "@chakra-ui/react";
import { useObservable } from "applesauce-react/hooks";
import useNetworkOverviewReport from "../../../../../hooks/reports/use-network-status-report";
import { controlApi } from "../../../../../services/bakery";
import useNetworkOverviewReport from "../../../../hooks/reports/use-network-status-report";
import { controlApi } from "../../../../services/bakery";
export default function HyperOutboundStatus() {
const config = useObservable(controlApi?.config);

View File

@ -2,10 +2,10 @@ import { ReactNode } from "react";
import { Alert, Button, Code, Flex, Heading, Link, Spinner, Switch } from "@chakra-ui/react";
import { useObservable } from "applesauce-react/hooks";
import useNetworkOverviewReport from "../../../../../hooks/reports/use-network-status-report";
import useNetworkOverviewReport from "../../../../hooks/reports/use-network-status-report";
import HyperInboundStatus from "./hyper-inbound";
import HyperOutboundStatus from "./hyper-outbound";
import { controlApi } from "../../../../../services/bakery";
import { controlApi } from "../../../../services/bakery";
export default function HyperNetworkStatus() {
const config = useObservable(controlApi?.config);

View File

@ -1,7 +1,7 @@
import { Alert, AlertIcon, Spinner } from "@chakra-ui/react";
import useNetworkOverviewReport from "../../../../../hooks/reports/use-network-status-report";
import PanelItemString from "../../../../../components/dashboard/panel-item-string";
import useNetworkOverviewReport from "../../../../hooks/reports/use-network-status-report";
import PanelItemString from "../../../../components/dashboard/panel-item-string";
export default function I2PInboundStatus() {
const status = useNetworkOverviewReport();

View File

@ -2,8 +2,8 @@ import { ReactNode } from "react";
import { Alert, AlertIcon, FormControl, FormHelperText, Switch } from "@chakra-ui/react";
import { useObservable } from "applesauce-react/hooks";
import { controlApi } from "../../../../../services/bakery";
import useNetworkOverviewReport from "../../../../../hooks/reports/use-network-status-report";
import { controlApi } from "../../../../services/bakery";
import useNetworkOverviewReport from "../../../../hooks/reports/use-network-status-report";
export default function I2POutboundStatus() {
const config = useObservable(controlApi?.config);

View File

@ -4,8 +4,8 @@ import { useObservable } from "applesauce-react/hooks";
import I2POutboundStatus from "./i2p-outbound";
import I2PInboundStatus from "./i2p-inbound";
import { controlApi } from "../../../../../services/bakery";
import useNetworkOverviewReport from "../../../../../hooks/reports/use-network-status-report";
import { controlApi } from "../../../../services/bakery";
import useNetworkOverviewReport from "../../../../hooks/reports/use-network-status-report";
export default function I2PNetworkStatus() {
const config = useObservable(controlApi?.config);

View File

@ -4,7 +4,7 @@ import HyperNetworkStatus from "./hyper";
import TorNetworkStatus from "./tor";
import I2PNetworkStatus from "./i2p";
import GossipSettings from "./gossip";
import SimpleView from "../../../../../components/layout/presets/simple-view";
import SimpleView from "../../../../components/layout/presets/simple-view";
export default function BakeryNetworkSettingsView() {
return (

View File

@ -1,7 +1,7 @@
import { Alert, AlertIcon, Spinner } from "@chakra-ui/react";
import useNetworkOverviewReport from "../../../../../hooks/reports/use-network-status-report";
import PanelItemString from "../../../../../components/dashboard/panel-item-string";
import useNetworkOverviewReport from "../../../../hooks/reports/use-network-status-report";
import PanelItemString from "../../../../components/dashboard/panel-item-string";
export default function TorInboundStatus() {
const status = useNetworkOverviewReport();

View File

@ -2,8 +2,8 @@ import { ReactNode } from "react";
import { Alert, AlertIcon, FormControl, FormHelperText, Switch } from "@chakra-ui/react";
import { useObservable } from "applesauce-react/hooks";
import { controlApi } from "../../../../../services/bakery";
import useNetworkOverviewReport from "../../../../../hooks/reports/use-network-status-report";
import { controlApi } from "../../../../services/bakery";
import useNetworkOverviewReport from "../../../../hooks/reports/use-network-status-report";
export default function TorOutboundStatus() {
const config = useObservable(controlApi?.config);

View File

@ -4,8 +4,8 @@ import { useObservable } from "applesauce-react/hooks";
import TorOutboundStatus from "./tor-outbound";
import TorInboundStatus from "./tor-inbound";
import { controlApi } from "../../../../../services/bakery";
import useNetworkOverviewReport from "../../../../../hooks/reports/use-network-status-report";
import { controlApi } from "../../../../services/bakery";
import useNetworkOverviewReport from "../../../../hooks/reports/use-network-status-report";
export default function TorNetworkStatus() {
const config = useObservable(controlApi?.config);

View File

@ -6,9 +6,9 @@ import { useObservable } from "applesauce-react/hooks";
import NtfyNotificationSettings from "./ntfy";
import OtherSubscriptions from "./other";
import WebPushNotificationSettings from "./web-push";
import { controlApi } from "../../../../../services/bakery";
import SimpleView from "../../../../../components/layout/presets/simple-view";
import { CAP_IS_NATIVE, CAP_IS_WEB } from "../../../../../env";
import { controlApi } from "../../../../services/bakery";
import SimpleView from "../../../../components/layout/presets/simple-view";
import { CAP_IS_NATIVE, CAP_IS_WEB } from "../../../../env";
function EmailForm() {
const config = useObservable(controlApi?.config);

View File

@ -4,12 +4,12 @@ import { nanoid } from "nanoid";
import { kinds, NostrEvent } from "nostr-tools";
import { useObservable } from "applesauce-react/hooks";
import useCurrentAccount from "../../../../../hooks/use-current-account";
import bakery, { controlApi } from "../../../../../services/bakery";
import localSettings from "../../../../../services/local-settings";
import useNotificationChannelsReport from "../../../../../hooks/reports/use-notification-channels";
import { CopyIconButton } from "../../../../../components/copy-icon-button";
import { ExternalLinkIcon } from "../../../../../components/icons";
import useCurrentAccount from "../../../../hooks/use-current-account";
import bakery, { controlApi } from "../../../../services/bakery";
import localSettings from "../../../../services/local-settings";
import useNotificationChannelsReport from "../../../../hooks/reports/use-notification-channels";
import { CopyIconButton } from "../../../../components/copy-icon-button";
import { ExternalLinkIcon } from "../../../../components/icons";
export default function NtfyNotificationSettings() {
const account = useCurrentAccount();

View File

@ -2,8 +2,8 @@ import { ReactNode } from "react";
import { Badge, Button, Flex, Heading, Text } from "@chakra-ui/react";
import { NotificationChannel } from "@satellite-earth/core/types/control-api/notifications.js";
import { controlApi } from "../../../../../services/bakery";
import useNotificationChannelsReport from "../../../../../hooks/reports/use-notification-channels";
import { controlApi } from "../../../../services/bakery";
import useNotificationChannelsReport from "../../../../hooks/reports/use-notification-channels";
function Channel({ channel }: { channel: NotificationChannel }) {
let details: ReactNode = null;

View File

@ -2,13 +2,13 @@ import { useEffect, useState } from "react";
import { Alert, AlertIcon, Button, Code, Flex, Heading, Link, Text, useToast } from "@chakra-ui/react";
import { useObservable } from "applesauce-react/hooks";
import { serviceWorkerRegistration } from "../../../../../services/worker";
import { serviceWorkerRegistration } from "../../../../services/worker";
import {
disableNotifications,
enableNotifications,
pushSubscription,
} from "../../../../../services/web-push-notifications";
import { controlApi } from "../../../../../services/bakery";
} from "../../../../services/web-push-notifications";
import { controlApi } from "../../../../services/bakery";
function WebPushNotificationStatus() {
const toast = useToast();

View File

@ -15,10 +15,10 @@ import {
} from "@chakra-ui/react";
import Convert from "ansi-to-html";
import useLogsReport from "../../../../../hooks/reports/use-logs-report";
import Timestamp from "../../../../../components/timestamp";
import SimpleView from "../../../../../components/layout/presets/simple-view";
import { controlApi } from "../../../../../services/bakery";
import useLogsReport from "../../../../hooks/reports/use-logs-report";
import Timestamp from "../../../../components/timestamp";
import SimpleView from "../../../../components/layout/presets/simple-view";
import { controlApi } from "../../../../services/bakery";
import ServicesTree from "./service-tree";
const convert = new Convert();

View File

@ -1,6 +1,6 @@
import { Button, ButtonGroup, Flex, FlexProps, IconButton, useDisclosure } from "@chakra-ui/react";
import useServicesReport from "../../../../../hooks/reports/use-services-report";
import { ChevronDownIcon, ChevronRightIcon } from "../../../../../components/icons";
import useServicesReport from "../../../../hooks/reports/use-services-report";
import { ChevronDownIcon, ChevronRightIcon } from "../../../../components/icons";
type Service = {
id: string;

View File

@ -1,8 +1,7 @@
import { Divider, Flex, Heading, Link } from "@chakra-ui/react";
import { Divider, Flex, Link, Spinner, Text } from "@chakra-ui/react";
import { Outlet, useMatch } from "react-router-dom";
import { useBreakpointValue } from "../../providers/global/breakpoint-provider";
import SimpleNavItem from "../../components/simple-nav-item";
import { ErrorBoundary } from "../../components/error-boundary";
import {
AppearanceIcon,
@ -18,6 +17,12 @@ import Image01 from "../../components/icons/image-01";
import UserAvatar from "../../components/user/user-avatar";
import VersionButton from "../../components/version-button";
import SimpleHeader from "../../components/layout/presets/simple-header";
import bakery from "../../services/bakery";
import SimpleNavItem from "../../components/layout/presets/simple-nav-item";
import Bell01 from "../../components/icons/bell-01";
import Share07 from "../../components/icons/share-07";
import Database01 from "../../components/icons/database-01";
import { Suspense } from "react";
export default function SettingsView() {
const account = useCurrentAccount();
@ -62,6 +67,28 @@ export default function SettingsView() {
Database Tools
</SimpleNavItem>
{bakery && (
<Flex direction="column" gap="2">
<Flex alignItems="center" gap="2">
<Divider />
<Text fontWeight="bold" fontSize="md">
Bakery
</Text>
<Divider />
</Flex>
<SimpleNavItem to="/settings/bakery">Bakery</SimpleNavItem>
<SimpleNavItem to="/settings/bakery/notifications" leftIcon={<Bell01 boxSize={5} />}>
Notifications
</SimpleNavItem>
<SimpleNavItem to="/settings/bakery/network" leftIcon={<Share07 boxSize={5} />}>
Network
</SimpleNavItem>
<SimpleNavItem to="/settings/bakery/logs" leftIcon={<Database01 />}>
Service Logs
</SimpleNavItem>
</Flex>
)}
<Divider />
<Flex alignItems="center">
@ -73,17 +100,21 @@ export default function SettingsView() {
</Flex>
</Flex>
{!isMobile && (
<ErrorBoundary>
<Outlet />
</ErrorBoundary>
<Suspense fallback={<Spinner />}>
<ErrorBoundary>
<Outlet />
</ErrorBoundary>
</Suspense>
)}
</Flex>
);
}
return (
<ErrorBoundary>
<Outlet />
</ErrorBoundary>
<Suspense fallback={<Spinner />}>
<ErrorBoundary>
<Outlet />
</ErrorBoundary>
</Suspense>
);
}

View File

@ -105,7 +105,12 @@ function MediaServersPage() {
};
return (
<SimpleView gap="2" title="Media Servers" actions={event && <DebugEventButton event={event} size="sm" ml="auto" />}>
<SimpleView
gap="2"
title="Media Servers"
actions={event && <DebugEventButton event={event} size="sm" ml="auto" />}
maxW="4xl"
>
<Text fontStyle="italic" mt="-2">
<Link href="https://github.com/hzrd149/blossom" target="_blank" color="blue.500">
Blossom

View File

@ -61,6 +61,8 @@ function StreamsPage() {
const liveStreams = streams.filter((stream) => getStreamStatus(stream) === "live");
const endedStreams = streams.filter((stream) => getStreamStatus(stream) === "ended");
const columns = { base: 1, md: 2, lg: 3, xl: 4, "2xl": 5 };
return (
<VerticalPageLayout>
<Flex gap="2" wrap="wrap" alignItems="center">
@ -75,7 +77,7 @@ function StreamsPage() {
<Heading size="lg" mt="2">
Favorites
</Heading>
<SimpleGrid columns={{ base: 1, md: 2, lg: 3, xl: 4 }} spacing="2">
<SimpleGrid columns={columns} spacing="2">
{favorites.map((stream) => (
<StreamCard key={getEventUID(stream)} stream={stream} />
))}
@ -85,7 +87,7 @@ function StreamsPage() {
<Heading size="lg" mt="2">
Live
</Heading>
<SimpleGrid columns={{ base: 1, md: 2, lg: 3, xl: 4 }} spacing="2">
<SimpleGrid columns={columns} spacing="2">
{liveStreams.map((stream) => (
<StreamCard key={getEventUID(stream)} stream={stream} />
))}
@ -95,7 +97,7 @@ function StreamsPage() {
<Heading size="lg" mt="4">
Ended
</Heading>
<SimpleGrid columns={{ base: 1, md: 2, lg: 3, xl: 4 }} spacing="2">
<SimpleGrid columns={columns} spacing="2">
{endedStreams.map((stream) => (
<StreamCard key={getEventUID(stream)} stream={stream} />
))}

View File

@ -154,7 +154,7 @@ export default function UserAboutTab() {
size="sm"
icon={<ChatIcon />}
aria-label="Message"
to={`/dm/${npub ?? pubkey}`}
to={`/messages/${npub ?? pubkey}`}
/>
<UserFollowButton pubkey={pubkey} size="sm" showLists />
<UserProfileMenu pubkey={pubkey} aria-label="More Options" size="sm" />

View File

@ -52,7 +52,11 @@ export const UserProfileMenu = ({
{isMuted ? "Unmute User" : "Mute User"}
</MenuItem>
)}
<MenuItem icon={<DirectMessagesIcon fontSize="1.5em" />} as={RouterLink} to={`/dm/${nip19.npubEncode(pubkey)}`}>
<MenuItem
icon={<DirectMessagesIcon fontSize="1.5em" />}
as={RouterLink}
to={`/messages/${nip19.npubEncode(pubkey)}`}
>
Direct messages
</MenuItem>
<MenuItem