"use client"; import React, { useEffect, useMemo, useState } from 'react'; import { Calendar, ChevronDown, ChevronLeft, ChevronRight, Loader2 } from 'lucide-react'; import { ScreenTimeResponse } from '../types'; import { formatDuration, formatScreenTimePeriod, getLocalDateInputValue, shiftDateInputValue } from '../utils'; import ScreenTimeOverviewChart from './ScreenTimeOverviewChart'; interface ScreenTimeTabProps { hostname: string; } export default function ScreenTimeTab({ hostname }: ScreenTimeTabProps) { const [unit, setUnit] = useState<'day' | 'week'>('day'); const [selectedDate, setSelectedDate] = useState(getLocalDateInputValue()); const [screenTime, setScreenTime] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [expandedApps, setExpandedApps] = useState>(new Set()); const [highlightedProcessId, setHighlightedProcessId] = useState(null); const [initialLoading, setInitialLoading] = useState(true); const abortControllerRef = React.useRef(null); useEffect(() => { const controller = new AbortController(); abortControllerRef.current?.abort(); abortControllerRef.current = controller; const fetchScreenTime = async () => { try { setLoading(true); setError(null); const searchParams = new URLSearchParams({ unit, date: selectedDate, tzOffsetMinutes: String(new Date().getTimezoneOffset()) }); const response = await fetch(`/hosts/${hostname}/screen-time?${searchParams.toString()}`, { signal: controller.signal }); if (!response.ok) { throw new Error('获取屏幕使用时间失败'); } const data: ScreenTimeResponse = await response.json(); setScreenTime(data); setInitialLoading(false); setExpandedApps((previous) => { const next = new Set(); data.apps.forEach((app) => { if (previous.has(app.processId)) { next.add(app.processId); } }); return next; }); } catch (fetchError) { if (controller.signal.aborted) return; setInitialLoading(false); setError(fetchError instanceof Error ? fetchError.message : '获取屏幕使用时间失败'); } finally { if (!controller.signal.aborted && abortControllerRef.current === controller) { setLoading(false); abortControllerRef.current = null; } } }; fetchScreenTime(); return () => { controller.abort(); if (abortControllerRef.current === controller) { abortControllerRef.current = null; } }; }, [hostname, selectedDate, unit]); const maxDurationSeconds = useMemo(() => { if (!screenTime || screenTime.apps.length === 0) return 0; return Math.max(...screenTime.apps.map((app) => app.durationSeconds)); }, [screenTime]); const isRefreshing = loading && !!screenTime; const periodLabel = screenTime ? formatScreenTimePeriod(screenTime.periodStartDate, screenTime.periodEndDate, screenTime.unit) : selectedDate; const toggleExpanded = (processId: string) => { setExpandedApps((previous) => { const next = new Set(previous); if (next.has(processId)) { next.delete(processId); } else { next.add(processId); } return next; }); }; const shiftPeriod = (direction: -1 | 1) => { setSelectedDate((current) => shiftDateInputValue(current, direction * (unit === 'week' ? 7 : 1))); }; const handleOverviewDateSelect = (date: string, nextUnit?: 'day' | 'week') => { if (nextUnit) { setUnit(nextUnit); } setSelectedDate(date); }; const handleSpotlightAppClick = (processId: string) => { setHighlightedProcessId(processId); setExpandedApps((previous) => new Set(previous).add(processId)); }; return (
屏幕使用时间

{periodLabel}

{error && (
{error}
)}

最常使用

{screenTime && (
{screenTime.apps.length} 个应用 · {formatDuration(screenTime.totalDurationSeconds)}
)}
{isRefreshing && (
更新中
)}
{loading && !screenTime && (
正在加载屏幕使用时间数据...
)} {!loading && screenTime && screenTime.apps.length === 0 && !error && (
当前时间段内没有可统计的活跃窗口记录。
)} {screenTime && screenTime.apps.length > 0 && (
{screenTime.apps.map((app, index) => { const isExpanded = expandedApps.has(app.processId); const processWidth = maxDurationSeconds > 0 ? Math.max((app.durationSeconds / maxDurationSeconds) * 100, 4) : 0; return (
{isExpanded && (
{app.titles.map((title) => (
{title.title}
{title.percentage}%
{formatDuration(title.durationSeconds)}
))}
{app.titleCount > app.titles.length && (
还有 {app.titleCount - app.titles.length} 个标题
)}
)}
); })}
)} {isRefreshing && screenTime && (
)}
); }