mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-24 23:06:51 +00:00
fix: include reverse mutual friends
This commit is contained in:
@@ -452,6 +452,12 @@
|
|||||||
border-color: color-mix(in srgb, var(--primary) 35%, var(--border-color));
|
border-color: color-mix(in srgb, var(--primary) 35%, var(--border-color));
|
||||||
background: color-mix(in srgb, var(--primary) 10%, var(--bg-secondary));
|
background: color-mix(in srgb, var(--primary) 10%, var(--bg-secondary));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.reverse {
|
||||||
|
color: #92400e;
|
||||||
|
border-color: color-mix(in srgb, #d97706 38%, var(--border-color));
|
||||||
|
background: color-mix(in srgb, #f59e0b 12%, var(--bg-secondary));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.session-mutual-friends-count,
|
.session-mutual-friends-count,
|
||||||
|
|||||||
@@ -489,6 +489,16 @@ const getAvatarLetter = (name: string): string => {
|
|||||||
return [...name][0] || '?'
|
return [...name][0] || '?'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const toComparableNameSet = (values: Array<string | undefined | null>): Set<string> => {
|
||||||
|
const set = new Set<string>()
|
||||||
|
for (const value of values) {
|
||||||
|
const normalized = String(value || '').trim()
|
||||||
|
if (!normalized) continue
|
||||||
|
set.add(normalized)
|
||||||
|
}
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
const matchesContactTab = (contact: ContactInfo, tab: ConversationTab): boolean => {
|
const matchesContactTab = (contact: ContactInfo, tab: ConversationTab): boolean => {
|
||||||
if (tab === 'private') return contact.type === 'friend'
|
if (tab === 'private') return contact.type === 'friend'
|
||||||
if (tab === 'group') return contact.type === 'group'
|
if (tab === 'group') return contact.type === 'group'
|
||||||
@@ -564,7 +574,7 @@ interface SessionSnsRankItem {
|
|||||||
latestTime: number
|
latestTime: number
|
||||||
}
|
}
|
||||||
|
|
||||||
type SessionMutualFriendSource = 'likes' | 'comments' | 'both'
|
type SessionMutualFriendSource = 'likes' | 'comments' | 'both' | 'reverse'
|
||||||
|
|
||||||
interface SessionMutualFriendItem {
|
interface SessionMutualFriendItem {
|
||||||
name: string
|
name: string
|
||||||
@@ -703,6 +713,7 @@ const buildSessionMutualFriendsMetric = (
|
|||||||
|
|
||||||
const getSessionMutualFriendSourceLabel = (source: SessionMutualFriendSource): string => {
|
const getSessionMutualFriendSourceLabel = (source: SessionMutualFriendSource): string => {
|
||||||
if (source === 'both') return '点赞/评论'
|
if (source === 'both') return '点赞/评论'
|
||||||
|
if (source === 'reverse') return '反向关联'
|
||||||
if (source === 'likes') return '仅点赞'
|
if (source === 'likes') return '仅点赞'
|
||||||
return '仅评论'
|
return '仅评论'
|
||||||
}
|
}
|
||||||
@@ -1496,6 +1507,7 @@ function ExportPage() {
|
|||||||
endIndex: -1
|
endIndex: -1
|
||||||
})
|
})
|
||||||
const sessionMutualFriendsMetricsRef = useRef<Record<string, SessionMutualFriendsMetric>>({})
|
const sessionMutualFriendsMetricsRef = useRef<Record<string, SessionMutualFriendsMetric>>({})
|
||||||
|
const sessionMutualFriendsDirectMetricsRef = useRef<Record<string, SessionMutualFriendsMetric>>({})
|
||||||
const sessionMutualFriendsQueueRef = useRef<string[]>([])
|
const sessionMutualFriendsQueueRef = useRef<string[]>([])
|
||||||
const sessionMutualFriendsQueuedSetRef = useRef<Set<string>>(new Set())
|
const sessionMutualFriendsQueuedSetRef = useRef<Set<string>>(new Set())
|
||||||
const sessionMutualFriendsLoadingSetRef = useRef<Set<string>>(new Set())
|
const sessionMutualFriendsLoadingSetRef = useRef<Set<string>>(new Set())
|
||||||
@@ -2636,6 +2648,7 @@ function ExportPage() {
|
|||||||
|
|
||||||
const resetSessionMutualFriendsLoader = useCallback(() => {
|
const resetSessionMutualFriendsLoader = useCallback(() => {
|
||||||
sessionMutualFriendsRunIdRef.current += 1
|
sessionMutualFriendsRunIdRef.current += 1
|
||||||
|
sessionMutualFriendsDirectMetricsRef.current = {}
|
||||||
sessionMutualFriendsQueueRef.current = []
|
sessionMutualFriendsQueueRef.current = []
|
||||||
sessionMutualFriendsQueuedSetRef.current.clear()
|
sessionMutualFriendsQueuedSetRef.current.clear()
|
||||||
sessionMutualFriendsLoadingSetRef.current.clear()
|
sessionMutualFriendsLoadingSetRef.current.clear()
|
||||||
@@ -2689,6 +2702,109 @@ function ExportPage() {
|
|||||||
snsUserPostCountsStatus === 'idle'
|
snsUserPostCountsStatus === 'idle'
|
||||||
), [snsUserPostCountsStatus])
|
), [snsUserPostCountsStatus])
|
||||||
|
|
||||||
|
const getSessionMutualFriendProfile = useCallback((sessionId: string): {
|
||||||
|
displayName: string
|
||||||
|
candidateNames: Set<string>
|
||||||
|
} => {
|
||||||
|
const normalizedSessionId = String(sessionId || '').trim()
|
||||||
|
const contact = contactsList.find(item => item.username === normalizedSessionId)
|
||||||
|
const session = sessionsRef.current.find(item => item.username === normalizedSessionId)
|
||||||
|
const displayName = contact?.displayName || contact?.remark || contact?.nickname || session?.displayName || normalizedSessionId
|
||||||
|
return {
|
||||||
|
displayName,
|
||||||
|
candidateNames: toComparableNameSet([
|
||||||
|
displayName,
|
||||||
|
contact?.displayName,
|
||||||
|
contact?.remark,
|
||||||
|
contact?.nickname,
|
||||||
|
contact?.alias
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}, [contactsList])
|
||||||
|
|
||||||
|
const rebuildSessionMutualFriendsMetric = useCallback((targetSessionId: string): SessionMutualFriendsMetric | null => {
|
||||||
|
const normalizedTargetSessionId = String(targetSessionId || '').trim()
|
||||||
|
if (!normalizedTargetSessionId) return null
|
||||||
|
|
||||||
|
const directMetrics = sessionMutualFriendsDirectMetricsRef.current
|
||||||
|
const directMetric = directMetrics[normalizedTargetSessionId]
|
||||||
|
if (!directMetric) return null
|
||||||
|
|
||||||
|
const { candidateNames } = getSessionMutualFriendProfile(normalizedTargetSessionId)
|
||||||
|
const mergedMap = new Map<string, SessionMutualFriendItem>()
|
||||||
|
for (const item of directMetric.items) {
|
||||||
|
mergedMap.set(item.name, { ...item })
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [sourceSessionId, sourceMetric] of Object.entries(directMetrics)) {
|
||||||
|
if (!sourceMetric || sourceSessionId === normalizedTargetSessionId) continue
|
||||||
|
const sourceProfile = getSessionMutualFriendProfile(sourceSessionId)
|
||||||
|
if (!sourceProfile.displayName) continue
|
||||||
|
if (mergedMap.has(sourceProfile.displayName)) continue
|
||||||
|
|
||||||
|
const reverseMatches = sourceMetric.items.filter(item => candidateNames.has(item.name))
|
||||||
|
if (reverseMatches.length === 0) continue
|
||||||
|
|
||||||
|
const reverseCount = reverseMatches.reduce((sum, item) => sum + item.totalCount, 0)
|
||||||
|
const reverseLatestTime = reverseMatches.reduce((latest, item) => Math.max(latest, item.latestTime), 0)
|
||||||
|
mergedMap.set(sourceProfile.displayName, {
|
||||||
|
name: sourceProfile.displayName,
|
||||||
|
likeCount: 0,
|
||||||
|
commentCount: 0,
|
||||||
|
totalCount: reverseCount,
|
||||||
|
latestTime: reverseLatestTime,
|
||||||
|
source: 'reverse'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = [...mergedMap.values()].sort((a, b) => {
|
||||||
|
if (b.totalCount !== a.totalCount) return b.totalCount - a.totalCount
|
||||||
|
if (b.latestTime !== a.latestTime) return b.latestTime - a.latestTime
|
||||||
|
return a.name.localeCompare(b.name, 'zh-CN')
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...directMetric,
|
||||||
|
count: items.length,
|
||||||
|
items
|
||||||
|
}
|
||||||
|
}, [getSessionMutualFriendProfile])
|
||||||
|
|
||||||
|
const applySessionMutualFriendsMetric = useCallback((sessionId: string, directMetric: SessionMutualFriendsMetric) => {
|
||||||
|
const normalizedSessionId = String(sessionId || '').trim()
|
||||||
|
if (!normalizedSessionId) return
|
||||||
|
sessionMutualFriendsDirectMetricsRef.current[normalizedSessionId] = directMetric
|
||||||
|
|
||||||
|
const impactedSessionIds = new Set<string>([normalizedSessionId])
|
||||||
|
const allSessionIds = sessionsRef.current
|
||||||
|
.filter(session => session.hasSession && isSingleContactSession(session.username))
|
||||||
|
.map(session => session.username)
|
||||||
|
|
||||||
|
for (const targetSessionId of allSessionIds) {
|
||||||
|
if (targetSessionId === normalizedSessionId) continue
|
||||||
|
const targetProfile = getSessionMutualFriendProfile(targetSessionId)
|
||||||
|
if (directMetric.items.some(item => targetProfile.candidateNames.has(item.name))) {
|
||||||
|
impactedSessionIds.add(targetSessionId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setSessionMutualFriendsMetrics(prev => {
|
||||||
|
const next = { ...prev }
|
||||||
|
let changed = false
|
||||||
|
for (const targetSessionId of impactedSessionIds) {
|
||||||
|
const rebuiltMetric = rebuildSessionMutualFriendsMetric(targetSessionId)
|
||||||
|
if (!rebuiltMetric) continue
|
||||||
|
const previousMetric = prev[targetSessionId]
|
||||||
|
const previousSerialized = previousMetric ? JSON.stringify(previousMetric) : ''
|
||||||
|
const nextSerialized = JSON.stringify(rebuiltMetric)
|
||||||
|
if (previousSerialized === nextSerialized) continue
|
||||||
|
next[targetSessionId] = rebuiltMetric
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
return changed ? next : prev
|
||||||
|
})
|
||||||
|
}, [getSessionMutualFriendProfile, rebuildSessionMutualFriendsMetric])
|
||||||
|
|
||||||
const isSessionMediaMetricReady = useCallback((sessionId: string): boolean => {
|
const isSessionMediaMetricReady = useCallback((sessionId: string): boolean => {
|
||||||
if (!sessionId) return true
|
if (!sessionId) return true
|
||||||
if (sessionMediaMetricReadySetRef.current.has(sessionId)) return true
|
if (sessionMediaMetricReadySetRef.current.has(sessionId)) return true
|
||||||
@@ -2892,10 +3008,7 @@ function ExportPage() {
|
|||||||
try {
|
try {
|
||||||
const metric = await loadSessionMutualFriendsMetric(sessionId)
|
const metric = await loadSessionMutualFriendsMetric(sessionId)
|
||||||
if (runId !== sessionMutualFriendsRunIdRef.current) return
|
if (runId !== sessionMutualFriendsRunIdRef.current) return
|
||||||
setSessionMutualFriendsMetrics(prev => ({
|
applySessionMutualFriendsMetric(sessionId, metric)
|
||||||
...prev,
|
|
||||||
[sessionId]: metric
|
|
||||||
}))
|
|
||||||
sessionMutualFriendsReadySetRef.current.add(sessionId)
|
sessionMutualFriendsReadySetRef.current.add(sessionId)
|
||||||
patchSessionLoadTraceStage([sessionId], 'mutualFriends', 'done')
|
patchSessionLoadTraceStage([sessionId], 'mutualFriends', 'done')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -2916,6 +3029,7 @@ function ExportPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
|
applySessionMutualFriendsMetric,
|
||||||
hasPendingMetricLoads,
|
hasPendingMetricLoads,
|
||||||
isSessionMutualFriendsReady,
|
isSessionMutualFriendsReady,
|
||||||
loadSessionMutualFriendsMetric,
|
loadSessionMutualFriendsMetric,
|
||||||
|
|||||||
Reference in New Issue
Block a user