refactor: Account management and settings navigation

Move profile page to /settings/account/profile and replace
CurrentAccountPicture with CurrentAccountIndicator component
using the new account manager pattern. Update identity type
to accept null instead of undefined.
This commit is contained in:
Shibo Lyu 2025-04-14 01:33:24 +08:00
parent 73522535f9
commit 924427a810
9 changed files with 39 additions and 51 deletions

View file

@ -3,7 +3,7 @@
import { AvatarBeam } from 'svelte-boring-avatars';
interface Props {
identity: BlahIdentityDescription | undefined;
identity: BlahIdentityDescription | null;
size?: number;
}

View file

@ -22,9 +22,9 @@
});
let isSettings = $derived($page.route.id?.startsWith('/(app)/settings') ?? true);
let mainVisible =
$derived(!!$page.params.chatId ||
(isSettings && !$page.route.id?.startsWith('/(app)/settings/_mobile_empty')));
let mainVisible = $derived(
!!$page.params.chatId || (isSettings && $page.route.id !== '/(app)/settings')
);
</script>
<div
@ -32,7 +32,7 @@
data-weblah-main-visible={mainVisible ? 'true' : undefined}
>
<aside
class="relative h-[100dvh] min-h-0 overflow-hidden border-ss-primary bg-sb-primary shadow-lg [view-transition-name:chat-list] after:pointer-events-none after:absolute after:inset-0 after:size-full after:bg-transparent group-data-weblah-main-visible:after:bg-black/30 sm:w-1/3 sm:border-e sm:after:hidden lg:w-1/4"
class="border-ss-primary bg-sb-primary relative h-[100dvh] min-h-0 overflow-hidden shadow-lg [view-transition-name:chat-list] after:pointer-events-none after:absolute after:inset-0 after:size-full after:bg-transparent group-data-weblah-main-visible:after:bg-black/30 sm:w-1/3 sm:border-e sm:after:hidden lg:w-1/4"
>
<ChatList />
{#if isSettings}
@ -41,7 +41,7 @@
</aside>
{#if mainVisible}
<main
class="absolute inset-0 w-full bg-sb-secondary shadow-lg [view-transition-name:main] sm:relative sm:flex-1 sm:shadow-none"
class="bg-sb-secondary absolute inset-0 w-full shadow-lg [view-transition-name:main] sm:relative sm:flex-1 sm:shadow-none"
>
{@render children?.()}
</main>

View file

@ -3,7 +3,7 @@
import InputFrame from '$lib/components/InputFrame.svelte';
import { Icon, MagnifyingGlass, PencilSquare, XCircle } from 'svelte-hero-icons';
import { tw } from '$lib/tw';
import CurrentAccountPicture from './CurrentAccountPicture.svelte';
import CurrentAccountIndicator from './CurrentAccountIndicator.svelte';
interface Props {
searchQuery?: string;
@ -20,15 +20,12 @@
</script>
<header class="border-ss-secondary flex items-center justify-stretch gap-2 border-b p-2 shadow-xs">
<a
<CurrentAccountIndicator
class={tw(
'transition-[opacity,transform] duration-200',
isSearchFocused && '-translate-x-full opacity-0'
)}
href="/settings"
>
<CurrentAccountPicture />
</a>
/>
<InputFrame
class={tw('z-10 h-8 flex-1 transition-all duration-200', isSearchFocused && '-mx-10')}
>

View file

@ -0,0 +1,19 @@
<script lang="ts">
import manager from '$lib/accounts/manager.svelte';
import ProfilePicture from '$lib/components/ProfilePicture.svelte';
import { tw } from '$lib/tw';
import type { HTMLAttributes } from 'svelte/elements';
interface Props extends HTMLAttributes<HTMLAnchorElement> {
class?: string;
}
const { class: classNames, ...rest }: Props = $props();
const currentAccount = $derived(manager.currentAccount);
const href = $derived(currentAccount ? '/settings/account/profile' : '/settings/account/add');
</script>
<a {href} class={tw('cursor-default', classNames)} {...rest}>
<ProfilePicture identity={currentAccount} size={32} />
</a>

View file

@ -1,30 +0,0 @@
<script lang="ts">
import {
currentAccountStore,
openAccountStore,
type AccountStore
} from '$lib/accounts/accountStore';
import ProfilePicture from '$lib/components/ProfilePicture.svelte';
import { onMount } from 'svelte';
interface Props {
size?: number;
}
let { size = 32 }: Props = $props();
let accountStore: AccountStore | undefined = $state();
onMount(() => {
openAccountStore().then((store) => {
accountStore = store;
});
});
</script>
{#if accountStore && $accountStore}
{@const currentAccount = $accountStore.find((account) => account.id_key === $currentAccountStore)}
<ProfilePicture identity={currentAccount} {size} />
{:else}
<ProfilePicture identity={undefined} {size} />
{/if}

View file

@ -35,7 +35,7 @@
<SettingsAccountSections />
<GroupedListSection>
<SettingsListItem icon={User} route="">My Profile</SettingsListItem>
<SettingsListItem icon={User} route="/account/profile">My Profile</SettingsListItem>
<GroupedListItem icon={DevicePhoneMobile}>Devices</GroupedListItem>
</GroupedListSection>

View file

@ -1,5 +1,5 @@
<script lang="ts">
import { page } from '$app/stores';
import { page } from '$app/state';
import { GroupedListItem } from '$lib/components/GroupedList';
import { type IconSource } from 'svelte-hero-icons';
@ -11,9 +11,11 @@
let { icon = undefined, route = undefined, children }: Props = $props();
let selected = $derived(route
? $page.route.id?.startsWith(`/(app)/settings${route}`)
: $page.route.id === '/(app)/settings');
let selected = $derived(
route
? page.route.id?.startsWith(`/(app)/settings${route}`)
: page.route.id === '/(app)/settings'
);
let href = $derived(`/settings${route}`);
</script>

View file

@ -1,7 +1,7 @@
<script lang="ts">
import { page } from '$app/state';
import { goto } from '$app/navigation';
import accountsManager from '$lib/accounts/manager.svelte';
import manager from '$lib/accounts/manager.svelte';
import Button from '$lib/components/Button.svelte';
import {
GroupedListContainer,
@ -47,8 +47,8 @@
isBusy = true;
try {
const idKeyId = await accountsManager.createAccount(profile, password);
accountsManager.currentAccountId = idKeyId;
const idKeyId = await manager.createAccount(profile, password);
manager.currentAccountId = idKeyId;
goto('/settings');
} catch (error) {
console.error(error);
@ -69,7 +69,7 @@
<GroupedListContainer>
<GroupedListSection>
<GroupedListContent class="flex items-center">
<ProfilePicture size={64} identity={undefined} />
<ProfilePicture size={64} identity={null} />
<input
type="text"
bind:value={name}