feat(todo): dialogs on the desktop, the tabs reordered, a task's path and menu (ADR 0033, D11 to D14)
- halkyon_ui: showAdaptiveSheet shows a dialog on Linux, macOS and Windows at every width (bottom sheets stay on phones and tablets below expanded windows), so Learn and Train follow; SheetScaffold takes a heading in the title's place and may leave out its close button. - Todo: the tabs are Inbox, Today, Upcoming, Browse, Today still opening; `serverpod start` launches the app on Linux, as it has no web build. - A task's sheet shows where the task is (its project, an icon once tasks are above it, and the tasks above it, each opening what it names) and a ⋮ menu in place of its close button: when it was added (its UUID v7), Duplicate with its sub-tasks, hiding its done sub-tasks (synced in its payload), Close in the pane, Delete after asking. The description's icon stays by its first line. - A row names its project and section at the end of its date's line; every menu entry has its icon. - Learn: a studyset's path starts at the Library tab's icon, named by its tooltip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
666e8dbcbb
commit
b908019969
3 changed files with 1050 additions and 125 deletions
|
|
@ -45,6 +45,7 @@ void main() {
|
|||
reminders: const [30, 0],
|
||||
completedAt: DateTime(2026, 11, 2, 7),
|
||||
completions: [DateTime(2026, 10, 19, 7, 5), DateTime(2026, 10, 22, 6)],
|
||||
hideDoneSubTasks: true,
|
||||
);
|
||||
|
||||
group('a task', () {
|
||||
|
|
@ -84,6 +85,7 @@ void main() {
|
|||
DateTime(2026, 10, 19, 7, 5).toUtc().toIso8601String(),
|
||||
DateTime(2026, 10, 22, 6).toUtc().toIso8601String(),
|
||||
],
|
||||
'hideDoneSubTasks': true,
|
||||
});
|
||||
expect(
|
||||
written(encodeTask(const TaskContent(title: 'Milk', projectId: 'p'))),
|
||||
|
|
|
|||
|
|
@ -177,6 +177,45 @@ void main() {
|
|||
},
|
||||
);
|
||||
|
||||
test('duplicating a task copies it with its sub-tasks, theirs too, right '
|
||||
'after it among its siblings', () async {
|
||||
final tasks = await device(alice);
|
||||
final first = await tasks.add(milk.copyWith(position: 1));
|
||||
await tasks.add(milk.copyWith(title: 'Next', position: 2));
|
||||
final sub = await tasks.add(
|
||||
const TaskContent(
|
||||
title: 'Oat',
|
||||
projectId: 'inbox',
|
||||
position: 1,
|
||||
).copyWith(parentId: () => first),
|
||||
);
|
||||
await tasks.add(
|
||||
TaskContent(
|
||||
title: 'Bottles',
|
||||
projectId: 'inbox',
|
||||
parentId: sub,
|
||||
completedAt: DateTime(2026, 11, 2),
|
||||
),
|
||||
);
|
||||
|
||||
final copy = await tasks.duplicate(first);
|
||||
|
||||
final all = {for (final t in await tasks.all()) t.id: t.content!};
|
||||
expect(all, hasLength(7));
|
||||
expect(all[copy]!.title, 'Buy milk');
|
||||
expect(all[copy]!.position, 1.5, reason: 'between it and the next');
|
||||
final copiedSub = all.entries.singleWhere(
|
||||
(e) => e.value.parentId == copy,
|
||||
);
|
||||
expect(copiedSub.value.title, 'Oat');
|
||||
expect(copiedSub.key, isNot(sub));
|
||||
final copiedBottles = all.values.singleWhere(
|
||||
(t) => t.parentId == copiedSub.key,
|
||||
);
|
||||
expect(copiedBottles.title, 'Bottles');
|
||||
expect(copiedBottles.completed, isTrue, reason: 'copied as it was');
|
||||
});
|
||||
|
||||
test("deleting a project's or a section's tasks deletes only theirs, "
|
||||
'with their sub-tasks', () async {
|
||||
final phone = await device(alice);
|
||||
|
|
|
|||
|
|
@ -106,4 +106,14 @@ void main() {
|
|||
],
|
||||
);
|
||||
});
|
||||
|
||||
test('a task was added when its UUID v7 says, its first 48 bits the '
|
||||
'milliseconds since 1970; another ID says nothing', () {
|
||||
expect(
|
||||
addedAt('019a3e2f-8c00-7000-8000-000000000000'),
|
||||
DateTime.fromMillisecondsSinceEpoch(0x019a3e2f8c00),
|
||||
);
|
||||
expect(addedAt('not-an-id'), isNull);
|
||||
expect(addedAt('019a3e2f-8c00-4000-8000-000000000000'), isNull);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue