update router

This commit is contained in:
hzrd149 2023-02-07 17:04:18 -06:00
parent 07f95cead0
commit 25c3c8ab5b
12 changed files with 172 additions and 192 deletions

View File

@ -1,14 +1,24 @@
import { Navigate, Route, Routes, useLocation } from "react-router-dom";
import React from "react";
import { createBrowserRouter, Navigate, Outlet, RouterProvider, useLocation } from "react-router-dom";
import { HomeView } from "./views/home";
import { UserPage } from "./views/user";
import { ErrorBoundary } from "./components/error-boundary";
import { Page } from "./components/page";
import { SettingsView } from "./views/settings";
import { LoginView } from "./views/login";
import { ProfileView } from "./views/profile";
import { EventPage } from "./views/event";
import useSubject from "./hooks/use-subject";
import identity from "./services/identity";
import { FollowingTab } from "./views/home/following-tab";
import { DiscoverTab } from "./views/home/discover-tab";
import { GlobalTab } from "./views/home/global-tab";
import { normalizeToHex } from "./helpers/nip-19";
import UserView from "./views/user";
import UserNotesTab from "./views/user/notes";
import UserRepliesTab from "./views/user/replies";
import UserFollowersTab from "./views/user/followers";
import UserRelaysTab from "./views/user/relays";
import UserFollowingTab from "./views/user/following";
import NoteView from "./views/note";
const RequireSetup = ({ children }: { children: JSX.Element }) => {
let location = useLocation();
@ -19,57 +29,72 @@ const RequireSetup = ({ children }: { children: JSX.Element }) => {
return children;
};
const HomePage = () => (
const RootPage = () => (
<RequireSetup>
<Page>
<HomeView />
<Outlet />
</Page>
</RequireSetup>
);
export const App = () => {
return (
<ErrorBoundary>
<Routes>
<Route path="/login" element={<LoginView />} />
<Route
path="/u/:pubkey"
element={
<RequireSetup>
<UserPage />
</RequireSetup>
}
/>
<Route
path="/n/:id"
element={
<RequireSetup>
<EventPage />
</RequireSetup>
}
/>
<Route
path="/settings"
element={
<RequireSetup>
<Page>
<SettingsView />
</Page>
</RequireSetup>
}
/>
<Route
path="/profile"
element={
<RequireSetup>
<Page>
<ProfileView />
</Page>
</RequireSetup>
}
/>
<Route path="/*" element={<HomePage />} />
</Routes>
</ErrorBoundary>
);
};
const router = createBrowserRouter([
{
path: "/",
element: <RootPage />,
children: [
{
path: "/u/:pubkey",
loader: ({ params }) => {
if (!params.pubkey) throw new Error("Missing pubkey");
const hexKey = normalizeToHex(params.pubkey);
if (!hexKey) throw new Error(params.pubkey + " is not a valid pubkey");
return { pubkey: hexKey };
},
element: <UserView />,
children: [
{ path: "", element: <UserNotesTab /> },
{ path: "notes", element: <UserNotesTab /> },
{ path: "replies", element: <UserRepliesTab /> },
{ path: "followers", element: <UserFollowersTab /> },
{ path: "following", element: <UserFollowingTab /> },
{ path: "relays", element: <UserRelaysTab /> },
],
},
{
path: "/n/:id",
loader: ({ params }) => {
if (!params.id) throw new Error("Missing pubkey");
const hex = normalizeToHex(params.id);
if (!hex) throw new Error(params.id + " is not a valid event id");
return { id: hex };
},
element: <NoteView />,
},
{
path: "settings",
element: <SettingsView />,
},
{
path: "profile",
element: <ProfileView />,
},
{ path: "login", element: <LoginView /> },
{
path: "",
element: <HomeView />,
children: [
{ path: "", element: <FollowingTab /> },
{ path: "following", element: <FollowingTab /> },
{ path: "discover", element: <DiscoverTab /> },
{ path: "global", element: <GlobalTab /> },
],
},
],
},
]);
export const App = () => (
<ErrorBoundary>
<RouterProvider router={router} />
</ErrorBoundary>
);

View File

@ -1,10 +1,9 @@
import React from "react";
import { ChakraProvider, localStorageManager } from "@chakra-ui/react";
import { BrowserRouter, HashRouter } from "react-router-dom";
import theme from "../theme";
export const Providers = ({ children }: { children: React.ReactNode }) => (
<ChakraProvider theme={theme} colorModeManager={localStorageManager}>
<HashRouter>{children}</HashRouter>
{children}
</ChakraProvider>
);

View File

@ -1,15 +1,17 @@
import { Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react";
// import { useMatch, useNavigate } from "react-router-dom";
import { DiscoverTab } from "./discover-tab";
import { FollowingTab } from "./following-tab";
import { GlobalTab } from "./global-tab";
import { Outlet, useMatches, useNavigate } from "react-router-dom";
const tabs = [
{ label: "Following", path: "/following" },
{ label: "Discover", path: "/discover" },
{ label: "Global", path: "/global" },
];
export const HomeView = () => {
// const navigate = useNavigate();
// const followingMatch = useMatch("/following");
// const discoverMatch = useMatch("/discover");
const navigate = useNavigate();
const matches = useMatches();
// const tabs = ["/following", "/discover", "/global"];
const activeTab = tabs.indexOf(tabs.find((t) => matches[matches.length - 1].pathname === t.path) ?? tabs[0]);
return (
<Tabs
@ -18,24 +20,20 @@ export const HomeView = () => {
flexGrow="1"
overflow="hidden"
isLazy
// index={discoverMatch ? 1 : 0}
// onChange={(v) => navigate(tabs[v])}
index={activeTab}
onChange={(v) => navigate(tabs[v].path)}
>
<TabList>
<Tab>Following</Tab>
<Tab>Discover</Tab>
<Tab>Global</Tab>
{tabs.map(({ label }) => (
<Tab>{label}</Tab>
))}
</TabList>
<TabPanels overflow="auto" height="100%">
<TabPanel pr={0} pl={0}>
<FollowingTab />
</TabPanel>
<TabPanel pr={0} pl={0}>
<DiscoverTab />
</TabPanel>
<TabPanel pr={0} pl={0}>
<GlobalTab />
</TabPanel>
{tabs.map(({ label }) => (
<TabPanel key={label} pr={0} pl={0}>
<Outlet />
</TabPanel>
))}
</TabPanels>
</Tabs>
);

View File

@ -1,42 +1,13 @@
import { Alert, AlertDescription, AlertIcon, AlertTitle, Flex, Spinner, Text } from "@chakra-ui/react";
import { Page } from "../../components/page";
import { useParams } from "react-router-dom";
import { normalizeToHex } from "../../helpers/nip-19";
import { Flex, Spinner } from "@chakra-ui/react";
import { useLoaderData } from "react-router-dom";
import { Note } from "../../components/note";
import { useThreadLoader } from "../../hooks/use-thread-loader";
import { ThreadPost } from "./thread-post";
export const EventPage = () => {
const params = useParams();
let id = normalizeToHex(params.id ?? "");
const NoteView = () => {
const { id } = useLoaderData() as { id: string };
if (!id) {
return (
<Page>
<Alert status="error">
<AlertIcon />
<AlertTitle>Invalid event id</AlertTitle>
<AlertDescription>"{params.id}" dose not look like a valid event id</AlertDescription>
</Alert>
</Page>
);
}
return (
<Page>
<EventView eventId={id} />
</Page>
);
};
export type EventViewProps = {
eventId: string;
};
export const EventView = ({ eventId }: EventViewProps) => {
const id = normalizeToHex(eventId) ?? "";
const { thread, events, rootId, focusId, loading } = useThreadLoader(id, { enabled: !!id });
if (loading) return <Spinner />;
let pageContent = <span>Missing Event</span>;
@ -76,3 +47,5 @@ export const EventView = ({ eventId }: EventViewProps) => {
</Flex>
);
};
export default NoteView;

View File

@ -1,10 +1,10 @@
import { Avatar, MenuItem } from "@chakra-ui/react";
import { MenuIconButton } from "../../components/menu-icon-button";
import { MenuIconButton } from "../../../components/menu-icon-button";
import { ClipboardIcon, IMAGE_ICONS } from "../../components/icons";
import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip-19";
import { ClipboardIcon, IMAGE_ICONS } from "../../../components/icons";
import { Bech32Prefix, normalizeToBech32 } from "../../../helpers/nip-19";
import { useCopyToClipboard } from "react-use";
import { truncatedId } from "../../helpers/nostr-event";
import { truncatedId } from "../../../helpers/nostr-event";
export const UserProfileMenu = ({ pubkey }: { pubkey: string }) => {
const [_clipboardState, copyToClipboard] = useCopyToClipboard();

View File

@ -2,8 +2,10 @@ import { Flex, FormControl, FormLabel, Grid, SkeletonText, Switch, useDisclosure
import { UserCard } from "./components/user-card";
import { useUserFollowers } from "../../hooks/use-user-followers";
import { useOutletContext } from "react-router-dom";
export const UserFollowersTab = ({ pubkey }: { pubkey: string }) => {
const UserFollowersTab = () => {
const { pubkey } = useOutletContext() as { pubkey: string };
const { isOpen, onToggle } = useDisclosure();
const followers = useUserFollowers(pubkey, [], isOpen);
@ -29,3 +31,5 @@ export const UserFollowersTab = ({ pubkey }: { pubkey: string }) => {
</Flex>
);
};
export default UserFollowersTab;

View File

@ -3,8 +3,10 @@ import { Flex, Grid, SkeletonText, Text } from "@chakra-ui/react";
import { UserCard } from "./components/user-card";
import { useUserContacts } from "../../hooks/use-user-contacts";
import { useOutletContext } from "react-router-dom";
export const UserFollowingTab = ({ pubkey }: { pubkey: string }) => {
const UserFollowingTab = () => {
const { pubkey } = useOutletContext() as { pubkey: string };
const contacts = useUserContacts(pubkey, [], true);
return (
@ -24,3 +26,5 @@ export const UserFollowingTab = ({ pubkey }: { pubkey: string }) => {
</Flex>
);
};
export default UserFollowingTab;

View File

@ -1,71 +1,37 @@
import {
Alert,
AlertDescription,
AlertIcon,
AlertTitle,
Flex,
Heading,
SkeletonText,
Tab,
TabList,
TabPanel,
TabPanels,
Tabs,
Text,
Box,
} from "@chakra-ui/react";
import { useParams } from "react-router-dom";
import { UserNotesTab } from "./notes";
import { Flex, Heading, SkeletonText, Tab, TabList, TabPanel, TabPanels, Tabs, Text, Box } from "@chakra-ui/react";
import { Outlet, useLoaderData, useMatches, useNavigate } from "react-router-dom";
import { useUserMetadata } from "../../hooks/use-user-metadata";
import { UserAvatar } from "../../components/user-avatar";
import { getUserDisplayName } from "../../helpers/user-metadata";
import { useIsMobile } from "../../hooks/use-is-mobile";
import { UserRelaysTab } from "./relays";
import { UserFollowingTab } from "./following";
import { normalizeToHex } from "../../helpers/nip-19";
import { Page } from "../../components/page";
import { UserProfileMenu } from "./user-profile-menu";
import { UserFollowersTab } from "./followers";
import { UserRepliesTab } from "./replies";
import { UserProfileMenu } from "./components/user-profile-menu";
export const UserPage = () => {
const params = useParams();
let id = normalizeToHex(params.pubkey ?? "");
const tabs = [
{ label: "Notes", path: "notes" },
{ label: "Replies", path: "replies" },
{ label: "Followers", path: "followers" },
{ label: "Following", path: "following" },
{ label: "Relays", path: "relays" },
];
if (!id) {
return (
<Page>
<Alert status="error">
<AlertIcon />
<AlertTitle>Invalid pubkey</AlertTitle>
<AlertDescription>"{params.pubkey}" dose not look like a valid pubkey</AlertDescription>
</Alert>
</Page>
);
}
return (
<Page>
<UserView pubkey={id} />
</Page>
);
};
export type UserViewProps = {
pubkey: string;
};
export const UserView = ({ pubkey }: UserViewProps) => {
const UserView = () => {
const isMobile = useIsMobile();
const navigate = useNavigate();
const { pubkey } = useLoaderData() as { pubkey: string };
const matches = useMatches();
const lastMatch = matches[matches.length - 1];
console.log(lastMatch);
const activeTab = tabs.indexOf(tabs.find((t) => lastMatch.pathname.includes(t.path)) ?? tabs[0]);
const metadata = useUserMetadata(pubkey, [], true);
const label = getUserDisplayName(metadata, pubkey);
const header = (
<Flex gap="4" padding="2">
<UserAvatar pubkey={pubkey} size={isMobile ? "md" : "xl"} />
<Flex direction="column" gap={isMobile ? 0 : 2}>
<Heading size={isMobile ? "md" : "lg"}>{label}</Heading>
<Heading size={isMobile ? "md" : "lg"}>{getUserDisplayName(metadata, pubkey)}</Heading>
{!metadata ? <SkeletonText /> : <Text>{metadata?.about}</Text>}
</Flex>
<Box ml="auto">
@ -78,33 +44,31 @@ export const UserView = ({ pubkey }: UserViewProps) => {
<Flex direction="column" alignItems="stretch" gap="2" overflow={isMobile ? "auto" : "hidden"} height="100%">
{/* {metadata?.banner && <Image src={metadata.banner} />} */}
{header}
<Tabs display="flex" flexDirection="column" flexGrow="1" overflow={isMobile ? undefined : "hidden"} isLazy>
<Tabs
display="flex"
flexDirection="column"
flexGrow="1"
overflow={isMobile ? undefined : "hidden"}
isLazy
index={activeTab}
onChange={(v) => navigate(tabs[v].path)}
>
<TabList overflow={isMobile ? "auto" : undefined}>
<Tab>Notes</Tab>
<Tab>Replies</Tab>
<Tab>Followers</Tab>
<Tab>Following</Tab>
<Tab>Relays</Tab>
{tabs.map(({ label }) => (
<Tab>{label}</Tab>
))}
</TabList>
<TabPanels overflow={isMobile ? undefined : "auto"} height="100%">
<TabPanel pr={0} pl={0}>
<UserNotesTab pubkey={pubkey} />
</TabPanel>
<TabPanel pr={0} pl={0}>
<UserRepliesTab pubkey={pubkey} />
</TabPanel>
<TabPanel>
<UserFollowersTab pubkey={pubkey} />
</TabPanel>
<TabPanel>
<UserFollowingTab pubkey={pubkey} />
</TabPanel>
<TabPanel pr={0} pl={0}>
<UserRelaysTab pubkey={pubkey} />
</TabPanel>
{tabs.map(({ label }) => (
<TabPanel key={label} pr={0} pl={0}>
<Outlet context={{ pubkey }} />
</TabPanel>
))}
</TabPanels>
</Tabs>
</Flex>
);
};
export default UserView;

View File

@ -1,10 +1,13 @@
import { Button, Flex, Spinner } from "@chakra-ui/react";
import moment from "moment";
import { useOutletContext } from "react-router-dom";
import { Note } from "../../components/note";
import { isNote } from "../../helpers/nostr-event";
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
export const UserNotesTab = ({ pubkey }: { pubkey: string }) => {
const UserNotesTab = () => {
const { pubkey } = useOutletContext() as { pubkey: string };
const { events, loading, loadMore } = useTimelineLoader(
`${pubkey} notes`,
{ authors: [pubkey], kinds: [1], since: moment().subtract(1, "day").unix() },
@ -21,3 +24,5 @@ export const UserNotesTab = ({ pubkey }: { pubkey: string }) => {
</Flex>
);
};
export default UserNotesTab;

View File

@ -3,8 +3,10 @@ import { Table, Thead, Tbody, Tr, Th, Td, TableContainer, Button, SkeletonText }
import settings from "../../services/settings";
import useSubject from "../../hooks/use-subject";
import { useUserContacts } from "../../hooks/use-user-contacts";
import { useOutletContext } from "react-router-dom";
export const UserRelaysTab = ({ pubkey }: { pubkey: string }) => {
const UserRelaysTab = () => {
const { pubkey } = useOutletContext() as { pubkey: string };
const contacts = useUserContacts(pubkey);
const relays = useSubject(settings.relays);
@ -44,3 +46,5 @@ export const UserRelaysTab = ({ pubkey }: { pubkey: string }) => {
</TableContainer>
);
};
export default UserRelaysTab;

View File

@ -1,10 +1,12 @@
import { Button, Flex, Spinner } from "@chakra-ui/react";
import moment from "moment";
import { useOutletContext } from "react-router-dom";
import { Note } from "../../components/note";
import { isReply } from "../../helpers/nostr-event";
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
export const UserRepliesTab = ({ pubkey }: { pubkey: string }) => {
const UserRepliesTab = () => {
const { pubkey } = useOutletContext() as { pubkey: string };
const { events, loading, loadMore } = useTimelineLoader(
`${pubkey} replies`,
{ authors: [pubkey], kinds: [1], since: moment().subtract(4, "hours").unix() },
@ -21,3 +23,5 @@ export const UserRepliesTab = ({ pubkey }: { pubkey: string }) => {
</Flex>
);
};
export default UserRepliesTab;