mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-08-21 21:47:00 +08:00
refactor: 拆分 AI 搜索证据集合 Hook
This commit is contained in:
@@ -9,7 +9,6 @@ import type {
|
||||
|
||||
import type {
|
||||
AISearchWorkspaceProps,
|
||||
EvidenceItem,
|
||||
SearchProgressByStage,
|
||||
SearchRange,
|
||||
SearchScope,
|
||||
@@ -42,8 +41,7 @@ import { createSearchResultResetState } from './searchState'
|
||||
import { useSearchHistory } from './hooks/useSearchHistory'
|
||||
import { useKnowledgeStatus } from './hooks/useKnowledgeStatus'
|
||||
import { useExternalProviderConsent } from './hooks/useExternalProviderConsent'
|
||||
|
||||
const EVIDENCE_PAGE_SIZE = 8
|
||||
import { EVIDENCE_PAGE_SIZE, useEvidenceCollection } from './hooks/useEvidenceCollection'
|
||||
|
||||
export function AISearchWorkspace({
|
||||
contacts,
|
||||
@@ -64,10 +62,6 @@ export function AISearchWorkspace({
|
||||
const [resultQuery, setResultQuery] = useState('')
|
||||
const [stage, setStage] = useState<SearchStage>('idle')
|
||||
const [answer, setAnswer] = useState('')
|
||||
const [evidence, setEvidence] = useState<EvidenceItem[]>([])
|
||||
const [evidenceCollection, setEvidenceCollection] = useState<EvidenceItem[]>([])
|
||||
const [visibleEvidenceCount, setVisibleEvidenceCount] = useState(0)
|
||||
const [selectedEvidence, setSelectedEvidence] = useState(0)
|
||||
const [analysisError, setAnalysisError] = useState('')
|
||||
const [messageCount, setMessageCount] = useState(0)
|
||||
const [senderNames, setSenderNames] = useState<Record<string, string>>({})
|
||||
@@ -83,12 +77,24 @@ export function AISearchWorkspace({
|
||||
const [appLogPath, setAppLogPath] = useState('')
|
||||
const searchRequestIdRef = useRef('')
|
||||
const composerRef = useRef<HTMLTextAreaElement>(null)
|
||||
const evidenceCardRefs = useRef(new Map<number, HTMLElement>())
|
||||
const [evidenceFlash, setEvidenceFlash] = useState({ index: -1, nonce: 0 })
|
||||
const visibleEvidence = useMemo(
|
||||
() => evidenceCollection.slice(0, visibleEvidenceCount),
|
||||
[evidenceCollection, visibleEvidenceCount]
|
||||
)
|
||||
const {
|
||||
evidence,
|
||||
setEvidence,
|
||||
evidenceCollection,
|
||||
setEvidenceCollection,
|
||||
setVisibleEvidenceCount,
|
||||
selectedEvidence,
|
||||
setSelectedEvidence,
|
||||
visibleEvidence,
|
||||
hasMoreEvidence,
|
||||
evidenceFlash,
|
||||
setEvidenceResult,
|
||||
clearEvidenceCollection,
|
||||
loadMoreEvidence,
|
||||
focusEvidence,
|
||||
jumpToEvidence,
|
||||
setEvidenceCardRef
|
||||
} = useEvidenceCollection({ onOpenEvidence })
|
||||
|
||||
const {
|
||||
history,
|
||||
@@ -149,10 +155,7 @@ export function AISearchWorkspace({
|
||||
const reset = createSearchResultResetState()
|
||||
setAnalysisError(reset.analysisError)
|
||||
setAnswer(reset.answer)
|
||||
setEvidence(reset.evidence)
|
||||
setEvidenceCollection(reset.evidenceCollection)
|
||||
setVisibleEvidenceCount(reset.visibleEvidenceCount)
|
||||
setSelectedEvidence(reset.selectedEvidence)
|
||||
clearEvidenceCollection()
|
||||
setCachedAt(reset.cachedAt)
|
||||
setSearchTrace(reset.searchTrace)
|
||||
setSearchProgress(reset.searchProgress)
|
||||
@@ -160,21 +163,6 @@ export function AISearchWorkspace({
|
||||
setSearchDetailsOpen(reset.searchDetailsOpen)
|
||||
}
|
||||
|
||||
const focusEvidence = (index: number): void => {
|
||||
if (!Number.isInteger(index) || index < 0 || index >= evidenceCollection.length) return
|
||||
setVisibleEvidenceCount((current) => Math.max(current, index + 1))
|
||||
setSelectedEvidence(index)
|
||||
setEvidenceFlash((current) => ({ index, nonce: current.nonce + 1 }))
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
if (evidenceFlash.index < 0) return
|
||||
evidenceCardRefs.current.get(evidenceFlash.index)?.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'nearest'
|
||||
})
|
||||
}, [evidenceFlash])
|
||||
|
||||
React.useEffect(() => {
|
||||
void Promise.all([window.api.getSettings(), window.api.getAppLogPath()]).then(
|
||||
([settingsResult, logPath]) => {
|
||||
@@ -355,9 +343,7 @@ export function AISearchWorkspace({
|
||||
)
|
||||
setSearchTrace(mapSearchResultToTrace(searchResult, evidenceItems.length))
|
||||
setAgentTrace(searchResult.agent.trace)
|
||||
setEvidence(evidenceItems)
|
||||
setEvidenceCollection(collectionItems)
|
||||
setVisibleEvidenceCount(Math.min(EVIDENCE_PAGE_SIZE, collectionItems.length))
|
||||
setEvidenceResult(evidenceItems, collectionItems)
|
||||
const nextSenderNames = mapEvidenceSenderNames(evidenceItems)
|
||||
setSenderNames(nextSenderNames)
|
||||
setMessageCount(searchResult.knowledge.totalMessages)
|
||||
@@ -1187,8 +1173,7 @@ export function AISearchWorkspace({
|
||||
<article
|
||||
key={`${messageIdentity(item.message)}-${index}-${evidenceFlash.index === index ? evidenceFlash.nonce : 0}`}
|
||||
ref={(node) => {
|
||||
if (node) evidenceCardRefs.current.set(index, node)
|
||||
else evidenceCardRefs.current.delete(index)
|
||||
setEvidenceCardRef(index, node)
|
||||
}}
|
||||
className={`ai-search-evidence-card ${selectedEvidence === index ? 'active' : ''} ${evidenceFlash.index === index ? 'focus-flash' : ''}`}
|
||||
style={{ animationDelay: `${Math.min(index, 7) * 45}ms` }}
|
||||
@@ -1213,7 +1198,7 @@ export function AISearchWorkspace({
|
||||
className="ai-search-evidence-link"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
onOpenEvidence(item.contact, item.message.createTime)
|
||||
jumpToEvidence(index)
|
||||
}}
|
||||
>
|
||||
跳转到原聊天 ↗
|
||||
@@ -1227,15 +1212,11 @@ export function AISearchWorkspace({
|
||||
<span>分析完成后,这里会显示支持结论的原始消息。</span>
|
||||
</div>
|
||||
)}
|
||||
{visibleEvidence.length > 0 && visibleEvidence.length < evidenceCollection.length && (
|
||||
{hasMoreEvidence && (
|
||||
<button
|
||||
type="button"
|
||||
className="ai-search-evidence-load-more"
|
||||
onClick={() =>
|
||||
setVisibleEvidenceCount((current) =>
|
||||
Math.min(current + EVIDENCE_PAGE_SIZE, evidenceCollection.length)
|
||||
)
|
||||
}
|
||||
onClick={loadMoreEvidence}
|
||||
>
|
||||
加载更多证据
|
||||
</button>
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
import { useEffect, useMemo, useRef, useState, type Dispatch, type SetStateAction } from 'react'
|
||||
import type { Contact } from '../../../../../shared/types'
|
||||
import type { EvidenceItem } from '../searchTypes'
|
||||
|
||||
export const EVIDENCE_PAGE_SIZE = 8
|
||||
|
||||
type UseEvidenceCollectionOptions = {
|
||||
onOpenEvidence: (contact: Contact, createTime?: number) => void
|
||||
}
|
||||
|
||||
export function useEvidenceCollection({ onOpenEvidence }: UseEvidenceCollectionOptions): {
|
||||
evidence: EvidenceItem[]
|
||||
setEvidence: Dispatch<SetStateAction<EvidenceItem[]>>
|
||||
evidenceCollection: EvidenceItem[]
|
||||
setEvidenceCollection: Dispatch<SetStateAction<EvidenceItem[]>>
|
||||
visibleEvidenceCount: number
|
||||
setVisibleEvidenceCount: Dispatch<SetStateAction<number>>
|
||||
selectedEvidence: number
|
||||
setSelectedEvidence: Dispatch<SetStateAction<number>>
|
||||
visibleEvidence: EvidenceItem[]
|
||||
hasMoreEvidence: boolean
|
||||
evidenceFlash: { index: number; nonce: number }
|
||||
evidenceCardRefs: React.MutableRefObject<Map<number, HTMLElement>>
|
||||
setEvidenceResult: (summaryEvidence: EvidenceItem[], collection: EvidenceItem[]) => void
|
||||
clearEvidenceCollection: () => void
|
||||
loadMoreEvidence: () => void
|
||||
focusEvidence: (index: number) => void
|
||||
jumpToEvidence: (index: number) => void
|
||||
setEvidenceCardRef: (index: number, node: HTMLElement | null) => void
|
||||
} {
|
||||
const [evidence, setEvidence] = useState<EvidenceItem[]>([])
|
||||
const [evidenceCollection, setEvidenceCollection] = useState<EvidenceItem[]>([])
|
||||
const [visibleEvidenceCount, setVisibleEvidenceCount] = useState(0)
|
||||
const [selectedEvidence, setSelectedEvidence] = useState(0)
|
||||
const [evidenceFlash, setEvidenceFlash] = useState({ index: -1, nonce: 0 })
|
||||
const evidenceCardRefs = useRef(new Map<number, HTMLElement>())
|
||||
const visibleEvidence = useMemo(
|
||||
() => evidenceCollection.slice(0, visibleEvidenceCount),
|
||||
[evidenceCollection, visibleEvidenceCount]
|
||||
)
|
||||
const hasMoreEvidence =
|
||||
visibleEvidence.length > 0 && visibleEvidence.length < evidenceCollection.length
|
||||
|
||||
const setEvidenceResult = (summaryEvidence: EvidenceItem[], collection: EvidenceItem[]): void => {
|
||||
setEvidence(summaryEvidence)
|
||||
setEvidenceCollection(collection)
|
||||
setVisibleEvidenceCount(Math.min(EVIDENCE_PAGE_SIZE, collection.length))
|
||||
}
|
||||
|
||||
const clearEvidenceCollection = (): void => {
|
||||
setEvidence([])
|
||||
setEvidenceCollection([])
|
||||
setVisibleEvidenceCount(0)
|
||||
setSelectedEvidence(0)
|
||||
}
|
||||
|
||||
const loadMoreEvidence = (): void => {
|
||||
setVisibleEvidenceCount((current) =>
|
||||
Math.min(current + EVIDENCE_PAGE_SIZE, evidenceCollection.length)
|
||||
)
|
||||
}
|
||||
|
||||
const focusEvidence = (index: number): void => {
|
||||
if (!Number.isInteger(index) || index < 0 || index >= evidenceCollection.length) return
|
||||
setVisibleEvidenceCount((current) => Math.max(current, index + 1))
|
||||
setSelectedEvidence(index)
|
||||
setEvidenceFlash((current) => ({ index, nonce: current.nonce + 1 }))
|
||||
}
|
||||
|
||||
const jumpToEvidence = (index: number): void => {
|
||||
const item = evidenceCollection[index]
|
||||
if (!item) return
|
||||
onOpenEvidence(item.contact, item.message.createTime)
|
||||
}
|
||||
|
||||
const setEvidenceCardRef = (index: number, node: HTMLElement | null): void => {
|
||||
if (node) evidenceCardRefs.current.set(index, node)
|
||||
else evidenceCardRefs.current.delete(index)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (evidenceFlash.index < 0) return
|
||||
evidenceCardRefs.current.get(evidenceFlash.index)?.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'nearest'
|
||||
})
|
||||
}, [evidenceFlash])
|
||||
|
||||
return {
|
||||
evidence,
|
||||
setEvidence,
|
||||
evidenceCollection,
|
||||
setEvidenceCollection,
|
||||
visibleEvidenceCount,
|
||||
setVisibleEvidenceCount,
|
||||
selectedEvidence,
|
||||
setSelectedEvidence,
|
||||
visibleEvidence,
|
||||
hasMoreEvidence,
|
||||
evidenceFlash,
|
||||
evidenceCardRefs,
|
||||
setEvidenceResult,
|
||||
clearEvidenceCollection,
|
||||
loadMoreEvidence,
|
||||
focusEvidence,
|
||||
jumpToEvidence,
|
||||
setEvidenceCardRef
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user