From fbc0f5e9f3be77c96b4c1bd42c53e7fd018841c9 Mon Sep 17 00:00:00 2001 From: mesalogo Date: Tue, 8 Sep 2026 21:38:33 +0800 Subject: [PATCH] feat: show source entries in todo view --- src/renderer/src/MagicNotesWorkspace.test.tsx | 12 +- src/renderer/src/MagicNotesWorkspace.tsx | 132 ++++++++++++++++++ .../src/i18n/locales/en-US/magicNotes.ts | 5 + .../src/i18n/locales/zh-CN/magicNotes.ts | 5 + src/renderer/src/styles.css | 68 +++++++++ 5 files changed, 221 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/MagicNotesWorkspace.test.tsx b/src/renderer/src/MagicNotesWorkspace.test.tsx index 6f49ed35..47a9d60c 100644 --- a/src/renderer/src/MagicNotesWorkspace.test.tsx +++ b/src/renderer/src/MagicNotesWorkspace.test.tsx @@ -463,6 +463,11 @@ describe('MagicNotesWorkspace', () => { expect( screen.getByRole('button', { name: '打开原笔记修改' }) ).toBeInTheDocument() + const sourceEntry = screen.getByRole('region', { + name: '对应的笔记记录' + }) + expect(within(sourceEntry).getByText('发布笔记')).toBeInTheDocument() + expect(within(sourceEntry).getByText('记录正文')).toBeInTheDocument() }) it('keeps history editing contained and de-emphasizes note deletion', async () => { @@ -1169,7 +1174,7 @@ describe('MagicNotesWorkspace', () => { expect( screen.queryByRole('group', { name: '待办列表方式' }) ).not.toBeInTheDocument() - expect(screen.getByText('发布笔记')).toBeInTheDocument() + expect(screen.getAllByText('发布笔记')).not.toHaveLength(0) expect(screen.getByText('演示笔记')).toBeInTheDocument() expect(screen.getByText('准备演示')).toBeInTheDocument() }) @@ -1203,6 +1208,11 @@ describe('MagicNotesWorkspace', () => { message: '待办已完成' }) ) + expect( + within( + screen.getByRole('region', { name: '对应的笔记记录' }) + ).getByTestId('magic-note-checklist-state') + ).toHaveTextContent('checked') fireEvent.click(screen.getByRole('tab', { name: '笔记' })) expect( diff --git a/src/renderer/src/MagicNotesWorkspace.tsx b/src/renderer/src/MagicNotesWorkspace.tsx index d3bad1da..fef9b90e 100644 --- a/src/renderer/src/MagicNotesWorkspace.tsx +++ b/src/renderer/src/MagicNotesWorkspace.tsx @@ -483,6 +483,12 @@ export function MagicNotesWorkspace({ const [selectedNoteId, setSelectedNoteId] = useState('') const [selectedTodoId, setSelectedTodoId] = useState('') const [detail, setDetail] = useState() + const [todoSourceDetail, setTodoSourceDetail] = + useState() + const [todoSourceError, setTodoSourceError] = useState<{ + message: string + noteId: string + }>() const [loadStatus, setLoadStatus] = useState('loading') const [loadError, setLoadError] = useState('') const [refreshError, setRefreshError] = useState('') @@ -537,6 +543,7 @@ export function MagicNotesWorkspace({ const [pendingDraftSwitch, setPendingDraftSwitch] = useState() const detailRequestRef = useRef(0) + const todoSourceRequestRef = useRef(0) const requestedNoteIdRef = useRef('') const refreshRequestRef = useRef(0) const hasLoadedRef = useRef(false) @@ -1398,6 +1405,77 @@ export function MagicNotesWorkspace({ () => todos.find((todo) => todo.id === selectedTodoId), [selectedTodoId, todos] ) + const selectedTodoNoteId = selectedTodo?.noteId + const selectedTodoEntryId = selectedTodo?.entryId + + const loadTodoSource = useCallback( + async (noteId: string): Promise => { + const requestId = ++todoSourceRequestRef.current + try { + const sourceDetail = await window.goodbuddy.magicNotes.get(noteId) + if (todoSourceRequestRef.current === requestId) { + setTodoSourceDetail(sourceDetail) + setTodoSourceError(undefined) + } + } catch (sourceError) { + if (todoSourceRequestRef.current === requestId) { + setTodoSourceError({ + message: errorMessage( + sourceError, + tRef.current('errors.operationFailed') + ), + noteId + }) + } + } + }, + [] + ) + + useEffect(() => { + if (libraryView !== 'todos' || !selectedTodoNoteId) { + return + } + const requestId = ++todoSourceRequestRef.current + void window.goodbuddy.magicNotes + .get(selectedTodoNoteId) + .then((sourceDetail) => { + if (todoSourceRequestRef.current === requestId) { + setTodoSourceDetail(sourceDetail) + setTodoSourceError(undefined) + } + }) + .catch((sourceError: unknown) => { + if (todoSourceRequestRef.current === requestId) { + setTodoSourceError({ + message: errorMessage( + sourceError, + tRef.current('errors.operationFailed') + ), + noteId: selectedTodoNoteId + }) + } + }) + return () => { + todoSourceRequestRef.current += 1 + } + }, [libraryView, selectedTodoNoteId]) + + const selectedTodoSourceEntry = useMemo( + () => + todoSourceDetail?.entries.find( + (entry) => entry.id === selectedTodoEntryId + ), + [selectedTodoEntryId, todoSourceDetail] + ) + const selectedTodoSourceError = + todoSourceError && todoSourceError.noteId === selectedTodoNoteId + ? todoSourceError.message + : '' + const todoSourceLoading = + Boolean(selectedTodoNoteId) && + todoSourceDetail?.id !== selectedTodoNoteId && + !selectedTodoSourceError const reloadTodos = useCallback(async (): Promise => { const snapshot = await window.goodbuddy.magicNotes.listTodos() @@ -1493,6 +1571,11 @@ export function MagicNotesWorkspace({ expectedRevision: todo.revision }) applyTodo(result.todo) + if (selectedTodoId === todo.id) { + todoSourceRequestRef.current += 1 + setTodoSourceDetail(result.note) + setTodoSourceError(undefined) + } if (requestedNoteIdRef.current === result.note.id) { applyDetail(result.note) } else { @@ -2755,6 +2838,55 @@ export function MagicNotesWorkspace({