Priority selector and tag system verson2#33
Conversation
📝 WalkthroughWalkthroughThis pull request enhances task management with tag-based organization. Changes include removing errant mock UI from the home screen, integrating tag selection and persistence in the task detail screen, introducing a new TagSelector widget for managing predefined and custom tags, and extending TaskViewModel to support multiple tag categories with SharedPreferences-backed custom tag storage. Changes
Sequence DiagramsequenceDiagram
participant User
participant TaskDetailScreen
participant TagSelector
participant TaskViewModel
participant SharedPreferences as SharedPreferences
User->>TaskDetailScreen: Opens task for editing
TaskDetailScreen->>TaskViewModel: watches TaskViewModel
TaskViewModel->>SharedPreferences: loads customTags from storage
SharedPreferences-->>TaskViewModel: returns custom tags JSON
TaskViewModel-->>TaskDetailScreen: emits tag lists (predefined + custom)
TaskDetailScreen->>TagSelector: displays tag selection UI
User->>TagSelector: selects tags
TaskDetailScreen->>TaskDetailScreen: updates _currentTags state
User->>TagSelector: clicks "Tạo tag" button
TagSelector->>TagSelector: shows tag creation dialog
User->>TagSelector: enters tag name and confirms
TagSelector->>TaskViewModel: calls addCustomTag(name)
TaskViewModel->>TaskViewModel: validates tag (length, count, uniqueness)
TaskViewModel->>SharedPreferences: persists updated custom tags
SharedPreferences-->>TaskViewModel: confirms save
TaskViewModel-->>TaskDetailScreen: notifies listeners
TaskDetailScreen->>TagSelector: refreshes tag list
User->>TaskDetailScreen: clicks save button
TaskDetailScreen->>TaskViewModel: calls updateTaskTags(taskId, _currentTags)
TaskViewModel->>TaskViewModel: updates task.tags in _tasks list
TaskViewModel-->>TaskDetailScreen: notifies listeners
TaskDetailScreen->>User: shows success SnackBar and navigates back
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (11)
src/lib/features/tasks/model/task_model.dart (2)
33-44: Consider distinct icons for each priority level.The
icongetter returnsIcons.flagformedium,high, andurgentpriorities, making them visually indistinguishable by icon alone. OnlylowusesIcons.flag_outlined.Consider using more distinct icons (e.g.,
Icons.flag_outlined,Icons.flag,Icons.priority_high,Icons.warning) to improve visual differentiation.♻️ Suggested distinct icons
IconData get icon { switch (this) { case Priority.low: return Icons.flag_outlined; case Priority.medium: return Icons.flag; case Priority.high: - return Icons.flag; + return Icons.priority_high; case Priority.urgent: - return Icons.flag; + return Icons.warning; } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/features/tasks/model/task_model.dart` around lines 33 - 44, The icon getter for the Priority enum returns the same icon for medium/high/urgent, so update the Priority.icon getter to return distinct Icons for each case (for example: Priority.low -> Icons.flag_outlined, Priority.medium -> Icons.flag, Priority.high -> Icons.priority_high, Priority.urgent -> Icons.warning) to make priorities visually distinct; modify the switch in the icon getter accordingly and ensure all enum cases are covered.
48-54: Consider implementing equality forTagModel.
TagModellacks==andhashCodeoverrides. The codebase works around this by comparingt.id == tag.idexplicitly, but implementing proper equality would make the code cleaner and prevent subtle bugs if tags are compared directly in the future.♻️ Add equality implementation
class TagModel { final String id; final String name; final Color color; const TagModel({required this.id, required this.name, required this.color}); + + `@override` + bool operator ==(Object other) => + identical(this, other) || + other is TagModel && runtimeType == other.runtimeType && id == other.id; + + `@override` + int get hashCode => id.hashCode; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/features/tasks/model/task_model.dart` around lines 48 - 54, TagModel lacks value equality; override operator == and hashCode (or implement Equatable) on class TagModel so instances with the same id (and optionally name/color) compare equal; update TagModel to implement equality by comparing id (and include name and color if desired) and compute hashCode from the same fields to ensure consistent behavior when tags are used in collections or compared directly.src/lib/features/tasks/view/screens/create_task_screen.dart (1)
105-110: Duplicatecategoriesarray defined in two places.The
categorieslist is defined both in theitemBuilder(lines 105-110) and in theonPressedhandler (lines 268-273). Extract this to a class-level constant to avoid duplication and ensure consistency.♻️ Extract categories to a constant
class _CreateTaskScreenState extends State<CreateTaskScreen> { + static const List<String> _categories = [ + 'Development', + 'Research', + 'Design', + 'Backend', + ]; + final TextEditingController _nameController = TextEditingController(Then use
_categoriesinstead of the inline list in both locations.Also applies to: 268-273
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/features/tasks/view/screens/create_task_screen.dart` around lines 105 - 110, The categories list is duplicated in the itemBuilder and the onPressed handler; extract it to a single class-level constant (e.g., add a static const List<String> _categories = [...]) in the CreateTaskScreen (or its State class) and replace the inline lists in both the itemBuilder and the onPressed handler with references to _categories to ensure a single source of truth and prevent drift.src/lib/features/tasks/view/widgets/tag_selector.dart (1)
107-107: Hardcoded magic number5should reference the constant.Line 107 uses
viewModel.customTags.length < 5butTaskViewModeldefines_maxCustomTags = 5. Consider exposing this constant from the ViewModel or defining a shared constant to avoid duplication.♻️ Expose constant from ViewModel
In
task_viewmodel.dart:- static const _maxCustomTags = 5; + static const maxCustomTags = 5;Then in
tag_selector.dart:- if (viewModel.customTags.length < 5) + if (viewModel.customTags.length < TaskViewModel.maxCustomTags)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/features/tasks/view/widgets/tag_selector.dart` at line 107, Replace the hardcoded 5 in tag availability checks with the canonical max-custom-tags constant: expose TaskViewModel._maxCustomTags (rename to public like maxCustomTags) or move the value to a shared constant (e.g., MAX_CUSTOM_TAGS) and import it into TagSelector; then update the condition in TagSelector (the code using viewModel.customTags.length < 5) to use viewModel.maxCustomTags or MAX_CUSTOM_TAGS so both files reference the same constant (update any other occurrences similarly).src/lib/features/tasks/view/widgets/priority_selector.dart (1)
21-59: Minor: Uneven padding on last priority item.Each item has
padding: const EdgeInsets.only(right: 8), causing the last item to have unnecessary right padding while the first item touches the left edge. Consider usingSizedBoxas spacers between items or adjusting the padding logic.♻️ Alternative using separated spacing
Row( - children: Priority.values.map((priority) { + children: Priority.values.asMap().entries.map((entry) { + final index = entry.key; + final priority = entry.value; final isSelected = viewModel.selectedPriority == priority; return Expanded( child: Padding( - padding: const EdgeInsets.only(right: 8), + padding: EdgeInsets.only( + right: index < Priority.values.length - 1 ? 8 : 0, + ), child: GestureDetector(🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/features/tasks/view/widgets/priority_selector.dart` around lines 21 - 59, The right-only padding on each priority item makes the last item have extra space and the first item touch the left edge; update the layout in the priority selector widget so spacing is applied between items instead of per-item right padding: remove the per-item padding (EdgeInsets.only(right: 8)) around the Expanded/GestureDetector/AnimatedContainer and instead insert horizontal spacing between items (e.g., wrap children in a Row and use SizedBox(width: 8) or use ListView.separated or Wrap with spacing) so setPriority, AnimatedContainer, GestureDetector and the Column contents remain unchanged while visual spacing is applied only between items.src/lib/features/tasks/viewmodel/task_viewmodel.dart (2)
95-104: Consider awaiting_saveCustomTags()or handling save failures.
_saveCustomTags()is called fire-and-forget. If the app terminates before the async save completes, data could be lost. Consider either awaiting the save or implementing a more robust persistence strategy with retry logic.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/features/tasks/viewmodel/task_viewmodel.dart` around lines 95 - 104, The code currently adds a new TagModel to _customTags and calls _saveCustomTags() fire-and-forget; change this to await the async _saveCustomTags() call (or handle its Future) inside the method that contains _customTags.add so you only call notifyListeners() and return after the save completes, and add error handling/retry logic to handle save failures (e.g., catch exceptions from _saveCustomTags(), log via your logger, and surface an error result instead of returning null). Ensure you update the method signature to be async if needed and reference the places: _customTags.add(... TagModel ...), _customTagColors, _saveCustomTags(), notifyListeners(), and the return path so the save is awaited and failures are handled.
144-174: Tasks are stored in-memory only and will be lost on app restart.The
_taskslist is not persisted. Unlike custom tags which use SharedPreferences, tasks added viaaddTask()will be lost when the app restarts. Consider whether this is intentional (e.g., tasks are synced from a backend like Supabase) or if local persistence should be added.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/features/tasks/viewmodel/task_viewmodel.dart` around lines 144 - 174, Tasks are only kept in the in-memory _tasks list and added via addTask(TaskModel), so they are lost on app restart; either wire local persistence or clearly delegate to remote sync. Fix by persisting task changes when addTask/remove/edit operations occur (and loading them on ViewModel init): update addTask(TaskModel), any mutators, and the getter tasks/_getFilteredAndSorted to read from a persisted store (e.g., SharedPreferences, Hive, or your Supabase sync layer) and implement a loadTasks() called in the TaskViewModel constructor/init; alternatively, if tasks are meant to be remote-synced, add explicit TODOs and throw/assert when no sync provider is configured so the behavior is clear.src/lib/features/tasks/view/screens/task_detail_screen.dart (1)
192-297: Duplicated tag section UI code.The "Thời gian" (Time Tags) and "Trạng thái" (Status Tags) sections have nearly identical code. Consider extracting a reusable widget similar to
_buildTagGroupinTagSelector.♻️ Extract reusable tag section widget
Widget _buildTagSection({ required String label, required List<TagModel> tags, }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: Theme.of(context).textTheme.labelLarge), const SizedBox(height: 10), Wrap( spacing: 8, runSpacing: 8, children: tags.map((tag) { final isSelected = _isTagSelected(tag); return GestureDetector( onTap: () => _toggleTag(tag), child: AnimatedContainer( // ... common styling ), ); }).toList(), ), ], ); }Then use:
_buildTagSection(label: 'Thời gian', tags: viewModel.timeTags), const SizedBox(height: 20), _buildTagSection(label: 'Trạng thái', tags: viewModel.statusTags),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/features/tasks/view/screens/task_detail_screen.dart` around lines 192 - 297, The two duplicated tag UI blocks for "Thời gian" and "Trạng thái" should be extracted into a reusable widget method (e.g., _buildTagSection) to avoid repetition; create a helper that takes label and List<TagModel> (used with viewModel.timeTags and viewModel.statusTags), reuse the existing logic that references _isTagSelected(tag) and _toggleTag(tag) and the AnimatedContainer styling, then replace both duplicated code blocks with calls to _buildTagSection(label: ..., tags: ...), keeping the const SizedBox(height: 20) between them.src/lib/features/tasks/view/screens/home_screen.dart (3)
208-215: Avoid two sources of truth for priority labels.The filter chips use
p.label, while section headers use_priorityGroupLabel(). Those will drift as soon as one side changes or gets localized differently. Prefer a single label/getter onPriorityfor both places.Also applies to: 316-327
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/features/tasks/view/screens/home_screen.dart` around lines 208 - 215, The UI currently uses two different label sources — p.label in the filter chips and _priorityGroupLabel() for section headers — which can diverge; consolidate to a single source by adding/using a unified getter on the Priority enum (e.g., Priority.label) and replace calls to _priorityGroupLabel() with that getter so both the _FilterChip construction (where p.label is used) and the section header rendering use the same Priority.label; update any localization logic to live on the Priority getter and adjust references in viewModel.setFilterPriority usage and the section header code to reference Priority.label instead of _priorityGroupLabel().
16-24: CacheviewModel.tasksbefore grouping.
tasksis a computed getter, so this loop rebuilds the filtered/sorted list once per priority on every widget rebuild. Read it once, then group the cached result.♻️ Proposed refactor
- Map<Priority, List<TaskModel>> grouped = {}; - for (var priority in Priority.values.reversed) { - final tasks = viewModel.tasks + final visibleTasks = viewModel.tasks; + final grouped = <Priority, List<TaskModel>>{}; + for (final priority in Priority.values.reversed) { + final tasks = visibleTasks .where((t) => t.priority == priority) .toList(); if (tasks.isNotEmpty) grouped[priority] = tasks; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/features/tasks/view/screens/home_screen.dart` around lines 16 - 24, The loop repeatedly calls the computed getter viewModel.tasks for each priority causing unnecessary recomputation; cache the result once (e.g., read viewModel.tasks into a local final like allTasks) before the grouping loop and then use that cached list when filtering by Priority.values.reversed to build grouped (referencing viewModel, TaskViewModel, the tasks getter, grouped, and Priority.values.reversed).
149-190: Prefer Material chips/buttons overGestureDetectorhere.These are primary interactive controls, but
GestureDetectorskips the built-in focus, hover, semantics, splash, and tap-target behavior you get fromChoiceChip/FilterChip/InkWell. That makes the bar harder to use on web/desktop and with accessibility tools.Also applies to: 371-390
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/features/tasks/view/screens/home_screen.dart` around lines 149 - 190, Replace the GestureDetector+AnimatedContainer block used for the "Sort" control with a Material chip or button (e.g., ChoiceChip or FilterChip) so you get built-in focus, hover, semantics, splash and correct tap-target behavior; use the chip's selected property tied to viewModel.sortByPriority and call viewModel.toggleSortByPriority (or onSelected) for taps, map the selected/unselected colors to AppColors.primaryBlue/Colors.white and update the Icon/Text color based on the chip's selected state, and ensure you apply the same change to the other similar control instance that also uses GestureDetector (the repeated block referenced in the review).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@README.md`:
- Around line 19-21: README currently instructs installing shared_preferences:
^2.3.2 but pubspec.yaml uses ^2.2.2 and the YAML code block in README is missing
the closing ```; fix by making versions consistent (either change the README
entry to shared_preferences: ^2.2.2 to match pubspec.yaml or update pubspec.yaml
to ^2.3.2) and add the missing closing triple-backtick to the README code fence
so the block is properly terminated; reference the shared_preferences dependency
name, the README.md instructions block, and pubspec.yaml entry when applying the
change.
In `@src/lib/features/tasks/view/screens/create_task_screen.dart`:
- Around line 266-289: Add input validation inside the onPressed handler before
constructing TaskModel: validate _nameController.text is non-empty and not
whitespace, ensure _startTime is before or equal to _endTime (or swap/raise UI
error), and enforce description rules if required (e.g., non-empty) — if any
check fails, show a user-facing error (e.g., SnackBar or dialog) and return
early; only call viewModel.addTask(newTask), viewModel.reset(), and
Navigator.pop(context) when all validations pass. Reference the onPressed
closure, TaskViewModel, TaskModel, _nameController, _descController, _startTime,
_endTime, _selectedCategoryIndex, and viewModel.selectedPriority/selectedTags
when implementing.
In `@src/lib/features/tasks/view/screens/home_screen.dart`:
- Around line 18-25: The grouped-by-priority block ignores the
viewModel.sortByPriority toggle because it always iterates
Priority.values.reversed; update it to honor viewModel.sortByPriority by
computing the priority iteration order first (e.g., var priorities =
viewModel.sortByPriority ? Priority.values.reversed.toList() :
Priority.values.toList()) and then build grouped by iterating over that
priorities list (leave Map<Priority, List<TaskModel>> grouped and the filtering
by t.priority in place), or alternatively remove the sort toggle UI if you
intend the grouped layout to have a fixed order; reference symbols:
TaskViewModel.tasks, viewModel.sortByPriority, Priority.values.reversed,
grouped.
In `@src/lib/features/tasks/view/screens/task_detail_screen.dart`:
- Around line 56-73: The _saveChanges method currently mutates widget.task
fields directly which can cause unexpected side effects; instead, add an
updateTask method on TaskViewModel (e.g., TaskViewModel.updateTask(String
taskId, {String? title, String? description, TimeOfDay? startTime, TimeOfDay?
endTime, String? category, List<TagModel>? tags})) that finds the task by id,
applies the non-null field updates, calls notifyListeners(), and then call that
method from _saveChanges (and keep calling updateTaskTags as needed) so the
ViewModel owns state changes rather than mutating widget.task directly.
In `@src/lib/features/tasks/viewmodel/task_viewmodel.dart`:
- Around line 56-72: _wrap the SharedPreferences access and jsonDecode in
_loadCustomTags() with a try-catch to prevent crashes from malformed JSON or
prefs failures: call SharedPreferences.getInstance() and jsonDecode inside the
try block, parse into TagModel as before, and in the catch block log the error
(e.g., debugPrint or your app logger), clear or remove the corrupt value via
prefs.remove(_customTagsKey) or set _customTags = [] and call notifyListeners()
so the app recovers gracefully; reference the existing symbols _loadCustomTags,
_customTagsKey, TagModel, _customTags, and notifyListeners when applying the
change.
---
Nitpick comments:
In `@src/lib/features/tasks/model/task_model.dart`:
- Around line 33-44: The icon getter for the Priority enum returns the same icon
for medium/high/urgent, so update the Priority.icon getter to return distinct
Icons for each case (for example: Priority.low -> Icons.flag_outlined,
Priority.medium -> Icons.flag, Priority.high -> Icons.priority_high,
Priority.urgent -> Icons.warning) to make priorities visually distinct; modify
the switch in the icon getter accordingly and ensure all enum cases are covered.
- Around line 48-54: TagModel lacks value equality; override operator == and
hashCode (or implement Equatable) on class TagModel so instances with the same
id (and optionally name/color) compare equal; update TagModel to implement
equality by comparing id (and include name and color if desired) and compute
hashCode from the same fields to ensure consistent behavior when tags are used
in collections or compared directly.
In `@src/lib/features/tasks/view/screens/create_task_screen.dart`:
- Around line 105-110: The categories list is duplicated in the itemBuilder and
the onPressed handler; extract it to a single class-level constant (e.g., add a
static const List<String> _categories = [...]) in the CreateTaskScreen (or its
State class) and replace the inline lists in both the itemBuilder and the
onPressed handler with references to _categories to ensure a single source of
truth and prevent drift.
In `@src/lib/features/tasks/view/screens/home_screen.dart`:
- Around line 208-215: The UI currently uses two different label sources —
p.label in the filter chips and _priorityGroupLabel() for section headers —
which can diverge; consolidate to a single source by adding/using a unified
getter on the Priority enum (e.g., Priority.label) and replace calls to
_priorityGroupLabel() with that getter so both the _FilterChip construction
(where p.label is used) and the section header rendering use the same
Priority.label; update any localization logic to live on the Priority getter and
adjust references in viewModel.setFilterPriority usage and the section header
code to reference Priority.label instead of _priorityGroupLabel().
- Around line 16-24: The loop repeatedly calls the computed getter
viewModel.tasks for each priority causing unnecessary recomputation; cache the
result once (e.g., read viewModel.tasks into a local final like allTasks) before
the grouping loop and then use that cached list when filtering by
Priority.values.reversed to build grouped (referencing viewModel, TaskViewModel,
the tasks getter, grouped, and Priority.values.reversed).
- Around line 149-190: Replace the GestureDetector+AnimatedContainer block used
for the "Sort" control with a Material chip or button (e.g., ChoiceChip or
FilterChip) so you get built-in focus, hover, semantics, splash and correct
tap-target behavior; use the chip's selected property tied to
viewModel.sortByPriority and call viewModel.toggleSortByPriority (or onSelected)
for taps, map the selected/unselected colors to
AppColors.primaryBlue/Colors.white and update the Icon/Text color based on the
chip's selected state, and ensure you apply the same change to the other similar
control instance that also uses GestureDetector (the repeated block referenced
in the review).
In `@src/lib/features/tasks/view/screens/task_detail_screen.dart`:
- Around line 192-297: The two duplicated tag UI blocks for "Thời gian" and
"Trạng thái" should be extracted into a reusable widget method (e.g.,
_buildTagSection) to avoid repetition; create a helper that takes label and
List<TagModel> (used with viewModel.timeTags and viewModel.statusTags), reuse
the existing logic that references _isTagSelected(tag) and _toggleTag(tag) and
the AnimatedContainer styling, then replace both duplicated code blocks with
calls to _buildTagSection(label: ..., tags: ...), keeping the const
SizedBox(height: 20) between them.
In `@src/lib/features/tasks/view/widgets/priority_selector.dart`:
- Around line 21-59: The right-only padding on each priority item makes the last
item have extra space and the first item touch the left edge; update the layout
in the priority selector widget so spacing is applied between items instead of
per-item right padding: remove the per-item padding (EdgeInsets.only(right: 8))
around the Expanded/GestureDetector/AnimatedContainer and instead insert
horizontal spacing between items (e.g., wrap children in a Row and use
SizedBox(width: 8) or use ListView.separated or Wrap with spacing) so
setPriority, AnimatedContainer, GestureDetector and the Column contents remain
unchanged while visual spacing is applied only between items.
In `@src/lib/features/tasks/view/widgets/tag_selector.dart`:
- Line 107: Replace the hardcoded 5 in tag availability checks with the
canonical max-custom-tags constant: expose TaskViewModel._maxCustomTags (rename
to public like maxCustomTags) or move the value to a shared constant (e.g.,
MAX_CUSTOM_TAGS) and import it into TagSelector; then update the condition in
TagSelector (the code using viewModel.customTags.length < 5) to use
viewModel.maxCustomTags or MAX_CUSTOM_TAGS so both files reference the same
constant (update any other occurrences similarly).
In `@src/lib/features/tasks/viewmodel/task_viewmodel.dart`:
- Around line 95-104: The code currently adds a new TagModel to _customTags and
calls _saveCustomTags() fire-and-forget; change this to await the async
_saveCustomTags() call (or handle its Future) inside the method that contains
_customTags.add so you only call notifyListeners() and return after the save
completes, and add error handling/retry logic to handle save failures (e.g.,
catch exceptions from _saveCustomTags(), log via your logger, and surface an
error result instead of returning null). Ensure you update the method signature
to be async if needed and reference the places: _customTags.add(... TagModel
...), _customTagColors, _saveCustomTags(), notifyListeners(), and the return
path so the save is awaited and failures are handled.
- Around line 144-174: Tasks are only kept in the in-memory _tasks list and
added via addTask(TaskModel), so they are lost on app restart; either wire local
persistence or clearly delegate to remote sync. Fix by persisting task changes
when addTask/remove/edit operations occur (and loading them on ViewModel init):
update addTask(TaskModel), any mutators, and the getter
tasks/_getFilteredAndSorted to read from a persisted store (e.g.,
SharedPreferences, Hive, or your Supabase sync layer) and implement a
loadTasks() called in the TaskViewModel constructor/init; alternatively, if
tasks are meant to be remote-synced, add explicit TODOs and throw/assert when no
sync provider is configured so the behavior is clear.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b4ebaa88-7d07-4fee-992b-088df9324a7a
📒 Files selected for processing (9)
README.mdsrc/lib/features/tasks/model/task_model.dartsrc/lib/features/tasks/view/screens/create_task_screen.dartsrc/lib/features/tasks/view/screens/home_screen.dartsrc/lib/features/tasks/view/screens/task_detail_screen.dartsrc/lib/features/tasks/view/widgets/priority_selector.dartsrc/lib/features/tasks/view/widgets/tag_selector.dartsrc/lib/features/tasks/viewmodel/task_viewmodel.dartsrc/lib/main.dart
| onPressed: () { | ||
| final viewModel = context.read<TaskViewModel>(); | ||
| final List<String> categories = [ | ||
| 'Development', | ||
| 'Research', | ||
| 'Design', | ||
| 'Backend', | ||
| ]; | ||
| final newTask = TaskModel( | ||
| id: DateTime.now().millisecondsSinceEpoch | ||
| .toString(), | ||
| title: _nameController.text, | ||
| description: _descController.text, | ||
| category: categories[_selectedCategoryIndex], | ||
| startTime: _startTime, | ||
| endTime: _endTime, | ||
| date: _selectedDate, | ||
| priority: viewModel.selectedPriority, | ||
| tags: List.from(viewModel.selectedTags), | ||
| ); | ||
| viewModel.addTask(newTask); | ||
| viewModel.reset(); | ||
| Navigator.pop(context); | ||
| }, |
There was a problem hiding this comment.
Missing input validation before task creation.
The task is created without validating:
- Empty or whitespace-only title
- Start time being after end time
- Empty description (if required)
Consider adding validation before creating the task.
🛡️ Suggested validation
onPressed: () {
+ final title = _nameController.text.trim();
+ if (title.isEmpty) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(
+ content: Text('Task name cannot be empty'),
+ backgroundColor: Colors.red,
+ ),
+ );
+ return;
+ }
+
final viewModel = context.read<TaskViewModel>();
final List<String> categories = [📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| onPressed: () { | |
| final viewModel = context.read<TaskViewModel>(); | |
| final List<String> categories = [ | |
| 'Development', | |
| 'Research', | |
| 'Design', | |
| 'Backend', | |
| ]; | |
| final newTask = TaskModel( | |
| id: DateTime.now().millisecondsSinceEpoch | |
| .toString(), | |
| title: _nameController.text, | |
| description: _descController.text, | |
| category: categories[_selectedCategoryIndex], | |
| startTime: _startTime, | |
| endTime: _endTime, | |
| date: _selectedDate, | |
| priority: viewModel.selectedPriority, | |
| tags: List.from(viewModel.selectedTags), | |
| ); | |
| viewModel.addTask(newTask); | |
| viewModel.reset(); | |
| Navigator.pop(context); | |
| }, | |
| onPressed: () { | |
| final title = _nameController.text.trim(); | |
| if (title.isEmpty) { | |
| ScaffoldMessenger.of(context).showSnackBar( | |
| const SnackBar( | |
| content: Text('Task name cannot be empty'), | |
| backgroundColor: Colors.red, | |
| ), | |
| ); | |
| return; | |
| } | |
| final viewModel = context.read<TaskViewModel>(); | |
| final List<String> categories = [ | |
| 'Development', | |
| 'Research', | |
| 'Design', | |
| 'Backend', | |
| ]; | |
| final newTask = TaskModel( | |
| id: DateTime.now().millisecondsSinceEpoch | |
| .toString(), | |
| title: _nameController.text, | |
| description: _descController.text, | |
| category: categories[_selectedCategoryIndex], | |
| startTime: _startTime, | |
| endTime: _endTime, | |
| date: _selectedDate, | |
| priority: viewModel.selectedPriority, | |
| tags: List.from(viewModel.selectedTags), | |
| ); | |
| viewModel.addTask(newTask); | |
| viewModel.reset(); | |
| Navigator.pop(context); | |
| }, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/features/tasks/view/screens/create_task_screen.dart` around lines 266
- 289, Add input validation inside the onPressed handler before constructing
TaskModel: validate _nameController.text is non-empty and not whitespace, ensure
_startTime is before or equal to _endTime (or swap/raise UI error), and enforce
description rules if required (e.g., non-empty) — if any check fails, show a
user-facing error (e.g., SnackBar or dialog) and return early; only call
viewModel.addTask(newTask), viewModel.reset(), and Navigator.pop(context) when
all validations pass. Reference the onPressed closure, TaskViewModel, TaskModel,
_nameController, _descController, _startTime, _endTime, _selectedCategoryIndex,
and viewModel.selectedPriority/selectedTags when implementing.
| // Nhóm task theo priority | ||
| Map<Priority, List<TaskModel>> grouped = {}; | ||
| for (var priority in Priority.values.reversed) { | ||
| final tasks = viewModel.tasks | ||
| .where((t) => t.priority == priority) | ||
| .toList(); | ||
| if (tasks.isNotEmpty) grouped[priority] = tasks; | ||
| } |
There was a problem hiding this comment.
sortByPriority has no visible effect in this screen.
TaskViewModel.tasks already applies the priority sort, but this block immediately re-buckets everything into a fixed Priority.values.reversed order. That means the toggle on Line 150 changes state without changing the rendered section order. Either remove the sort toggle for this grouped layout or make the section order depend on viewModel.sortByPriority.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/features/tasks/view/screens/home_screen.dart` around lines 18 - 25,
The grouped-by-priority block ignores the viewModel.sortByPriority toggle
because it always iterates Priority.values.reversed; update it to honor
viewModel.sortByPriority by computing the priority iteration order first (e.g.,
var priorities = viewModel.sortByPriority ? Priority.values.reversed.toList() :
Priority.values.toList()) and then build grouped by iterating over that
priorities list (leave Map<Priority, List<TaskModel>> grouped and the filtering
by t.priority in place), or alternatively remove the sort toggle UI if you
intend the grouped layout to have a fixed order; reference symbols:
TaskViewModel.tasks, viewModel.sortByPriority, Priority.values.reversed,
grouped.
| void _saveChanges() { | ||
| // Update the local model | ||
| // (Note: Later, this will call TaskViewModel -> TaskService -> your ASP.NET Core API to update the database) | ||
| widget.task.title = _titleController.text; | ||
| widget.task.description = _descController.text; | ||
| widget.task.startTime = _startTime; | ||
| widget.task.endTime = _endTime; | ||
| widget.task.category = _currentCategory; | ||
|
|
||
| // Show success message | ||
| // Lưu tags mới vào task qua ViewModel | ||
| context.read<TaskViewModel>().updateTaskTags(widget.task.id, _currentTags); | ||
|
|
||
| ScaffoldMessenger.of(context).showSnackBar( | ||
| const SnackBar(content: Text('Task updated successfully!'), backgroundColor: Colors.green), | ||
| const SnackBar( | ||
| content: Text('Task updated successfully!'), | ||
| backgroundColor: Colors.green, | ||
| ), | ||
| ); | ||
|
|
||
| // Return to the previous screen | ||
| Navigator.pop(context); | ||
| } |
There was a problem hiding this comment.
Direct mutation of widget.task may cause unexpected side effects.
_saveChanges() directly mutates widget.task properties (lines 57-61). This modifies the original TaskModel object that was passed to this screen, which can lead to:
- UI inconsistencies if the parent widget doesn't expect the object to change
- Difficulty tracking state changes
- Potential issues with provider/state management
Consider updating the task through the ViewModel instead of direct mutation.
🐛 Suggested approach using ViewModel
Add an updateTask method to TaskViewModel that handles all task field updates:
// In TaskViewModel
void updateTask(String taskId, {
String? title,
String? description,
TimeOfDay? startTime,
TimeOfDay? endTime,
String? category,
List<TagModel>? tags,
}) {
final index = _tasks.indexWhere((t) => t.id == taskId);
if (index != -1) {
final task = _tasks[index];
if (title != null) task.title = title;
if (description != null) task.description = description;
// ... etc
notifyListeners();
}
}Then call this method from _saveChanges() instead of direct mutation.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/features/tasks/view/screens/task_detail_screen.dart` around lines 56
- 73, The _saveChanges method currently mutates widget.task fields directly
which can cause unexpected side effects; instead, add an updateTask method on
TaskViewModel (e.g., TaskViewModel.updateTask(String taskId, {String? title,
String? description, TimeOfDay? startTime, TimeOfDay? endTime, String? category,
List<TagModel>? tags})) that finds the task by id, applies the non-null field
updates, calls notifyListeners(), and then call that method from _saveChanges
(and keep calling updateTaskTags as needed) so the ViewModel owns state changes
rather than mutating widget.task directly.
| Future<void> _loadCustomTags() async { | ||
| final prefs = await SharedPreferences.getInstance(); | ||
| final raw = prefs.getString(_customTagsKey); | ||
| if (raw != null) { | ||
| final List decoded = jsonDecode(raw); | ||
| _customTags = decoded | ||
| .map( | ||
| (e) => TagModel( | ||
| id: e['id'], | ||
| name: e['name'], | ||
| color: Color(e['color']), | ||
| ), | ||
| ) | ||
| .toList(); | ||
| notifyListeners(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Add error handling for SharedPreferences operations.
_loadCustomTags() lacks try-catch for potential jsonDecode failures (malformed JSON) or SharedPreferences errors. A corrupt stored value would crash the app.
🛡️ Proposed fix with error handling
Future<void> _loadCustomTags() async {
+ try {
final prefs = await SharedPreferences.getInstance();
final raw = prefs.getString(_customTagsKey);
if (raw != null) {
final List decoded = jsonDecode(raw);
_customTags = decoded
.map(
(e) => TagModel(
id: e['id'],
name: e['name'],
color: Color(e['color']),
),
)
.toList();
notifyListeners();
}
+ } catch (e) {
+ debugPrint('Failed to load custom tags: $e');
+ // Optionally clear corrupted data
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Future<void> _loadCustomTags() async { | |
| final prefs = await SharedPreferences.getInstance(); | |
| final raw = prefs.getString(_customTagsKey); | |
| if (raw != null) { | |
| final List decoded = jsonDecode(raw); | |
| _customTags = decoded | |
| .map( | |
| (e) => TagModel( | |
| id: e['id'], | |
| name: e['name'], | |
| color: Color(e['color']), | |
| ), | |
| ) | |
| .toList(); | |
| notifyListeners(); | |
| } | |
| } | |
| Future<void> _loadCustomTags() async { | |
| try { | |
| final prefs = await SharedPreferences.getInstance(); | |
| final raw = prefs.getString(_customTagsKey); | |
| if (raw != null) { | |
| final List decoded = jsonDecode(raw); | |
| _customTags = decoded | |
| .map( | |
| (e) => TagModel( | |
| id: e['id'], | |
| name: e['name'], | |
| color: Color(e['color']), | |
| ), | |
| ) | |
| .toList(); | |
| notifyListeners(); | |
| } | |
| } catch (e) { | |
| debugPrint('Failed to load custom tags: $e'); | |
| // Optionally clear corrupted data | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/features/tasks/viewmodel/task_viewmodel.dart` around lines 56 - 72,
_wrap the SharedPreferences access and jsonDecode in _loadCustomTags() with a
try-catch to prevent crashes from malformed JSON or prefs failures: call
SharedPreferences.getInstance() and jsonDecode inside the try block, parse into
TagModel as before, and in the catch block log the error (e.g., debugPrint or
your app logger), clear or remove the corrupt value via
prefs.remove(_customTagsKey) or set _customTags = [] and call notifyListeners()
so the app recovers gracefully; reference the existing symbols _loadCustomTags,
_customTagsKey, TagModel, _customTags, and notifyListeners when applying the
change.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/lib/features/tasks/view/screens/task_detail_screen.dart (1)
204-309: Consider extracting duplicated tag section widget.The "Thời gian" (lines 204-255) and "Trạng thái" (lines 258-309) sections share identical structure. Extracting a reusable
_buildTagSection(String title, List<TagModel> tags)method would reduce duplication and improve maintainability.Also note:
workTypeTagsis defined in the ViewModel but not displayed here. If this is intentional, consider documenting why.♻️ Proposed extraction
Widget _buildTagSection(String title, List<TagModel> tags) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: Theme.of(context).textTheme.labelLarge), const SizedBox(height: 10), Wrap( spacing: 8, runSpacing: 8, children: tags.map((tag) { final isSelected = _isTagSelected(tag); return GestureDetector( onTap: () => _toggleTag(tag), child: AnimatedContainer( duration: const Duration(milliseconds: 200), padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), decoration: BoxDecoration( color: isSelected ? tag.color : tag.color.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(20), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ if (isSelected) ...[ const Icon(Icons.check, color: Colors.white, size: 14), const SizedBox(width: 4), ], Text( tag.name, style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: isSelected ? Colors.white : tag.color, ), ), ], ), ), ); }).toList(), ), ], ); }Then use as:
_buildTagSection('Thời gian', viewModel.timeTags), const SizedBox(height: 20), _buildTagSection('Trạng thái', viewModel.statusTags),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/features/tasks/view/screens/task_detail_screen.dart` around lines 204 - 309, The two tag sections duplicate the same UI; extract a reusable widget method (e.g., _buildTagSection) and replace the repeated blocks with calls like _buildTagSection('Thời gian', viewModel.timeTags) and _buildTagSection('Trạng thái', viewModel.statusTags). The extracted method should use the existing helpers _isTagSelected(tag) and _toggleTag(tag), render the Wrap of tags with the AnimatedContainer / Icon / Text logic, and preserve spacing (include the SizedBox between sections); also review whether viewModel.workTypeTags should be displayed or document why it is omitted.src/lib/features/tasks/viewmodel/task_viewmodel.dart (1)
177-183: Task tag updates are not persisted.
updateTaskTags()modifies the in-memory task list but doesn't persist changes. Unlike custom tags (saved to SharedPreferences), task modifications will be lost on app restart. Consider whether task persistence is planned separately or if this should be added here.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/features/tasks/viewmodel/task_viewmodel.dart` around lines 177 - 183, updateTaskTags currently only updates the in-memory _tasks list and calls notifyListeners, so tag changes are lost on restart; modify updateTaskTags to also persist the change by invoking the existing task persistence API (e.g., call the repository/storage method that saves a single task or the entire tasks list) after assigning _tasks[index].tags = newTags, or implement a new private helper like _saveTasksToStorage() and call it there; ensure you reference updateTaskTags, _tasks, notifyListeners and the repository/storage save method so the change is durable (use the same persistence mechanism used for custom tags, e.g., SharedPreferences or the app’s TaskRepository).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/lib/features/tasks/viewmodel/task_viewmodel.dart`:
- Around line 102-104: The call to _saveCustomTags() in addCustomTag currently
isn't awaited causing potential silent failures; change addCustomTag(String
name) to be async (Future<String?>) and await _saveCustomTags(), catch and
handle any exceptions (e.g., log or surface an error and return null or rethrow)
before calling notifyListeners(), ensuring the save completes or errors are
handled so data isn't lost silently.
---
Nitpick comments:
In `@src/lib/features/tasks/view/screens/task_detail_screen.dart`:
- Around line 204-309: The two tag sections duplicate the same UI; extract a
reusable widget method (e.g., _buildTagSection) and replace the repeated blocks
with calls like _buildTagSection('Thời gian', viewModel.timeTags) and
_buildTagSection('Trạng thái', viewModel.statusTags). The extracted method
should use the existing helpers _isTagSelected(tag) and _toggleTag(tag), render
the Wrap of tags with the AnimatedContainer / Icon / Text logic, and preserve
spacing (include the SizedBox between sections); also review whether
viewModel.workTypeTags should be displayed or document why it is omitted.
In `@src/lib/features/tasks/viewmodel/task_viewmodel.dart`:
- Around line 177-183: updateTaskTags currently only updates the in-memory
_tasks list and calls notifyListeners, so tag changes are lost on restart;
modify updateTaskTags to also persist the change by invoking the existing task
persistence API (e.g., call the repository/storage method that saves a single
task or the entire tasks list) after assigning _tasks[index].tags = newTags, or
implement a new private helper like _saveTasksToStorage() and call it there;
ensure you reference updateTaskTags, _tasks, notifyListeners and the
repository/storage save method so the change is durable (use the same
persistence mechanism used for custom tags, e.g., SharedPreferences or the app’s
TaskRepository).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 58f70d19-2c83-48ff-b03a-5e231926e39f
📒 Files selected for processing (3)
src/lib/features/tasks/view/screens/home_screen.dartsrc/lib/features/tasks/view/screens/task_detail_screen.dartsrc/lib/features/tasks/viewmodel/task_viewmodel.dart
💤 Files with no reviewable changes (1)
- src/lib/features/tasks/view/screens/home_screen.dart
| _saveCustomTags(); | ||
| notifyListeners(); | ||
| return null; |
There was a problem hiding this comment.
_saveCustomTags() is not awaited, risking silent data loss.
If the save operation fails, the user sees no error and notifyListeners() proceeds normally. On app restart, the tag will be missing. Consider awaiting the save and handling errors.
🛡️ Proposed fix to await save operation
- _saveCustomTags();
- notifyListeners();
- return null;
+ await _saveCustomTags();
+ notifyListeners();
+ return null;Note: This requires changing the method signature to Future<String?> addCustomTag(String name) async.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| _saveCustomTags(); | |
| notifyListeners(); | |
| return null; | |
| await _saveCustomTags(); | |
| notifyListeners(); | |
| return null; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/features/tasks/viewmodel/task_viewmodel.dart` around lines 102 - 104,
The call to _saveCustomTags() in addCustomTag currently isn't awaited causing
potential silent failures; change addCustomTag(String name) to be async
(Future<String?>) and await _saveCustomTags(), catch and handle any exceptions
(e.g., log or surface an error and return null or rethrow) before calling
notifyListeners(), ensuring the save completes or errors are handled so data
isn't lost silently.
* Feature/user profile (#30) * feat(UserProfile): build screen UserProfile # Conflicts: # src/lib/features/main/view/screens/main_screen.dart * feat: switch dark/light theme * fix: black color theme * fix: black theme in statistics screen * feat: add dark theme to auth screen * feat: apply dark theme for bottom navigation bar * feat(priority task):Implement priority and tag selection in task creation version 1 (#31) * feat(task): implement priority and tag selection features in task creation * Update README.md * Update README.md * Update README.md --------- Co-authored-by: Tran Quang Ha <tranquangha2006@gmail.com> * Update README.md (#32) * feat: Priority selector and tag system verson2 (#33) * feat(task): implement priority and tag selection features in task creation * feat(tags): enhance tag management with custom tag creation and selection * Update README.md * Update README.md * fix: error screen in main.dart (#34) * Enhance authentication UI and implement Focus feature set (#35) * feat(core): add auth layout template, custom textfield and colors * feat(auth): implement viewmodels for auth flow (MVVM) * feat(auth): build complete auth UI screens (Login, Register, OTP, Passwords) * chore(main): set LoginView as initial route * refactor(auth) : delete .gitkeep * chore: update dependencies and pubspec.lock * refactor(auth): optimize registration logic, timezone handling, and form validation * feat(auth): update UI for login, registration, and forgot password screens * feat(tasks): update task management UI and statistics screen * chore: update main entry point and fix widget tests * chore: ignore devtools_options.yaml * chore: ignore devtools_options.yaml * style(login) : rewrite title for login view * feat(auth): configure android deep link for supabase oauth * refactor(ui): add social login callbacks to auth layout template * feat(auth): update oauth methods with redirect url and signout * feat(auth): implement AuthGate using StreamBuilder for session tracking * feat(viewmodel): add oauth logic and improve provider lifecycle * refactor(ui): migrate LoginView to Provider pattern * chore(main): set AuthGate as initial route and setup provider * feat: implement full Focus feature set - Added Pomodoro timer with Start/Reset/Skip logic. - Integrated local Quick Notes with Pin/Delete functionality. - Supported image attachments in notes using image_picker. - Added Focus settings: time duration, vibration, and ringtones. * fix (auth) : dispose TextEditingControllers to prevent memory leaks * refactor (alarm ) : create off alarm button when time out * fix: apply CodeRabbit auto-fixes Fixed 3 file(s) based on 4 unresolved review comments. Co-authored-by: CodeRabbit <noreply@coderabbit.ai> * fix(timer): prevent division by zero in progress calculation and sanitize negative settings input * fix(timer): prevent division by zero in progress calculation and sanitize negative settings input * fix(auth): unblock new-user login and add settings logout * refactor(LoginScreen) : compact all items to fit in screen to help users interface easily --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: CodeRabbit <noreply@coderabbit.ai> * build(deps)(deps): bump shared_preferences from 2.5.4 to 2.5.5 in /src (#36) Bumps [shared_preferences](https://github.com/flutter/packages/tree/main/packages/shared_preferences) from 2.5.4 to 2.5.5. - [Commits](https://github.com/flutter/packages/commits/shared_preferences-v2.5.5/packages/shared_preferences) --- updated-dependencies: - dependency-name: shared_preferences dependency-version: 2.5.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Feature/user profile (#37) * feat(UserProfile): build screen UserProfile # Conflicts: # src/lib/features/main/view/screens/main_screen.dart * feat: switch dark/light theme * fix: black color theme * fix: black theme in statistics screen * feat: add dark theme to auth screen * feat: apply dark theme for bottom navigation bar * feat(RPC): update RPC to get data for heatmap * feat(RPC): update new RPC to get data for heatmap * feat: integrate chatbot assistant * feat(chatbot): integrate create task, answer question for chatbot * feat: remove mock data and get data tags and categories from supabase * feat: integrate chatbot's ability to create task --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Nguyễn Anh Kiệt <griffinbienhoa@gmail.com> Co-authored-by: Nguyễn Lê Hoàng Hảo <nlhhao888@gmail.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: CodeRabbit <noreply@coderabbit.ai> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* feat: add create task screen and fix home screen provider errors * Feature/user profile (#30) * feat(UserProfile): build screen UserProfile # Conflicts: # src/lib/features/main/view/screens/main_screen.dart * feat: switch dark/light theme * fix: black color theme * fix: black theme in statistics screen * feat: add dark theme to auth screen * feat: apply dark theme for bottom navigation bar * feat(priority task):Implement priority and tag selection in task creation version 1 (#31) * feat(task): implement priority and tag selection features in task creation * Update README.md * Update README.md * Update README.md --------- Co-authored-by: Tran Quang Ha <tranquangha2006@gmail.com> * Remove comment about main UI in home_screen.dart Removed commented section about main UI. * Update README.md (#32) * feat: Priority selector and tag system verson2 (#33) * feat(task): implement priority and tag selection features in task creation * feat(tags): enhance tag management with custom tag creation and selection * Update README.md * Update README.md * fix: error screen in main.dart (#34) * Enhance authentication UI and implement Focus feature set (#35) * feat(core): add auth layout template, custom textfield and colors * feat(auth): implement viewmodels for auth flow (MVVM) * feat(auth): build complete auth UI screens (Login, Register, OTP, Passwords) * chore(main): set LoginView as initial route * refactor(auth) : delete .gitkeep * chore: update dependencies and pubspec.lock * refactor(auth): optimize registration logic, timezone handling, and form validation * feat(auth): update UI for login, registration, and forgot password screens * feat(tasks): update task management UI and statistics screen * chore: update main entry point and fix widget tests * chore: ignore devtools_options.yaml * chore: ignore devtools_options.yaml * style(login) : rewrite title for login view * feat(auth): configure android deep link for supabase oauth * refactor(ui): add social login callbacks to auth layout template * feat(auth): update oauth methods with redirect url and signout * feat(auth): implement AuthGate using StreamBuilder for session tracking * feat(viewmodel): add oauth logic and improve provider lifecycle * refactor(ui): migrate LoginView to Provider pattern * chore(main): set AuthGate as initial route and setup provider * feat: implement full Focus feature set - Added Pomodoro timer with Start/Reset/Skip logic. - Integrated local Quick Notes with Pin/Delete functionality. - Supported image attachments in notes using image_picker. - Added Focus settings: time duration, vibration, and ringtones. * fix (auth) : dispose TextEditingControllers to prevent memory leaks * refactor (alarm ) : create off alarm button when time out * fix: apply CodeRabbit auto-fixes Fixed 3 file(s) based on 4 unresolved review comments. Co-authored-by: CodeRabbit <noreply@coderabbit.ai> * fix(timer): prevent division by zero in progress calculation and sanitize negative settings input * fix(timer): prevent division by zero in progress calculation and sanitize negative settings input * fix(auth): unblock new-user login and add settings logout * refactor(LoginScreen) : compact all items to fit in screen to help users interface easily --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: CodeRabbit <noreply@coderabbit.ai> * build(deps)(deps): bump shared_preferences from 2.5.4 to 2.5.5 in /src (#36) Bumps [shared_preferences](https://github.com/flutter/packages/tree/main/packages/shared_preferences) from 2.5.4 to 2.5.5. - [Commits](https://github.com/flutter/packages/commits/shared_preferences-v2.5.5/packages/shared_preferences) --- updated-dependencies: - dependency-name: shared_preferences dependency-version: 2.5.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Feature/user profile (#37) * feat(UserProfile): build screen UserProfile # Conflicts: # src/lib/features/main/view/screens/main_screen.dart * feat: switch dark/light theme * fix: black color theme * fix: black theme in statistics screen * feat: add dark theme to auth screen * feat: apply dark theme for bottom navigation bar * feat(RPC): update RPC to get data for heatmap * feat(RPC): update new RPC to get data for heatmap * feat: integrate chatbot assistant * feat(chatbot): integrate create task, answer question for chatbot * feat: remove mock data and get data tags and categories from supabase * fixed codes, added save delete and create tasks and notes * Delete .vs/TaskManagement.slnx/v18/.wsuo * Delete .vs directory * Delete .vscode directory * Delete src/.gitignore * Delete src/lib/features/auth/viewmodels/task_provider.dart * Delete src/web_entrypoint.dart --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Tran Quang Ha <tranquangha2006@gmail.com> Co-authored-by: Nguyễn Anh Kiệt <griffinbienhoa@gmail.com> Co-authored-by: Nguyễn Lê Hoàng Hảo <nlhhao888@gmail.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: CodeRabbit <noreply@coderabbit.ai> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Update tags
Summary by CodeRabbit
New Features
Bug Fixes