Skip to content

fix: ensure atomic DB updates for tasks and tags#644

Open
sahu-virendra-1908 wants to merge 2 commits intoCCExtractor:mainfrom
sahu-virendra-1908:fix/db-transaction-bug
Open

fix: ensure atomic DB updates for tasks and tags#644
sahu-virendra-1908 wants to merge 2 commits intoCCExtractor:mainfrom
sahu-virendra-1908:fix/db-transaction-bug

Conversation

@sahu-virendra-1908
Copy link
Copy Markdown

@sahu-virendra-1908 sahu-virendra-1908 commented Mar 28, 2026

Description

This PR fixes a data inconsistency issue in the local database when editing tasks.

Previously, task details and associated tags were updated using separate database operations without a shared transaction. This could lead to partial updates if an error or app crash occurred between operations.

Changes made

  • Wrapped task update and tag update inside a single SQLite transaction
  • Ensured all database operations use the same transaction (txn)
  • Removed reliance on separate transactions for tag updates
  • Improved overall data consistency and reliability

Result

  • Prevents partial updates
  • Ensures atomic operations (all-or-nothing)
  • Improves user trust in data integrity

Fixes #643


Screenshots

Not applicable (database-level fix)


Checklist

  • Code follows the established coding style guidelines
  • All tests are passing
  • Tests have been added or updated to cover the changes
  • Documentation has been updated to reflect the changes

Summary by CodeRabbit

  • New Features

    • App Links support added for macOS and Windows
    • GTK integration added for Linux
  • Improvements

    • More reliable task saving with transactional updates to prevent partial updates
  • Chores

    • Flutter SDK pinned to 3.29.0

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 28, 2026

📝 Walkthrough

Walkthrough

Updated Flutter version pins; added/updated native plugin registrations for Linux, macOS, and Windows; and refactored task database edits so task fields and tags are updated atomically inside a single SQLite transaction. Many files received formatting/whitespace adjustments.

Changes

Cohort / File(s) Summary
Version Configuration
\.fvmrc, .vscode/settings.json
Flutter version downgraded from 3.29.23.29.0 and VS Code Dart SDK path updated accordingly.
Database Transaction Refactor
lib/app/v3/db/task_database.dart
saveEditedTaskInDB now runs task update, tag deletion, and tag insertion inside one txn transaction using the provided txn handle; checks update result and row id, trims and skips empty tags, and removes prior non-transactional/tag-retrieval flow.
Native Plugin Registrations
linux/flutter/generated_plugin_registrant.cc, linux/flutter/generated_plugins.cmake, macos/Flutter/GeneratedPluginRegistrant.swift, windows/flutter/generated_plugin_registrant.cc, windows/flutter/generated_plugins.cmake
Added GTK on Linux and App Links on macOS/Windows to generated plugin registrants and CMake plugin lists; inserted plugin includes/registration calls and build-link entries.
Theme/Extension Update
lib/app/utils/themes/theme_extension.dart
Reformatted TaskwarriorColorTheme; adjusted copyWith and lerp implementations and formatting (icons no longer interpolated).
New/Impl Home Path File
lib/app/utils/home_path/impl/taskchampion_config_file_paths.dart
Added new file (implementation content not shown in summary).
Formatting & Whitespace Changes
multiple files: lib/app/models/*, lib/app/modules/*, lib/app/utils/*, test/* (see diff)
Wide set of non-functional edits: reflowing lines, adding/removing trailing newlines, minor formatting/indentation changes, and small text-wrapping adjustments across many Dart files and tests. These are formatting-only (no logic changes).

Sequence Diagram(s)

mermaid
sequenceDiagram
actor App
participant TaskDB as TaskDatabase
participant SQLite as SQLite (txn)
App->>TaskDB: saveEditedTaskInDB(uuid, newFields, newTags)
TaskDB->>SQLite: transaction((txn) => ...)
Note over SQLite,TaskDB: inside txn: update Tasks row
SQLite-->>TaskDB: update result (rowsAffected)
TaskDB->>SQLite: query Tasks for id where uuid=...
SQLite-->>TaskDB: task row (id)
TaskDB->>SQLite: delete Tags where task_uuid & task_id
TaskDB->>SQLite: insert Tag rows (trimmed, non-empty)
SQLite-->>TaskDB: commit/rollback
TaskDB-->>App: return / throw on error

(Note: rectangles omitted; primary flow between App, TaskDatabase, and SQLite transaction shown.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I nibbled bugs with careful chew,
I wrapped task edits safe and true,
Tags and fields now hop as one,
Plugins join the platform fun,
A rabbit’s patch — quick, small, and new.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes numerous formatting and whitespace-only changes across many files (indentation, line breaks, trailing newlines, blank lines) that are unrelated to the core objective of fixing database transaction atomicity for task/tag updates. Separate formatting and whitespace-only changes into a dedicated PR to keep the transaction fix focused and easier to review. Consider using automated formatting tools (dartfmt) in a separate commit.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: ensure atomic DB updates for tasks and tags' clearly and concisely describes the main change: making database updates atomic for tasks and tags to prevent data inconsistency.
Description check ✅ Passed The PR description follows the template with all required sections: summary of changes, issue reference (#643), acknowledgment that screenshots are not applicable, and a completed checklist addressing the repository's requirements.
Linked Issues check ✅ Passed The PR meets all coding objectives from issue #643: task and tag updates are wrapped in a single SQLite transaction, the provided transaction context (txn) is used for all reads/writes, and partial updates are prevented through atomic operations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/app/v3/db/task_database.dart`:
- Line 279: The line assigning taskId uses an unsafe cast (final taskId =
taskMaps.first['id'] as int;) which will throw if id is null; change it to a
null-safe cast and handle the null case explicitly (e.g. final int? taskId =
taskMaps.first['id'] as int?; then either throw a descriptive error like throw
StateError('Missing task id for taskMap: $taskMaps.first') or handle/return a
nullable value as appropriate), and update any downstream code that assumes
taskId is non-null to account for the new nullable type or the explicit error
path.
- Around line 282-301: The current conditional prevents clearing tags because
the txn.delete call is only run when newTags.isNotEmpty; always perform the
delete of the 'Tags' rows for the given task (use the existing txn.delete with
where 'task_uuid = ? AND task_id = ?' and whereArgs [uuid, taskId]) before
inserting any new tags, and then only iterate/insert when newTags contains
non-empty values (preserve the tag.trim().isNotEmpty check) so an empty newTags
list will correctly clear existing tags.
🪄 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: 070ad8b6-0307-4758-8f46-445f4c31f93e

📥 Commits

Reviewing files that changed from the base of the PR and between f058b4a and b9fff37.

⛔ Files ignored due to path filters (1)
  • pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • .fvmrc
  • .vscode/settings.json
  • lib/app/v3/db/task_database.dart
  • linux/flutter/generated_plugin_registrant.cc
  • linux/flutter/generated_plugins.cmake
  • macos/Flutter/GeneratedPluginRegistrant.swift
  • windows/flutter/generated_plugin_registrant.cc
  • windows/flutter/generated_plugins.cmake

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
lib/app/v3/db/task_database.dart (1)

253-317: Please add a rollback regression test for this transaction path.

Given this is a data-integrity fix, add at least one test that forces failure during tag insert and asserts task fields and tags both remain unchanged after rollback.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/app/v3/db/task_database.dart` around lines 253 - 317, Add a regression
test that verifies the transaction rolls back when tag insertion fails: create a
fixture task row in the Tasks table with initial fields and associated Tags,
then call the transaction wrapper (the update method that contains the shown
transaction code—e.g., updateTask) with new field values and newTags that will
force a failure during tag insertion (for example, include a tag value that
triggers a DB constraint error or arrange a test double to throw when inserting
into 'Tags'); catch the thrown exception, then query the 'Tasks' row (columns:
description, project, status, priority, due, modified) and the 'Tags' for that
task_uuid/task_id and assert they match the original pre-update values—this
ensures both the Tasks and Tags tables were rolled back on failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/app/utils/themes/theme_extension.dart`:
- Line 82: The current code always passes the starting IconData via "icons:
icons" so the visual doesn't flip during the theme animation; change the
property to pick between the start and end icon at the animation midpoint (e.g.
use the animation's value or t and set icons: (t.value < 0.5) ? startIcon :
endIcon). Update the call site that currently sets "icons: icons" to instead
reference the start/end icon variables (e.g. beginIcon / endIcon) and switch
when t >= 0.5 so the icon swaps at the midpoint of the transition.
- Around line 42-56: The copyWith implementation for TaskwarriorColorTheme
incorrectly uses parameterName ?? parameterName for all fields so it never falls
back to existing instance values; update each null-coalescing expression to use
the incoming parameter or this.field (e.g., dialogBackgroundColor ??
this.dialogBackgroundColor, primaryBackgroundColor ??
this.primaryBackgroundColor, etc.), and add purpleShade to the copyWith method
signature so it can be overridden (ensure the returned TaskwarriorColorTheme
uses purpleShade ?? this.purpleShade and any other fields similarly).

In `@lib/app/v3/db/task_database.dart`:
- Around line 270-290: The new saveEditedTaskInDB method now throws when a task
is missing or the id is null; update its caller(s) to catch those exceptions and
surface user-friendly feedback instead of letting them bubble as unhandled async
errors. Wrap the await call to saveEditedTaskInDB inside saveTask() (and
similarly in handleWillPop() if it calls saveEditedTaskInDB) with a try-catch
that catches Exception/StateError, logs the error, updates any UI state (e.g.,
stop loading spinner), and shows an error message to the user (toast/dialog) or
falls back to a safe path; ensure you reference saveEditedTaskInDB, saveTask,
and handleWillPop when locating where to add the try-catch and cleanup.

---

Nitpick comments:
In `@lib/app/v3/db/task_database.dart`:
- Around line 253-317: Add a regression test that verifies the transaction rolls
back when tag insertion fails: create a fixture task row in the Tasks table with
initial fields and associated Tags, then call the transaction wrapper (the
update method that contains the shown transaction code—e.g., updateTask) with
new field values and newTags that will force a failure during tag insertion (for
example, include a tag value that triggers a DB constraint error or arrange a
test double to throw when inserting into 'Tags'); catch the thrown exception,
then query the 'Tasks' row (columns: description, project, status, priority,
due, modified) and the 'Tags' for that task_uuid/task_id and assert they match
the original pre-update values—this ensures both the Tasks and Tags tables were
rolled back on failure.
🪄 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: aa15fc1f-4350-4589-ba96-c6ef08ad4849

📥 Commits

Reviewing files that changed from the base of the PR and between b9fff37 and 330c364.

📒 Files selected for processing (62)
  • lib/app/models/data.dart
  • lib/app/models/filters.dart
  • lib/app/models/json/annotation.dart
  • lib/app/models/json/serializers.dart
  • lib/app/models/json/task.dart
  • lib/app/models/models.dart
  • lib/app/models/onboarding_model.dart
  • lib/app/models/size_config_model.dart
  • lib/app/models/storage.dart
  • lib/app/models/storage/set_config.dart
  • lib/app/modules/about/views/about_page_body.dart
  • lib/app/modules/about/views/about_view.dart
  • lib/app/modules/detailRoute/views/dateTimePicker.dart
  • lib/app/modules/detailRoute/views/priority_widget.dart
  • lib/app/modules/detailRoute/views/status_widget.dart
  • lib/app/modules/home/views/home_page_nav_drawer_menu_item.dart
  • lib/app/modules/home/views/home_view.dart
  • lib/app/modules/home/views/project_column_home_page.dart
  • lib/app/modules/home/views/tag_filters_wrap.dart
  • lib/app/modules/home/views/tas_list_item.dart
  • lib/app/modules/home/views/tasks_builder.dart
  • lib/app/modules/manageTaskServer/views/manage_task_server_page_app_bar.dart
  • lib/app/modules/manageTaskServer/views/manage_task_server_page_body.dart
  • lib/app/modules/manageTaskServer/views/manage_task_server_view.dart
  • lib/app/modules/manageTaskServer/views/pem_widget.dart
  • lib/app/modules/onboarding/views/onboarding_page_bottom_section.dart
  • lib/app/modules/onboarding/views/onboarding_view.dart
  • lib/app/modules/profile/controllers/profile_controller.dart
  • lib/app/modules/reports/views/burn_down_monthly.dart
  • lib/app/modules/reports/views/burn_down_weekly.dart
  • lib/app/modules/reports/views/common_chart_indicator.dart
  • lib/app/modules/settings/views/settings_page_highlist_task_list_tile_trailing.dart
  • lib/app/modules/settings/views/settings_page_list_tile.dart
  • lib/app/modules/settings/views/settings_page_on_task_create_list_tile_trailing.dart
  • lib/app/modules/settings/views/settings_page_on_task_start_list_tile_trailing.dart
  • lib/app/services/tag_filter.dart
  • lib/app/utils/constants/constants.dart
  • lib/app/utils/constants/onboarding_screen_content.dart
  • lib/app/utils/constants/permissions.dart
  • lib/app/utils/constants/utilites.dart
  • lib/app/utils/home_path/home_path.dart
  • lib/app/utils/home_path/impl/data.dart
  • lib/app/utils/home_path/impl/gui_pem_file_paths.dart
  • lib/app/utils/home_path/impl/taskchampion_config_file_paths.dart
  • lib/app/utils/home_path/impl/taskd_client.dart
  • lib/app/utils/language/german_sentences.dart
  • lib/app/utils/permissions/permissions_manager.dart
  • lib/app/utils/taskfunctions/comparator.dart
  • lib/app/utils/taskfunctions/query.dart
  • lib/app/utils/taskfunctions/tags.dart
  • lib/app/utils/taskfunctions/urgency.dart
  • lib/app/utils/themes/light_theme.dart
  • lib/app/utils/themes/theme_extension.dart
  • lib/app/utils/themes/themes.dart
  • lib/app/v3/db/task_database.dart
  • test/api_service_test.dart
  • test/models/chart_test.dart
  • test/sentence_manager/sentence_manager_test.dart
  • test/taskfunctions/comparator_test.dart
  • test/taskfunctions/draft_test.dart
  • test/taskfunctions/projects_test.dart
  • test/taskfunctions/validate_test.dart
💤 Files with no reviewable changes (17)
  • lib/app/models/json/task.dart
  • lib/app/models/storage/set_config.dart
  • lib/app/modules/home/views/tag_filters_wrap.dart
  • lib/app/models/json/serializers.dart
  • lib/app/utils/home_path/impl/gui_pem_file_paths.dart
  • lib/app/models/storage.dart
  • lib/app/modules/settings/views/settings_page_highlist_task_list_tile_trailing.dart
  • lib/app/models/json/annotation.dart
  • lib/app/modules/home/views/home_view.dart
  • lib/app/utils/taskfunctions/tags.dart
  • lib/app/utils/taskfunctions/comparator.dart
  • lib/app/utils/home_path/impl/data.dart
  • lib/app/modules/profile/controllers/profile_controller.dart
  • lib/app/utils/taskfunctions/urgency.dart
  • lib/app/utils/home_path/impl/taskd_client.dart
  • lib/app/modules/settings/views/settings_page_on_task_create_list_tile_trailing.dart
  • lib/app/services/tag_filter.dart
✅ Files skipped from review due to trivial changes (42)
  • lib/app/models/data.dart
  • test/taskfunctions/draft_test.dart
  • lib/app/modules/manageTaskServer/views/manage_task_server_page_app_bar.dart
  • lib/app/models/onboarding_model.dart
  • lib/app/modules/about/views/about_view.dart
  • lib/app/utils/taskfunctions/query.dart
  • lib/app/utils/constants/permissions.dart
  • lib/app/models/models.dart
  • lib/app/utils/permissions/permissions_manager.dart
  • lib/app/modules/home/views/home_page_nav_drawer_menu_item.dart
  • lib/app/utils/constants/utilites.dart
  • test/models/chart_test.dart
  • lib/app/utils/themes/themes.dart
  • lib/app/modules/manageTaskServer/views/manage_task_server_view.dart
  • lib/app/models/size_config_model.dart
  • lib/app/utils/constants/onboarding_screen_content.dart
  • lib/app/modules/detailRoute/views/priority_widget.dart
  • test/taskfunctions/projects_test.dart
  • test/taskfunctions/validate_test.dart
  • lib/app/models/filters.dart
  • lib/app/modules/detailRoute/views/dateTimePicker.dart
  • lib/app/modules/settings/views/settings_page_list_tile.dart
  • lib/app/utils/home_path/impl/taskchampion_config_file_paths.dart
  • lib/app/modules/manageTaskServer/views/pem_widget.dart
  • lib/app/modules/manageTaskServer/views/manage_task_server_page_body.dart
  • test/api_service_test.dart
  • lib/app/modules/home/views/tasks_builder.dart
  • lib/app/modules/about/views/about_page_body.dart
  • test/taskfunctions/comparator_test.dart
  • lib/app/modules/home/views/tas_list_item.dart
  • lib/app/utils/home_path/home_path.dart
  • lib/app/modules/reports/views/burn_down_weekly.dart
  • lib/app/modules/onboarding/views/onboarding_view.dart
  • lib/app/modules/reports/views/burn_down_monthly.dart
  • lib/app/modules/onboarding/views/onboarding_page_bottom_section.dart
  • lib/app/utils/constants/constants.dart
  • test/sentence_manager/sentence_manager_test.dart
  • lib/app/modules/detailRoute/views/status_widget.dart
  • lib/app/utils/themes/light_theme.dart
  • lib/app/modules/settings/views/settings_page_on_task_start_list_tile_trailing.dart
  • lib/app/modules/reports/views/common_chart_indicator.dart
  • lib/app/utils/language/german_sentences.dart

Comment on lines +42 to +56
return TaskwarriorColorTheme(
dialogBackgroundColor: dialogBackgroundColor ?? dialogBackgroundColor,
primaryBackgroundColor:
primaryBackgroundColor ?? primaryBackgroundColor,
primaryDisabledTextColor:
primaryDisabledTextColor ?? primaryDisabledTextColor,
primaryTextColor: primaryTextColor ?? primaryTextColor,
secondaryBackgroundColor:
secondaryBackgroundColor ?? secondaryBackgroundColor,
secondaryTextColor: secondaryTextColor ?? secondaryTextColor,
dividerColor: dividerColor ?? dividerColor,
purpleShade: purpleShade ?? purpleShade,
greyShade: greyShade ?? greyShade,
icons: icons ?? icons,
dimCol: dimCol ?? dimCol);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

copyWith fallback logic is broken—always evaluates to the parameter, never the current field.

All null-coalescing expressions like dialogBackgroundColor ?? dialogBackgroundColor reference the same parameter name on both sides, so they never fall back to the existing instance values. This makes copyWith unable to preserve current values when only some fields are provided.

The correct pattern is parameterName ?? this.fieldName.

Additionally, purpleShade is missing from the method parameters (lines 31-40), so it cannot be overridden.

🐛 Proposed fix
   `@override`
   ThemeExtension<TaskwarriorColorTheme> copyWith({
     Color? dialogBackgroundColor,
     Color? primaryBackgroundColor,
     Color? secondaryBackgroundColor,
     Color? primaryTextColor,
     Color? secondaryTextColor,
     Color? primaryDisabledTextColor,
     Color? dividerColor,
+    Color? purpleShade,
     Color? greyShade,
     Color? dimCol,
     IconData? icons,
   }) {
     return TaskwarriorColorTheme(
-        dialogBackgroundColor: dialogBackgroundColor ?? dialogBackgroundColor,
-        primaryBackgroundColor:
-            primaryBackgroundColor ?? primaryBackgroundColor,
-        primaryDisabledTextColor:
-            primaryDisabledTextColor ?? primaryDisabledTextColor,
-        primaryTextColor: primaryTextColor ?? primaryTextColor,
-        secondaryBackgroundColor:
-            secondaryBackgroundColor ?? secondaryBackgroundColor,
-        secondaryTextColor: secondaryTextColor ?? secondaryTextColor,
-        dividerColor: dividerColor ?? dividerColor,
-        purpleShade: purpleShade ?? purpleShade,
-        greyShade: greyShade ?? greyShade,
-        icons: icons ?? icons,
-        dimCol: dimCol ?? dimCol);
+        dialogBackgroundColor: dialogBackgroundColor ?? this.dialogBackgroundColor,
+        primaryBackgroundColor: primaryBackgroundColor ?? this.primaryBackgroundColor,
+        primaryDisabledTextColor: primaryDisabledTextColor ?? this.primaryDisabledTextColor,
+        primaryTextColor: primaryTextColor ?? this.primaryTextColor,
+        secondaryBackgroundColor: secondaryBackgroundColor ?? this.secondaryBackgroundColor,
+        secondaryTextColor: secondaryTextColor ?? this.secondaryTextColor,
+        dividerColor: dividerColor ?? this.dividerColor,
+        purpleShade: purpleShade ?? this.purpleShade,
+        greyShade: greyShade ?? this.greyShade,
+        icons: icons ?? this.icons,
+        dimCol: dimCol ?? this.dimCol);
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/app/utils/themes/theme_extension.dart` around lines 42 - 56, The copyWith
implementation for TaskwarriorColorTheme incorrectly uses parameterName ??
parameterName for all fields so it never falls back to existing instance values;
update each null-coalescing expression to use the incoming parameter or
this.field (e.g., dialogBackgroundColor ?? this.dialogBackgroundColor,
primaryBackgroundColor ?? this.primaryBackgroundColor, etc.), and add
purpleShade to the copyWith method signature so it can be overridden (ensure the
returned TaskwarriorColorTheme uses purpleShade ?? this.purpleShade and any
other fields similarly).

purpleShade: Color.lerp(purpleShade, other.purpleShade, t),
greyShade: Color.lerp(greyShade, other.greyShade, t),
dimCol: Color.lerp(dimCol, other.dimCol, t),
icons: icons);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Icon doesn't switch during theme transition—consider flipping at midpoint.

icons: icons always uses the starting theme's icon throughout the entire animation. Since IconData can't be interpolated, the standard approach is to switch at t >= 0.5 so the icon flips at the midpoint of the transition rather than staying on the initial value until the animation completes.

💡 Suggested fix
-        icons: icons);
+        icons: t < 0.5 ? icons : other.icons);
📝 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.

Suggested change
icons: icons);
icons: t < 0.5 ? icons : other.icons);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/app/utils/themes/theme_extension.dart` at line 82, The current code
always passes the starting IconData via "icons: icons" so the visual doesn't
flip during the theme animation; change the property to pick between the start
and end icon at the animation midpoint (e.g. use the animation's value or t and
set icons: (t.value < 0.5) ? startIcon : endIcon). Update the call site that
currently sets "icons: icons" to instead reference the start/end icon variables
(e.g. beginIcon / endIcon) and switch when t >= 0.5 so the icon swaps at the
midpoint of the transition.

Comment on lines +270 to +290
if (updatedCount == 0) {
throw Exception("Task not found for uuid: $uuid");
}

// 2. Get task ID safely
final taskMaps = await txn.query(
'Tasks',
columns: ['id'],
where: 'uuid = ?',
whereArgs: [uuid],
);

if (taskMaps.isEmpty) {
throw Exception("No task found after update for uuid: $uuid");
}

final int? taskId = taskMaps.first['id'] as int?;

if (taskId == null) {
throw StateError("Missing task id for task: ${taskMaps.first}");
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all call sites of saveEditedTaskInDB with context
rg -n -C6 --type=dart '\bawait\s+[^;]*saveEditedTaskInDB\s*\('

# Inspect try/catch presence in the known primary caller
rg -n -C6 --type=dart '\bsaveTask\s*\(|\btry\s*\{|\bcatch\s*\(' lib/app/modules/taskc_details/controllers/taskc_details_controller.dart

Repository: CCExtractor/taskwarrior-flutter

Length of output: 8263


🏁 Script executed:

# Get complete view of saveTask method around the TaskForC call
sed -n '230,265p' lib/app/modules/taskc_details/controllers/taskc_details_controller.dart

# Verify there are no other calls to saveEditedTaskInDB elsewhere
rg -c 'saveEditedTaskInDB' --type=dart

Repository: CCExtractor/taskwarrior-flutter

Length of output: 1394


🏁 Script executed:

# Check what calls saveTask() and if there's error handling at the call site
rg -n -B5 -A10 'await.*saveTask\(\)' --type=dart lib/app/modules/taskc_details/controllers/taskc_details_controller.dart

# Also check the complete method signature and any wrapping
rg -n -B3 'Future<void> saveTask' --type=dart lib/app/modules/taskc_details/controllers/taskc_details_controller.dart

Repository: CCExtractor/taskwarrior-flutter

Length of output: 634


🏁 Script executed:

# Find ALL calls to saveTask()
rg -n 'saveTask\(\)' --type=dart

# Get full context of handleWillPop method to see if there's any outer error handling
sed -n '337,351p' lib/app/modules/taskc_details/controllers/taskc_details_controller.dart

Repository: CCExtractor/taskwarrior-flutter

Length of output: 658


🏁 Script executed:

# Verify saveEditedTaskInDB is only called from saveTask()
rg -n 'saveEditedTaskInDB' --type=dart

# Verify the actual exception-throwing code in saveEditedTaskInDB
sed -n '270,290p' lib/app/v3/db/task_database.dart

Repository: CCExtractor/taskwarrior-flutter

Length of output: 853


Add error handling for new exceptions from saveEditedTaskInDB at call sites.

This method now throws when the task is missing or id is null. The only call site (saveTask() at line 240) awaits it without local handling, so failed edits will bubble as unhandled async errors and break UX state after submit. Add try-catch handling in saveTask() or handleWillPop() to gracefully handle these exceptions and provide user feedback.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/app/v3/db/task_database.dart` around lines 270 - 290, The new
saveEditedTaskInDB method now throws when a task is missing or the id is null;
update its caller(s) to catch those exceptions and surface user-friendly
feedback instead of letting them bubble as unhandled async errors. Wrap the
await call to saveEditedTaskInDB inside saveTask() (and similarly in
handleWillPop() if it calls saveEditedTaskInDB) with a try-catch that catches
Exception/StateError, logs the error, updates any UI state (e.g., stop loading
spinner), and shows an error message to the user (toast/dialog) or falls back to
a safe path; ensure you reference saveEditedTaskInDB, saveTask, and
handleWillPop when locating where to add the try-catch and cleanup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Data Inconsistency When Editing Task (Missing Transaction)

1 participant