mirror of
				https://github.com/Blah-IM/Weblah.git
				synced 2025-10-30 17:41:37 +00:00 
			
		
		
		
	refactor: account management system
Replace store-based approach with manager-based implementation and update related components to use the new system. Change ProfilePicture props from `account` to `identity`.
This commit is contained in:
		
							parent
							
								
									055f7240df
								
							
						
					
					
						commit
						b467ec1491
					
				
					 8 changed files with 91 additions and 88 deletions
				
			
		|  | @ -6,7 +6,6 @@ import { | |||
| } from '@blah-im/core/identity'; | ||||
| import { type IdentityDB, openIdentityDB } from './identityFileDB'; | ||||
| import { BlahKeyPair } from '@blah-im/core/crypto'; | ||||
| import { persisted } from 'svelte-persisted-store'; | ||||
| import { browser } from '$app/environment'; | ||||
| 
 | ||||
| export type Account = BlahIdentityDescription & { | ||||
|  | @ -30,24 +29,25 @@ class AccountManager { | |||
| 	constructor() { | ||||
| 		if (browser) { | ||||
| 			this.currentAccountId = localStorage.getItem(localStorageCurrentAccountIdKey); | ||||
| 			console.log('currentAccountId', this.currentAccountId); | ||||
| 
 | ||||
| 			$effect.root(() => { | ||||
| 				$effect(() => | ||||
| 					this.currentAccountId | ||||
| 						? localStorage.setItem(localStorageCurrentAccountIdKey, this.currentAccountId) | ||||
| 						: localStorage.removeItem(localStorageCurrentAccountIdKey) | ||||
| 				); | ||||
| 			}); | ||||
| 
 | ||||
| 			(async () => { | ||||
| 				this.inProgress = true; | ||||
| 				const [keyDB, identityDB] = await Promise.all([openAccountKeyDB(), openIdentityDB()]); | ||||
| 				this.keyDB = keyDB; | ||||
| 				this.identityDB = identityDB; | ||||
| 				await this.loadAccounts(); | ||||
| 				this.inProgress = false; | ||||
| 			})(); | ||||
| 		} | ||||
| 
 | ||||
| 		$effect.root(() => { | ||||
| 			$effect(() => | ||||
| 				this.currentAccountId | ||||
| 					? localStorage.setItem(localStorageCurrentAccountIdKey, this.currentAccountId) | ||||
| 					: localStorage.removeItem(localStorageCurrentAccountIdKey) | ||||
| 			); | ||||
| 		}); | ||||
| 
 | ||||
| 		(async () => { | ||||
| 			this.inProgress = true; | ||||
| 			const [keyDB, identityDB] = await Promise.all([openAccountKeyDB(), openIdentityDB()]); | ||||
| 			this.keyDB = keyDB; | ||||
| 			this.identityDB = identityDB; | ||||
| 			await this.loadAccounts(); | ||||
| 			this.inProgress = false; | ||||
| 		})(); | ||||
| 	} | ||||
| 
 | ||||
| 	async loadAccounts() { | ||||
|  |  | |||
|  | @ -2,7 +2,6 @@ import { persisted } from 'svelte-persisted-store'; | |||
| import { get } from 'svelte/store'; | ||||
| import { BlahChatServerConnection } from './blah/connection/chatServer'; | ||||
| import { BlahKeyPair, type EncodedBlahKeyPair } from '@blah-im/core/crypto'; | ||||
| import { currentKeyPair } from './keystore'; | ||||
| import { ChatListManager } from './chatList'; | ||||
| import { browser } from '$app/environment'; | ||||
| import { GlobalSearchManager } from './globalSearch'; | ||||
|  | @ -18,7 +17,7 @@ class ChatServerConnectionPool { | |||
| 	constructor() { | ||||
| 		if (browser) { | ||||
| 			chatServers.subscribe(this.onChatServersChange.bind(this)); | ||||
| 			currentKeyPair.subscribe(this.onKeyPairChange.bind(this)); | ||||
| 			// currentKeyPair.subscribe(this.onKeyPairChange.bind(this));
 | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,14 +1,15 @@ | |||
| <script lang="ts"> | ||||
| 	import { tw } from '$lib/tw'; | ||||
| 	import type { HTMLAttributes } from 'svelte/elements'; | ||||
| 
 | ||||
| 	interface Props { | ||||
| 	interface Props extends HTMLAttributes<HTMLDivElement> { | ||||
| 		class?: string; | ||||
| 		children?: import('svelte').Snippet; | ||||
| 	} | ||||
| 
 | ||||
| 	let { children, class: classNames }: Props = $props(); | ||||
| 	let { children, class: classNames, ...rest }: Props = $props(); | ||||
| </script> | ||||
| 
 | ||||
| <div class={tw('px-4 py-3', classNames)}> | ||||
| <div class={tw('px-4 py-3', classNames)} {...rest}> | ||||
| 	{@render children?.()} | ||||
| </div> | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| 
 | ||||
| 	import { formatMessageDate, formatFullMessageDate, formatUnreadCount } from '$lib/formatters'; | ||||
| 	import type { Chat } from '$lib/types'; | ||||
| 	import { currentKeyPair } from '$lib/keystore'; | ||||
| 	import accountManager from '$lib/accounts/manager.svelte'; | ||||
| 	import { toPlainText } from '@blah-im/core/richText'; | ||||
| 	import { page } from '$app/state'; | ||||
| 	import { tw } from '$lib/tw'; | ||||
|  | @ -56,7 +56,7 @@ | |||
| 					{#if chat.lastMessage} | ||||
| 						{#if chat.id !== chat.lastMessage.sender.id} | ||||
| 							<span class="text-sf-primary"> | ||||
| 								{chat.lastMessage.sender.id === $currentKeyPair.id | ||||
| 								{chat.lastMessage.sender.id === accountManager.currentAccountId | ||||
| 									? 'You' | ||||
| 									: chat.lastMessage.sender.name}: | ||||
| 							</span> | ||||
|  |  | |||
|  | @ -24,7 +24,7 @@ | |||
| 
 | ||||
| {#if accountStore && $accountStore} | ||||
| 	{@const currentAccount = $accountStore.find((account) => account.id_key === $currentAccountStore)} | ||||
| 	<ProfilePicture account={currentAccount} {size} /> | ||||
| 	<ProfilePicture identity={currentAccount} {size} /> | ||||
| {:else} | ||||
| 	<ProfilePicture account={undefined} {size} /> | ||||
| 	<ProfilePicture identity={undefined} {size} /> | ||||
| {/if} | ||||
|  |  | |||
|  | @ -0,0 +1,14 @@ | |||
| <script lang="ts"> | ||||
| 	import LoadingIndicator from '$lib/components/LoadingIndicator.svelte'; | ||||
| 	import PageHeader from '$lib/components/PageHeader.svelte'; | ||||
| 	import { Button } from 'bits-ui'; | ||||
| 	import accountsManager from '$lib/accounts/manager.svelte'; | ||||
| 
 | ||||
| 	const currentAccount = $derived(accountsManager.currentAccount); | ||||
| </script> | ||||
| 
 | ||||
| {#if currentAccount} | ||||
| 	<PageHeader> | ||||
| 		<h3 class="flex-1">{currentAccount.profile.signee.payload.name}</h3> | ||||
| 	</PageHeader> | ||||
| {/if} | ||||
|  | @ -2,76 +2,66 @@ | |||
| 	import { GroupedListItem, GroupedListSection } from '$lib/components/GroupedList'; | ||||
| 	import { ArrowRightEndOnRectangle, Plus, 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 manager, { type Account } from '$lib/accounts/manager.svelte'; | ||||
| 	import ProfilePicture from '$lib/components/ProfilePicture.svelte'; | ||||
| 	import { flip } from 'svelte/animate'; | ||||
| 	import { blur } from 'svelte/transition'; | ||||
| 	import GroupedListContent from '$lib/components/GroupedList/GroupedListContent.svelte'; | ||||
| 
 | ||||
| 	let accountStore: AccountStore | undefined = $state(); | ||||
| 
 | ||||
| 	onMount(() => { | ||||
| 		openAccountStore().then((store) => { | ||||
| 			accountStore = store; | ||||
| 		}); | ||||
| 	}); | ||||
| 	const currentAccount = $derived(manager.currentAccount); | ||||
| 	const remainingAccounts = $derived( | ||||
| 		manager.accounts | ||||
| 			.filter((acc) => acc.id_key !== manager.currentAccountId) | ||||
| 			.toSorted((a, b) => | ||||
| 				a.profile.signee.payload.name.localeCompare(b.profile.signee.payload.name) | ||||
| 			) | ||||
| 	); | ||||
| 
 | ||||
| 	function switchToAccount(account: Account) { | ||||
| 		$currentAccountStore = account.id_key; | ||||
| 		manager.currentAccountId = account.id_key; | ||||
| 	} | ||||
| </script> | ||||
| 
 | ||||
| {#if accountStore && $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-sf-primary text-xl font-semibold"> | ||||
| 						{currentAccount.profile.signee.payload.name} | ||||
| 					</span> | ||||
| 				</p> | ||||
| 				<p> | ||||
| 					<code class="text-sf-secondary text-sm"> | ||||
| 						{currentAccount.id_key.slice(0, 4) + '..' + currentAccount.id_key.slice(-4)} | ||||
| 					</code> | ||||
| 				</p> | ||||
| {#if currentAccount} | ||||
| 	{#key currentAccount.id_key} | ||||
| 		<div class="mt-6 p-4 text-center" in:blur> | ||||
| 			<div class="inline-block"> | ||||
| 				<ProfilePicture identity={currentAccount} size={68} /> | ||||
| 			</div> | ||||
| 		{/key} | ||||
| 	{/if} | ||||
| 
 | ||||
| 	{#if remainingAccounts.length > 0} | ||||
| 		<GroupedListSection> | ||||
| 			{#each remainingAccounts as account (account.id_key)} | ||||
| 				<div animate:flip={{ duration: 250 }} transition:blur> | ||||
| 					<GroupedListItem onclick={() => switchToAccount(account)}> | ||||
| 						<div class="-mx-0.5"><ProfilePicture {account} size={24} /></div> | ||||
| 						{account.profile.signee.payload.name} | ||||
| 					</GroupedListItem> | ||||
| 				</div> | ||||
| 			{/each} | ||||
| 		</GroupedListSection> | ||||
| 	{/if} | ||||
| 			<p> | ||||
| 				<span class="text-sf-primary text-xl font-semibold"> | ||||
| 					{currentAccount.profile.signee.payload.name} | ||||
| 				</span> | ||||
| 			</p> | ||||
| 			<p> | ||||
| 				<code class="text-sf-secondary text-sm"> | ||||
| 					{currentAccount.id_key.slice(0, 4) + '..' + currentAccount.id_key.slice(-4)} | ||||
| 				</code> | ||||
| 			</p> | ||||
| 		</div> | ||||
| 	{/key} | ||||
| {/if} | ||||
| 
 | ||||
| <GroupedListSection> | ||||
| 	{#if ($accountStore?.length ?? 0) > 0} | ||||
| {#if remainingAccounts.length > 0} | ||||
| 	<GroupedListSection> | ||||
| 		{#each remainingAccounts as account (account.id_key)} | ||||
| 			<GroupedListContent | ||||
| 				class="flex gap-2" | ||||
| 				role="button" | ||||
| 				tabindex={0} | ||||
| 				onclick={() => switchToAccount(account)} | ||||
| 			> | ||||
| 				<div class="-mx-0.5"><ProfilePicture identity={account} size={24} /></div> | ||||
| 				{account.profile.signee.payload.name} | ||||
| 			</GroupedListContent> | ||||
| 		{/each} | ||||
| 		<SettingsListItem icon={Plus} route="/account/add">Add Account</SettingsListItem> | ||||
| 	{:else} | ||||
| 	</GroupedListSection> | ||||
| {:else} | ||||
| 	<GroupedListSection> | ||||
| 		<SettingsListItem icon={ArrowRightEndOnRectangle} route="/account/add"> | ||||
| 			Sign in | ||||
| 		</SettingsListItem> | ||||
| 		<SettingsListItem icon={UserPlus} route="/account/new">Create Account</SettingsListItem> | ||||
| 	{/if} | ||||
| </GroupedListSection> | ||||
| 	</GroupedListSection> | ||||
| {/if} | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| <script lang="ts"> | ||||
| 	import { page } from '$app/state'; | ||||
| 	import { goto } from '$app/navigation'; | ||||
| 	import { currentAccountStore, openAccountStore } from '$lib/accounts/accountStore'; | ||||
| 	import accountsManager from '$lib/accounts/manager.svelte'; | ||||
| 	import Button from '$lib/components/Button.svelte'; | ||||
| 	import { | ||||
| 		GroupedListContainer, | ||||
|  | @ -47,9 +47,8 @@ | |||
| 		isBusy = true; | ||||
| 
 | ||||
| 		try { | ||||
| 			const accountStore = await openAccountStore(); | ||||
| 			const idKeyId = await accountStore.createAccount(profile, password); | ||||
| 			$currentAccountStore = idKeyId; | ||||
| 			const idKeyId = await accountsManager.createAccount(profile, password); | ||||
| 			accountsManager.currentAccountId = idKeyId; | ||||
| 			goto('/settings'); | ||||
| 		} catch (error) { | ||||
| 			console.error(error); | ||||
|  | @ -70,7 +69,7 @@ | |||
| <GroupedListContainer> | ||||
| 	<GroupedListSection> | ||||
| 		<GroupedListContent class="flex items-center"> | ||||
| 			<ProfilePicture size={64} account={undefined} /> | ||||
| 			<ProfilePicture size={64} identity={undefined} /> | ||||
| 			<input | ||||
| 				type="text" | ||||
| 				bind:value={name} | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Shibo Lyu
						Shibo Lyu