mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-25 15:25:50 +00:00
feat(sns): add header overview stats line
This commit is contained in:
@@ -45,12 +45,29 @@
|
|||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
|
|
||||||
|
.feed-header-main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.feed-stats-line {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
line-height: 1.4;
|
||||||
|
|
||||||
|
&.loading {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.header-actions {
|
.header-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -13,11 +13,25 @@ interface Contact {
|
|||||||
type?: 'friend' | 'former_friend' | 'sns_only'
|
type?: 'friend' | 'former_friend' | 'sns_only'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SnsOverviewStats {
|
||||||
|
totalPosts: number
|
||||||
|
totalFriends: number
|
||||||
|
earliestTime: number | null
|
||||||
|
latestTime: number | null
|
||||||
|
}
|
||||||
|
|
||||||
export default function SnsPage() {
|
export default function SnsPage() {
|
||||||
const [posts, setPosts] = useState<SnsPost[]>([])
|
const [posts, setPosts] = useState<SnsPost[]>([])
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [hasMore, setHasMore] = useState(true)
|
const [hasMore, setHasMore] = useState(true)
|
||||||
const loadingRef = useRef(false)
|
const loadingRef = useRef(false)
|
||||||
|
const [overviewStats, setOverviewStats] = useState<SnsOverviewStats>({
|
||||||
|
totalPosts: 0,
|
||||||
|
totalFriends: 0,
|
||||||
|
earliestTime: null,
|
||||||
|
latestTime: null
|
||||||
|
})
|
||||||
|
const [overviewStatsLoading, setOverviewStatsLoading] = useState(false)
|
||||||
|
|
||||||
// Filter states
|
// Filter states
|
||||||
const [searchKeyword, setSearchKeyword] = useState('')
|
const [searchKeyword, setSearchKeyword] = useState('')
|
||||||
@@ -75,6 +89,58 @@ export default function SnsPage() {
|
|||||||
}
|
}
|
||||||
}, [posts])
|
}, [posts])
|
||||||
|
|
||||||
|
const formatDateOnly = (timestamp: number | null): string => {
|
||||||
|
if (!timestamp || timestamp <= 0) return '--'
|
||||||
|
const date = new Date(timestamp * 1000)
|
||||||
|
if (Number.isNaN(date.getTime())) return '--'
|
||||||
|
const year = date.getFullYear()
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(date.getDate()).padStart(2, '0')
|
||||||
|
return `${year}-${month}-${day}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadOverviewStats = useCallback(async () => {
|
||||||
|
setOverviewStatsLoading(true)
|
||||||
|
try {
|
||||||
|
const statsResult = await window.electronAPI.sns.getExportStats()
|
||||||
|
if (!statsResult.success || !statsResult.data) {
|
||||||
|
throw new Error(statsResult.error || '获取朋友圈统计失败')
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalPosts = Math.max(0, Number(statsResult.data.totalPosts || 0))
|
||||||
|
const totalFriends = Math.max(0, Number(statsResult.data.totalFriends || 0))
|
||||||
|
let earliestTime: number | null = null
|
||||||
|
let latestTime: number | null = null
|
||||||
|
|
||||||
|
if (totalPosts > 0) {
|
||||||
|
const [latestResult, earliestResult] = await Promise.all([
|
||||||
|
window.electronAPI.sns.getTimeline(1, 0),
|
||||||
|
window.electronAPI.sns.getTimeline(1, Math.max(totalPosts - 1, 0))
|
||||||
|
])
|
||||||
|
const latestTs = Number(latestResult.timeline?.[0]?.createTime || 0)
|
||||||
|
const earliestTs = Number(earliestResult.timeline?.[0]?.createTime || 0)
|
||||||
|
|
||||||
|
if (latestResult.success && Number.isFinite(latestTs) && latestTs > 0) {
|
||||||
|
latestTime = Math.floor(latestTs)
|
||||||
|
}
|
||||||
|
if (earliestResult.success && Number.isFinite(earliestTs) && earliestTs > 0) {
|
||||||
|
earliestTime = Math.floor(earliestTs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setOverviewStats({
|
||||||
|
totalPosts,
|
||||||
|
totalFriends,
|
||||||
|
earliestTime,
|
||||||
|
latestTime
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load SNS overview stats:', error)
|
||||||
|
} finally {
|
||||||
|
setOverviewStatsLoading(false)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
const loadPosts = useCallback(async (options: { reset?: boolean, direction?: 'older' | 'newer' } = {}) => {
|
const loadPosts = useCallback(async (options: { reset?: boolean, direction?: 'older' | 'newer' } = {}) => {
|
||||||
const { reset = false, direction = 'older' } = options
|
const { reset = false, direction = 'older' } = options
|
||||||
if (loadingRef.current) return
|
if (loadingRef.current) return
|
||||||
@@ -244,7 +310,8 @@ export default function SnsPage() {
|
|||||||
// Initial Load & Listeners
|
// Initial Load & Listeners
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadContacts()
|
loadContacts()
|
||||||
}, [loadContacts])
|
loadOverviewStats()
|
||||||
|
}, [loadContacts, loadOverviewStats])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleChange = () => {
|
const handleChange = () => {
|
||||||
@@ -252,11 +319,12 @@ export default function SnsPage() {
|
|||||||
setPosts([]); setHasMore(true); setHasNewer(false);
|
setPosts([]); setHasMore(true); setHasNewer(false);
|
||||||
setSelectedUsernames([]); setSearchKeyword(''); setJumpTargetDate(undefined);
|
setSelectedUsernames([]); setSearchKeyword(''); setJumpTargetDate(undefined);
|
||||||
loadContacts();
|
loadContacts();
|
||||||
|
loadOverviewStats();
|
||||||
loadPosts({ reset: true });
|
loadPosts({ reset: true });
|
||||||
}
|
}
|
||||||
window.addEventListener('wxid-changed', handleChange as EventListener)
|
window.addEventListener('wxid-changed', handleChange as EventListener)
|
||||||
return () => window.removeEventListener('wxid-changed', handleChange as EventListener)
|
return () => window.removeEventListener('wxid-changed', handleChange as EventListener)
|
||||||
}, [loadContacts, loadPosts])
|
}, [loadContacts, loadOverviewStats, loadPosts])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
@@ -288,7 +356,12 @@ export default function SnsPage() {
|
|||||||
<div className="sns-main-viewport" onScroll={handleScroll} onWheel={handleWheel} ref={postsContainerRef}>
|
<div className="sns-main-viewport" onScroll={handleScroll} onWheel={handleWheel} ref={postsContainerRef}>
|
||||||
<div className="sns-feed-container">
|
<div className="sns-feed-container">
|
||||||
<div className="feed-header">
|
<div className="feed-header">
|
||||||
|
<div className="feed-header-main">
|
||||||
<h2>朋友圈</h2>
|
<h2>朋友圈</h2>
|
||||||
|
<div className={`feed-stats-line ${overviewStatsLoading ? 'loading' : ''}`}>
|
||||||
|
共 {overviewStats.totalPosts} 条 | {formatDateOnly(overviewStats.earliestTime)} ~ {formatDateOnly(overviewStats.latestTime)} | {overviewStats.totalFriends} 位好友
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="header-actions">
|
<div className="header-actions">
|
||||||
<button
|
<button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
@@ -325,6 +398,7 @@ export default function SnsPage() {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setRefreshSpin(true)
|
setRefreshSpin(true)
|
||||||
loadPosts({ reset: true })
|
loadPosts({ reset: true })
|
||||||
|
loadOverviewStats()
|
||||||
setTimeout(() => setRefreshSpin(false), 800)
|
setTimeout(() => setRefreshSpin(false), 800)
|
||||||
}}
|
}}
|
||||||
disabled={loading || loadingNewer}
|
disabled={loading || loadingNewer}
|
||||||
@@ -362,7 +436,10 @@ export default function SnsPage() {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onDebug={(p) => setDebugPost(p)}
|
onDebug={(p) => setDebugPost(p)}
|
||||||
onDelete={(postId) => setPosts(prev => prev.filter(p => p.id !== postId))}
|
onDelete={(postId) => {
|
||||||
|
setPosts(prev => prev.filter(p => p.id !== postId))
|
||||||
|
loadOverviewStats()
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user