mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-25 07:16:51 +00:00
refactor(sns): move jump calendar to header
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import React, { useEffect, useRef, useState, useCallback } from 'react'
|
||||
import { Search, Calendar, User, X, Loader2 } from 'lucide-react'
|
||||
import React from 'react'
|
||||
import { Search, User, X, Loader2 } from 'lucide-react'
|
||||
import { Avatar } from '../Avatar'
|
||||
import JumpToDatePopover from '../JumpToDatePopover'
|
||||
|
||||
interface Contact {
|
||||
username: string
|
||||
@@ -20,8 +19,6 @@ interface ContactsCountProgress {
|
||||
interface SnsFilterPanelProps {
|
||||
searchKeyword: string
|
||||
setSearchKeyword: (val: string) => void
|
||||
jumpTargetDate?: Date
|
||||
setJumpTargetDate: (date?: Date) => void
|
||||
totalFriendsLabel?: string
|
||||
selectedUsernames: string[]
|
||||
setSelectedUsernames: (val: string[]) => void
|
||||
@@ -35,8 +32,6 @@ interface SnsFilterPanelProps {
|
||||
export const SnsFilterPanel: React.FC<SnsFilterPanelProps> = ({
|
||||
searchKeyword,
|
||||
setSearchKeyword,
|
||||
jumpTargetDate,
|
||||
setJumpTargetDate,
|
||||
totalFriendsLabel,
|
||||
selectedUsernames,
|
||||
setSelectedUsernames,
|
||||
@@ -46,104 +41,6 @@ export const SnsFilterPanel: React.FC<SnsFilterPanelProps> = ({
|
||||
loading,
|
||||
contactsCountProgress
|
||||
}) => {
|
||||
const [showJumpPopover, setShowJumpPopover] = useState(false)
|
||||
const [jumpPopoverDate, setJumpPopoverDate] = useState<Date>(jumpTargetDate || new Date())
|
||||
const [jumpDateCounts, setJumpDateCounts] = useState<Record<string, number>>({})
|
||||
const [jumpDateMessageDates, setJumpDateMessageDates] = useState<Set<string>>(new Set())
|
||||
const [hasLoadedJumpDateCounts, setHasLoadedJumpDateCounts] = useState(false)
|
||||
const [loadingJumpDateCounts, setLoadingJumpDateCounts] = useState(false)
|
||||
const jumpCalendarWrapRef = useRef<HTMLDivElement | null>(null)
|
||||
const jumpDateCountsCacheRef = useRef<Map<string, Record<string, number>>>(new Map())
|
||||
const jumpDateRequestSeqRef = useRef(0)
|
||||
|
||||
useEffect(() => {
|
||||
if (!showJumpPopover) return
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (!jumpCalendarWrapRef.current) return
|
||||
if (jumpCalendarWrapRef.current.contains(event.target as Node)) return
|
||||
setShowJumpPopover(false)
|
||||
}
|
||||
document.addEventListener('mousedown', handleClickOutside)
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside)
|
||||
}, [showJumpPopover])
|
||||
|
||||
useEffect(() => {
|
||||
if (showJumpPopover) return
|
||||
setJumpPopoverDate(jumpTargetDate || new Date())
|
||||
}, [jumpTargetDate, showJumpPopover])
|
||||
|
||||
const toMonthKey = useCallback((date: Date) => {
|
||||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`
|
||||
}, [])
|
||||
|
||||
const toDateKey = useCallback((timestampSeconds: number) => {
|
||||
const date = new Date(timestampSeconds * 1000)
|
||||
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 applyJumpDateCounts = useCallback((counts: Record<string, number>) => {
|
||||
setJumpDateCounts(counts)
|
||||
setJumpDateMessageDates(new Set(Object.keys(counts)))
|
||||
setHasLoadedJumpDateCounts(true)
|
||||
}, [])
|
||||
|
||||
const loadJumpDateCounts = useCallback(async (monthDate: Date) => {
|
||||
const monthKey = toMonthKey(monthDate)
|
||||
const cached = jumpDateCountsCacheRef.current.get(monthKey)
|
||||
if (cached) {
|
||||
applyJumpDateCounts(cached)
|
||||
setLoadingJumpDateCounts(false)
|
||||
return
|
||||
}
|
||||
|
||||
const requestSeq = ++jumpDateRequestSeqRef.current
|
||||
setLoadingJumpDateCounts(true)
|
||||
setHasLoadedJumpDateCounts(false)
|
||||
|
||||
const year = monthDate.getFullYear()
|
||||
const month = monthDate.getMonth()
|
||||
const monthStart = new Date(year, month, 1, 0, 0, 0, 0)
|
||||
const monthEnd = new Date(year, month + 1, 0, 23, 59, 59, 999)
|
||||
const startTime = Math.floor(monthStart.getTime() / 1000)
|
||||
const endTime = Math.floor(monthEnd.getTime() / 1000)
|
||||
const pageSize = 200
|
||||
let offset = 0
|
||||
const counts: Record<string, number> = {}
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const result = await window.electronAPI.sns.getTimeline(pageSize, offset, [], '', startTime, endTime)
|
||||
if (!result?.success || !Array.isArray(result.timeline) || result.timeline.length === 0) {
|
||||
break
|
||||
}
|
||||
result.timeline.forEach((post) => {
|
||||
const key = toDateKey(Number(post.createTime || 0))
|
||||
if (!key) return
|
||||
counts[key] = (counts[key] || 0) + 1
|
||||
})
|
||||
if (result.timeline.length < pageSize) break
|
||||
offset += pageSize
|
||||
}
|
||||
|
||||
if (requestSeq !== jumpDateRequestSeqRef.current) return
|
||||
jumpDateCountsCacheRef.current.set(monthKey, counts)
|
||||
applyJumpDateCounts(counts)
|
||||
} catch (error) {
|
||||
console.error('加载朋友圈按日条数失败:', error)
|
||||
if (requestSeq !== jumpDateRequestSeqRef.current) return
|
||||
setJumpDateCounts({})
|
||||
setJumpDateMessageDates(new Set())
|
||||
setHasLoadedJumpDateCounts(true)
|
||||
} finally {
|
||||
if (requestSeq === jumpDateRequestSeqRef.current) {
|
||||
setLoadingJumpDateCounts(false)
|
||||
}
|
||||
}
|
||||
}, [applyJumpDateCounts, toDateKey, toMonthKey])
|
||||
|
||||
const filteredContacts = contacts.filter(c =>
|
||||
(c.displayName || '').toLowerCase().includes(contactSearch.toLowerCase()) ||
|
||||
c.username.toLowerCase().includes(contactSearch.toLowerCase())
|
||||
@@ -153,7 +50,6 @@ export const SnsFilterPanel: React.FC<SnsFilterPanelProps> = ({
|
||||
if (selectedUsernames.includes(username)) {
|
||||
setSelectedUsernames(selectedUsernames.filter(u => u !== username))
|
||||
} else {
|
||||
setJumpTargetDate(undefined) // Reset date jump when selecting user
|
||||
setSelectedUsernames([...selectedUsernames, username])
|
||||
}
|
||||
}
|
||||
@@ -161,7 +57,6 @@ export const SnsFilterPanel: React.FC<SnsFilterPanelProps> = ({
|
||||
const clearFilters = () => {
|
||||
setSearchKeyword('')
|
||||
setSelectedUsernames([])
|
||||
setJumpTargetDate(undefined)
|
||||
}
|
||||
|
||||
const getEmptyStateText = () => {
|
||||
@@ -178,7 +73,7 @@ export const SnsFilterPanel: React.FC<SnsFilterPanelProps> = ({
|
||||
<aside className="sns-filter-panel">
|
||||
<div className="filter-header">
|
||||
<h3>筛选条件</h3>
|
||||
{(searchKeyword || jumpTargetDate || selectedUsernames.length > 0) && (
|
||||
{(searchKeyword || selectedUsernames.length > 0) && (
|
||||
<button className="reset-all-btn" onClick={clearFilters} title="重置所有筛选">
|
||||
<RefreshCw size={14} />
|
||||
</button>
|
||||
@@ -206,64 +101,6 @@ export const SnsFilterPanel: React.FC<SnsFilterPanelProps> = ({
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Date Widget */}
|
||||
<div className="filter-widget date-widget">
|
||||
<div className="date-widget-row">
|
||||
<div className="widget-header">
|
||||
<Calendar size={14} />
|
||||
<span>时间跳转</span>
|
||||
</div>
|
||||
<div className="jump-calendar-anchor" ref={jumpCalendarWrapRef}>
|
||||
<button
|
||||
className={`date-picker-trigger ${jumpTargetDate ? 'active' : ''}`}
|
||||
onClick={() => {
|
||||
if (!showJumpPopover) {
|
||||
const nextDate = jumpTargetDate || new Date()
|
||||
setJumpPopoverDate(nextDate)
|
||||
void loadJumpDateCounts(nextDate)
|
||||
}
|
||||
setShowJumpPopover(prev => !prev)
|
||||
}}
|
||||
>
|
||||
<span className="date-text">
|
||||
{jumpTargetDate
|
||||
? jumpTargetDate.toLocaleDateString('zh-CN', { year: 'numeric', month: 'long', day: 'numeric' })
|
||||
: '选择日期...'}
|
||||
</span>
|
||||
{jumpTargetDate && (
|
||||
<div
|
||||
className="clear-date-btn"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setJumpTargetDate(undefined)
|
||||
}}
|
||||
>
|
||||
<X size={12} />
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
<JumpToDatePopover
|
||||
isOpen={showJumpPopover}
|
||||
currentDate={jumpPopoverDate}
|
||||
onClose={() => setShowJumpPopover(false)}
|
||||
onMonthChange={(date) => {
|
||||
setJumpPopoverDate(date)
|
||||
void loadJumpDateCounts(date)
|
||||
}}
|
||||
onSelect={(date) => {
|
||||
setJumpPopoverDate(date)
|
||||
setJumpTargetDate(date)
|
||||
}}
|
||||
messageDates={jumpDateMessageDates}
|
||||
hasLoadedMessageDates={hasLoadedJumpDateCounts}
|
||||
messageDateCounts={jumpDateCounts}
|
||||
loadingDateCounts={loadingJumpDateCounts}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Contact Widget */}
|
||||
<div className="filter-widget contact-widget">
|
||||
<div className="widget-header">
|
||||
|
||||
Reference in New Issue
Block a user