feat: account switcher

This commit is contained in:
reya 2024-02-24 14:41:57 +07:00
parent 84584a4d1f
commit 88a6c3c81f
14 changed files with 114 additions and 34 deletions

View File

@ -1,29 +1,65 @@
import { useArk } from "@lume/ark";
import { PlusIcon } from "@lume/icons";
import { Account } from "@lume/types";
import { User } from "@lume/ui";
import { Link, useNavigate, useParams } from "@tanstack/react-router";
import { useEffect, useState } from "react";
export function Accounts() {
const ark = useArk();
const params = useParams({ strict: false });
const [accounts, setAccounts] = useState<Account[]>(null);
useEffect(() => {
async function getAllAccounts() {
const data = await ark.get_all_accounts();
if (data) setAccounts(data);
}
getAllAccounts();
}, []);
return (
<div data-tauri-drag-region className="flex items-center gap-4">
{ark.accounts.map((account) =>
account.npub === ark.account.npub ? (
<Active pubkey={account.npub} />
<Link
to="/landing"
className="inline-flex size-7 items-center justify-center rounded-full bg-neutral-300 ring-offset-2 ring-offset-neutral-200 hover:ring-1 hover:ring-blue-500 dark:bg-neutral-700 dark:ring-offset-neutral-950"
>
<PlusIcon className="size-4" />
</Link>
{accounts
? accounts.map((account) =>
// @ts-ignore, useless
account.npub === params.account ? (
<Active key={account.npub} pubkey={account.npub} />
) : (
<Inactive pubkey={ark.account.npub} />
<Inactive key={account.npub} pubkey={account.npub} />
),
)}
)
: null}
</div>
);
}
function Inactive({ pubkey }: { pubkey: string }) {
const ark = useArk();
const navigate = useNavigate();
const changeAccount = async (npub: string) => {
const select = await ark.load_selected_account(npub);
if (select)
navigate({ to: "/$account/home/local", params: { account: npub } });
};
return (
<button type="button" onClick={() => changeAccount(pubkey)}>
<User.Provider pubkey={pubkey}>
<User.Root className="rounded-full ring-offset-2 ring-offset-neutral-200 hover:ring-1 hover:ring-blue-500 dark:ring-offset-neutral-950">
<User.Avatar className="aspect-square h-auto w-7 rounded-full object-cover" />
</User.Root>
</User.Provider>
</button>
);
}

View File

@ -7,13 +7,13 @@ import {
SpaceFilledIcon,
SpaceIcon,
} from "@lume/icons";
import { Link } from "@tanstack/react-router";
import { Link, useParams } 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";
export const Route = createFileRoute("/app")({
export const Route = createFileRoute("/$account")({
component: App,
});
@ -53,12 +53,15 @@ function App() {
}
function Navigation() {
// @ts-ignore, useless
const { account } = useParams({ strict: false });
return (
<div
data-tauri-drag-region
className="flex h-full flex-1 items-center gap-2"
>
<Link to="/app/home/local">
<Link to="/$account/home/local" params={{ account }}>
{({ isActive }) => (
<div
className={cn(
@ -75,7 +78,7 @@ function Navigation() {
</div>
)}
</Link>
<Link to="/app/space">
<Link to="/$account/space" params={{ account }}>
{({ isActive }) => (
<div
className={cn(
@ -92,7 +95,7 @@ function Navigation() {
</div>
)}
</Link>
<Link to="/app/activity">
<Link to="/$account/activity" params={{ account }}>
{({ isActive }) => (
<div
className={cn(

View File

@ -1,6 +1,6 @@
import { createLazyFileRoute } from "@tanstack/react-router";
export const Route = createLazyFileRoute("/app/activity")({
export const Route = createLazyFileRoute("/$account/activity")({
component: Activity,
});

View File

@ -1,18 +1,29 @@
import { cn } from "@lume/utils";
import { Link } from "@tanstack/react-router";
import { Outlet, createFileRoute } from "@tanstack/react-router";
import {
Outlet,
Link,
createFileRoute,
useParams,
} from "@tanstack/react-router";
export const Route = createFileRoute("/app/home")({
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="/app/home/local" className="h-11 flex-1 p-1">
<Link
to="/$account/home/local"
params={{ account }}
className="h-11 flex-1 p-1"
>
{({ isActive }) => (
<div
className={cn(
@ -26,7 +37,11 @@ function Home() {
</div>
)}
</Link>
<Link to="/app/home/global" className="h-11 flex-1 p-1">
<Link
to="/$account/home/global"
params={{ account }}
className="h-11 flex-1 p-1"
>
{({ isActive }) => (
<div
className={cn(

View File

@ -9,7 +9,7 @@ import { Virtualizer } from "virtua";
import { TextNote } from "./-components/text";
import { RepostNote } from "./-components/repost";
export const Route = createLazyFileRoute("/app/home/global")({
export const Route = createLazyFileRoute("/$account/home/global")({
component: GlobalTimeline,
});

View File

@ -9,15 +9,17 @@ import { Virtualizer } from "virtua";
import { TextNote } from "./-components/text";
import { RepostNote } from "./-components/repost";
export const Route = createLazyFileRoute("/app/home/local")({
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: ["events", "local"],
queryKey: ["newsfeed", account],
initialPageParam: 0,
queryFn: async ({ pageParam }: { pageParam: number }) => {
const events = await ark.get_events(
@ -38,6 +40,7 @@ function LocalTimeline() {
});
const renderItem = (event: Event) => {
if (!event) return;
switch (event.kind) {
case Kind.Repost:
return <RepostNote key={event.id} event={event} />;

View File

@ -1,6 +1,6 @@
import { createLazyFileRoute } from "@tanstack/react-router";
export const Route = createLazyFileRoute("/app/space")({
export const Route = createLazyFileRoute("/$account/space")({
component: Space,
});

View File

@ -26,7 +26,8 @@ function Create() {
try {
await ark.save_account(keys);
navigate({
to: "/app/home/local",
to: "/$account/home/local",
params: { account: keys.npub },
search: { onboarding: true },
replace: true,
});

View File

@ -32,7 +32,8 @@ function Import() {
nsec: key,
});
navigate({
to: "/app/home",
to: "/$account/home/local",
params: { account: npub },
search: { onboarding: true },
replace: true,
});

View File

@ -20,10 +20,12 @@ export const Route = createFileRoute("/")({
});
// Only 1 account, skip account selection screen
case 1:
const loadAccount = await ark.load_selected_account(accounts[0].npub);
const account = accounts[0].npub;
const loadAccount = await ark.load_selected_account(account);
if (loadAccount) {
throw redirect({
to: "/app/home/local",
to: "/$account/home/local",
params: { account },
search: {
redirect: location.href,
},
@ -48,7 +50,8 @@ function Screen() {
const loadAccount = await ark.load_selected_account(npub);
if (loadAccount) {
navigate({
to: "/app/home",
to: "/$account/home/local",
params: { account: npub },
replace: true,
});
}

View File

@ -47,3 +47,18 @@ default = ["custom-protocol"]
# this feature is used used for production builds where `devPath` points to the filesystem
# DO NOT remove this
custom-protocol = ["tauri/custom-protocol"]
[profile.dev.package."*"]
opt-level = 3
[profile.release]
codegen-units = 1
lto = true
panic = "abort"
incremental = false
opt-level = 3
strip = true
rpath = false
debug = false
debug-assertions = false
overflow-checks = false

View File

@ -26,8 +26,11 @@ pub async fn get_profile(id: &str, state: State<'_, Nostr>) -> Result<Metadata,
if let Ok(events) = query {
if let Some(event) = events.first() {
let metadata: Metadata = Metadata::from_json(&event.content).unwrap();
if let Ok(metadata) = Metadata::from_json(&event.content) {
Ok(metadata)
} else {
Err("Parse metadata failed".into())
}
} else {
let rand_metadata = Metadata::new();
Ok(rand_metadata)