feat: clarify mutual friends direction labels

This commit is contained in:
aits2026
2026-03-06 16:58:44 +08:00
parent 4a1933e924
commit 58cfd49859
2 changed files with 158 additions and 39 deletions

View File

@@ -405,9 +405,9 @@
.session-mutual-friends-row { .session-mutual-friends-row {
display: grid; display: grid;
grid-template-columns: 42px minmax(0, 1fr) 96px 72px 110px; grid-template-columns: 42px minmax(0, 1fr) 72px 110px;
gap: 10px; gap: 10px;
align-items: center; align-items: start;
padding: 11px 12px; padding: 11px 12px;
border-bottom: 1px solid color-mix(in srgb, var(--border-color) 68%, transparent); border-bottom: 1px solid color-mix(in srgb, var(--border-color) 68%, transparent);
font-size: 13px; font-size: 13px;
@@ -426,6 +426,14 @@
.session-mutual-friends-rank { .session-mutual-friends-rank {
color: var(--text-tertiary); color: var(--text-tertiary);
padding-top: 3px;
}
.session-mutual-friends-main {
min-width: 0;
display: flex;
flex-direction: column;
gap: 6px;
} }
.session-mutual-friends-name { .session-mutual-friends-name {
@@ -437,6 +445,13 @@
font-weight: 600; font-weight: 600;
} }
.session-mutual-friends-tags {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.session-mutual-friends-source { .session-mutual-friends-source {
justify-self: start; justify-self: start;
border-radius: 999px; border-radius: 999px;
@@ -447,22 +462,56 @@
background: var(--bg-secondary); background: var(--bg-secondary);
color: var(--text-secondary); color: var(--text-secondary);
&.both { &.incoming {
color: var(--primary); color: #065f46;
border-color: color-mix(in srgb, var(--primary) 35%, var(--border-color)); border-color: color-mix(in srgb, #10b981 38%, var(--border-color));
background: color-mix(in srgb, var(--primary) 10%, var(--bg-secondary)); background: color-mix(in srgb, #10b981 10%, var(--bg-secondary));
} }
&.reverse { &.outgoing {
color: #92400e; color: #92400e;
border-color: color-mix(in srgb, #d97706 38%, var(--border-color)); border-color: color-mix(in srgb, #d97706 38%, var(--border-color));
background: color-mix(in srgb, #f59e0b 12%, var(--bg-secondary)); background: color-mix(in srgb, #f59e0b 12%, var(--bg-secondary));
} }
&.bidirectional {
color: var(--primary);
border-color: color-mix(in srgb, var(--primary) 35%, var(--border-color));
background: color-mix(in srgb, var(--primary) 10%, var(--bg-secondary));
}
}
.session-mutual-friends-behavior {
&.likes {
color: #1d4ed8;
border-color: color-mix(in srgb, #3b82f6 34%, var(--border-color));
background: color-mix(in srgb, #3b82f6 9%, var(--bg-secondary));
}
&.comments {
color: #7c3aed;
border-color: color-mix(in srgb, #8b5cf6 34%, var(--border-color));
background: color-mix(in srgb, #8b5cf6 9%, var(--bg-secondary));
}
&.both {
color: #be185d;
border-color: color-mix(in srgb, #ec4899 34%, var(--border-color));
background: color-mix(in srgb, #ec4899 9%, var(--bg-secondary));
}
}
.session-mutual-friends-desc {
min-width: 0;
color: var(--text-tertiary);
font-size: 12px;
line-height: 1.45;
} }
.session-mutual-friends-count, .session-mutual-friends-count,
.session-mutual-friends-latest { .session-mutual-friends-latest {
text-align: right; text-align: right;
padding-top: 3px;
} }
.session-mutual-friends-empty { .session-mutual-friends-empty {
@@ -3828,7 +3877,7 @@
} }
.session-mutual-friends-row { .session-mutual-friends-row {
grid-template-columns: 34px minmax(0, 1fr) 82px 56px 88px; grid-template-columns: 34px minmax(0, 1fr) 56px 88px;
gap: 8px; gap: 8px;
font-size: 12px; font-size: 12px;
} }

View File

@@ -574,15 +574,19 @@ interface SessionSnsRankItem {
latestTime: number latestTime: number
} }
type SessionMutualFriendSource = 'likes' | 'comments' | 'both' | 'reverse' type SessionMutualFriendDirection = 'incoming' | 'outgoing' | 'bidirectional'
type SessionMutualFriendBehavior = 'likes' | 'comments' | 'both'
interface SessionMutualFriendItem { interface SessionMutualFriendItem {
name: string name: string
likeCount: number incomingLikeCount: number
commentCount: number incomingCommentCount: number
outgoingLikeCount: number
outgoingCommentCount: number
totalCount: number totalCount: number
latestTime: number latestTime: number
source: SessionMutualFriendSource direction: SessionMutualFriendDirection
behavior: SessionMutualFriendBehavior
} }
interface SessionMutualFriendsMetric { interface SessionMutualFriendsMetric {
@@ -659,19 +663,22 @@ const buildSessionMutualFriendsMetric = (
const name = String(likeNameRaw || '').trim() || '未知用户' const name = String(likeNameRaw || '').trim() || '未知用户'
const existing = friendMap.get(name) const existing = friendMap.get(name)
if (existing) { if (existing) {
existing.likeCount += 1 existing.incomingLikeCount += 1
existing.totalCount += 1 existing.totalCount += 1
existing.source = existing.commentCount > 0 ? 'both' : 'likes' existing.behavior = existing.incomingCommentCount > 0 ? 'both' : 'likes'
if (createTime > existing.latestTime) existing.latestTime = createTime if (createTime > existing.latestTime) existing.latestTime = createTime
continue continue
} }
friendMap.set(name, { friendMap.set(name, {
name, name,
likeCount: 1, incomingLikeCount: 1,
commentCount: 0, incomingCommentCount: 0,
outgoingLikeCount: 0,
outgoingCommentCount: 0,
totalCount: 1, totalCount: 1,
latestTime: createTime, latestTime: createTime,
source: 'likes' direction: 'incoming',
behavior: 'likes'
}) })
} }
@@ -679,19 +686,22 @@ const buildSessionMutualFriendsMetric = (
const name = String(comment?.nickname || '').trim() || '未知用户' const name = String(comment?.nickname || '').trim() || '未知用户'
const existing = friendMap.get(name) const existing = friendMap.get(name)
if (existing) { if (existing) {
existing.commentCount += 1 existing.incomingCommentCount += 1
existing.totalCount += 1 existing.totalCount += 1
existing.source = existing.likeCount > 0 ? 'both' : 'comments' existing.behavior = existing.incomingLikeCount > 0 ? 'both' : 'comments'
if (createTime > existing.latestTime) existing.latestTime = createTime if (createTime > existing.latestTime) existing.latestTime = createTime
continue continue
} }
friendMap.set(name, { friendMap.set(name, {
name, name,
likeCount: 0, incomingLikeCount: 0,
commentCount: 1, incomingCommentCount: 1,
outgoingLikeCount: 0,
outgoingCommentCount: 0,
totalCount: 1, totalCount: 1,
latestTime: createTime, latestTime: createTime,
source: 'comments' direction: 'incoming',
behavior: 'comments'
}) })
} }
} }
@@ -711,11 +721,41 @@ const buildSessionMutualFriendsMetric = (
} }
} }
const getSessionMutualFriendSourceLabel = (source: SessionMutualFriendSource): string => { const getSessionMutualFriendDirectionLabel = (direction: SessionMutualFriendDirection): string => {
if (source === 'both') return '赞/评' if (direction === 'incoming') return '对方赞/评TA'
if (source === 'reverse') return '反向关联' if (direction === 'outgoing') return 'TA赞/评对方'
if (source === 'likes') return '仅点赞' return '双方有互动'
return '仅评论' }
const getSessionMutualFriendBehaviorLabel = (behavior: SessionMutualFriendBehavior): string => {
if (behavior === 'likes') return '赞'
if (behavior === 'comments') return '评'
return '赞/评'
}
const summarizeMutualFriendBehavior = (likeCount: number, commentCount: number): SessionMutualFriendBehavior => {
if (likeCount > 0 && commentCount > 0) return 'both'
if (likeCount > 0) return 'likes'
return 'comments'
}
const describeSessionMutualFriendRelation = (
item: SessionMutualFriendItem,
targetDisplayName: string
): string => {
if (item.direction === 'incoming') {
if (item.behavior === 'likes') return `${item.name}${targetDisplayName} 点过赞`
if (item.behavior === 'comments') return `${item.name}${targetDisplayName} 评论过`
return `${item.name}${targetDisplayName} 点过赞、评论过`
}
if (item.direction === 'outgoing') {
if (item.behavior === 'likes') return `${targetDisplayName}${item.name} 点过赞`
if (item.behavior === 'comments') return `${targetDisplayName}${item.name} 评论过`
return `${targetDisplayName}${item.name} 点过赞、评论过`
}
if (item.behavior === 'likes') return `${targetDisplayName}${item.name} 双方都有点赞互动`
if (item.behavior === 'comments') return `${targetDisplayName}${item.name} 双方都有评论互动`
return `${targetDisplayName}${item.name} 双方都有点赞或评论互动`
} }
interface SessionExportMetric { interface SessionExportMetric {
@@ -2746,16 +2786,36 @@ function ExportPage() {
if (reverseMatches.length === 0) continue if (reverseMatches.length === 0) continue
const reverseCount = reverseMatches.reduce((sum, item) => sum + item.totalCount, 0) const reverseCount = reverseMatches.reduce((sum, item) => sum + item.totalCount, 0)
const reverseLikeCount = reverseMatches.reduce((sum, item) => sum + item.incomingLikeCount, 0)
const reverseCommentCount = reverseMatches.reduce((sum, item) => sum + item.incomingCommentCount, 0)
const reverseLatestTime = reverseMatches.reduce((latest, item) => Math.max(latest, item.latestTime), 0) const reverseLatestTime = reverseMatches.reduce((latest, item) => Math.max(latest, item.latestTime), 0)
const existing = mergedMap.get(sourceProfile.displayName)
if (existing) {
existing.outgoingLikeCount += reverseLikeCount
existing.outgoingCommentCount += reverseCommentCount
existing.totalCount += reverseCount
existing.latestTime = Math.max(existing.latestTime, reverseLatestTime)
existing.direction = (existing.incomingLikeCount + existing.incomingCommentCount) > 0
? 'bidirectional'
: 'outgoing'
existing.behavior = summarizeMutualFriendBehavior(
existing.incomingLikeCount + existing.outgoingLikeCount,
existing.incomingCommentCount + existing.outgoingCommentCount
)
} else {
mergedMap.set(sourceProfile.displayName, { mergedMap.set(sourceProfile.displayName, {
name: sourceProfile.displayName, name: sourceProfile.displayName,
likeCount: 0, incomingLikeCount: 0,
commentCount: 0, incomingCommentCount: 0,
outgoingLikeCount: reverseLikeCount,
outgoingCommentCount: reverseCommentCount,
totalCount: reverseCount, totalCount: reverseCount,
latestTime: reverseLatestTime, latestTime: reverseLatestTime,
source: 'reverse' direction: 'outgoing',
behavior: summarizeMutualFriendBehavior(reverseLikeCount, reverseCommentCount)
}) })
} }
}
const items = [...mergedMap.values()].sort((a, b) => { const items = [...mergedMap.values()].sort((a, b) => {
if (b.totalCount !== a.totalCount) return b.totalCount - a.totalCount if (b.totalCount !== a.totalCount) return b.totalCount - a.totalCount
@@ -6439,10 +6499,20 @@ function ExportPage() {
{filteredSessionMutualFriendsDialogItems.map((item, index) => ( {filteredSessionMutualFriendsDialogItems.map((item, index) => (
<div className="session-mutual-friends-row" key={`${sessionMutualFriendsDialogTarget.username}-${item.name}`}> <div className="session-mutual-friends-row" key={`${sessionMutualFriendsDialogTarget.username}-${item.name}`}>
<span className="session-mutual-friends-rank">{index + 1}</span> <span className="session-mutual-friends-rank">{index + 1}</span>
<div className="session-mutual-friends-main">
<span className="session-mutual-friends-name" title={item.name}>{item.name}</span> <span className="session-mutual-friends-name" title={item.name}>{item.name}</span>
<span className={`session-mutual-friends-source ${item.source}`}> <div className="session-mutual-friends-tags">
{getSessionMutualFriendSourceLabel(item.source)} <span className={`session-mutual-friends-source ${item.direction}`}>
{getSessionMutualFriendDirectionLabel(item.direction)}
</span> </span>
<span className={`session-mutual-friends-source session-mutual-friends-behavior ${item.behavior}`}>
{getSessionMutualFriendBehaviorLabel(item.behavior)}
</span>
</div>
<div className="session-mutual-friends-desc">
{describeSessionMutualFriendRelation(item, sessionMutualFriendsDialogTarget.displayName)}
</div>
</div>
<span className="session-mutual-friends-count">{item.totalCount.toLocaleString('zh-CN')}</span> <span className="session-mutual-friends-count">{item.totalCount.toLocaleString('zh-CN')}</span>
<span className="session-mutual-friends-latest">{formatYmdDateFromSeconds(item.latestTime)}</span> <span className="session-mutual-friends-latest">{formatYmdDateFromSeconds(item.latestTime)}</span>
</div> </div>