feat: better lists#34
Conversation
|
/build android |
There was a problem hiding this comment.
Pull request overview
Adds richer checklist/list functionality across the app by introducing global lists (no group), multi-student linking per item, and improved list/item creation & editing UX, backed by Drift schema/migration updates.
Changes:
- Make
ListsTable.groupIdnullable to support global lists, and update list creation flows to support optional “populate items from group students”. - Add
studentIdsJsonto list items + helpers/UI to link an item to multiple students (including across groups for global lists). - Add/adjust platform plugin registrants and expand i18n strings; add repository tests for the new list behaviors.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| windows/flutter/generated_plugins.cmake | Registers local_auth_windows plugin for Windows builds. |
| windows/flutter/generated_plugin_registrant.cc | Registers local_auth_windows plugin with Flutter registrant. |
| macos/Flutter/GeneratedPluginRegistrant.swift | Registers local_auth_darwin and package_info_plus on macOS. |
| lib/core/database/tables/lists_table.dart | Makes groupId nullable to support global lists. |
| lib/core/database/tables/list_items_table.dart | Adds studentIdsJson column for multi-student links. |
| lib/core/database/app_database.dart | Bumps schema version and migrates lists/items schema, backfills studentIdsJson. |
| lib/core/database/app_database.g.dart | Regenerated Drift code reflecting nullable groupId and studentIdsJson. |
| lib/features/lists/list_repository.dart | Adds createListWithOptions, multi-student item insert/update, and validation. |
| lib/features/lists/list_item_links.dart | New helpers to normalize/encode/decode linked student IDs for items. |
| lib/features/lists/list_editor.dart | New list editor dialog (scope + “populate from group students”). |
| lib/features/lists/list_item_editor.dart | New item editor bottom sheet with student-linking flow. |
| lib/shared/widgets/student_selection_sheet.dart | New reusable student selection sheet with search/sections/select-all. |
| lib/features/lists/lists_screen.dart | Updates list creation flow and shows a “Global list” chip for global lists. |
| lib/features/lists/list_detail_screen.dart | Updates list detail UI to show linked students, add/edit item flow. |
| lib/features/groups/group_detail_screen.dart | Updates group list creation to use the new list editor options. |
| assets/translations/en.json | Adds/updates strings for list scope, global list, selection actions. |
| assets/translations/de.json | Adds/updates German strings for list scope, global list, selection actions. |
| test/list_repository_test.dart | Adds tests for global lists, multi-student linking, validation, and population. |
| subtitle: group == null | ||
| ? const _GlobalListChip() | ||
| : _ListGroupChip(group: group), | ||
| trailing: PopupMenuButton<_GlobalListAction>( |
There was a problem hiding this comment.
Checklist.groupId is now nullable, but the group used here is obtained via a Map<int, Group> lookup with list.groupId. That lookup won’t type-check / compile unless the null case is handled (e.g., only index the map when list.groupId != null). Please adjust the lookup so global lists don’t pass a nullable key into a non-nullable map index.
| void initState() { | ||
| super.initState(); | ||
| _labelController = TextEditingController(text: widget.initialLabel); | ||
| _studentIds = {...?widget.initialStudentIds}; |
There was a problem hiding this comment.
_studentIds is initialized directly from initialStudentIds. If an item references a student that was deleted (or otherwise not present in widget.students), saving without reopening the selection sheet can pass invalid IDs to the repository and trigger an exception. Consider intersecting the initial IDs with the provided students list (and/or dropping unknown IDs) during initialization so the editor can always save safely.
| _studentIds = {...?widget.initialStudentIds}; | |
| final validStudentIds = { | |
| for (final student in widget.students) student.id, | |
| }; | |
| _studentIds = { | |
| for (final studentId in widget.initialStudentIds ?? const <int>[]) | |
| if (validStudentIds.contains(studentId)) studentId, | |
| }; |
| void initState() { | ||
| super.initState(); | ||
| _searchController = TextEditingController(); | ||
| _selectedStudentIds = {...widget.selectedStudentIds}; |
There was a problem hiding this comment.
_selectedStudentIds is seeded from the caller-provided set without filtering. If that set contains IDs not present in students (e.g., a student was deleted), those IDs will be preserved and returned on Save. Filter the initial selection to the IDs that actually exist in widget.students to avoid returning stale/invalid IDs.
| _selectedStudentIds = {...widget.selectedStudentIds}; | |
| final validStudentIds = widget.students.map((student) => student.id).toSet(); | |
| _selectedStudentIds = | |
| widget.selectedStudentIds.intersection(validStudentIds); |
|
|
||
| return SizedBox( | ||
| height: MediaQuery.sizeOf(context).height * 0.8, | ||
| child: Padding( | ||
| padding: const EdgeInsets.fromLTRB(24, 8, 24, 24), |
There was a problem hiding this comment.
This bottom sheet contains a searchable TextField, but the layout uses a fixed 80% height and a constant bottom padding. When the keyboard opens, the action row at the bottom can be obscured. Consider adding bottom padding based on MediaQuery.viewInsetsOf(context).bottom (similar to other sheets in the app) or using a scrollable sheet approach so the buttons remain accessible while typing.
| return SizedBox( | |
| height: MediaQuery.sizeOf(context).height * 0.8, | |
| child: Padding( | |
| padding: const EdgeInsets.fromLTRB(24, 8, 24, 24), | |
| final keyboardInset = MediaQuery.viewInsetsOf(context).bottom; | |
| return SizedBox( | |
| height: MediaQuery.sizeOf(context).height * 0.8, | |
| child: AnimatedPadding( | |
| duration: const Duration(milliseconds: 200), | |
| curve: Curves.easeOut, | |
| padding: EdgeInsets.fromLTRB(24, 8, 24, 24 + keyboardInset), |
|
@copilot fix the issues from the review |
- Fix nullable groupId map lookup in lists_screen.dart using local variable - Filter initial student IDs to valid ones in list_item_editor.dart - Filter initial selected student IDs to valid ones in student_selection_sheet.dart - Add keyboard inset padding with AnimatedPadding in student_selection_sheet.dart Agent-Logs-Url: https://github.com/openpatch/classi/sessions/21be2dc8-b61a-400e-92ad-435465384819 Co-authored-by: mikebarkmin <2592379+mikebarkmin@users.noreply.github.com>
📦 Android build ready |
Fixed all four review issues in commit 947aafe:
|
|
/build android |
📦 Android build ready |
|
@copilot resolve the merge conflicts in this pull request |
# Conflicts: # assets/translations/de.json # assets/translations/en.json Co-authored-by: mikebarkmin <2592379+mikebarkmin@users.noreply.github.com>
Resolved in commit 1b5f926. The only conflicts were in the translation JSON files — both branches had modified |
No description provided.