import React, { useEffect, useState, useCallback } from 'react'; import type { WatchedDir, ActivityEvent } from '../../core/types'; const api = (window as any).api; export function Popover() { const [dirs, setDirs] = useState([]); const [activity, setActivity] = useState([]); const [paused, setPaused] = useState(false); const refresh = useCallback(async () => { setDirs(await api.dirs.list()); }, []); useEffect(() => { // 초기 상태: main process에서 직접 읽음 (단일 진실의 원천) api.watcher.status().then(({ paused: p }: { paused: boolean }) => setPaused(p)); refresh(); const unsubActivity = api.events.onActivity((ev: ActivityEvent) => { setActivity((prev) => [ev, ...prev].slice(0, 20)); refresh(); }); // main에서 pause 상태가 바뀔 때마다 업데이트 (트레이 컨텍스트 메뉴 포함) const unsubPaused = api.events.onPausedChange((p: boolean) => setPaused(p)); return () => { unsubActivity(); unsubPaused(); }; }, [refresh]); const togglePause = async () => { if (paused) { await api.watcher.resumeAll(); } else { await api.watcher.pauseAll(); } // 상태는 onPausedChange 이벤트로 자동 반영되므로 직접 setPaused 불필요 }; return (

NFD → NFC

{dirs.length === 0 && (
감시 중인 디렉토리가 없습니다.
설정에서 추가하세요.
)} {dirs.map((dir) => ( ))}

최근 활동

{activity.length === 0 &&
활동 없음
} {activity.map((ev, i) => (
{ev.message.split('/').pop() ?? ev.message}
))}
); } function DirRow({ dir, paused, onRefresh }: { dir: WatchedDir; paused: boolean; onRefresh: () => void }) { const [pending, setPending] = useState(0); useEffect(() => { if (dir.mode === 'manual') { api.dirs.pendingQueue(dir.id).then((q: string[]) => setPending(q.length)); } }, [dir]); const applyQueue = async () => { await api.dirs.applyQueue(dir.id); setPending(0); onRefresh(); }; const statusClass = paused ? 'paused' : dir.enabled ? 'active' : 'disabled'; const dirName = dir.path.split('/').pop() ?? dir.path; return (
{dirName}
{dir.path}
{dir.mode === 'manual' && pending > 0 && ( )}
); }