mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-25 07:16:51 +00:00
朋友圈支持定位解析;导出时表情包支持语义化补充导出
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useMemo, useEffect } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { Heart, ChevronRight, ImageIcon, Code, Trash2 } from 'lucide-react'
|
||||
import { SnsPost, SnsLinkCardData } from '../../types/sns'
|
||||
import { Heart, ChevronRight, ImageIcon, Code, Trash2, MapPin } from 'lucide-react'
|
||||
import { SnsPost, SnsLinkCardData, SnsLocation } from '../../types/sns'
|
||||
import { Avatar } from '../Avatar'
|
||||
import { SnsMediaGrid } from './SnsMediaGrid'
|
||||
import { getEmojiPath } from 'wechat-emojis'
|
||||
@@ -134,6 +134,30 @@ const buildLinkCardData = (post: SnsPost): SnsLinkCardData | null => {
|
||||
}
|
||||
}
|
||||
|
||||
const buildLocationText = (location?: SnsLocation): string => {
|
||||
if (!location) return ''
|
||||
|
||||
const normalize = (value?: string): string => (
|
||||
decodeHtmlEntities(String(value || '')).replace(/\s+/g, ' ').trim()
|
||||
)
|
||||
|
||||
const primary = [
|
||||
normalize(location.poiName),
|
||||
normalize(location.poiAddressName),
|
||||
normalize(location.label),
|
||||
normalize(location.poiAddress)
|
||||
].find(Boolean) || ''
|
||||
|
||||
const region = [normalize(location.country), normalize(location.city)]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
if (primary && region && !primary.includes(region)) {
|
||||
return `${primary} · ${region}`
|
||||
}
|
||||
return primary || region
|
||||
}
|
||||
|
||||
const SnsLinkCard = ({ card }: { card: SnsLinkCardData }) => {
|
||||
const [thumbFailed, setThumbFailed] = useState(false)
|
||||
const hostname = useMemo(() => {
|
||||
@@ -254,6 +278,7 @@ export const SnsPostItem: React.FC<SnsPostItemProps> = ({ post, onPreview, onDeb
|
||||
const [deleting, setDeleting] = useState(false)
|
||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false)
|
||||
const linkCard = buildLinkCardData(post)
|
||||
const locationText = useMemo(() => buildLocationText(post.location), [post.location])
|
||||
const hasVideoMedia = post.type === 15 || post.media.some((item) => isSnsVideoUrl(item.url))
|
||||
const showLinkCard = Boolean(linkCard) && post.media.length <= 1 && !hasVideoMedia
|
||||
const showMediaGrid = post.media.length > 0 && !showLinkCard
|
||||
@@ -379,6 +404,13 @@ export const SnsPostItem: React.FC<SnsPostItemProps> = ({ post, onPreview, onDeb
|
||||
<div className="post-text">{renderTextWithEmoji(decodeHtmlEntities(post.contentDesc))}</div>
|
||||
)}
|
||||
|
||||
{locationText && (
|
||||
<div className="post-location" title={locationText}>
|
||||
<MapPin size={14} />
|
||||
<span className="post-location-text">{locationText}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showLinkCard && linkCard && (
|
||||
<SnsLinkCard card={linkCard} />
|
||||
)}
|
||||
|
||||
@@ -1026,7 +1026,7 @@ const toSessionRowsWithContacts = (
|
||||
kind: toKindByContact(contact),
|
||||
wechatId: contact.username,
|
||||
displayName: contact.displayName || session?.displayName || contact.username,
|
||||
avatarUrl: contact.avatarUrl || session?.avatarUrl,
|
||||
avatarUrl: session?.avatarUrl || contact.avatarUrl,
|
||||
hasSession: Boolean(session)
|
||||
} as SessionRow
|
||||
})
|
||||
@@ -1046,7 +1046,7 @@ const toSessionRowsWithContacts = (
|
||||
kind: toKindByContactType(session, contact),
|
||||
wechatId: contact?.username || session.username,
|
||||
displayName: contact?.displayName || session.displayName || session.username,
|
||||
avatarUrl: contact?.avatarUrl || session.avatarUrl,
|
||||
avatarUrl: session.avatarUrl || contact?.avatarUrl,
|
||||
hasSession: true
|
||||
} as SessionRow
|
||||
})
|
||||
@@ -5582,6 +5582,45 @@ function ExportPage() {
|
||||
return map
|
||||
}, [contactsList])
|
||||
|
||||
useEffect(() => {
|
||||
if (!showSessionDetailPanel) return
|
||||
const sessionId = String(sessionDetail?.wxid || '').trim()
|
||||
if (!sessionId) return
|
||||
|
||||
const mappedSession = sessionRowByUsername.get(sessionId)
|
||||
const mappedContact = contactByUsername.get(sessionId)
|
||||
if (!mappedSession && !mappedContact) return
|
||||
|
||||
setSessionDetail((prev) => {
|
||||
if (!prev || prev.wxid !== sessionId) return prev
|
||||
|
||||
const nextDisplayName = mappedSession?.displayName || mappedContact?.displayName || prev.displayName || sessionId
|
||||
const nextRemark = mappedContact?.remark ?? prev.remark
|
||||
const nextNickName = mappedContact?.nickname ?? prev.nickName
|
||||
const nextAlias = mappedContact?.alias ?? prev.alias
|
||||
const nextAvatarUrl = mappedSession?.avatarUrl || mappedContact?.avatarUrl || prev.avatarUrl
|
||||
|
||||
if (
|
||||
nextDisplayName === prev.displayName &&
|
||||
nextRemark === prev.remark &&
|
||||
nextNickName === prev.nickName &&
|
||||
nextAlias === prev.alias &&
|
||||
nextAvatarUrl === prev.avatarUrl
|
||||
) {
|
||||
return prev
|
||||
}
|
||||
|
||||
return {
|
||||
...prev,
|
||||
displayName: nextDisplayName,
|
||||
remark: nextRemark,
|
||||
nickName: nextNickName,
|
||||
alias: nextAlias,
|
||||
avatarUrl: nextAvatarUrl
|
||||
}
|
||||
})
|
||||
}, [contactByUsername, sessionDetail?.wxid, sessionRowByUsername, showSessionDetailPanel])
|
||||
|
||||
const currentSessionExportRecords = useMemo(() => {
|
||||
const sessionId = String(sessionDetail?.wxid || '').trim()
|
||||
if (!sessionId) return [] as configService.ExportSessionRecordEntry[]
|
||||
@@ -5987,7 +6026,11 @@ function ExportPage() {
|
||||
loadSnsUserPostCounts({ force: true })
|
||||
])
|
||||
|
||||
if (String(sessionDetail?.wxid || '').trim()) {
|
||||
const currentDetailSessionId = showSessionDetailPanel
|
||||
? String(sessionDetail?.wxid || '').trim()
|
||||
: ''
|
||||
if (currentDetailSessionId) {
|
||||
await loadSessionDetail(currentDetailSessionId)
|
||||
void loadSessionRelationStats({ forceRefresh: true })
|
||||
}
|
||||
}, [
|
||||
@@ -5998,11 +6041,13 @@ function ExportPage() {
|
||||
filteredContacts,
|
||||
isSessionCountStageReady,
|
||||
loadContactsList,
|
||||
loadSessionDetail,
|
||||
loadSessionRelationStats,
|
||||
loadSnsStats,
|
||||
loadSnsUserPostCounts,
|
||||
resetSessionMutualFriendsLoader,
|
||||
scheduleSessionMutualFriendsWorker,
|
||||
showSessionDetailPanel,
|
||||
sessionDetail?.wxid
|
||||
])
|
||||
|
||||
|
||||
@@ -759,6 +759,26 @@
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.post-location {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 6px;
|
||||
margin: -4px 0 12px;
|
||||
font-size: 13px;
|
||||
line-height: 1.45;
|
||||
color: var(--text-secondary);
|
||||
|
||||
svg {
|
||||
flex-shrink: 0;
|
||||
margin-top: 1px;
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
}
|
||||
|
||||
.post-location-text {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.post-media-container {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
10
src/types/electron.d.ts
vendored
10
src/types/electron.d.ts
vendored
@@ -790,6 +790,16 @@ export interface ElectronAPI {
|
||||
}>
|
||||
likes: Array<string>
|
||||
comments: Array<{ id: string; nickname: string; content: string; refCommentId: string; refNickname?: string; emojis?: Array<{ url: string; md5: string; width: number; height: number; encryptUrl?: string; aesKey?: string }> }>
|
||||
location?: {
|
||||
latitude?: number
|
||||
longitude?: number
|
||||
city?: string
|
||||
country?: string
|
||||
poiName?: string
|
||||
poiAddress?: string
|
||||
poiAddressName?: string
|
||||
label?: string
|
||||
}
|
||||
rawXml?: string
|
||||
}>
|
||||
error?: string
|
||||
|
||||
@@ -34,6 +34,17 @@ export interface SnsComment {
|
||||
emojis?: SnsCommentEmoji[]
|
||||
}
|
||||
|
||||
export interface SnsLocation {
|
||||
latitude?: number
|
||||
longitude?: number
|
||||
city?: string
|
||||
country?: string
|
||||
poiName?: string
|
||||
poiAddress?: string
|
||||
poiAddressName?: string
|
||||
label?: string
|
||||
}
|
||||
|
||||
export interface SnsPost {
|
||||
id: string
|
||||
tid?: string // 数据库主键(雪花 ID),用于精确删除
|
||||
@@ -46,6 +57,7 @@ export interface SnsPost {
|
||||
media: SnsMedia[]
|
||||
likes: string[]
|
||||
comments: SnsComment[]
|
||||
location?: SnsLocation
|
||||
rawXml?: string
|
||||
linkTitle?: string
|
||||
linkUrl?: string
|
||||
|
||||
Reference in New Issue
Block a user