mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-25 07:16:51 +00:00
feat: 优化会话加载速度;优化动画表现;支持中文数据库路径
This commit is contained in:
@@ -49,6 +49,8 @@ export class WcdbService {
|
||||
private wcdbGetEmoticonCdnUrl: any = null
|
||||
private avatarUrlCache: Map<string, { url?: string; updatedAt: number }> = new Map()
|
||||
private readonly avatarCacheTtlMs = 10 * 60 * 1000
|
||||
private logTimer: NodeJS.Timeout | null = null
|
||||
private lastLogTail: string | null = null
|
||||
|
||||
setPaths(resourcesPath: string, userDataPath: string): void {
|
||||
this.resourcesPath = resourcesPath
|
||||
@@ -57,6 +59,11 @@ export class WcdbService {
|
||||
|
||||
setLogEnabled(enabled: boolean): void {
|
||||
this.logEnabled = enabled
|
||||
if (this.isLogEnabled() && this.initialized) {
|
||||
this.startLogPolling()
|
||||
} else {
|
||||
this.stopLogPolling()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -433,6 +440,49 @@ export class WcdbService {
|
||||
}
|
||||
}
|
||||
|
||||
private startLogPolling(): void {
|
||||
if (this.logTimer || !this.isLogEnabled()) return
|
||||
this.logTimer = setInterval(() => {
|
||||
void this.pollLogs()
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
private stopLogPolling(): void {
|
||||
if (this.logTimer) {
|
||||
clearInterval(this.logTimer)
|
||||
this.logTimer = null
|
||||
}
|
||||
this.lastLogTail = null
|
||||
}
|
||||
|
||||
private async pollLogs(): Promise<void> {
|
||||
try {
|
||||
if (!this.wcdbGetLogs || !this.isLogEnabled()) return
|
||||
const outPtr = [null as any]
|
||||
const result = this.wcdbGetLogs(outPtr)
|
||||
if (result !== 0 || !outPtr[0]) return
|
||||
let jsonStr = ''
|
||||
try {
|
||||
jsonStr = this.koffi.decode(outPtr[0], 'char', -1)
|
||||
} finally {
|
||||
try { this.wcdbFreeString(outPtr[0]) } catch { }
|
||||
}
|
||||
const logs = JSON.parse(jsonStr) as string[]
|
||||
if (!Array.isArray(logs) || logs.length === 0) return
|
||||
let startIdx = 0
|
||||
if (this.lastLogTail) {
|
||||
const idx = logs.lastIndexOf(this.lastLogTail)
|
||||
if (idx >= 0) startIdx = idx + 1
|
||||
}
|
||||
for (let i = startIdx; i < logs.length; i += 1) {
|
||||
this.writeLog(`wcdb: ${logs[i]}`)
|
||||
}
|
||||
this.lastLogTail = logs[logs.length - 1]
|
||||
} catch (e) {
|
||||
// ignore polling errors
|
||||
}
|
||||
}
|
||||
|
||||
private decodeJsonPtr(outPtr: any): string | null {
|
||||
if (!outPtr) return null
|
||||
try {
|
||||
@@ -545,6 +595,9 @@ export class WcdbService {
|
||||
console.warn('设置 wxid 失败:', e)
|
||||
}
|
||||
}
|
||||
if (this.isLogEnabled()) {
|
||||
this.startLogPolling()
|
||||
}
|
||||
this.writeLog(`open ok handle=${handle}`)
|
||||
return true
|
||||
} catch (e) {
|
||||
@@ -571,6 +624,7 @@ export class WcdbService {
|
||||
this.currentKey = null
|
||||
this.currentWxid = null
|
||||
this.initialized = false
|
||||
this.stopLogPolling()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "weflow",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"description": "WeFlow - 微信聊天记录查看工具",
|
||||
"main": "dist-electron/main.js",
|
||||
"author": "cc",
|
||||
|
||||
Binary file not shown.
@@ -883,6 +883,23 @@
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
-webkit-app-region: no-drag;
|
||||
position: relative;
|
||||
|
||||
&.loading .message-list {
|
||||
opacity: 0;
|
||||
transform: translateY(8px);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&.loaded .message-list {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
&.loaded .loading-overlay {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.message-list {
|
||||
@@ -898,6 +915,7 @@
|
||||
background-color: var(--bg-tertiary);
|
||||
position: relative;
|
||||
-webkit-app-region: no-drag !important;
|
||||
transition: opacity 240ms ease, transform 240ms ease;
|
||||
|
||||
// 滚动条样式
|
||||
&::-webkit-scrollbar {
|
||||
@@ -918,6 +936,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
.loading-messages.loading-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
background: rgba(10, 10, 10, 0.28);
|
||||
backdrop-filter: blur(6px);
|
||||
transition: opacity 200ms ease;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.message-list * {
|
||||
-webkit-app-region: no-drag !important;
|
||||
}
|
||||
@@ -1108,6 +1139,7 @@
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
word-break: break-word;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
// 表情包消息
|
||||
@@ -1432,6 +1464,7 @@
|
||||
|
||||
.quoted-text {
|
||||
color: var(--text-secondary);
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -108,6 +108,7 @@ function ChatPage(_props: ChatPageProps) {
|
||||
const messageListRef = useRef<HTMLDivElement>(null)
|
||||
const searchInputRef = useRef<HTMLInputElement>(null)
|
||||
const sidebarRef = useRef<HTMLDivElement>(null)
|
||||
const initialRevealTimerRef = useRef<number | null>(null)
|
||||
const [currentOffset, setCurrentOffset] = useState(0)
|
||||
const [myAvatarUrl, setMyAvatarUrl] = useState<string | undefined>(undefined)
|
||||
const [showScrollToBottom, setShowScrollToBottom] = useState(false)
|
||||
@@ -118,6 +119,7 @@ function ChatPage(_props: ChatPageProps) {
|
||||
const [isLoadingDetail, setIsLoadingDetail] = useState(false)
|
||||
const [highlightedMessageKeys, setHighlightedMessageKeys] = useState<string[]>([])
|
||||
const [isRefreshingSessions, setIsRefreshingSessions] = useState(false)
|
||||
const [hasInitialMessages, setHasInitialMessages] = useState(false)
|
||||
|
||||
|
||||
const highlightedMessageSet = useMemo(() => new Set(highlightedMessageKeys), [highlightedMessageKeys])
|
||||
@@ -126,6 +128,7 @@ function ChatPage(_props: ChatPageProps) {
|
||||
const sessionMapRef = useRef<Map<string, ChatSession>>(new Map())
|
||||
const sessionsRef = useRef<ChatSession[]>([])
|
||||
const currentSessionRef = useRef<string | null>(null)
|
||||
const prevSessionRef = useRef<string | null>(null)
|
||||
const isLoadingMessagesRef = useRef(false)
|
||||
const isLoadingMoreRef = useRef(false)
|
||||
const isConnectedRef = useRef(false)
|
||||
@@ -511,6 +514,53 @@ function ChatPage(_props: ChatPageProps) {
|
||||
isLoadingMoreRef.current = isLoadingMore
|
||||
}, [isLoadingMessages, isLoadingMore])
|
||||
|
||||
useEffect(() => {
|
||||
if (initialRevealTimerRef.current !== null) {
|
||||
window.clearTimeout(initialRevealTimerRef.current)
|
||||
initialRevealTimerRef.current = null
|
||||
}
|
||||
if (!isLoadingMessages) {
|
||||
if (messages.length === 0) {
|
||||
setHasInitialMessages(true)
|
||||
} else {
|
||||
initialRevealTimerRef.current = window.setTimeout(() => {
|
||||
setHasInitialMessages(true)
|
||||
initialRevealTimerRef.current = null
|
||||
}, 120)
|
||||
}
|
||||
}
|
||||
}, [isLoadingMessages, messages.length])
|
||||
|
||||
useEffect(() => {
|
||||
if (currentSessionId !== prevSessionRef.current) {
|
||||
prevSessionRef.current = currentSessionId
|
||||
if (initialRevealTimerRef.current !== null) {
|
||||
window.clearTimeout(initialRevealTimerRef.current)
|
||||
initialRevealTimerRef.current = null
|
||||
}
|
||||
if (messages.length === 0) {
|
||||
setHasInitialMessages(false)
|
||||
} else if (!isLoadingMessages) {
|
||||
setHasInitialMessages(true)
|
||||
}
|
||||
}
|
||||
}, [currentSessionId, messages.length, isLoadingMessages])
|
||||
|
||||
useEffect(() => {
|
||||
if (currentSessionId && messages.length === 0 && !isLoadingMessages && !isLoadingMore) {
|
||||
loadMessages(currentSessionId, 0)
|
||||
}
|
||||
}, [currentSessionId, messages.length, isLoadingMessages, isLoadingMore])
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (initialRevealTimerRef.current !== null) {
|
||||
window.clearTimeout(initialRevealTimerRef.current)
|
||||
initialRevealTimerRef.current = null
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
isConnectedRef.current = isConnected
|
||||
}, [isConnected])
|
||||
@@ -710,15 +760,15 @@ function ChatPage(_props: ChatPageProps) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="message-content-wrapper">
|
||||
{isLoadingMessages ? (
|
||||
<div className="loading-messages">
|
||||
<div className={`message-content-wrapper ${hasInitialMessages ? 'loaded' : 'loading'}`}>
|
||||
{isLoadingMessages && !hasInitialMessages && (
|
||||
<div className="loading-messages loading-overlay">
|
||||
<Loader2 size={24} />
|
||||
<span>加载消息中...</span>
|
||||
</div>
|
||||
) : (
|
||||
)}
|
||||
<div
|
||||
className="message-list"
|
||||
className={`message-list ${hasInitialMessages ? 'loaded' : 'loading'}`}
|
||||
ref={messageListRef}
|
||||
onScroll={handleScroll}
|
||||
>
|
||||
@@ -772,7 +822,6 @@ function ChatPage(_props: ChatPageProps) {
|
||||
<span>回到底部</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 会话详情面板 */}
|
||||
{showDetailPanel && (
|
||||
|
||||
Reference in New Issue
Block a user