fix: move todo source entry to detail pane

This commit is contained in:
mesalogo
2026-09-09 01:26:10 +08:00
parent e95c7a60d4
commit b1ba14f98e
5 changed files with 39 additions and 122 deletions
@@ -466,6 +466,10 @@ describe('MagicNotesWorkspace', () => {
const sourceEntry = screen.getByRole('region', {
name: '对应的笔记记录'
})
const todoDetail = screen.getByRole('region', { name: '待办详情' })
const aiComments = screen.getByRole('complementary', { name: 'AI 评论' })
expect(todoDetail).toContainElement(sourceEntry)
expect(aiComments).not.toContainElement(sourceEntry)
expect(within(sourceEntry).getByText('发布笔记')).toBeInTheDocument()
expect(within(sourceEntry).getByText('记录正文')).toBeInTheDocument()
})
+33 -100
View File
@@ -485,10 +485,6 @@ export function MagicNotesWorkspace({
const [detail, setDetail] = useState<MagicNoteDetail>()
const [todoSourceDetail, setTodoSourceDetail] =
useState<MagicNoteDetail>()
const [todoSourceError, setTodoSourceError] = useState<{
message: string
noteId: string
}>()
const [loadStatus, setLoadStatus] = useState<LoadStatus>('loading')
const [loadError, setLoadError] = useState('')
const [refreshError, setRefreshError] = useState('')
@@ -1408,30 +1404,6 @@ export function MagicNotesWorkspace({
const selectedTodoNoteId = selectedTodo?.noteId
const selectedTodoEntryId = selectedTodo?.entryId
const loadTodoSource = useCallback(
async (noteId: string): Promise<void> => {
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
@@ -1442,41 +1414,27 @@ export function MagicNotesWorkspace({
.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
})
notifyError(sourceError)
}
})
return () => {
todoSourceRequestRef.current += 1
}
}, [libraryView, selectedTodoNoteId])
}, [libraryView, notifyError, selectedTodoNoteId])
const selectedTodoSourceEntry = useMemo(
() =>
todoSourceDetail?.entries.find(
(entry) => entry.id === selectedTodoEntryId
),
[selectedTodoEntryId, todoSourceDetail]
todoSourceDetail && todoSourceDetail.id === selectedTodoNoteId
? todoSourceDetail.entries.find(
(entry) => entry.id === selectedTodoEntryId
)
: undefined,
[selectedTodoEntryId, selectedTodoNoteId, todoSourceDetail]
)
const selectedTodoSourceError =
todoSourceError && todoSourceError.noteId === selectedTodoNoteId
? todoSourceError.message
: ''
const todoSourceLoading =
Boolean(selectedTodoNoteId) &&
todoSourceDetail?.id !== selectedTodoNoteId &&
!selectedTodoSourceError
const reloadTodos = useCallback(async (): Promise<void> => {
const snapshot = await window.goodbuddy.magicNotes.listTodos()
setTodos(snapshot.todos)
@@ -1574,7 +1532,6 @@ export function MagicNotesWorkspace({
if (selectedTodoId === todo.id) {
todoSourceRequestRef.current += 1
setTodoSourceDetail(result.note)
setTodoSourceError(undefined)
}
if (requestedNoteIdRef.current === result.note.id) {
applyDetail(result.note)
@@ -2767,6 +2724,31 @@ export function MagicNotesWorkspace({
{t('actions.openSourceNote')}
</button>
</div>
<section
aria-label={t('todos.sourceEntryLabel')}
className="magic-todo-source-entry"
>
<header>
<div>
<strong>{t('todos.sourceEntryHeading')}</strong>
<small>{selectedTodo.noteTitle}</small>
</div>
{selectedTodoSourceEntry && (
<time dateTime={selectedTodoSourceEntry.updatedAt}>
{dateFormatter.format(
new Date(selectedTodoSourceEntry.updatedAt)
)}
</time>
)}
</header>
{selectedTodoSourceEntry ? (
<MagicNoteContent content={selectedTodoSourceEntry.content} />
) : (
<p className="magic-notes-muted">
{t('todos.sourceEntryMissing')}
</p>
)}
</section>
</section>
)}
</section>
@@ -2838,55 +2820,6 @@ export function MagicNotesWorkspace({
<PanelRightClose aria-hidden="true" size={15} />
</button>
</div>
{libraryView === 'todos' && selectedTodo && (
<section
aria-label={t('todos.sourceEntryLabel')}
className="magic-todo-source-entry"
>
<header>
<div>
<strong>{t('todos.sourceEntryHeading')}</strong>
<small>{selectedTodo.noteTitle}</small>
</div>
{selectedTodoSourceEntry && (
<time dateTime={selectedTodoSourceEntry.updatedAt}>
{dateFormatter.format(
new Date(selectedTodoSourceEntry.updatedAt)
)}
</time>
)}
</header>
{todoSourceLoading ? (
<p className="magic-notes-muted">
{t('todos.loadingSourceEntry')}
</p>
) : selectedTodoSourceError ? (
<div className="magic-todo-source-entry__error" role="alert">
<span>
{t('todos.sourceEntryLoadFailed', {
error: selectedTodoSourceError
})}
</span>
<button
className="secondary-button"
onClick={() => {
setTodoSourceError(undefined)
void loadTodoSource(selectedTodo.noteId)
}}
type="button"
>
{t('actions.retry')}
</button>
</div>
) : selectedTodoSourceEntry ? (
<MagicNoteContent content={selectedTodoSourceEntry.content} />
) : (
<p className="magic-notes-muted">
{t('todos.sourceEntryMissing')}
</p>
)}
</section>
)}
<div className="magic-notes-ai-controls">
<label>
<span>{t('comments.directionLabel')}</span>
@@ -117,9 +117,7 @@ export const magicNotes = {
sourceNote: 'From note: {{title}}',
sourceEntryLabel: 'Related note entry',
sourceEntryHeading: 'Related entry',
loadingSourceEntry: 'Loading the related entry…',
sourceEntryMissing: 'The note entry for this to-do could not be found.',
sourceEntryLoadFailed: 'Could not load the related entry: {{error}}',
defaultInstructions: 'This to-do comes from a checklist in a note.',
markComplete: 'Mark complete: {{title}}',
markIncomplete: 'Mark incomplete: {{title}}'
@@ -110,9 +110,7 @@ export const magicNotes = {
sourceNote: '来自笔记:{{title}}',
sourceEntryLabel: '对应的笔记记录',
sourceEntryHeading: '对应记录',
loadingSourceEntry: '正在加载对应记录…',
sourceEntryMissing: '未找到这条待办对应的笔记记录。',
sourceEntryLoadFailed: '对应记录加载失败:{{error}}',
defaultInstructions: '此待办来自笔记正文中的待办清单。',
markComplete: '标记为已完成:{{title}}',
markIncomplete: '标记为未完成:{{title}}'
+2 -18
View File
@@ -1399,9 +1399,7 @@
.magic-todo-source-entry {
display: grid;
padding: var(--space-3) 0;
border-bottom: 1px solid var(--border-subtle);
gap: var(--space-2);
gap: var(--space-3);
}
.magic-todo-source-entry > header {
@@ -1438,11 +1436,9 @@
}
.magic-todo-source-entry > .magic-note-content {
max-height: 240px;
overflow: auto;
border: 1px solid var(--border-default);
border-radius: var(--radius-control);
background: var(--surface-raised);
background: var(--surface-subtle);
}
.magic-todo-source-entry > .magic-note-content .ql-editor {
@@ -1453,18 +1449,6 @@
padding-inline: 0;
}
.magic-todo-source-entry__error {
display: grid;
color: var(--danger);
font-size: var(--font-caption);
line-height: 1.5;
gap: var(--space-2);
}
.magic-todo-source-entry__error .secondary-button {
width: fit-content;
}
.magic-notes-ai-controls {
display: grid;
padding: var(--space-3) 0;