feat(todo): a project's Calendar as a month on wide windows (ADR 0033, D17)
From 720 pixels of the Calendar's own width on, a month grid in place of the agenda: the month's weeks from the week's first day, the days of the months beside it muted, each day with as many of its tasks as fit and the rest counted, opening the day's tasks in a sheet; past days with their overdue and done tasks. A task held and dropped on another day moves there (moveTask), a day's free space tapped adds a task on it, a line tapped opens its task. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
0373f723e7
commit
0b6f79244f
1 changed files with 761 additions and 51 deletions
|
|
@ -12,6 +12,7 @@ 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/project/widget/month_grid.dart';
|
||||
import 'package:halkyon_todo_flutter/src/feature/project/widget/project_board.dart';
|
||||
import 'package:halkyon_todo_flutter/src/feature/project/widget/project_calendar_view.dart';
|
||||
import 'package:halkyon_todo_flutter/src/feature/section/data/section_repository.dart';
|
||||
|
|
@ -713,6 +714,146 @@ void main() {
|
|||
);
|
||||
expect(find.byTooltip('Mark as done'), findsOneWidget);
|
||||
});
|
||||
|
||||
group('as a month on a wide window', () {
|
||||
/// Another day of today's month, so in its grid.
|
||||
Day otherDay() {
|
||||
final today = Day.of(DateTime.now());
|
||||
return today.plus(1).month == today.month
|
||||
? today.plus(1)
|
||||
: today.plus(-1);
|
||||
}
|
||||
|
||||
/// The Inbox's Calendar on a desktop's window, [titles] today.
|
||||
Future<List<String>> monthOf(
|
||||
WidgetTester tester, [
|
||||
List<String> titles = const ['Dentist'],
|
||||
]) async {
|
||||
await calendarOf(tester);
|
||||
for (final title in titles) {
|
||||
await seed(
|
||||
tester,
|
||||
TaskContent(
|
||||
title: title,
|
||||
projectId: inbox(),
|
||||
date: TaskDate(Day.of(DateTime.now())),
|
||||
),
|
||||
);
|
||||
}
|
||||
tester.view.physicalSize = const Size(1000, 900);
|
||||
await tester.pumpAndSettle();
|
||||
final stored = await tester.runAsync(() => tasks().all());
|
||||
return [
|
||||
for (final title in titles)
|
||||
stored!.singleWhere((t) => t.content?.title == title).id,
|
||||
];
|
||||
}
|
||||
|
||||
testWidgets('the month’s weeks, each day with its tasks; the agenda '
|
||||
'again once the window narrows', (tester) async {
|
||||
final [id] = await monthOf(tester);
|
||||
final today = Day.of(DateTime.now());
|
||||
expect(find.byType(MonthGrid), findsOneWidget);
|
||||
expect(find.byType(WeekStrip), findsNothing);
|
||||
expect(
|
||||
find.byKey(MonthGrid.chipKey(id, today)),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
find.byKey(MonthGrid.dayKey(Day(today.year, today.month, 1))),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
await tester.tap(find.byTooltip('Following month'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byKey(MonthGrid.chipKey(id, today)), findsNothing);
|
||||
await tester.tap(find.byTooltip('Back to today'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
find.byKey(MonthGrid.chipKey(id, today)),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
tester.view.physicalSize = const Size(412, 900);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(MonthGrid), findsNothing);
|
||||
expect(find.byType(WeekStrip), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('a task held and dropped on another day moves there, '
|
||||
'which can be undone', (tester) async {
|
||||
final [id] = await monthOf(tester);
|
||||
final today = Day.of(DateTime.now());
|
||||
final target = otherDay();
|
||||
final from = tester.getCenter(
|
||||
find.byKey(MonthGrid.chipKey(id, today)),
|
||||
);
|
||||
final to = tester.getCenter(find.byKey(MonthGrid.dayKey(target)));
|
||||
final gesture = await tester.startGesture(from);
|
||||
await tester.pump(
|
||||
kLongPressTimeout + const Duration(milliseconds: 50),
|
||||
);
|
||||
for (var step = 1; step <= 10; step++) {
|
||||
await gesture.moveTo(Offset.lerp(from, to, step / 10)!);
|
||||
await tester.pump();
|
||||
}
|
||||
await gesture.up();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
Future<Day?> dayOf() async {
|
||||
final stored = await tester.runAsync(() => tasks().all());
|
||||
return stored!.singleWhere((t) => t.id == id).content!.date?.day;
|
||||
}
|
||||
|
||||
expect(await dayOf(), target);
|
||||
expect(find.byKey(MonthGrid.chipKey(id, target)), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Undo'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(await dayOf(), today);
|
||||
});
|
||||
|
||||
testWidgets('a day with more than fits counts the rest, which open '
|
||||
'with the others in a sheet', (tester) async {
|
||||
final titles = [for (var i = 1; i <= 8; i++) 'Task $i'];
|
||||
await monthOf(tester, titles);
|
||||
final today = Day.of(DateTime.now());
|
||||
final more = find.byKey(MonthGrid.moreKey(today));
|
||||
expect(more, findsOneWidget);
|
||||
final shown = titles
|
||||
.where((title) => find.text(title).evaluate().isNotEmpty)
|
||||
.length;
|
||||
expect(shown, lessThan(titles.length));
|
||||
expect(find.text('${titles.length - shown} more'), findsOneWidget);
|
||||
|
||||
await tester.tap(more);
|
||||
await tester.pumpAndSettle();
|
||||
for (final title in titles) {
|
||||
expect(row(title), findsOneWidget);
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets('a day’s free space tapped adds a task on it', (
|
||||
tester,
|
||||
) async {
|
||||
await monthOf(tester);
|
||||
final target = otherDay();
|
||||
final cell = find.byKey(MonthGrid.dayKey(target));
|
||||
await tester.tapAt(
|
||||
tester.getBottomRight(cell) - const Offset(8, 8),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.enterText(find.byKey(quickAddTitleKey), 'Call');
|
||||
await tester.pump();
|
||||
await tester.tap(find.widgetWithText(FilledButton, 'Add'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final stored = await tester.runAsync(() => tasks().all());
|
||||
final call = stored!.singleWhere((t) => t.content?.title == 'Call');
|
||||
expect(call.content!.date!.day, target);
|
||||
expect(call.content!.projectId, inbox());
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
group("Upcoming's Display (D17)", () {
|
||||
|
|
|
|||
Loading…
Reference in a new issue