feat: improve
This commit is contained in:
@@ -1,17 +1,19 @@
|
||||
import {
|
||||
BellFilledIcon,
|
||||
BellIcon,
|
||||
EditIcon,
|
||||
ComposeFilledIcon,
|
||||
HomeFilledIcon,
|
||||
HomeIcon,
|
||||
HorizontalDotsIcon,
|
||||
SpaceFilledIcon,
|
||||
SpaceIcon,
|
||||
} from "@lume/icons";
|
||||
import { Link, useParams } from "@tanstack/react-router";
|
||||
import { Link } from "@tanstack/react-router";
|
||||
import { Outlet, createFileRoute } from "@tanstack/react-router";
|
||||
import { cn } from "@lume/utils";
|
||||
import { Accounts } from "@/components/accounts";
|
||||
import { useArk } from "@lume/ark";
|
||||
import { Box } from "@lume/ui";
|
||||
|
||||
export const Route = createFileRoute("/$account")({
|
||||
component: App,
|
||||
@@ -36,37 +38,37 @@ function App() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => ark.open_editor()}
|
||||
className="inline-flex h-7 w-max items-center justify-center gap-1 rounded-full bg-blue-500 px-2.5 text-sm font-medium text-white hover:bg-blue-600"
|
||||
className="inline-flex h-8 w-max items-center justify-center gap-1 rounded-full bg-blue-500 px-3 text-sm font-medium text-white hover:bg-blue-600"
|
||||
>
|
||||
<EditIcon className="size-4" />
|
||||
New
|
||||
<ComposeFilledIcon className="size-4" />
|
||||
New post
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex h-full min-h-0 w-full">
|
||||
<div className="h-full w-full flex-1 px-2 pb-2">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
<Box>
|
||||
<Outlet />
|
||||
</Box>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Navigation() {
|
||||
// @ts-ignore, useless
|
||||
const { account } = useParams({ strict: false });
|
||||
const { account } = Route.useParams();
|
||||
|
||||
return (
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className="flex h-full flex-1 items-center gap-2"
|
||||
>
|
||||
<Link to="/$account/home/local" params={{ account }}>
|
||||
<Link to="/$account/home" params={{ account }}>
|
||||
{({ isActive }) => (
|
||||
<div
|
||||
className={cn(
|
||||
"inline-flex h-9 w-max items-center justify-center gap-2 rounded-lg px-3 hover:bg-black/10 dark:hover:bg-white/10",
|
||||
isActive ? "bg-white shadow dark:bg-neutral-950" : "",
|
||||
"inline-flex h-8 w-max items-center justify-center gap-2 rounded-full px-3",
|
||||
isActive
|
||||
? "bg-neutral-300 hover:bg-neutral-400 dark:bg-neutral-800 dark:hover:bg-neutral-700"
|
||||
: "hover:bg-black/10 dark:hover:bg-white/10",
|
||||
)}
|
||||
>
|
||||
{isActive ? (
|
||||
@@ -82,8 +84,10 @@ function Navigation() {
|
||||
{({ isActive }) => (
|
||||
<div
|
||||
className={cn(
|
||||
"inline-flex h-9 w-max items-center justify-center gap-2 rounded-lg px-3 hover:bg-black/10 dark:hover:bg-white/10",
|
||||
isActive ? "bg-white shadow dark:bg-neutral-950" : "",
|
||||
"inline-flex h-8 w-max items-center justify-center gap-2 rounded-full px-3 hover:bg-black/10 dark:hover:bg-white/10",
|
||||
isActive
|
||||
? "bg-neutral-300 hover:bg-neutral-400 dark:bg-neutral-800 dark:hover:bg-neutral-700"
|
||||
: "hover:bg-black/10 dark:hover:bg-white/10",
|
||||
)}
|
||||
>
|
||||
{isActive ? (
|
||||
@@ -99,8 +103,10 @@ function Navigation() {
|
||||
{({ isActive }) => (
|
||||
<div
|
||||
className={cn(
|
||||
"inline-flex h-9 w-max items-center justify-center gap-2 rounded-lg px-3 hover:bg-black/10 dark:hover:bg-white/10",
|
||||
isActive ? "bg-white shadow dark:bg-neutral-950" : "",
|
||||
"inline-flex h-8 w-max items-center justify-center gap-2 rounded-full px-3 hover:bg-black/10 dark:hover:bg-white/10",
|
||||
isActive
|
||||
? "bg-neutral-300 hover:bg-neutral-400 dark:bg-neutral-800 dark:hover:bg-neutral-700"
|
||||
: "hover:bg-black/10 dark:hover:bg-white/10",
|
||||
)}
|
||||
>
|
||||
{isActive ? (
|
||||
|
||||
133
apps/desktop2/src/routes/$account/home.lazy.tsx
Normal file
133
apps/desktop2/src/routes/$account/home.lazy.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
import { RepostNote } from "@/components/repost";
|
||||
import { Suggest } from "@/components/suggest";
|
||||
import { TextNote } from "@/components/text";
|
||||
import { useArk } from "@lume/ark";
|
||||
import {
|
||||
LoaderIcon,
|
||||
ArrowRightCircleIcon,
|
||||
RefreshIcon,
|
||||
InfoIcon,
|
||||
} from "@lume/icons";
|
||||
import { Event, Kind } from "@lume/types";
|
||||
import { EmptyFeed } from "@lume/ui";
|
||||
import { FETCH_LIMIT } from "@lume/utils";
|
||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||
import { Virtualizer } from "virtua";
|
||||
|
||||
export const Route = createLazyFileRoute("/$account/home")({
|
||||
component: Home,
|
||||
});
|
||||
|
||||
function Home() {
|
||||
const ark = useArk();
|
||||
const currentDate = new Date().toLocaleString("default", {
|
||||
weekday: "long",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
});
|
||||
|
||||
const { account } = Route.useParams();
|
||||
const {
|
||||
data,
|
||||
hasNextPage,
|
||||
isLoading,
|
||||
isRefetching,
|
||||
isFetchingNextPage,
|
||||
fetchNextPage,
|
||||
refetch,
|
||||
} = useInfiniteQuery({
|
||||
queryKey: ["local_newsfeed", account],
|
||||
initialPageParam: 0,
|
||||
queryFn: async ({ pageParam }: { pageParam: number }) => {
|
||||
const events = await ark.get_events(
|
||||
"local",
|
||||
FETCH_LIMIT,
|
||||
pageParam,
|
||||
true,
|
||||
);
|
||||
return events;
|
||||
},
|
||||
getNextPageParam: (lastPage) => {
|
||||
const lastEvent = lastPage?.at(-1);
|
||||
if (!lastEvent) return;
|
||||
return lastEvent.created_at - 1;
|
||||
},
|
||||
select: (data) => data?.pages.flatMap((page) => page),
|
||||
refetchOnWindowFocus: false,
|
||||
});
|
||||
|
||||
const renderItem = (event: Event) => {
|
||||
if (!event) return;
|
||||
switch (event.kind) {
|
||||
case Kind.Repost:
|
||||
return <RepostNote key={event.id} event={event} />;
|
||||
default:
|
||||
return <TextNote key={event.id} event={event} />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="mx-auto flex h-12 w-full max-w-xl shrink-0 items-center justify-between border-b border-neutral-100 dark:border-neutral-900">
|
||||
<h3 className="text-sm font-medium uppercase leading-tight text-neutral-600 dark:text-neutral-400">
|
||||
{currentDate}
|
||||
</h3>
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => refetch()}
|
||||
className="text-neutral-700 hover:text-blue-500 dark:text-neutral-300"
|
||||
>
|
||||
<RefreshIcon className="size-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-auto flex w-full max-w-xl flex-1 flex-col">
|
||||
<div className="flex-1">
|
||||
{isLoading || isRefetching ? (
|
||||
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
|
||||
<LoaderIcon className="size-5 animate-spin" />
|
||||
</div>
|
||||
) : !data.length ? (
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-center gap-2 rounded-xl bg-neutral-100 p-3 dark:bg-neutral-900">
|
||||
<InfoIcon className="size-5" />
|
||||
<p>
|
||||
Empty newsfeed. Or you can go to{" "}
|
||||
<a href="" className="text-blue-500 hover:text-blue-600">
|
||||
Discover
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<Suggest />
|
||||
</div>
|
||||
) : (
|
||||
<Virtualizer overscan={3}>
|
||||
{data.map((item) => renderItem(item))}
|
||||
</Virtualizer>
|
||||
)}
|
||||
<div className="flex h-20 items-center justify-center">
|
||||
{hasNextPage ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => fetchNextPage()}
|
||||
disabled={!hasNextPage || isFetchingNextPage}
|
||||
className="inline-flex h-12 w-36 items-center justify-center gap-2 rounded-full bg-neutral-100 px-3 font-medium hover:bg-neutral-200 focus:outline-none dark:bg-neutral-900 dark:hover:bg-neutral-800"
|
||||
>
|
||||
{isFetchingNextPage ? (
|
||||
<LoaderIcon className="size-5 animate-spin" />
|
||||
) : (
|
||||
<>
|
||||
<ArrowRightCircleIcon className="size-5" />
|
||||
Load more
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import { cn } from "@lume/utils";
|
||||
import {
|
||||
Outlet,
|
||||
Link,
|
||||
createFileRoute,
|
||||
useParams,
|
||||
} from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/$account/home")({
|
||||
component: Home,
|
||||
});
|
||||
|
||||
function Home() {
|
||||
// @ts-ignore, useless
|
||||
const { account } = useParams({ strict: false });
|
||||
|
||||
return (
|
||||
<div className="h-full w-full overflow-hidden overflow-y-auto rounded-xl bg-white shadow-[rgba(50,_50,_105,_0.15)_0px_2px_5px_0px,_rgba(0,_0,_0,_0.05)_0px_1px_1px_0px] dark:bg-black dark:shadow-none dark:ring-1 dark:ring-white/5">
|
||||
<div className="mx-auto flex w-full max-w-xl flex-col">
|
||||
<div className="mx-auto flex h-28 w-1/2 items-center">
|
||||
<div className="flex h-11 w-full flex-1 items-center rounded-full bg-neutral-100 dark:bg-neutral-900">
|
||||
<Link
|
||||
to="/$account/home/local"
|
||||
params={{ account }}
|
||||
className="h-11 flex-1 p-1"
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<div
|
||||
className={cn(
|
||||
"inline-flex h-full w-full items-center justify-center rounded-full text-sm font-medium",
|
||||
isActive
|
||||
? "bg-white shadow shadow-neutral-500/20 dark:bg-black dark:shadow-none dark:ring-1 dark:ring-neutral-800"
|
||||
: "",
|
||||
)}
|
||||
>
|
||||
Local
|
||||
</div>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
to="/$account/home/global"
|
||||
params={{ account }}
|
||||
className="h-11 flex-1 p-1"
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<div
|
||||
className={cn(
|
||||
"inline-flex h-full w-full items-center justify-center rounded-full text-sm font-medium",
|
||||
isActive
|
||||
? "bg-white shadow shadow-neutral-500/20 dark:bg-black dark:shadow-none dark:ring-1 dark:ring-neutral-800"
|
||||
: "",
|
||||
)}
|
||||
>
|
||||
Global
|
||||
</div>
|
||||
)}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
import { useArk } from "@lume/ark";
|
||||
import { ArrowRightCircleIcon, LoaderIcon, SearchIcon } from "@lume/icons";
|
||||
import { Event, Kind } from "@lume/types";
|
||||
import { EmptyFeed } from "@lume/ui";
|
||||
import { FETCH_LIMIT } from "@lume/utils";
|
||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||
import { Virtualizer } from "virtua";
|
||||
import { TextNote } from "./-components/text";
|
||||
import { RepostNote } from "./-components/repost";
|
||||
|
||||
export const Route = createLazyFileRoute("/$account/home/global")({
|
||||
component: GlobalTimeline,
|
||||
});
|
||||
|
||||
function GlobalTimeline() {
|
||||
const ark = useArk();
|
||||
const { data, hasNextPage, isLoading, isFetchingNextPage, fetchNextPage } =
|
||||
useInfiniteQuery({
|
||||
queryKey: ["events", "global"],
|
||||
initialPageParam: 0,
|
||||
queryFn: async ({ pageParam }: { pageParam: number }) => {
|
||||
const events = await ark.get_events(
|
||||
"global",
|
||||
FETCH_LIMIT,
|
||||
pageParam,
|
||||
true,
|
||||
);
|
||||
return events;
|
||||
},
|
||||
getNextPageParam: (lastPage) => {
|
||||
const lastEvent = lastPage.at(-1);
|
||||
if (!lastEvent) return;
|
||||
return lastEvent.created_at - 1;
|
||||
},
|
||||
select: (data) => data?.pages.flatMap((page) => page),
|
||||
refetchOnWindowFocus: false,
|
||||
});
|
||||
|
||||
const renderItem = (event: Event) => {
|
||||
switch (event.kind) {
|
||||
case Kind.Repost:
|
||||
return <RepostNote key={event.id} event={event} />;
|
||||
default:
|
||||
return <TextNote key={event.id} event={event} />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isLoading ? (
|
||||
<div className="flex h-20 w-full items-center justify-center">
|
||||
<LoaderIcon className="size-5 animate-spin" />
|
||||
</div>
|
||||
) : !data.length ? (
|
||||
<div className="flex flex-col gap-3">
|
||||
<EmptyFeed />
|
||||
<a
|
||||
href="/suggest"
|
||||
className="inline-flex h-9 w-full items-center justify-center gap-2 rounded-lg bg-blue-500 text-sm font-medium text-white hover:bg-blue-600"
|
||||
>
|
||||
<SearchIcon className="size-5" />
|
||||
Find accounts to follow
|
||||
</a>
|
||||
</div>
|
||||
) : (
|
||||
<Virtualizer overscan={3}>
|
||||
{data.map((item) => renderItem(item))}
|
||||
</Virtualizer>
|
||||
)}
|
||||
<div className="flex h-20 items-center justify-center">
|
||||
{hasNextPage ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => fetchNextPage()}
|
||||
disabled={!hasNextPage || isFetchingNextPage}
|
||||
className="inline-flex h-12 w-36 items-center justify-center gap-2 rounded-full bg-neutral-100 px-3 font-medium hover:bg-neutral-200 focus:outline-none dark:bg-neutral-900 dark:hover:bg-neutral-800"
|
||||
>
|
||||
{isFetchingNextPage ? (
|
||||
<LoaderIcon className="size-5 animate-spin" />
|
||||
) : (
|
||||
<>
|
||||
<ArrowRightCircleIcon className="size-5" />
|
||||
Load more
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
import { useArk } from "@lume/ark";
|
||||
import { ArrowRightCircleIcon, ArrowRightIcon, LoaderIcon } from "@lume/icons";
|
||||
import { Event, Kind } from "@lume/types";
|
||||
import { EmptyFeed } from "@lume/ui";
|
||||
import { FETCH_LIMIT } from "@lume/utils";
|
||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||
import { Virtualizer } from "virtua";
|
||||
import { TextNote } from "@/components/text";
|
||||
import { RepostNote } from "@/components/repost";
|
||||
|
||||
export const Route = createLazyFileRoute("/$account/home/local")({
|
||||
component: LocalTimeline,
|
||||
});
|
||||
|
||||
function LocalTimeline() {
|
||||
const ark = useArk();
|
||||
|
||||
const { account } = Route.useParams();
|
||||
const { data, hasNextPage, isLoading, isFetchingNextPage, fetchNextPage } =
|
||||
useInfiniteQuery({
|
||||
queryKey: ["local_newsfeed", account],
|
||||
initialPageParam: 0,
|
||||
queryFn: async ({ pageParam }: { pageParam: number }) => {
|
||||
const events = await ark.get_events(
|
||||
"local",
|
||||
FETCH_LIMIT,
|
||||
pageParam,
|
||||
true,
|
||||
);
|
||||
return events;
|
||||
},
|
||||
getNextPageParam: (lastPage) => {
|
||||
const lastEvent = lastPage?.at(-1);
|
||||
if (!lastEvent) return;
|
||||
return lastEvent.created_at - 1;
|
||||
},
|
||||
select: (data) => data?.pages.flatMap((page) => page),
|
||||
refetchOnWindowFocus: false,
|
||||
});
|
||||
|
||||
const renderItem = (event: Event) => {
|
||||
if (!event) return;
|
||||
switch (event.kind) {
|
||||
case Kind.Repost:
|
||||
return <RepostNote key={event.id} event={event} />;
|
||||
default:
|
||||
return <TextNote key={event.id} event={event} />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isLoading ? (
|
||||
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
|
||||
<LoaderIcon className="size-5 animate-spin" />
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
) : !data.length ? (
|
||||
<div className="flex flex-col gap-3">
|
||||
<EmptyFeed />
|
||||
</div>
|
||||
) : (
|
||||
<Virtualizer overscan={3}>
|
||||
{data.map((item) => renderItem(item))}
|
||||
</Virtualizer>
|
||||
)}
|
||||
<div className="flex h-20 items-center justify-center">
|
||||
{hasNextPage ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => fetchNextPage()}
|
||||
disabled={!hasNextPage || isFetchingNextPage}
|
||||
className="inline-flex h-12 w-36 items-center justify-center gap-2 rounded-full bg-neutral-100 px-3 font-medium hover:bg-neutral-200 focus:outline-none dark:bg-neutral-900 dark:hover:bg-neutral-800"
|
||||
>
|
||||
{isFetchingNextPage ? (
|
||||
<LoaderIcon className="size-5 animate-spin" />
|
||||
) : (
|
||||
<>
|
||||
<ArrowRightCircleIcon className="size-5" />
|
||||
Load more
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -26,7 +26,7 @@ function Create() {
|
||||
try {
|
||||
await ark.save_account(keys);
|
||||
navigate({
|
||||
to: "/$account/home/local",
|
||||
to: "/$account/home",
|
||||
params: { account: keys.npub },
|
||||
search: { onboarding: true },
|
||||
replace: true,
|
||||
|
||||
@@ -32,7 +32,7 @@ function Import() {
|
||||
nsec: key,
|
||||
});
|
||||
navigate({
|
||||
to: "/$account/home/local",
|
||||
to: "/$account/home",
|
||||
params: { account: npub },
|
||||
search: { onboarding: true },
|
||||
replace: true,
|
||||
|
||||
20
apps/desktop2/src/routes/backup.lazy.tsx
Normal file
20
apps/desktop2/src/routes/backup.lazy.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export const Route = createLazyFileRoute("/backup")({
|
||||
component: Screen,
|
||||
});
|
||||
|
||||
function Screen() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<div className="mx-auto flex w-full max-w-md flex-col gap-8">
|
||||
<div className="flex flex-col items-center gap-1 text-center">
|
||||
<h1 className="text-2xl font-semibold">{t("backup.title")}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -31,7 +31,7 @@ function Event() {
|
||||
return (
|
||||
<WindowVirtualizer>
|
||||
<Container withDrag>
|
||||
<Box>
|
||||
<Box className="px-3 pt-3">
|
||||
<MainNote data={data} />
|
||||
{data ? <ReplyList eventId={eventId} /> : null}
|
||||
</Box>
|
||||
|
||||
@@ -11,13 +11,14 @@ export const Route = createFileRoute("/")({
|
||||
const accounts = await ark.get_all_accounts();
|
||||
|
||||
switch (accounts.length) {
|
||||
// Empty account
|
||||
// Guest account
|
||||
case 0:
|
||||
const guest = await ark.create_guest_account();
|
||||
throw redirect({
|
||||
to: "/landing",
|
||||
search: {
|
||||
redirect: location.href,
|
||||
},
|
||||
to: "/$account/home",
|
||||
params: { account: guest },
|
||||
search: { guest: true },
|
||||
replace: true,
|
||||
});
|
||||
// Only 1 account, skip account selection screen
|
||||
case 1:
|
||||
@@ -25,11 +26,9 @@ export const Route = createFileRoute("/")({
|
||||
const loadAccount = await ark.load_selected_account(account);
|
||||
if (loadAccount) {
|
||||
throw redirect({
|
||||
to: "/$account/home/local",
|
||||
to: "/$account/home",
|
||||
params: { account },
|
||||
search: {
|
||||
redirect: location.href,
|
||||
},
|
||||
replace: true,
|
||||
});
|
||||
}
|
||||
// Account selection
|
||||
@@ -51,24 +50,24 @@ function Screen() {
|
||||
const loadAccount = await ark.load_selected_account(npub);
|
||||
if (loadAccount) {
|
||||
navigate({
|
||||
to: "/$account/home/local",
|
||||
to: "/$account/home",
|
||||
params: { account: npub },
|
||||
replace: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const weekday = new Date().toLocaleString("default", { weekday: "long" });
|
||||
const day = new Date().getDate();
|
||||
const month = new Date()
|
||||
.toLocaleString("default", { month: "long" })
|
||||
.toString();
|
||||
const currentDate = new Date().toLocaleString("default", {
|
||||
weekday: "long",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="relative flex h-full w-full items-center justify-center">
|
||||
<div className="relative z-20 flex flex-col items-center gap-16">
|
||||
<div className="text-center text-white">
|
||||
<h2 className="mb-1 text-2xl">{`${weekday}, ${month} ${day}`}</h2>
|
||||
<h2 className="mb-1 text-2xl">{currentDate}</h2>
|
||||
<h2 className="text-2xl font-semibold">Welcome back!</h2>
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-6">
|
||||
|
||||
14
apps/desktop2/src/routes/login.tsx
Normal file
14
apps/desktop2/src/routes/login.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Outlet, createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/login")({
|
||||
component: Screen,
|
||||
});
|
||||
|
||||
function Screen() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Login</h1>
|
||||
<Outlet />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
9
apps/desktop2/src/routes/settings/index.lazy.tsx
Normal file
9
apps/desktop2/src/routes/settings/index.lazy.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createLazyFileRoute("/settings/")({
|
||||
component: Screen,
|
||||
});
|
||||
|
||||
function Screen() {
|
||||
return <div>Settings</div>;
|
||||
}
|
||||
Reference in New Issue
Block a user