mirror of
https://github.com/luminous-devs/lume.git
synced 2024-10-01 09:21:07 +00:00
feat: account switcher
This commit is contained in:
parent
84584a4d1f
commit
88a6c3c81f
@ -1,29 +1,65 @@
|
|||||||
import { useArk } from "@lume/ark";
|
import { useArk } from "@lume/ark";
|
||||||
|
import { PlusIcon } from "@lume/icons";
|
||||||
|
import { Account } from "@lume/types";
|
||||||
import { User } from "@lume/ui";
|
import { User } from "@lume/ui";
|
||||||
|
import { Link, useNavigate, useParams } from "@tanstack/react-router";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
export function Accounts() {
|
export function Accounts() {
|
||||||
const ark = useArk();
|
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 (
|
return (
|
||||||
<div data-tauri-drag-region className="flex items-center gap-4">
|
<div data-tauri-drag-region className="flex items-center gap-4">
|
||||||
{ark.accounts.map((account) =>
|
<Link
|
||||||
account.npub === ark.account.npub ? (
|
to="/landing"
|
||||||
<Active pubkey={account.npub} />
|
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"
|
||||||
) : (
|
>
|
||||||
<Inactive pubkey={ark.account.npub} />
|
<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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Inactive({ pubkey }: { pubkey: string }) {
|
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 (
|
return (
|
||||||
<User.Provider pubkey={pubkey}>
|
<button type="button" onClick={() => changeAccount(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.Provider pubkey={pubkey}>
|
||||||
<User.Avatar className="aspect-square h-auto w-7 rounded-full object-cover" />
|
<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.Root>
|
<User.Avatar className="aspect-square h-auto w-7 rounded-full object-cover" />
|
||||||
</User.Provider>
|
</User.Root>
|
||||||
|
</User.Provider>
|
||||||
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,13 +7,13 @@ import {
|
|||||||
SpaceFilledIcon,
|
SpaceFilledIcon,
|
||||||
SpaceIcon,
|
SpaceIcon,
|
||||||
} from "@lume/icons";
|
} from "@lume/icons";
|
||||||
import { Link } from "@tanstack/react-router";
|
import { Link, useParams } from "@tanstack/react-router";
|
||||||
import { Outlet, createFileRoute } from "@tanstack/react-router";
|
import { Outlet, createFileRoute } from "@tanstack/react-router";
|
||||||
import { cn } from "@lume/utils";
|
import { cn } from "@lume/utils";
|
||||||
import { Accounts } from "@/components/accounts";
|
import { Accounts } from "@/components/accounts";
|
||||||
import { useArk } from "@lume/ark";
|
import { useArk } from "@lume/ark";
|
||||||
|
|
||||||
export const Route = createFileRoute("/app")({
|
export const Route = createFileRoute("/$account")({
|
||||||
component: App,
|
component: App,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -53,12 +53,15 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Navigation() {
|
function Navigation() {
|
||||||
|
// @ts-ignore, useless
|
||||||
|
const { account } = useParams({ strict: false });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-tauri-drag-region
|
data-tauri-drag-region
|
||||||
className="flex h-full flex-1 items-center gap-2"
|
className="flex h-full flex-1 items-center gap-2"
|
||||||
>
|
>
|
||||||
<Link to="/app/home/local">
|
<Link to="/$account/home/local" params={{ account }}>
|
||||||
{({ isActive }) => (
|
{({ isActive }) => (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
@ -75,7 +78,7 @@ function Navigation() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
<Link to="/app/space">
|
<Link to="/$account/space" params={{ account }}>
|
||||||
{({ isActive }) => (
|
{({ isActive }) => (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
@ -92,7 +95,7 @@ function Navigation() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
<Link to="/app/activity">
|
<Link to="/$account/activity" params={{ account }}>
|
||||||
{({ isActive }) => (
|
{({ isActive }) => (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
@ -1,6 +1,6 @@
|
|||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/app/activity")({
|
export const Route = createLazyFileRoute("/$account/activity")({
|
||||||
component: Activity,
|
component: Activity,
|
||||||
});
|
});
|
||||||
|
|
@ -1,18 +1,29 @@
|
|||||||
import { cn } from "@lume/utils";
|
import { cn } from "@lume/utils";
|
||||||
import { Link } from "@tanstack/react-router";
|
import {
|
||||||
import { Outlet, createFileRoute } from "@tanstack/react-router";
|
Outlet,
|
||||||
|
Link,
|
||||||
|
createFileRoute,
|
||||||
|
useParams,
|
||||||
|
} from "@tanstack/react-router";
|
||||||
|
|
||||||
export const Route = createFileRoute("/app/home")({
|
export const Route = createFileRoute("/$account/home")({
|
||||||
component: Home,
|
component: Home,
|
||||||
});
|
});
|
||||||
|
|
||||||
function Home() {
|
function Home() {
|
||||||
|
// @ts-ignore, useless
|
||||||
|
const { account } = useParams({ strict: false });
|
||||||
|
|
||||||
return (
|
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="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 w-full max-w-xl flex-col">
|
||||||
<div className="mx-auto flex h-28 w-1/2 items-center">
|
<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">
|
<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 }) => (
|
{({ isActive }) => (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
@ -26,7 +37,11 @@ function Home() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Link>
|
</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 }) => (
|
{({ isActive }) => (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
@ -9,7 +9,7 @@ import { Virtualizer } from "virtua";
|
|||||||
import { TextNote } from "./-components/text";
|
import { TextNote } from "./-components/text";
|
||||||
import { RepostNote } from "./-components/repost";
|
import { RepostNote } from "./-components/repost";
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/app/home/global")({
|
export const Route = createLazyFileRoute("/$account/home/global")({
|
||||||
component: GlobalTimeline,
|
component: GlobalTimeline,
|
||||||
});
|
});
|
||||||
|
|
@ -9,15 +9,17 @@ import { Virtualizer } from "virtua";
|
|||||||
import { TextNote } from "./-components/text";
|
import { TextNote } from "./-components/text";
|
||||||
import { RepostNote } from "./-components/repost";
|
import { RepostNote } from "./-components/repost";
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/app/home/local")({
|
export const Route = createLazyFileRoute("/$account/home/local")({
|
||||||
component: LocalTimeline,
|
component: LocalTimeline,
|
||||||
});
|
});
|
||||||
|
|
||||||
function LocalTimeline() {
|
function LocalTimeline() {
|
||||||
const ark = useArk();
|
const ark = useArk();
|
||||||
|
|
||||||
|
const { account } = Route.useParams();
|
||||||
const { data, hasNextPage, isLoading, isFetchingNextPage, fetchNextPage } =
|
const { data, hasNextPage, isLoading, isFetchingNextPage, fetchNextPage } =
|
||||||
useInfiniteQuery({
|
useInfiniteQuery({
|
||||||
queryKey: ["events", "local"],
|
queryKey: ["newsfeed", account],
|
||||||
initialPageParam: 0,
|
initialPageParam: 0,
|
||||||
queryFn: async ({ pageParam }: { pageParam: number }) => {
|
queryFn: async ({ pageParam }: { pageParam: number }) => {
|
||||||
const events = await ark.get_events(
|
const events = await ark.get_events(
|
||||||
@ -38,6 +40,7 @@ function LocalTimeline() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const renderItem = (event: Event) => {
|
const renderItem = (event: Event) => {
|
||||||
|
if (!event) return;
|
||||||
switch (event.kind) {
|
switch (event.kind) {
|
||||||
case Kind.Repost:
|
case Kind.Repost:
|
||||||
return <RepostNote key={event.id} event={event} />;
|
return <RepostNote key={event.id} event={event} />;
|
@ -1,6 +1,6 @@
|
|||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/app/space")({
|
export const Route = createLazyFileRoute("/$account/space")({
|
||||||
component: Space,
|
component: Space,
|
||||||
});
|
});
|
||||||
|
|
@ -26,7 +26,8 @@ function Create() {
|
|||||||
try {
|
try {
|
||||||
await ark.save_account(keys);
|
await ark.save_account(keys);
|
||||||
navigate({
|
navigate({
|
||||||
to: "/app/home/local",
|
to: "/$account/home/local",
|
||||||
|
params: { account: keys.npub },
|
||||||
search: { onboarding: true },
|
search: { onboarding: true },
|
||||||
replace: true,
|
replace: true,
|
||||||
});
|
});
|
||||||
|
@ -32,7 +32,8 @@ function Import() {
|
|||||||
nsec: key,
|
nsec: key,
|
||||||
});
|
});
|
||||||
navigate({
|
navigate({
|
||||||
to: "/app/home",
|
to: "/$account/home/local",
|
||||||
|
params: { account: npub },
|
||||||
search: { onboarding: true },
|
search: { onboarding: true },
|
||||||
replace: true,
|
replace: true,
|
||||||
});
|
});
|
||||||
|
@ -20,10 +20,12 @@ export const Route = createFileRoute("/")({
|
|||||||
});
|
});
|
||||||
// Only 1 account, skip account selection screen
|
// Only 1 account, skip account selection screen
|
||||||
case 1:
|
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) {
|
if (loadAccount) {
|
||||||
throw redirect({
|
throw redirect({
|
||||||
to: "/app/home/local",
|
to: "/$account/home/local",
|
||||||
|
params: { account },
|
||||||
search: {
|
search: {
|
||||||
redirect: location.href,
|
redirect: location.href,
|
||||||
},
|
},
|
||||||
@ -48,7 +50,8 @@ function Screen() {
|
|||||||
const loadAccount = await ark.load_selected_account(npub);
|
const loadAccount = await ark.load_selected_account(npub);
|
||||||
if (loadAccount) {
|
if (loadAccount) {
|
||||||
navigate({
|
navigate({
|
||||||
to: "/app/home",
|
to: "/$account/home/local",
|
||||||
|
params: { account: npub },
|
||||||
replace: true,
|
replace: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -47,3 +47,18 @@ default = ["custom-protocol"]
|
|||||||
# this feature is used used for production builds where `devPath` points to the filesystem
|
# this feature is used used for production builds where `devPath` points to the filesystem
|
||||||
# DO NOT remove this
|
# DO NOT remove this
|
||||||
custom-protocol = ["tauri/custom-protocol"]
|
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 Ok(events) = query {
|
||||||
if let Some(event) = events.first() {
|
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)
|
Ok(metadata)
|
||||||
|
} else {
|
||||||
|
Err("Parse metadata failed".into())
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let rand_metadata = Metadata::new();
|
let rand_metadata = Metadata::new();
|
||||||
Ok(rand_metadata)
|
Ok(rand_metadata)
|
||||||
|
Loading…
Reference in New Issue
Block a user