mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-05-06 07:26:48 +00:00
支持一键已读
This commit is contained in:
@@ -2253,6 +2253,10 @@ function registerIpcHandlers() {
|
|||||||
return chatService.getSessions()
|
return chatService.getSessions()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('chat:markAllSessionsRead', async () => {
|
||||||
|
return chatService.markAllSessionsRead()
|
||||||
|
})
|
||||||
|
|
||||||
ipcMain.handle('chat:getSessionStatuses', async (_, usernames: string[]) => {
|
ipcMain.handle('chat:getSessionStatuses', async (_, usernames: string[]) => {
|
||||||
return chatService.getSessionStatuses(usernames)
|
return chatService.getSessionStatuses(usernames)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -185,6 +185,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
chat: {
|
chat: {
|
||||||
connect: () => ipcRenderer.invoke('chat:connect'),
|
connect: () => ipcRenderer.invoke('chat:connect'),
|
||||||
getSessions: () => ipcRenderer.invoke('chat:getSessions'),
|
getSessions: () => ipcRenderer.invoke('chat:getSessions'),
|
||||||
|
markAllSessionsRead: () => ipcRenderer.invoke('chat:markAllSessionsRead'),
|
||||||
getAntiRevokeSessions: () => ipcRenderer.invoke('chat:getAntiRevokeSessions'),
|
getAntiRevokeSessions: () => ipcRenderer.invoke('chat:getAntiRevokeSessions'),
|
||||||
getSessionStatuses: (usernames: string[]) => ipcRenderer.invoke('chat:getSessionStatuses', usernames),
|
getSessionStatuses: (usernames: string[]) => ipcRenderer.invoke('chat:getSessionStatuses', usernames),
|
||||||
getExportTabCounts: () => ipcRenderer.invoke('chat:getExportTabCounts'),
|
getExportTabCounts: () => ipcRenderer.invoke('chat:getExportTabCounts'),
|
||||||
|
|||||||
@@ -978,6 +978,23 @@ class ChatService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async markAllSessionsRead(): Promise<{ success: boolean; error?: string }> {
|
||||||
|
try {
|
||||||
|
const connectResult = await this.ensureConnected()
|
||||||
|
if (!connectResult.success) {
|
||||||
|
return { success: false, error: connectResult.error }
|
||||||
|
}
|
||||||
|
const result = await wcdbService.markAllSessionsRead()
|
||||||
|
if (result.success) {
|
||||||
|
this.syntheticUnreadState.clear()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} catch (e) {
|
||||||
|
console.error('ChatService: 一键已读失败:', e)
|
||||||
|
return { success: false, error: String(e) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private getSessionUsername(row: Record<string, any>): string {
|
private getSessionUsername(row: Record<string, any>): string {
|
||||||
return String(
|
return String(
|
||||||
row.username ||
|
row.username ||
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export class WcdbCore {
|
|||||||
private wcdbUpdateMessage: any = null
|
private wcdbUpdateMessage: any = null
|
||||||
private wcdbDeleteMessage: any = null
|
private wcdbDeleteMessage: any = null
|
||||||
private wcdbGetSessions: any = null
|
private wcdbGetSessions: any = null
|
||||||
|
private wcdbMarkAllSessionsRead: any = null
|
||||||
private wcdbGetMessages: any = null
|
private wcdbGetMessages: any = null
|
||||||
private wcdbGetMessageCount: any = null
|
private wcdbGetMessageCount: any = null
|
||||||
private wcdbGetDisplayNames: any = null
|
private wcdbGetDisplayNames: any = null
|
||||||
@@ -810,6 +811,13 @@ export class WcdbCore {
|
|||||||
// wcdb_status wcdb_get_sessions(wcdb_handle handle, char** out_json)
|
// wcdb_status wcdb_get_sessions(wcdb_handle handle, char** out_json)
|
||||||
this.wcdbGetSessions = this.lib.func('int32 wcdb_get_sessions(int64 handle, _Out_ void** outJson)')
|
this.wcdbGetSessions = this.lib.func('int32 wcdb_get_sessions(int64 handle, _Out_ void** outJson)')
|
||||||
|
|
||||||
|
// wcdb_status wcdb_mark_all_sessions_read(wcdb_handle handle, char** out_error)
|
||||||
|
try {
|
||||||
|
this.wcdbMarkAllSessionsRead = this.lib.func('int32 wcdb_mark_all_sessions_read(int64 handle, _Out_ void** outError)')
|
||||||
|
} catch {
|
||||||
|
this.wcdbMarkAllSessionsRead = null
|
||||||
|
}
|
||||||
|
|
||||||
// wcdb_status wcdb_get_messages(wcdb_handle handle, const char* username, int32_t limit, int32_t offset, char** out_json)
|
// wcdb_status wcdb_get_messages(wcdb_handle handle, const char* username, int32_t limit, int32_t offset, char** out_json)
|
||||||
this.wcdbGetMessages = this.lib.func('int32 wcdb_get_messages(int64 handle, const char* username, int32 limit, int32 offset, _Out_ void** outJson)')
|
this.wcdbGetMessages = this.lib.func('int32 wcdb_get_messages(int64 handle, const char* username, int32 limit, int32 offset, _Out_ void** outJson)')
|
||||||
|
|
||||||
@@ -1697,6 +1705,39 @@ export class WcdbCore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async markAllSessionsRead(): Promise<{ success: boolean; error?: string }> {
|
||||||
|
if (!this.ensureReady()) {
|
||||||
|
return { success: false, error: 'WCDB 未连接' }
|
||||||
|
}
|
||||||
|
if (!this.wcdbMarkAllSessionsRead) {
|
||||||
|
return { success: false, error: '当前数据服务版本不支持一键已读' }
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await new Promise(resolve => setImmediate(resolve))
|
||||||
|
|
||||||
|
const outPtr = [null as any]
|
||||||
|
const result = this.wcdbMarkAllSessionsRead(this.handle, outPtr)
|
||||||
|
let message = ''
|
||||||
|
if (outPtr[0]) {
|
||||||
|
try { message = this.koffi.decode(outPtr[0], 'char', -1) } catch { }
|
||||||
|
try { this.wcdbFreeString(outPtr[0]) } catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
await new Promise(resolve => setImmediate(resolve))
|
||||||
|
|
||||||
|
if (result !== 0) {
|
||||||
|
this.writeLog(`markAllSessionsRead failed: code=${result} error=${message}`)
|
||||||
|
return { success: false, error: message || `一键已读失败: ${result}` }
|
||||||
|
}
|
||||||
|
this.clearMediaStreamSessionCache()
|
||||||
|
this.writeLog('markAllSessionsRead ok')
|
||||||
|
return { success: true }
|
||||||
|
} catch (e) {
|
||||||
|
this.writeLog(`markAllSessionsRead exception: ${String(e)}`)
|
||||||
|
return { success: false, error: String(e) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async getMessages(sessionId: string, limit: number, offset: number): Promise<{ success: boolean; messages?: any[]; error?: string }> {
|
async getMessages(sessionId: string, limit: number, offset: number): Promise<{ success: boolean; messages?: any[]; error?: string }> {
|
||||||
if (!this.ensureReady()) {
|
if (!this.ensureReady()) {
|
||||||
return { success: false, error: 'WCDB 未连接' }
|
return { success: false, error: 'WCDB 未连接' }
|
||||||
|
|||||||
@@ -204,6 +204,10 @@ export class WcdbService {
|
|||||||
return this.callWorker('getSessions')
|
return this.callWorker('getSessions')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async markAllSessionsRead(): Promise<{ success: boolean; error?: string }> {
|
||||||
|
return this.callWorker('markAllSessionsRead')
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取消息列表
|
* 获取消息列表
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ if (parentPort) {
|
|||||||
case 'getSessions':
|
case 'getSessions':
|
||||||
result = await core.getSessions()
|
result = await core.getSessions()
|
||||||
break
|
break
|
||||||
|
case 'markAllSessionsRead':
|
||||||
|
result = await core.markAllSessionsRead()
|
||||||
|
break
|
||||||
case 'getMessages':
|
case 'getMessages':
|
||||||
result = await core.getMessages(payload.sessionId, payload.limit, payload.offset)
|
result = await core.getMessages(payload.sessionId, payload.limit, payload.offset)
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -1453,6 +1453,7 @@ function ChatPage(props: ChatPageProps) {
|
|||||||
const [showScrollToBottom, setShowScrollToBottom] = useState(false)
|
const [showScrollToBottom, setShowScrollToBottom] = useState(false)
|
||||||
const [sidebarWidth, setSidebarWidth] = useState(260)
|
const [sidebarWidth, setSidebarWidth] = useState(260)
|
||||||
const [isResizing, setIsResizing] = useState(false)
|
const [isResizing, setIsResizing] = useState(false)
|
||||||
|
const [isMarkingAllSessionsRead, setIsMarkingAllSessionsRead] = useState(false)
|
||||||
const [showDetailPanel, setShowDetailPanel] = useState(false)
|
const [showDetailPanel, setShowDetailPanel] = useState(false)
|
||||||
const [showGroupMembersPanel, setShowGroupMembersPanel] = useState(false)
|
const [showGroupMembersPanel, setShowGroupMembersPanel] = useState(false)
|
||||||
const [sessionDetail, setSessionDetail] = useState<SessionDetail | null>(null)
|
const [sessionDetail, setSessionDetail] = useState<SessionDetail | null>(null)
|
||||||
@@ -3130,6 +3131,35 @@ function ChatPage(props: ChatPageProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleMarkAllSessionsRead = async () => {
|
||||||
|
if (isMarkingAllSessionsRead || isLoadingSessions || isRefreshingSessions) return
|
||||||
|
setIsMarkingAllSessionsRead(true)
|
||||||
|
setConnectionError(null)
|
||||||
|
try {
|
||||||
|
const result = await window.electronAPI.chat.markAllSessionsRead()
|
||||||
|
if (!result.success) {
|
||||||
|
setConnectionError(result.error || '一键已读失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const latestSessions = useChatStore.getState().sessions || []
|
||||||
|
const nextSessions = latestSessions.map((session) => (
|
||||||
|
session.unreadCount > 0 ? { ...session, unreadCount: 0 } : session
|
||||||
|
))
|
||||||
|
setSessions(nextSessions)
|
||||||
|
sessionsRef.current = nextSessions
|
||||||
|
|
||||||
|
const scope = await resolveChatCacheScope()
|
||||||
|
persistSessionListCache(scope, nextSessions)
|
||||||
|
await loadSessions({ silent: true })
|
||||||
|
} catch (e) {
|
||||||
|
console.error('一键已读失败:', e)
|
||||||
|
setConnectionError(`一键已读失败: ${String(e)}`)
|
||||||
|
} finally {
|
||||||
|
setIsMarkingAllSessionsRead(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 分批异步加载联系人信息(优化:缓存优先 + 可持续队列 + 首屏优先批次)
|
// 分批异步加载联系人信息(优化:缓存优先 + 可持续队列 + 首屏优先批次)
|
||||||
const enrichSessionsContactInfo = async (sessions: ChatSession[]) => {
|
const enrichSessionsContactInfo = async (sessions: ChatSession[]) => {
|
||||||
if (Array.isArray(sessions) && sessions.length > 0) {
|
if (Array.isArray(sessions) && sessions.length > 0) {
|
||||||
@@ -6775,6 +6805,15 @@ function ChatPage(props: ChatPageProps) {
|
|||||||
<button className="icon-btn refresh-btn" onClick={handleRefresh} disabled={isLoadingSessions || isRefreshingSessions}>
|
<button className="icon-btn refresh-btn" onClick={handleRefresh} disabled={isLoadingSessions || isRefreshingSessions}>
|
||||||
<RefreshCw size={16} className={(isLoadingSessions || isRefreshingSessions) ? 'spin' : ''} />
|
<RefreshCw size={16} className={(isLoadingSessions || isRefreshingSessions) ? 'spin' : ''} />
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
className="icon-btn refresh-btn mark-read-btn"
|
||||||
|
onClick={handleMarkAllSessionsRead}
|
||||||
|
disabled={isMarkingAllSessionsRead || isLoadingSessions || isRefreshingSessions}
|
||||||
|
title="一键已读"
|
||||||
|
aria-label="一键已读"
|
||||||
|
>
|
||||||
|
{isMarkingAllSessionsRead ? <Loader2 size={16} className="spin" /> : <CheckSquare size={16} />}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* 折叠群 header */}
|
{/* 折叠群 header */}
|
||||||
|
|||||||
1
src/types/electron.d.ts
vendored
1
src/types/electron.d.ts
vendored
@@ -271,6 +271,7 @@ export interface ElectronAPI {
|
|||||||
chat: {
|
chat: {
|
||||||
connect: () => Promise<{ success: boolean; error?: string }>
|
connect: () => Promise<{ success: boolean; error?: string }>
|
||||||
getSessions: () => Promise<{ success: boolean; sessions?: ChatSession[]; error?: string }>
|
getSessions: () => Promise<{ success: boolean; sessions?: ChatSession[]; error?: string }>
|
||||||
|
markAllSessionsRead: () => Promise<{ success: boolean; error?: string }>
|
||||||
getAntiRevokeSessions: () => Promise<{ success: boolean; sessions?: ChatSession[]; error?: string }>
|
getAntiRevokeSessions: () => Promise<{ success: boolean; sessions?: ChatSession[]; error?: string }>
|
||||||
getSessionStatuses: (usernames: string[]) => Promise<{
|
getSessionStatuses: (usernames: string[]) => Promise<{
|
||||||
success: boolean
|
success: boolean
|
||||||
|
|||||||
Reference in New Issue
Block a user