diff --git a/web/src/app/AppShell.tsx b/web/src/app/AppShell.tsx index cf9863c3..4b767d6f 100644 --- a/web/src/app/AppShell.tsx +++ b/web/src/app/AppShell.tsx @@ -1,9 +1,11 @@ import { useEffect } from 'react'; +import { useQuery } from '@tanstack/react-query'; import { Outlet } from 'react-router-dom'; import { CallRoot } from '@/features/calls/CallRoot'; import { useBootstrap } from '@/lib/api/hooks'; +import { getMe } from '@/lib/api/profile'; import { useChatList } from '@/lib/tinode/hooks'; import { initCallSignaling } from '@/stores/calls'; @@ -19,6 +21,12 @@ import { NavRail } from './NavRail'; export function AppShell() { const contacts = useChatList(); const bootstrap = useBootstrap(); + // Same `['me']` query SettingsLayout already runs -- sharing the key means whichever mounts + // first fetches it and the other just reads the cache, not two redundant requests. This was + // never wired up here before, so the rail's self-tile (NavRail below) had no avatar/name data + // at all and fell back to a permanently-hardcoded "我" glyph regardless of who was logged in or + // what avatar they'd uploaded -- see Avatar.tsx's own comment for the wider version of this gap. + const meQuery = useQuery({ queryKey: ['me'], queryFn: getMe }); // Once per app session, regardless of which page is open -- an incoming call must surface even // while browsing contacts/settings/etc, not only while a conversation happens to be open. useEffect(() => { @@ -37,7 +45,12 @@ export function AppShell() { return (
- +
diff --git a/web/src/app/NavRail.tsx b/web/src/app/NavRail.tsx index 7934b815..d2d6a06f 100644 --- a/web/src/app/NavRail.tsx +++ b/web/src/app/NavRail.tsx @@ -1,5 +1,6 @@ import { Link, useLocation } from 'react-router-dom'; +import { Avatar } from './Avatar'; import { Icon } from './Icon'; interface NavRailItem { @@ -54,13 +55,20 @@ function NavRailButton({ item }: { item: NavRailItem }) { ); } +interface NavRailProps { + unreadMessages?: number; + /** The logged-in user's own id/name/avatar (`GET /v1/im/me`) -- drives the rail's top self-tile. + * All optional so the rail still renders (as a letter-tile fallback) before that request settles. */ + selfId?: string; + selfName?: string; + selfAvatarFileId?: string; +} + /** Persistent 64px icon rail, visible at >=640px (see App.tsx for the <640 bottom-tab fallback). */ -export function NavRail({ selfInitial = '我', unreadMessages = 0 }: { selfInitial?: string; unreadMessages?: number }) { +export function NavRail({ unreadMessages = 0, selfId, selfName, selfAvatarFileId }: NavRailProps) { return (