Show current user username and email in the TopBar user menu trigger
This commit is contained in:
@@ -29,6 +29,7 @@ import { useAuthStore } from '@/stores/authStore';
|
|||||||
import { buildEndSessionUrl, getPostLogoutRedirectUri } from '@/services/auth/oauth';
|
import { buildEndSessionUrl, getPostLogoutRedirectUri } from '@/services/auth/oauth';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useAccountStore } from '@/stores/accountStore';
|
import { useAccountStore } from '@/stores/accountStore';
|
||||||
|
import { useCurrentAccountDetails } from '@/hooks/useCurrentAccount';
|
||||||
import { useSchemaStore } from '@/stores/schemaStore';
|
import { useSchemaStore } from '@/stores/schemaStore';
|
||||||
|
|
||||||
function getIcon(name: string): LucideIcons.LucideIcon {
|
function getIcon(name: string): LucideIcons.LucideIcon {
|
||||||
@@ -48,6 +49,10 @@ export function TopBar() {
|
|||||||
const activeAccountId = useAuthStore((s) => s.activeAccountId);
|
const activeAccountId = useAuthStore((s) => s.activeAccountId);
|
||||||
const switchAccount = useAuthStore((s) => s.switchAccount);
|
const switchAccount = useAuthStore((s) => s.switchAccount);
|
||||||
const logout = useAuthStore((s) => s.logout);
|
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 edition = useAccountStore((s) => s.edition);
|
||||||
const hasObjectPermission = useAccountStore((s) => s.hasObjectPermission);
|
const hasObjectPermission = useAccountStore((s) => s.hasObjectPermission);
|
||||||
const hasPermission = useAccountStore((s) => s.hasPermission);
|
const hasPermission = useAccountStore((s) => s.hasPermission);
|
||||||
@@ -126,8 +131,12 @@ export function TopBar() {
|
|||||||
|
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="ghost" size="icon" aria-label={t('userMenu', 'User menu')}>
|
<Button variant="ghost" className="h-auto gap-2 px-2" aria-label={t('userMenu', 'User menu')}>
|
||||||
<User className="h-4 w-4" />
|
<User className="h-4 w-4" />
|
||||||
|
<div className="hidden flex-col items-start text-left md:flex">
|
||||||
|
<span className="text-sm font-medium leading-none">{displayName}</span>
|
||||||
|
<span className="text-xs text-muted-foreground leading-none">{displayEmail}</span>
|
||||||
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end" className="w-56">
|
<DropdownMenuContent align="end" className="w-56">
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
|
||||||
|
*
|
||||||
|
* 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<CurrentAccountDetails>({
|
||||||
|
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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user