mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-24 23:06:51 +00:00
feat(export): prioritize tab counts via lightweight api
This commit is contained in:
@@ -912,6 +912,10 @@ function registerIpcHandlers() {
|
||||
return chatService.getSessions()
|
||||
})
|
||||
|
||||
ipcMain.handle('chat:getExportTabCounts', async () => {
|
||||
return chatService.getExportTabCounts()
|
||||
})
|
||||
|
||||
ipcMain.handle('chat:enrichSessionsContactInfo', async (_, usernames: string[]) => {
|
||||
return chatService.enrichSessionsContactInfo(usernames)
|
||||
})
|
||||
|
||||
@@ -130,6 +130,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
chat: {
|
||||
connect: () => ipcRenderer.invoke('chat:connect'),
|
||||
getSessions: () => ipcRenderer.invoke('chat:getSessions'),
|
||||
getExportTabCounts: () => ipcRenderer.invoke('chat:getExportTabCounts'),
|
||||
enrichSessionsContactInfo: (usernames: string[]) =>
|
||||
ipcRenderer.invoke('chat:enrichSessionsContactInfo', usernames),
|
||||
getMessages: (sessionId: string, offset?: number, limit?: number, startTime?: number, endTime?: number, ascending?: boolean) =>
|
||||
|
||||
@@ -151,6 +151,13 @@ interface ExportSessionStats {
|
||||
groupMutualFriends?: number
|
||||
}
|
||||
|
||||
interface ExportTabCounts {
|
||||
private: number
|
||||
group: number
|
||||
official: number
|
||||
former_friend: number
|
||||
}
|
||||
|
||||
// 表情包缓存
|
||||
const emojiCache: Map<string, string> = new Map()
|
||||
const emojiDownloading: Map<string, Promise<string | null>> = new Map()
|
||||
@@ -657,6 +664,112 @@ class ChatService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取导出页会话分类数量(轻量接口,优先用于顶部 Tab 数量展示)
|
||||
*/
|
||||
async getExportTabCounts(): Promise<{ success: boolean; counts?: ExportTabCounts; error?: string }> {
|
||||
try {
|
||||
const connectResult = await this.ensureConnected()
|
||||
if (!connectResult.success) {
|
||||
return { success: false, error: connectResult.error }
|
||||
}
|
||||
|
||||
const sessionResult = await wcdbService.getSessions()
|
||||
if (!sessionResult.success || !sessionResult.sessions) {
|
||||
return { success: false, error: sessionResult.error || '获取会话失败' }
|
||||
}
|
||||
|
||||
const counts: ExportTabCounts = {
|
||||
private: 0,
|
||||
group: 0,
|
||||
official: 0,
|
||||
former_friend: 0
|
||||
}
|
||||
|
||||
const nonGroupUsernames: string[] = []
|
||||
const usernameSet = new Set<string>()
|
||||
|
||||
for (const row of sessionResult.sessions as Record<string, any>[]) {
|
||||
const username =
|
||||
row.username ||
|
||||
row.user_name ||
|
||||
row.userName ||
|
||||
row.usrName ||
|
||||
row.UsrName ||
|
||||
row.talker ||
|
||||
row.talker_id ||
|
||||
row.talkerId ||
|
||||
''
|
||||
|
||||
if (!this.shouldKeepSession(username)) continue
|
||||
if (usernameSet.has(username)) continue
|
||||
usernameSet.add(username)
|
||||
|
||||
if (username.endsWith('@chatroom')) {
|
||||
counts.group += 1
|
||||
} else {
|
||||
nonGroupUsernames.push(username)
|
||||
}
|
||||
}
|
||||
|
||||
if (nonGroupUsernames.length === 0) {
|
||||
return { success: true, counts }
|
||||
}
|
||||
|
||||
const contactTypeMap = new Map<string, 'official' | 'former_friend'>()
|
||||
const chunkSize = 400
|
||||
|
||||
for (let i = 0; i < nonGroupUsernames.length; i += chunkSize) {
|
||||
const chunk = nonGroupUsernames.slice(i, i + chunkSize)
|
||||
if (chunk.length === 0) continue
|
||||
|
||||
const usernamesExpr = chunk.map((name) => `'${this.escapeSqlString(name)}'`).join(',')
|
||||
const contactSql = `
|
||||
SELECT username, local_type, quan_pin
|
||||
FROM contact
|
||||
WHERE username IN (${usernamesExpr})
|
||||
`
|
||||
|
||||
const contactResult = await wcdbService.execQuery('contact', null, contactSql)
|
||||
if (!contactResult.success || !contactResult.rows) {
|
||||
continue
|
||||
}
|
||||
|
||||
for (const row of contactResult.rows as Record<string, any>[]) {
|
||||
const username = String(row.username || '').trim()
|
||||
if (!username) continue
|
||||
|
||||
if (username.startsWith('gh_')) {
|
||||
contactTypeMap.set(username, 'official')
|
||||
continue
|
||||
}
|
||||
|
||||
const localType = this.getRowInt(row, ['local_type', 'localType', 'WCDB_CT_local_type'], 0)
|
||||
const quanPin = String(this.getRowField(row, ['quan_pin', 'quanPin', 'WCDB_CT_quan_pin']) || '').trim()
|
||||
if (localType === 0 && quanPin) {
|
||||
contactTypeMap.set(username, 'former_friend')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const username of nonGroupUsernames) {
|
||||
const type = contactTypeMap.get(username)
|
||||
if (type === 'official') {
|
||||
counts.official += 1
|
||||
} else if (type === 'former_friend') {
|
||||
counts.former_friend += 1
|
||||
} else {
|
||||
counts.private += 1
|
||||
}
|
||||
}
|
||||
|
||||
return { success: true, counts }
|
||||
} catch (e) {
|
||||
console.error('ChatService: 获取导出页会话分类数量失败:', e)
|
||||
return { success: false, error: String(e) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取通讯录列表
|
||||
*/
|
||||
|
||||
@@ -242,10 +242,12 @@ function ExportPage() {
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [isSessionEnriching, setIsSessionEnriching] = useState(false)
|
||||
const [isTabCountsLoading, setIsTabCountsLoading] = useState(true)
|
||||
const [isSnsStatsLoading, setIsSnsStatsLoading] = useState(true)
|
||||
const [isBaseConfigLoading, setIsBaseConfigLoading] = useState(true)
|
||||
const [isTaskCenterExpanded, setIsTaskCenterExpanded] = useState(false)
|
||||
const [sessions, setSessions] = useState<SessionRow[]>([])
|
||||
const [prefetchedTabCounts, setPrefetchedTabCounts] = useState<Record<ConversationTab, number> | null>(null)
|
||||
const [sessionMetrics, setSessionMetrics] = useState<Record<string, SessionMetrics>>({})
|
||||
const [searchKeyword, setSearchKeyword] = useState('')
|
||||
const [activeTab, setActiveTab] = useState<ConversationTab>('private')
|
||||
@@ -373,6 +375,20 @@ function ExportPage() {
|
||||
}
|
||||
}, [])
|
||||
|
||||
const loadTabCounts = useCallback(async () => {
|
||||
setIsTabCountsLoading(true)
|
||||
try {
|
||||
const result = await window.electronAPI.chat.getExportTabCounts()
|
||||
if (result.success && result.counts) {
|
||||
setPrefetchedTabCounts(result.counts)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载导出页会话分类数量失败:', error)
|
||||
} finally {
|
||||
setIsTabCountsLoading(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const loadSnsStats = useCallback(async () => {
|
||||
setIsSnsStatsLoading(true)
|
||||
try {
|
||||
@@ -493,7 +509,10 @@ function ExportPage() {
|
||||
|
||||
useEffect(() => {
|
||||
void loadBaseConfig()
|
||||
void loadSessions()
|
||||
void (async () => {
|
||||
await loadTabCounts()
|
||||
await loadSessions()
|
||||
})()
|
||||
|
||||
// 朋友圈统计延后一点加载,避免与首屏会话初始化抢占。
|
||||
const timer = window.setTimeout(() => {
|
||||
@@ -501,7 +520,7 @@ function ExportPage() {
|
||||
}, 180)
|
||||
|
||||
return () => window.clearTimeout(timer)
|
||||
}, [loadBaseConfig, loadSessions, loadSnsStats])
|
||||
}, [loadTabCounts, loadBaseConfig, loadSessions, loadSnsStats])
|
||||
|
||||
useEffect(() => {
|
||||
preselectAppliedRef.current = false
|
||||
@@ -1057,7 +1076,7 @@ function ExportPage() {
|
||||
return set
|
||||
}, [tasks])
|
||||
|
||||
const tabCounts = useMemo(() => {
|
||||
const sessionTabCounts = useMemo(() => {
|
||||
const counts: Record<ConversationTab, number> = {
|
||||
private: 0,
|
||||
group: 0,
|
||||
@@ -1070,6 +1089,16 @@ function ExportPage() {
|
||||
return counts
|
||||
}, [sessions])
|
||||
|
||||
const tabCounts = useMemo(() => {
|
||||
if (sessions.length > 0) {
|
||||
return sessionTabCounts
|
||||
}
|
||||
if (prefetchedTabCounts) {
|
||||
return prefetchedTabCounts
|
||||
}
|
||||
return sessionTabCounts
|
||||
}, [sessions.length, sessionTabCounts, prefetchedTabCounts])
|
||||
|
||||
const contentCards = useMemo(() => {
|
||||
const scopeSessions = sessions.filter(session => session.kind === 'private' || session.kind === 'group')
|
||||
const totalSessions = scopeSessions.length
|
||||
@@ -1295,7 +1324,8 @@ function ExportPage() {
|
||||
const formatCandidateOptions = exportDialog.scope === 'sns'
|
||||
? formatOptions.filter(option => option.value === 'html' || option.value === 'json')
|
||||
: formatOptions
|
||||
const isTabCountComputing = isLoading || isSessionEnriching
|
||||
const hasTabCountsSource = prefetchedTabCounts !== null || sessions.length > 0
|
||||
const isTabCountComputing = isTabCountsLoading && !hasTabCountsSource
|
||||
const isSessionCardStatsLoading = isLoading || isBaseConfigLoading
|
||||
const taskRunningCount = tasks.filter(task => task.status === 'running').length
|
||||
const taskQueuedCount = tasks.filter(task => task.status === 'queued').length
|
||||
|
||||
10
src/types/electron.d.ts
vendored
10
src/types/electron.d.ts
vendored
@@ -74,6 +74,16 @@ export interface ElectronAPI {
|
||||
chat: {
|
||||
connect: () => Promise<{ success: boolean; error?: string }>
|
||||
getSessions: () => Promise<{ success: boolean; sessions?: ChatSession[]; error?: string }>
|
||||
getExportTabCounts: () => Promise<{
|
||||
success: boolean
|
||||
counts?: {
|
||||
private: number
|
||||
group: number
|
||||
official: number
|
||||
former_friend: number
|
||||
}
|
||||
error?: string
|
||||
}>
|
||||
enrichSessionsContactInfo: (usernames: string[]) => Promise<{
|
||||
success: boolean
|
||||
contacts?: Record<string, { displayName?: string; avatarUrl?: string }>
|
||||
|
||||
Reference in New Issue
Block a user