perf(sidebar): show cached user profile before async refresh

This commit is contained in:
tisonhuang
2026-03-01 16:03:49 +08:00
parent 0444ca143e
commit de7cbdf494

View File

@@ -12,6 +12,42 @@ interface SidebarUserProfile {
avatarUrl?: string avatarUrl?: string
} }
const SIDEBAR_USER_PROFILE_CACHE_KEY = 'sidebar_user_profile_cache_v1'
interface SidebarUserProfileCache extends SidebarUserProfile {
updatedAt: number
}
const readSidebarUserProfileCache = (): SidebarUserProfile | null => {
try {
const raw = window.localStorage.getItem(SIDEBAR_USER_PROFILE_CACHE_KEY)
if (!raw) return null
const parsed = JSON.parse(raw) as SidebarUserProfileCache
if (!parsed || typeof parsed !== 'object') return null
if (!parsed.wxid || !parsed.displayName) return null
return {
wxid: parsed.wxid,
displayName: parsed.displayName,
avatarUrl: parsed.avatarUrl
}
} catch {
return null
}
}
const writeSidebarUserProfileCache = (profile: SidebarUserProfile): void => {
if (!profile.wxid || !profile.displayName) return
try {
const payload: SidebarUserProfileCache = {
...profile,
updatedAt: Date.now()
}
window.localStorage.setItem(SIDEBAR_USER_PROFILE_CACHE_KEY, JSON.stringify(payload))
} catch {
// 忽略本地缓存失败,不影响主流程
}
}
function Sidebar() { function Sidebar() {
const location = useLocation() const location = useLocation()
const [collapsed, setCollapsed] = useState(false) const [collapsed, setCollapsed] = useState(false)
@@ -28,10 +64,6 @@ function Sidebar() {
useEffect(() => { useEffect(() => {
const loadCurrentUser = async () => { const loadCurrentUser = async () => {
try {
const wxid = await configService.getMyWxid()
let displayName = wxid || '未识别用户'
const normalizeName = (value?: string | null): string | undefined => { const normalizeName = (value?: string | null): string | undefined => {
if (!value) return undefined if (!value) return undefined
const trimmed = value.trim() const trimmed = value.trim()
@@ -39,48 +71,86 @@ function Sidebar() {
return trimmed return trimmed
} }
let enrichedDisplayName: string | undefined const patchUserProfile = (patch: Partial<SidebarUserProfile>, expectedWxid?: string) => {
let fallbackSelfName: string | undefined setUserProfile(prev => {
if (expectedWxid && prev.wxid && prev.wxid !== expectedWxid) {
return prev
}
const next: SidebarUserProfile = {
...prev,
...patch
}
if (!next.displayName) {
next.displayName = next.wxid || '未识别用户'
}
writeSidebarUserProfileCache(next)
return next
})
}
if (wxid) { try {
const [myContact, enrichedResult] = await Promise.all([ const wxid = await configService.getMyWxid()
window.electronAPI.chat.getContact(wxid), const resolvedWxid = wxid || ''
window.electronAPI.chat.enrichSessionsContactInfo([wxid, 'self']) const fallbackDisplayName = resolvedWxid || '未识别用户'
])
enrichedDisplayName = normalizeName(enrichedResult.contacts?.[wxid]?.displayName) // 第一阶段:先把 wxid/名称打上,保证侧边栏第一时间可见。
fallbackSelfName = normalizeName(enrichedResult.contacts?.self?.displayName) patchUserProfile({
wxid: resolvedWxid,
displayName: fallbackDisplayName
})
const bestName = if (!resolvedWxid) return
// 第二阶段:后台补齐名称(不会阻塞首屏)。
void (async () => {
try {
const myContact = await window.electronAPI.chat.getContact(resolvedWxid)
const fromContact =
normalizeName(myContact?.remark) || normalizeName(myContact?.remark) ||
normalizeName(myContact?.nickName) || normalizeName(myContact?.nickName) ||
normalizeName(myContact?.alias) || normalizeName(myContact?.alias)
enrichedDisplayName ||
fallbackSelfName
if (fromContact) {
patchUserProfile({ displayName: fromContact }, resolvedWxid)
return
}
const enrichedResult = await window.electronAPI.chat.enrichSessionsContactInfo([resolvedWxid, 'self'])
const enrichedDisplayName = normalizeName(enrichedResult.contacts?.[resolvedWxid]?.displayName)
const fallbackSelfName = normalizeName(enrichedResult.contacts?.self?.displayName)
const bestName = enrichedDisplayName || fallbackSelfName
if (bestName) { if (bestName) {
displayName = bestName patchUserProfile({ displayName: bestName }, resolvedWxid)
} else if (fallbackSelfName && fallbackSelfName !== wxid) {
displayName = fallbackSelfName
} }
} catch (nameError) {
console.error('加载侧边栏用户昵称失败:', nameError)
} }
})()
let avatarUrl: string | undefined // 第二阶段:后台补齐头像(不会阻塞首屏)。
void (async () => {
try {
const avatarResult = await window.electronAPI.chat.getMyAvatarUrl() const avatarResult = await window.electronAPI.chat.getMyAvatarUrl()
if (avatarResult.success && avatarResult.avatarUrl) { if (avatarResult.success && avatarResult.avatarUrl) {
avatarUrl = avatarResult.avatarUrl patchUserProfile({ avatarUrl: avatarResult.avatarUrl }, resolvedWxid)
} }
} catch (avatarError) {
setUserProfile({ console.error('加载侧边栏用户头像失败:', avatarError)
wxid: wxid || '', }
displayName, })()
avatarUrl
})
} catch (error) { } catch (error) {
console.error('加载侧边栏用户信息失败:', error) console.error('加载侧边栏用户信息失败:', error)
} }
} }
const cachedProfile = readSidebarUserProfileCache()
if (cachedProfile) {
setUserProfile(prev => ({
...prev,
...cachedProfile
}))
}
void loadCurrentUser() void loadCurrentUser()
const onWxidChanged = () => { void loadCurrentUser() } const onWxidChanged = () => { void loadCurrentUser() }
window.addEventListener('wxid-changed', onWxidChanged as EventListener) window.addEventListener('wxid-changed', onWxidChanged as EventListener)