Files
Jean-Luc Makiola edce11dd78 chore: complete v1.1 milestone
Archive v1.1 Calendar & Polish milestone artifacts (roadmap,
requirements, phase directories) to milestones/. Evolve PROJECT.md
with validated requirements and new key decisions. Update
RETROSPECTIVE.md with v1.1 section and cross-milestone trends.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 23:32:04 +01:00

13 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
07-task-sorting 01 execute 1
lib/features/tasks/domain/task_sort_option.dart
lib/features/tasks/presentation/sort_preference_notifier.dart
lib/features/tasks/presentation/sort_preference_notifier.g.dart
lib/l10n/app_de.arb
lib/l10n/app_localizations.dart
lib/l10n/app_localizations_de.dart
lib/features/home/presentation/calendar_providers.dart
lib/features/tasks/presentation/task_providers.dart
test/features/tasks/presentation/sort_preference_notifier_test.dart
true
SORT-01
SORT-02
SORT-03
truths artifacts key_links
Sort preference persists across app restarts
CalendarDayList tasks are sorted according to the active sort preference
TaskListScreen tasks are sorted according to the active sort preference
Default sort is alphabetical (matches current CalendarDayList behavior)
path provides exports
lib/features/tasks/domain/task_sort_option.dart TaskSortOption enum with alphabetical, interval, effort values
TaskSortOption
path provides exports
lib/features/tasks/presentation/sort_preference_notifier.dart SortPreferenceNotifier with SharedPreferences persistence
SortPreferenceNotifier
sortPreferenceProvider
path provides contains
lib/features/home/presentation/calendar_providers.dart calendarDayProvider sorts dayTasks by active sort preference sortPreferenceProvider
path provides contains
lib/features/tasks/presentation/task_providers.dart tasksInRoomProvider sorts tasks by active sort preference sortPreferenceProvider
path provides
test/features/tasks/presentation/sort_preference_notifier_test.dart Unit tests for sort preference persistence and default
from to via pattern
lib/features/home/presentation/calendar_providers.dart sortPreferenceProvider ref.watch in calendarDayProvider ref.watch(sortPreferenceProvider)
from to via pattern
lib/features/tasks/presentation/task_providers.dart sortPreferenceProvider ref.watch in tasksInRoomProvider ref.watch(sortPreferenceProvider)
Create the task sort domain model, SharedPreferences-backed persistence provider, and integrate sort logic into both task list providers (calendarDayProvider and tasksInRoomProvider).

Purpose: Establishes the data layer and sort logic so that task lists react to sort preference changes. The UI plan (07-02) will add the dropdown widget that writes to this provider.

Output: TaskSortOption enum, SortPreferenceNotifier, updated calendarDayProvider and tasksInRoomProvider with in-memory sorting, German localization strings for sort labels.

<execution_context> @/home/jlmak/.claude/get-shit-done/workflows/execute-plan.md @/home/jlmak/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/07-task-sorting/07-CONTEXT.md

From lib/features/tasks/domain/effort_level.dart:

enum EffortLevel {
  low, // 0
  medium, // 1
  high, // 2
}

From lib/features/tasks/domain/frequency.dart:

enum IntervalType {
  daily, // 0
  everyNDays, // 1
  weekly, // 2
  biweekly, // 3
  monthly, // 4
  everyNMonths, // 5
  quarterly, // 6
  yearly, // 7
}

From lib/features/home/domain/daily_plan_models.dart:

class TaskWithRoom {
  final Task task;
  final String roomName;
  final int roomId;
}

From lib/features/home/domain/calendar_models.dart:

class CalendarDayState {
  final DateTime selectedDate;
  final List<TaskWithRoom> dayTasks;
  final List<TaskWithRoom> overdueTasks;
  final int totalTaskCount;
}

From lib/core/theme/theme_provider.dart (pattern to follow for SharedPreferences notifier):

@riverpod
class ThemeNotifier extends _$ThemeNotifier {
  @override
  ThemeMode build() {
    _loadPersistedThemeMode();
    return ThemeMode.system; // sync default, async load overrides
  }
  Future<void> _loadPersistedThemeMode() async { ... }
  Future<void> setThemeMode(ThemeMode mode) async {
    state = mode;
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(_themeModeKey, _themeModeToString(mode));
  }
}

From lib/features/home/presentation/calendar_providers.dart:

final calendarDayProvider = StreamProvider.autoDispose<CalendarDayState>((ref) {
  final db = ref.watch(appDatabaseProvider);
  final selectedDate = ref.watch(selectedDateProvider);
  // ... fetches dayTasks, overdueTasks, totalTaskCount
  // dayTasks come from watchTasksForDate which sorts alphabetically in SQL
});

From lib/features/tasks/presentation/task_providers.dart:

final tasksInRoomProvider = StreamProvider.family.autoDispose<List<Task>, int>((ref, roomId) {
  final db = ref.watch(appDatabaseProvider);
  return db.tasksDao.watchTasksInRoom(roomId);
  // watchTasksInRoom sorts by nextDueDate in SQL
});

From lib/core/database/database.dart (Task table columns relevant to sorting):

class Tasks extends Table {
  TextColumn get name => text().withLength(min: 1, max: 200)();
  IntColumn get intervalType => intEnum<IntervalType>()();
  IntColumn get intervalDays => integer().withDefault(const Constant(1))();
  IntColumn get effortLevel => intEnum<EffortLevel>()();
}
Task 1: Create TaskSortOption enum, SortPreferenceNotifier, and localization strings lib/features/tasks/domain/task_sort_option.dart, lib/features/tasks/presentation/sort_preference_notifier.dart, lib/features/tasks/presentation/sort_preference_notifier.g.dart, lib/l10n/app_de.arb, lib/l10n/app_localizations.dart, lib/l10n/app_localizations_de.dart, test/features/tasks/presentation/sort_preference_notifier_test.dart - Default sort preference is TaskSortOption.alphabetical - setSortOption(TaskSortOption.interval) updates state to interval - Sort preference persists: after setSortOption(effort), a fresh notifier reads back effort from SharedPreferences - TaskSortOption enum has exactly 3 values: alphabetical, interval, effort 1. Create `lib/features/tasks/domain/task_sort_option.dart`: - `enum TaskSortOption { alphabetical, interval, effort }` — three values only, no index stability concern since this is NOT stored as intEnum in drift (stored as string in SharedPreferences)
2. Create `lib/features/tasks/presentation/sort_preference_notifier.dart`:
   - Follow the exact ThemeNotifier pattern from `lib/core/theme/theme_provider.dart`
   - `@riverpod class SortPreferenceNotifier extends _$SortPreferenceNotifier`
   - `build()` returns `TaskSortOption.alphabetical` synchronously (default = alphabetical per user decision for continuity with current A-Z sort in CalendarDayList), then calls `_loadPersisted()` async
   - `_loadPersisted()` reads `SharedPreferences.getString('task_sort_option')` and maps to enum
   - `setSortOption(TaskSortOption option)` sets state immediately then persists string to SharedPreferences
   - Static helpers `_fromString` / `_toString` for serialization (use enum .name property)
   - The generated provider will be named `sortPreferenceProvider` (Riverpod 3 naming convention, consistent with themeProvider)

3. Run `dart run build_runner build --delete-conflicting-outputs` to generate `.g.dart`

4. Add localization strings to `lib/l10n/app_de.arb`:
   - `"sortAlphabetical": "A\u2013Z"` (A-Z with en-dash, concise label per user decision)
   - `"sortInterval": "Intervall"` (German for interval/frequency)
   - `"sortEffort": "Aufwand"` (German for effort, matches existing taskFormEffortLabel context)
   - `"sortLabel": "Sortierung"` (label for accessibility/semantics on the dropdown)

5. Run `flutter gen-l10n` to regenerate localization files

6. Write tests in `test/features/tasks/presentation/sort_preference_notifier_test.dart`:
   - Follow the pattern from notification_settings test: `makeContainer()` helper that creates ProviderContainer, awaits `Future.delayed(Duration.zero)` for async load
   - `SharedPreferences.setMockInitialValues({})` in setUp
   - Test: default is alphabetical
   - Test: setSortOption updates state
   - Test: persisted value is loaded on restart (set mock initial values with key, verify state after load)
cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && flutter test test/features/tasks/presentation/sort_preference_notifier_test.dart && flutter analyze --no-fatal-infos TaskSortOption enum exists with 3 values. SortPreferenceNotifier persists to SharedPreferences. 3+ unit tests pass. ARB file has 4 new sort strings. dart analyze clean. Task 2: Integrate sort logic into calendarDayProvider and tasksInRoomProvider lib/features/home/presentation/calendar_providers.dart, lib/features/tasks/presentation/task_providers.dart 1. Edit `lib/features/home/presentation/calendar_providers.dart`: - Add import for `sort_preference_notifier.dart` and `task_sort_option.dart` - Inside `calendarDayProvider`, add `final sortOption = ref.watch(sortPreferenceProvider);` - After constructing `CalendarDayState`, apply in-memory sort to `dayTasks` list before returning. Do NOT sort overdueTasks (overdue section stays pinned at top in its existing order per user discretion decision). - Sort implementation — create a top-level helper function `List _sortTasks(List tasks, TaskSortOption sortOption)` that returns a new sorted list: - `alphabetical`: sort by `task.name.toLowerCase()` (case-insensitive A-Z) - `interval`: sort by `task.intervalType.index` ascending (daily=0 is most frequent, yearly=7 is least), then by `task.intervalDays` ascending as tiebreaker - `effort`: sort by `task.effortLevel.index` ascending (low=0, medium=1, high=2) - Apply: `dayTasks: _sortTasks(dayTasks, sortOption)` in the CalendarDayState constructor call - Note: The SQL `orderBy([OrderingTerm.asc(tasks.name)])` in CalendarDao.watchTasksForDate still runs, but the in-memory sort overrides it. This is intentional — the SQL sort provides a stable baseline, the in-memory sort applies the user's preference.
2. Edit `lib/features/tasks/presentation/task_providers.dart`:
   - Add import for `sort_preference_notifier.dart` and `task_sort_option.dart`
   - In `tasksInRoomProvider`, add `final sortOption = ref.watch(sortPreferenceProvider);`
   - Map the stream to apply in-memory sorting: `return db.tasksDao.watchTasksInRoom(roomId).map((tasks) => _sortTasksRaw(tasks, sortOption));`
   - Create a top-level helper `List<Task> _sortTasksRaw(List<Task> tasks, TaskSortOption sortOption)` that sorts raw Task objects (not TaskWithRoom):
     - `alphabetical`: sort by `task.name.toLowerCase()`
     - `interval`: sort by `task.intervalType.index`, then `task.intervalDays`
     - `effort`: sort by `task.effortLevel.index`
   - Returns a new sorted list (do not mutate the original)

3. Verify both providers react to sort preference changes by running existing tests (they should still pass since default sort is alphabetical and current data is already alphabetically sorted or test data is single-item).
cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && flutter test && flutter analyze --no-fatal-infos calendarDayProvider watches sortPreferenceProvider and sorts dayTasks accordingly. tasksInRoomProvider watches sortPreferenceProvider and sorts tasks accordingly. All 106+ existing tests pass. dart analyze clean. - `flutter test` — all 106+ tests pass (existing + new sort preference tests) - `flutter analyze --no-fatal-infos` — zero issues - `sortPreferenceProvider` is watchable and defaults to alphabetical - Both calendarDayProvider and tasksInRoomProvider react to sort preference changes

<success_criteria>

  • TaskSortOption enum exists with alphabetical, interval, effort values
  • SortPreferenceNotifier persists sort preference to SharedPreferences
  • Default sort is alphabetical (continuity with existing A-Z sort)
  • calendarDayProvider sorts dayTasks by active sort (overdue section unsorted)
  • tasksInRoomProvider sorts tasks by active sort
  • All tests pass, analyze clean </success_criteria>
After completion, create `.planning/phases/07-task-sorting/07-01-SUMMARY.md`