import { useEffect, useState, useRef, useCallback } from 'react' import { RefreshCw, Heart, Search, Calendar, User, X, Filter } from 'lucide-react' import { Avatar } from '../components/Avatar' import { ImagePreview } from '../components/ImagePreview' import './SnsPage.scss' interface SnsPost { id: string username: string nickname: string avatarUrl?: string createTime: number contentDesc: string type?: number media: { url: string; thumb: string }[] likes: string[] comments: { id: string; nickname: string; content: string; refCommentId: string; refNickname?: string }[] } const MediaItem = ({ url, thumb, onPreview }: { url: string, thumb: string, onPreview: () => void }) => { const [error, setError] = useState(false); if (error) { return (
无法加载
); } return (
setError(true)} />
); }; interface Contact { username: string displayName: string avatarUrl?: string } export default function SnsPage() { const [posts, setPosts] = useState([]) const [loading, setLoading] = useState(false) const [offset, setOffset] = useState(0) const [hasMore, setHasMore] = useState(true) const loadingRef = useRef(false) // 筛选与搜索状态 const [searchKeyword, setSearchKeyword] = useState('') const [selectedUsernames, setSelectedUsernames] = useState([]) const [startDate, setStartDate] = useState('') const [endDate, setEndDate] = useState('') const [isSidebarOpen, setIsSidebarOpen] = useState(true) // 联系人列表状态 const [contacts, setContacts] = useState([]) const [contactSearch, setContactSearch] = useState('') const [contactsLoading, setContactsLoading] = useState(false) const [previewImage, setPreviewImage] = useState(null) const loadPosts = useCallback(async (reset = false) => { if (loadingRef.current) return loadingRef.current = true setLoading(true) try { const currentOffset = reset ? 0 : offset const limit = 20 // 转换日期为秒级时间戳 const startTs = startDate ? Math.floor(new Date(startDate).getTime() / 1000) : undefined const endTs = endDate ? Math.floor(new Date(endDate).getTime() / 1000) + 86399 : undefined // 包含当天 const result = await window.electronAPI.sns.getTimeline( limit, currentOffset, selectedUsernames, searchKeyword, startTs, endTs ) if (result.success && result.timeline) { if (reset) { setPosts(result.timeline) setOffset(limit) setHasMore(result.timeline.length >= limit) } else { setPosts(prev => [...prev, ...result.timeline!]) setOffset(prev => prev + limit) if (result.timeline.length < limit) { setHasMore(false) } } } } catch (error) { console.error('Failed to load SNS timeline:', error) } finally { setLoading(false) loadingRef.current = false } }, [offset, selectedUsernames, searchKeyword, startDate, endDate]) // 获取联系人列表 const loadContacts = async () => { setContactsLoading(true) try { const result = await window.electronAPI.chat.getSessions() if (result.success && result.sessions) { // 系统账号和特殊前缀 const systemAccounts = ['filehelper', 'fmessage', 'newsapp', 'weixin', 'qqmail', 'tmessage', 'floatbottle', 'medianote', 'brandsessionholder']; // 初步提取并过滤联系人 const initialContacts = result.sessions .filter((s: any) => { if (!s.username) return false; const u = s.username.toLowerCase(); // 1. 排除群聊 (WeChat 群组以 @chatroom 结尾) if (u.includes('@chatroom') || u.endsWith('@chatroom') || u.endsWith('@openim')) { return false; } // 2. 排除公众号 (通常以 gh_ 开头) if (u.startsWith('gh_')) { return false; } // 3. 排除系统账号 if (systemAccounts.includes(u) || u.includes('helper') || u.includes('sessionholder')) { return false; } return true; }) .map((s: any) => ({ username: s.username, displayName: s.displayName || s.username, avatarUrl: s.avatarUrl })) setContacts(initialContacts) // 异步进一步富化(获取更多准确的昵称和头像) const usernames = initialContacts.map(c => c.username) const enriched = await window.electronAPI.chat.enrichSessionsContactInfo(usernames) if (enriched.success && enriched.contacts) { setContacts(prev => prev.map(c => { const extra = enriched.contacts![c.username] if (extra) { return { ...c, displayName: extra.displayName || c.displayName, avatarUrl: extra.avatarUrl || c.avatarUrl } } return c })) } } } catch (error) { console.error('Failed to load contacts:', error) } finally { setContactsLoading(false) } } useEffect(() => { loadContacts() }, []) useEffect(() => { loadPosts(true) }, [selectedUsernames, searchKeyword, startDate, endDate]) const handleScroll = (e: React.UIEvent) => { const { scrollTop, clientHeight, scrollHeight } = e.currentTarget if (scrollHeight - scrollTop - clientHeight < 200 && hasMore && !loading) { loadPosts() } } const formatTime = (ts: number) => { const date = new Date(ts * 1000) const isCurrentYear = date.getFullYear() === new Date().getFullYear() return date.toLocaleString('zh-CN', { year: isCurrentYear ? undefined : 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) } const toggleUserSelection = (username: string) => { setSelectedUsernames(prev => { if (prev.includes(username)) { return prev.filter(u => u !== username) } else { return [...prev, username] } }) } const clearFilters = () => { setSearchKeyword('') setSelectedUsernames([]) setStartDate('') setEndDate('') } const filteredContacts = contacts.filter(c => c.displayName.toLowerCase().includes(contactSearch.toLowerCase()) || c.username.toLowerCase().includes(contactSearch.toLowerCase()) ) return (
{/* 侧边栏:过滤与搜索 */}
{!isSidebarOpen && ( )}

朋友圈

{selectedUsernames.length > 0 && (
筛选中: {selectedUsernames.length} 位好友
)} {posts.map(post => (
{post.nickname}
{formatTime(post.createTime)}
{post.contentDesc &&
{post.contentDesc}
} {post.type === 15 ? (
[视频]
) : post.media.length > 0 && (
{post.media.map((m, idx) => ( setPreviewImage(m.url)} /> ))}
)}
{(post.likes.length > 0 || post.comments.length > 0) && (
{post.likes.length > 0 && (
{post.likes.join('、')}
)} {post.comments.length > 0 && (
{post.comments.map((c, idx) => (
{c.nickname} {c.refNickname && ( <> 回复 {c.refNickname} )} : {c.content}
))}
)}
)}
))} {loading &&
加载中...
} {!hasMore && posts.length > 0 &&
没有更多了
} {!loading && posts.length === 0 && (

没有找到符合条件的朋友圈

{selectedUsernames.length > 0 && ( )}
)}
{previewImage && ( setPreviewImage(null)} /> )}
) }