diff --git a/src/components/layout/TopBar.tsx b/src/components/layout/TopBar.tsx index f2dd57e..8539617 100644 --- a/src/components/layout/TopBar.tsx +++ b/src/components/layout/TopBar.tsx @@ -29,6 +29,7 @@ import { useAuthStore } from '@/stores/authStore'; import { buildEndSessionUrl, getPostLogoutRedirectUri } from '@/services/auth/oauth'; import { useEffect, useState } from 'react'; import { useAccountStore } from '@/stores/accountStore'; +import { useCurrentAccountDetails } from '@/hooks/useCurrentAccount'; import { useSchemaStore } from '@/stores/schemaStore'; function getIcon(name: string): LucideIcons.LucideIcon { @@ -48,6 +49,10 @@ export function TopBar() { const activeAccountId = useAuthStore((s) => s.activeAccountId); const switchAccount = useAuthStore((s) => s.switchAccount); const logout = useAuthStore((s) => s.logout); + const activeAccount = activeAccountId ? accounts[activeAccountId] : null; + const currentAccount = useCurrentAccountDetails(); + const displayName = currentAccount.username?.trim() || activeAccount?.name?.trim() || activeAccountId?.split('@')[0] || activeAccountId; + const displayEmail = currentAccount.email || activeAccountId; const edition = useAccountStore((s) => s.edition); const hasObjectPermission = useAccountStore((s) => s.hasObjectPermission); const hasPermission = useAccountStore((s) => s.hasPermission); @@ -126,8 +131,12 @@ export function TopBar() { - diff --git a/src/hooks/useCurrentAccount.ts b/src/hooks/useCurrentAccount.ts new file mode 100644 index 0000000..e5d5d80 --- /dev/null +++ b/src/hooks/useCurrentAccount.ts @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL + */ + +import { useEffect, useState } from 'react'; + +import { useAuthStore } from '@/stores/authStore'; +import { jmapGetBatched } from '@/services/jmap/client'; + +export interface CurrentAccountDetails { + username: string | null; + email: string | null; + fullName: string | null; +} + +// Fetches the current user's x:Account record to expose the login name, +// email address and full name independently from the JMAP session account name. +export function useCurrentAccountDetails(): CurrentAccountDetails { + const { primaryAccountId } = useAuthStore(); + const [details, setDetails] = useState({ + username: null, + email: null, + fullName: null, + }); + + useEffect(() => { + if (!primaryAccountId) return; + + let cancelled = false; + + void (async () => { + try { + const list = await jmapGetBatched( + 'x:Account', + primaryAccountId, + [primaryAccountId], + ['id', 'name', 'emailAddress', 'description'], + ); + const record = list[0]; + if (!record || cancelled) return; + setDetails({ + username: (record.name as string | undefined) ?? null, + email: (record.emailAddress as string | undefined) ?? null, + fullName: (record.description as string | undefined) ?? null, + }); + } catch (err) { + console.error('Failed to fetch current account details:', err); + } + })(); + + return () => { + cancelled = true; + }; + }, [primaryAccountId]); + + return details; +} \ No newline at end of file