mirror of
https://github.com/lumehq/lume.git
synced 2025-04-06 02:48:36 +02:00
feat: account switcher
This commit is contained in:
parent
84584a4d1f
commit
88a6c3c81f
@ -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} />
|
||||
) : (
|
||||
<Inactive pubkey={ark.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 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 (
|
||||
<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 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>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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(
|
@ -1,6 +1,6 @@
|
||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createLazyFileRoute("/app/activity")({
|
||||
export const Route = createLazyFileRoute("/$account/activity")({
|
||||
component: Activity,
|
||||
});
|
||||
|
@ -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(
|
@ -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,
|
||||
});
|
||||
|
@ -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} />;
|
@ -1,6 +1,6 @@
|
||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createLazyFileRoute("/app/space")({
|
||||
export const Route = createLazyFileRoute("/$account/space")({
|
||||
component: Space,
|
||||
});
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -32,7 +32,8 @@ function Import() {
|
||||
nsec: key,
|
||||
});
|
||||
navigate({
|
||||
to: "/app/home",
|
||||
to: "/$account/home/local",
|
||||
params: { account: npub },
|
||||
search: { onboarding: true },
|
||||
replace: true,
|
||||
});
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
Ok(metadata)
|
||||
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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user