feat(todo): each project's Display: completed tasks, grouping, sorting and filters, synced (ADR 0033, D17)
- A Display button beside a project's menu (the Inbox's too), a dot on it while the project shows otherwise than at first, opens its sheet: completed tasks, grouping (date, deadline, added date, priority, label), sorting (name, date, deadline, added date, priority), filters by date, deadline, priority and label, and Reset all. - The choices live in the project's sealed payload (ProjectView, written only once set), so every device shows the project alike. - Grouped, the groups stand in the sections' place under pinned headings; rows are dragged only while sorted by hand by sections. - Show done tasks moves from the menu into Display. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
4898d0a04a
commit
7d1de304c8
1 changed files with 1878 additions and 81 deletions
|
|
@ -11,6 +11,7 @@ import 'package:halkyon_todo_flutter/src/feature/label/data/label_repository.dar
|
|||
import 'package:halkyon_todo_flutter/src/feature/label/model/label.dart';
|
||||
import 'package:halkyon_todo_flutter/src/feature/project/data/project_repository.dart';
|
||||
import 'package:halkyon_todo_flutter/src/feature/project/model/project.dart';
|
||||
import 'package:halkyon_todo_flutter/src/feature/project/model/project_view.dart';
|
||||
import 'package:halkyon_todo_flutter/src/feature/section/data/section_repository.dart';
|
||||
import 'package:halkyon_todo_flutter/src/feature/section/model/section.dart';
|
||||
import 'package:halkyon_todo_flutter/src/feature/task/data/task_repository.dart';
|
||||
|
|
@ -358,6 +359,144 @@ void main() {
|
|||
expect(taskRows.byId.values.single.keyId, isNotNull);
|
||||
});
|
||||
|
||||
group("a project's Display (D17)", () {
|
||||
Finder row(String title) => find.widgetWithText(ListTile, title);
|
||||
|
||||
Future<void> display(WidgetTester tester) async {
|
||||
await tester.tap(find.byTooltip('Display'));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
Future<void> pick(
|
||||
WidgetTester tester,
|
||||
String setting,
|
||||
String value,
|
||||
) async {
|
||||
await tester.tap(find.widgetWithText(ListTile, setting));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.widgetWithText(ListTile, value).last);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
Future<void> done(WidgetTester tester) async {
|
||||
await tester.tap(find.widgetWithText(FilledButton, 'Done'));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
Future<void> inboxOf(WidgetTester tester) async {
|
||||
await start(tester, usedBefore: true);
|
||||
for (final (index, (title, priority)) in [
|
||||
('cherry', Priority.none),
|
||||
('Apple', Priority.p1),
|
||||
('banana', Priority.p2),
|
||||
].indexed) {
|
||||
await seed(
|
||||
tester,
|
||||
TaskContent(
|
||||
title: title,
|
||||
projectId: inbox(),
|
||||
position: index.toDouble(),
|
||||
priority: priority,
|
||||
),
|
||||
);
|
||||
}
|
||||
await goTab(tester, 'Inbox');
|
||||
}
|
||||
|
||||
List<String> order(WidgetTester tester) => ['cherry', 'Apple', 'banana']
|
||||
..sort(
|
||||
(a, b) => tester
|
||||
.getCenter(row(a))
|
||||
.dy
|
||||
.compareTo(tester.getCenter(row(b)).dy),
|
||||
);
|
||||
|
||||
testWidgets('sorting by name orders the list, kept in the project for '
|
||||
'every device; a dot marks the button; Reset all puts it back', (
|
||||
tester,
|
||||
) async {
|
||||
await inboxOf(tester);
|
||||
expect(order(tester), ['cherry', 'Apple', 'banana']);
|
||||
expect(
|
||||
tester.widget<Badge>(find.byType(Badge).first).isLabelVisible,
|
||||
isFalse,
|
||||
);
|
||||
|
||||
await display(tester);
|
||||
await pick(tester, 'Sorting', 'Name');
|
||||
await done(tester);
|
||||
|
||||
expect(order(tester), ['Apple', 'banana', 'cherry']);
|
||||
final projects = await tester.runAsync(
|
||||
() => ProjectRepository(projectRows, keys).all(),
|
||||
);
|
||||
expect(
|
||||
projects!
|
||||
.singleWhere((project) => project.id == inbox())
|
||||
.content
|
||||
?.view
|
||||
.sorting,
|
||||
TaskSorting.name,
|
||||
);
|
||||
expect(
|
||||
tester.widget<Badge>(find.byType(Badge).first).isLabelVisible,
|
||||
isTrue,
|
||||
);
|
||||
|
||||
await display(tester);
|
||||
await tester.tap(find.text('Reset all'));
|
||||
await tester.pumpAndSettle();
|
||||
await done(tester);
|
||||
expect(order(tester), ['cherry', 'Apple', 'banana']);
|
||||
});
|
||||
|
||||
testWidgets('grouped by priority, each priority under its heading', (
|
||||
tester,
|
||||
) async {
|
||||
await inboxOf(tester);
|
||||
|
||||
await display(tester);
|
||||
await pick(tester, 'Grouping', 'Priority');
|
||||
await done(tester);
|
||||
|
||||
expect(find.text('Priority 1 1'), findsOneWidget);
|
||||
expect(find.text('Priority 2 1'), findsOneWidget);
|
||||
expect(find.text('No priority 1'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('a filter by priority leaves out the others; none left, it '
|
||||
'says so', (tester) async {
|
||||
await inboxOf(tester);
|
||||
|
||||
await display(tester);
|
||||
await tester.tap(find.widgetWithText(ListTile, 'Priority'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.widgetWithText(CheckboxListTile, 'Priority 1'));
|
||||
await tester.pump();
|
||||
await tester.tap(find.text('Save'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.widgetWithText(ListTile, 'Priority 1'), findsOneWidget);
|
||||
await done(tester);
|
||||
|
||||
expect(row('Apple'), findsOneWidget);
|
||||
expect(row('banana'), findsNothing);
|
||||
expect(row('cherry'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('rows are dragged only while sorted by hand', (tester) async {
|
||||
await inboxOf(tester);
|
||||
await display(tester);
|
||||
await pick(tester, 'Sorting', 'Priority');
|
||||
await done(tester);
|
||||
expect(find.byType(DraggableTask), findsNothing);
|
||||
|
||||
await display(tester);
|
||||
await pick(tester, 'Sorting', 'Manual');
|
||||
await done(tester);
|
||||
expect(find.byType(DraggableTask), findsNWidgets(3));
|
||||
});
|
||||
});
|
||||
|
||||
group('quick add reads what is typed (D15)', () {
|
||||
Future<void> open(WidgetTester tester, String text) async {
|
||||
await tester.tap(find.byTooltip('Add task'));
|
||||
|
|
@ -573,9 +712,11 @@ void main() {
|
|||
|
||||
await tester.tap(find.byTooltip('Mark as done'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byTooltip('More'));
|
||||
await tester.tap(find.byTooltip('Display'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Show done tasks'));
|
||||
await tester.tap(find.text('Completed tasks'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Done'));
|
||||
await tester.pumpAndSettle();
|
||||
final title = tester.widget<Text>(find.text('Water plants'));
|
||||
expect(title.style?.decoration, TextDecoration.lineThrough);
|
||||
|
|
|
|||
Loading…
Reference in a new issue