feat: account switching

This commit is contained in:
Shibo Lyu 2024-10-16 03:21:26 +08:00
parent 2b47eeb146
commit e78279114a
6 changed files with 98 additions and 31 deletions

View file

@ -75,13 +75,15 @@ class AccountStore implements Readable<Account[]> {
await this.identityFileDB.updateIdentityFile(identityFile); await this.identityFileDB.updateIdentityFile(identityFile);
} }
async createAccount(profile: BlahProfile, password: string) { async createAccount(profile: BlahProfile, password: string): Promise<string> {
const idKeyPair = await BlahKeyPair.generate(true); const idKeyPair = await BlahKeyPair.generate(true);
const actKeyPair = await BlahKeyPair.generate(false); const actKeyPair = await BlahKeyPair.generate(false);
const identity = await BlahIdentity.create(idKeyPair, actKeyPair, profile); const identity = await BlahIdentity.create(idKeyPair, actKeyPair, profile);
const encodedIdKeyPair = await idKeyPair.encode(password); const encodedIdKeyPair = await idKeyPair.encode(password);
await this.keyDB.addAccount(idKeyPair.id, actKeyPair, encodedIdKeyPair); await this.keyDB.addAccount(idKeyPair.id, actKeyPair, encodedIdKeyPair);
await this.saveIdentityFile(identity); await this.saveIdentityFile(identity);
await this.loadAccounts();
return idKeyPair.id;
} }
} }

View file

@ -8,12 +8,15 @@
</script> </script>
<svelte:element <svelte:element
this={href ? 'a' : 'div'} this={href ? 'a' : 'button'}
{href} {href}
class={tw( class={tw(
'flex cursor-default items-center gap-2 px-4 py-3 font-medium text-sf-primary first:rounded-t-lg last:rounded-b-lg', 'flex w-full cursor-default items-center gap-2 px-4 py-3 font-medium text-sf-primary first:rounded-t-lg last:rounded-b-lg',
selected && 'bg-accent-500 text-white shadow-inner dark:bg-accent-900 dark:text-sf-primary' selected && 'bg-accent-500 text-white shadow-inner dark:bg-accent-900 dark:text-sf-primary'
)} )}
tabindex="0"
role="button"
on:click
> >
{#if icon} {#if icon}
<Icon <Icon

View file

@ -2,31 +2,25 @@
import { import {
currentAccountStore, currentAccountStore,
openAccountStore, openAccountStore,
type Account,
type AccountStore type AccountStore
} from '$lib/accounts/accountStore'; } from '$lib/accounts/accountStore';
import ProfilePicture from '$lib/components/ProfilePicture.svelte'; import ProfilePicture from '$lib/components/ProfilePicture.svelte';
import { onMount } from 'svelte';
export let size: number = 32; export let size: number = 32;
let accountStore: AccountStore; let accountStore: AccountStore;
async function getAccount(idKeyId: string | null): Promise<Account | undefined> { onMount(() => {
if (!accountStore) { openAccountStore().then((store) => {
accountStore = await openAccountStore(); accountStore = store;
} });
if (!idKeyId) return; });
let currentAccount = $accountStore.find((account) => account.id_key === idKeyId);
if (!currentAccount && $accountStore.length > 0) {
currentAccount = $accountStore[0];
$currentAccountStore = currentAccount.id_key;
}
return currentAccount;
}
</script> </script>
{#await getAccount($currentAccountStore)} {#if accountStore}
<ProfilePicture account={undefined} /> {@const currentAccount = $accountStore.find((account) => account.id_key === $currentAccountStore)}
{:then currentAccount}
<ProfilePicture account={currentAccount} {size} /> <ProfilePicture account={currentAccount} {size} />
{/await} {:else}
<ProfilePicture account={undefined} {size} />
{/if}

View file

@ -0,0 +1,71 @@
<script lang="ts">
import { GroupedListItem, GroupedListSection } from '$lib/components/GroupedList';
import { ArrowRightEndOnRectangle, UserPlus } from 'svelte-hero-icons';
import SettingsListItem from './SettingsListItem.svelte';
import {
openAccountStore,
currentAccountStore,
type AccountStore,
type Account
} from '$lib/accounts/accountStore';
import { onMount } from 'svelte';
import ProfilePicture from '$lib/components/ProfilePicture.svelte';
import { flip } from 'svelte/animate';
import { blur, scale } from 'svelte/transition';
let accountStore: AccountStore;
onMount(() => {
openAccountStore().then((store) => {
accountStore = store;
});
});
function switchToAccount(account: Account) {
$currentAccountStore = account.id_key;
}
</script>
{#if accountStore}
{@const currentAccount = $accountStore.find((acc) => acc.id_key === $currentAccountStore)}
{@const remainingAccounts = $accountStore
.filter((acc) => acc.id_key !== $currentAccountStore)
.toSorted((a, b) => a.profile.signee.payload.name.localeCompare(b.profile.signee.payload.name))}
{#if currentAccount}
{#key currentAccount.id_key}
<div class="mt-6 p-4 text-center" in:blur>
<div class="inline-block">
<ProfilePicture account={currentAccount} size={68} />
</div>
<p>
<span class="text-xl font-semibold text-sf-primary">
{currentAccount.profile.signee.payload.name}
</span>
</p>
<p>
<code class="text-sm text-sf-secondary">
{currentAccount.profile.signee.id_key.slice(0, 6) +
'...' +
currentAccount.profile.signee.id_key.slice(-6)}
</code>
</p>
</div>
{/key}
{/if}
<GroupedListSection>
{#each remainingAccounts as account (account.id_key)}
<div animate:flip={{ duration: 250 }} transition:blur>
<GroupedListItem on:click={() => switchToAccount(account)}>
<div class="-mx-0.5"><ProfilePicture {account} size={24} /></div>
{account.profile.signee.payload.name}
</GroupedListItem>
</div>
{/each}
</GroupedListSection>
{/if}
<GroupedListSection>
<SettingsListItem icon={ArrowRightEndOnRectangle} route="/account/add">Sign in</SettingsListItem>
<SettingsListItem icon={UserPlus} route="/account/new">Create Account</SettingsListItem>
</GroupedListSection>

View file

@ -3,18 +3,17 @@
import { GroupedListSection, GroupedListItem } from '$lib/components/GroupedList'; import { GroupedListSection, GroupedListItem } from '$lib/components/GroupedList';
import { tw } from '$lib/tw'; import { tw } from '$lib/tw';
import { import {
ArrowRightEndOnRectangle,
Bell, Bell,
Cog, Cog,
DevicePhoneMobile, DevicePhoneMobile,
InformationCircle, InformationCircle,
LockClosed, LockClosed,
QuestionMarkCircle, QuestionMarkCircle
UserPlus
} from 'svelte-hero-icons'; } from 'svelte-hero-icons';
import { scale } from 'svelte/transition'; import { scale } from 'svelte/transition';
import SettingsListItem from './SettingsListItem.svelte'; import SettingsListItem from './SettingsListItem.svelte';
import PageHeader from '$lib/components/PageHeader.svelte'; import PageHeader from '$lib/components/PageHeader.svelte';
import SettingsAccountSections from './SettingsAccountSections.svelte';
let className = ''; let className = '';
export { className as class }; export { className as class };
@ -30,18 +29,15 @@
<Button href="/account/profile">Edit</Button> <Button href="/account/profile">Edit</Button>
</PageHeader> </PageHeader>
<div class="flex-1 overflow-y-scroll"> <div class="flex-1 overflow-y-scroll">
<GroupedListSection> <SettingsAccountSections />
<SettingsListItem icon={ArrowRightEndOnRectangle} route="/account/add">
Sign in
</SettingsListItem>
<SettingsListItem icon={UserPlus} route="/account/new">Create Account</SettingsListItem>
</GroupedListSection>
<GroupedListSection> <GroupedListSection>
<SettingsListItem icon={Cog} route="">General</SettingsListItem> <SettingsListItem icon={Cog} route="">General</SettingsListItem>
<GroupedListItem icon={Bell}>Notifications</GroupedListItem> <GroupedListItem icon={Bell}>Notifications</GroupedListItem>
<GroupedListItem icon={LockClosed}>Privacy and Security</GroupedListItem> <GroupedListItem icon={LockClosed}>Privacy and Security</GroupedListItem>
<GroupedListItem icon={DevicePhoneMobile}>Devices</GroupedListItem> <GroupedListItem icon={DevicePhoneMobile}>Devices</GroupedListItem>
</GroupedListSection> </GroupedListSection>
<GroupedListSection> <GroupedListSection>
<GroupedListItem icon={InformationCircle}>About Blah & Weblah</GroupedListItem> <GroupedListItem icon={InformationCircle}>About Blah & Weblah</GroupedListItem>
<GroupedListItem icon={QuestionMarkCircle}>Ask a Question</GroupedListItem> <GroupedListItem icon={QuestionMarkCircle}>Ask a Question</GroupedListItem>

View file

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { openAccountStore } from '$lib/accounts/accountStore'; import { currentAccountStore, openAccountStore } from '$lib/accounts/accountStore';
import Button from '$lib/components/Button.svelte'; import Button from '$lib/components/Button.svelte';
import { import {
GroupedListContainer, GroupedListContainer,
@ -57,7 +57,8 @@
try { try {
const accountStore = await openAccountStore(); const accountStore = await openAccountStore();
await accountStore.createAccount(profile, password); const idKeyId = await accountStore.createAccount(profile, password);
$currentAccountStore = idKeyId;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }