Skip to content

Conversation

@dab246
Copy link
Member

@dab246 dab246 commented Mar 26, 2025

Issue

#3358

Resolved

  • Reload page & Token Expired:
Screen.Recording.2025-03-26.at.09.02.02.mov
  • Save after 15s:
Screen.Recording.2025-03-26.at.09.16.31.mov

Summary by CodeRabbit

  • New Features
    • Local Drafts: save drafts locally and manage them in a "Local Draft List" dialog with edit, restore, discard, and restore-all actions.
  • Improvements
    • Autosave: periodic automatic saving of open composer windows.
    • Dialog handling: dialogs now block background interactions reliably.
    • UI: simplified avatar rendering and added localized strings for draft flows.
  • Bug Fixes
    • Fixed a download cancel behavior to use correct cancel reason.

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions
Copy link

This PR has been deployed to https://linagora.github.io/tmail-flutter/3598.

@dab246 dab246 requested a review from tddang-linagora March 26, 2025 08:10
@dab246 dab246 force-pushed the enhancement/tf-3358-save-draft-locally-periodically branch from cc29684 to 1582106 Compare April 4, 2025 11:55
tddang-linagora
tddang-linagora previously approved these changes Apr 8, 2025
@hoangdat
Copy link
Member

please rebase it, we will merge it next sprint

@dab246 dab246 force-pushed the enhancement/tf-3358-save-draft-locally-periodically branch from 1582106 to 02a1b0f Compare September 22, 2025 05:42
@dab246
Copy link
Member Author

dab246 commented Sep 22, 2025

please rebase it, we will merge it next sprint

Done

@dab246 dab246 force-pushed the enhancement/tf-3358-save-draft-locally-periodically branch from 02a1b0f to 4ba978c Compare December 26, 2025 03:36
@coderabbitai
Copy link

coderabbitai bot commented Dec 26, 2025

Walkthrough

This PR replaces the previous web/composer-cache flow with a local email draft system. It adds LocalEmailDraft data model, Hive adapter, caching client, manager, worker queue, datasource/repository implementations, domain interactors (get/save/remove), presentation models and extensions, UI list/dialog widgets, ComposerTimer and composer persistence logic, DialogBuilderManager, composer and mailbox bindings/controller updates, EmailAvatarBuilder API change, EmailActionType rename to composeFromLocalEmailDraft, localization entries, and corresponding test adjustments.

Possibly related PRs

  • AI scribe UI #4197: Touches ComposerController and editor-related mixins/fields — overlaps controller-level refactors in this PR.
  • AI scribe toggle #4208: Modifies composer wiring and bindings similar to this PR’s changes to composer_bindings and interactor injections.

Suggested reviewers

  • hoangdat
  • tddang-linagora

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main objective: implementing periodic local saving of email drafts. It is specific, concise, and clearly conveys the primary feature being added.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch enhancement/tf-3358-save-draft-locally-periodically

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

@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: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart (1)

493-513: Duplicate MailboxRepositoryImpl binding.

Lines 493-499 and lines 507-513 both register MailboxRepositoryImpl with identical configuration. This duplicate should be removed.

🔎 Proposed fix
     Get.lazyPut(() => MailboxRepositoryImpl(
       {
         DataSourceType.network: Get.find<MailboxDataSource>(),
         DataSourceType.local: Get.find<MailboxCacheDataSourceImpl>()
       },
       Get.find<StateDataSource>(),
     ));
     Get.lazyPut(() => LocalEmailDraftRepositoryImpl(Get.find<LocalEmailDraftDatasource>()));
     Get.lazyPut(() => SpamReportRepositoryImpl(
       {
         DataSourceType.local: Get.find<LocalSpamReportDataSourceImpl>(),
         DataSourceType.hiveCache: Get.find<HiveSpamReportDataSourceImpl>()
       },
     ));
-    Get.lazyPut(() => MailboxRepositoryImpl(
-      {
-        DataSourceType.network: Get.find<MailboxDataSource>(),
-        DataSourceType.local: Get.find<MailboxCacheDataSourceImpl>()
-      },
-      Get.find<StateDataSource>(),
-    ));
     Get.lazyPut(() => ServerSettingsRepositoryImpl(Get.find<ServerSettingsDataSource>()));
🧹 Nitpick comments (9)
lib/features/mailbox_dashboard/domain/exceptions/local_email_draft_exception.dart (1)

1-1: Consider adding a message field for better debugging.

The exception class is minimal. Adding a message field or toString() method would improve error diagnostics when drafts are not found.

🔎 Proposed enhancement
-class NotFoundLocalEmailDraftException implements Exception {}
+class NotFoundLocalEmailDraftException implements Exception {
+  final String? message;
+  
+  NotFoundLocalEmailDraftException([this.message]);
+  
+  @override
+  String toString() => message ?? 'Local email draft not found';
+}
lib/features/composer/presentation/manager/composer_timer.dart (1)

16-21: Consider wrapping onTick in error handling.

If onTick() throws an exception, the timer will stop. Adding a try-catch would make the timer more resilient to callback failures.

🔎 Proposed enhancement
 void start() {
   stop();
   _timer = Timer.periodic(interval, (timer) {
-    onTick();
+    try {
+      onTick();
+    } catch (e) {
+      // Log error but continue timer
+    }
   });
 }
lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart (1)

167-179: Consider removing trailing margin from the last button.

The Discard button has margin: const EdgeInsetsDirectional.only(end: 8), adding extra space on the right. Typically, the last button in a row doesn't need trailing margin, which could cause slight alignment inconsistency.

🔎 Proposed fix
                        Flexible(
                          child: TMailButtonWidget(
                            text: AppLocalizations.of(context).discard,
                            textStyle: Theme.of(context)
                                .textTheme
                                .bodyMedium
                                ?.copyWith(color: AppColor.colorActionDeleteConfirmDialog, fontSize: 12),
                            maxLines: 1,
                            padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 8),
-                           margin: const EdgeInsetsDirectional.only(end: 8),
                            onTapActionCallback: () => onDiscardLocalEmailDraftAction?.call(draftLocal),
                          ),
                        ),
lib/features/composer/domain/usecases/save_local_email_draft_interactor.dart (1)

21-43: LGTM! Interactor correctly orchestrates draft saving.

The implementation properly:

  • Generates the email with appropriate draft flags
  • Converts the result to a local draft
  • Persists via the repository
  • Handles errors with proper state types

Consider adding validation before saving (e.g., checking if email content is substantive) to avoid cluttering local storage with empty drafts. However, this can be deferred based on user feedback.

lib/features/mailbox_dashboard/presentation/extensions/local_email_draft_extension.dart (1)

18-18: Consider handling JSON parsing failures.

jsonDecode(email!) and Email.fromJson() can throw exceptions if the stored email string is malformed or corrupted. An unhandled exception here would prevent displaying any drafts. Consider wrapping in try-catch with a fallback or logging.

🔎 Proposed defensive parsing
-      email: email != null ? Email.fromJson(jsonDecode(email!)) : null,
+      email: _parseEmail(email),

Add a helper method:

Email? _parseEmail(String? emailJson) {
  if (emailJson == null) return null;
  try {
    return Email.fromJson(jsonDecode(emailJson));
  } catch (e) {
    // Log error if needed
    return null;
  }
}
lib/features/mailbox_dashboard/presentation/extensions/remove_local_email_draft_extension.dart (1)

26-36: Consider extracting common key generation logic.

The TupleKey(composerId, accountId.asString, userName.value).encodeKey pattern is duplicated in CreateEmailRequestExtension.generateLocalEmailDraftFromEmail. For maintainability, consider extracting this to a shared utility in LocalEmailDraft or a dedicated helper.

lib/features/mailbox_dashboard/presentation/model/presentation_local_email_draft.dart (1)

34-58: copyWith method cannot reset nullable fields to null.

The current copyWith implementation uses ?? operator, which means you cannot intentionally set a nullable field (e.g., email, composerIndex) back to null once it has a value. If this is needed, consider using a sentinel pattern or wrapper.

If resetting nullable fields to null is required, use a wrapper pattern:

🔎 Proposed fix
// Add a private sentinel class at top of file
class _Absent {
  const _Absent();
}
const _absent = _Absent();

// Then in copyWith, use Object? type and check for sentinel:
PresentationLocalEmailDraft copyWith({
  String? id,
  String? composerId,
  DateTime? savedTime,
  Object? email = _absent,
  // ... other nullable fields with = _absent
}) => PresentationLocalEmailDraft(
  id: id ?? this.id,
  composerId: composerId ?? this.composerId,
  savedTime: savedTime ?? this.savedTime,
  email: email == _absent ? this.email : email as Email?,
  // ...
);
lib/features/mailbox_dashboard/data/local/local_email_draft_manager.dart (1)

16-25: Throwing exception for empty list may be unconventional.

Returning an empty list when no drafts exist is often more idiomatic than throwing an exception. Exceptions are typically reserved for error conditions, not expected empty states. This forces callers to handle the exception even when an empty result is valid.

Consider returning an empty list and letting the caller decide how to handle it:

🔎 Proposed fix
 Future<List<LocalEmailDraft>> getAllLocalEmailDraft(AccountId accountId, UserName userName) async {
   final nestedKey = TupleKey(accountId.asString, userName.value).encodeKey;
   final listLocalEmailDraft = await _localEmailDraftClient.getListByNestedKey(nestedKey);
-
-  if (listLocalEmailDraft.isEmpty) {
-    throw NotFoundLocalEmailDraftException();
-  }
-
   return listLocalEmailDraft;
 }
lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_list_dialog_builder.dart (1)

369-371: Consider passing the exception directly to CancelToken.cancel

You currently call cancelToken?.cancel([SavingEmailToDraftsCanceledException()]);, i.e., the cancel reason is a List containing the exception. Unless downstream logic explicitly expects a list, it’s more conventional (and easier to pattern-match) to pass the exception itself:

Possible refinement
-  void _handleCancelSavingMessageToDrafts({CancelToken? cancelToken}) {
-    cancelToken?.cancel([SavingEmailToDraftsCanceledException()]);
-  }
+  void _handleCancelSavingMessageToDrafts({CancelToken? cancelToken}) {
+    cancelToken?.cancel(SavingEmailToDraftsCanceledException());
+  }

Please double-check how the cancel reason is consumed elsewhere to ensure this aligns with your existing Dio usage.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 29b54a7 and 4ba978c.

📒 Files selected for processing (75)
  • lib/features/base/widget/dialog_builder/dialog_builder_manager.dart
  • lib/features/base/widget/email_avatar_builder.dart
  • lib/features/caching/clients/local_email_draft_client.dart
  • lib/features/caching/config/hive_cache_config.dart
  • lib/features/caching/utils/caching_constants.dart
  • lib/features/composer/domain/state/save_local_email_draft_state.dart
  • lib/features/composer/domain/usecases/create_new_and_save_email_to_drafts_interactor.dart
  • lib/features/composer/domain/usecases/restore_email_inline_images_interactor.dart
  • lib/features/composer/domain/usecases/save_composer_cache_on_web_interactor.dart
  • lib/features/composer/domain/usecases/save_local_email_draft_interactor.dart
  • lib/features/composer/presentation/composer_bindings.dart
  • lib/features/composer/presentation/composer_controller.dart
  • lib/features/composer/presentation/composer_view_web.dart
  • lib/features/composer/presentation/extensions/create_email_request_extension.dart
  • lib/features/composer/presentation/extensions/email_action_type_extension.dart
  • lib/features/composer/presentation/extensions/handle_local_email_draft_extension.dart
  • lib/features/composer/presentation/extensions/setup_email_attachments_extension.dart
  • lib/features/composer/presentation/extensions/setup_email_content_extension.dart
  • lib/features/composer/presentation/extensions/setup_email_important_flag_extension.dart
  • lib/features/composer/presentation/extensions/setup_email_other_components_extension.dart
  • lib/features/composer/presentation/extensions/setup_email_recipients_extension.dart
  • lib/features/composer/presentation/extensions/setup_email_request_read_receipt_flag_extension.dart
  • lib/features/composer/presentation/extensions/setup_email_subject_extension.dart
  • lib/features/composer/presentation/extensions/setup_email_template_id_extension.dart
  • lib/features/composer/presentation/extensions/setup_selected_identity_extension.dart
  • lib/features/composer/presentation/manager/composer_manager.dart
  • lib/features/composer/presentation/manager/composer_timer.dart
  • lib/features/composer/presentation/mixin/handle_message_failure_mixin.dart
  • lib/features/composer/presentation/model/create_email_request.dart
  • lib/features/composer/presentation/view/mobile/mobile_editor_view.dart
  • lib/features/composer/presentation/view/web/web_editor_view.dart
  • lib/features/email/presentation/email_view.dart
  • lib/features/email/presentation/model/composer_arguments.dart
  • lib/features/email/presentation/widgets/information_sender_and_receiver_builder.dart
  • lib/features/mailbox_dashboard/data/datasource/local_email_draft_datasource.dart
  • lib/features/mailbox_dashboard/data/datasource_impl/local_email_draft_datasource_impl.dart
  • lib/features/mailbox_dashboard/data/datasource_impl/session_storage_composer_datasoure_impl.dart
  • lib/features/mailbox_dashboard/data/local/local_email_draft_manager.dart
  • lib/features/mailbox_dashboard/data/local/local_email_draft_worker_queue.dart
  • lib/features/mailbox_dashboard/data/model/composer_cache.dart
  • lib/features/mailbox_dashboard/data/model/local_email_draft.dart
  • lib/features/mailbox_dashboard/data/repository/composer_cache_repository_impl.dart
  • lib/features/mailbox_dashboard/data/repository/local_email_draft_repository_impl.dart
  • lib/features/mailbox_dashboard/domain/exceptions/local_email_draft_exception.dart
  • lib/features/mailbox_dashboard/domain/repository/local_email_draft_repository.dart
  • lib/features/mailbox_dashboard/domain/state/get_all_local_email_draft_state.dart
  • lib/features/mailbox_dashboard/domain/state/get_composer_cache_state.dart
  • lib/features/mailbox_dashboard/domain/state/remove_all_local_email_draft_state.dart
  • lib/features/mailbox_dashboard/domain/state/remove_composer_cache_state.dart
  • lib/features/mailbox_dashboard/domain/state/remove_local_email_draft_state.dart
  • lib/features/mailbox_dashboard/domain/state/save_composer_cache_state.dart
  • lib/features/mailbox_dashboard/domain/usecases/get_all_local_email_draft_interactor.dart
  • lib/features/mailbox_dashboard/domain/usecases/get_composer_cache_on_web_interactor.dart
  • lib/features/mailbox_dashboard/domain/usecases/remove_all_local_email_drafts_interactor.dart
  • lib/features/mailbox_dashboard/domain/usecases/remove_composer_cache_by_id_on_web_interactor.dart
  • lib/features/mailbox_dashboard/domain/usecases/remove_local_email_draft_interactor.dart
  • lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart
  • lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart
  • lib/features/mailbox_dashboard/presentation/extensions/local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart
  • lib/features/mailbox_dashboard/presentation/extensions/presentation_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/presentation/extensions/remove_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/presentation/extensions/reopen_composer_cache_extension.dart
  • lib/features/mailbox_dashboard/presentation/extensions/restore_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/presentation/model/presentation_local_email_draft.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_list_dialog_builder.dart
  • lib/l10n/intl_messages.arb
  • lib/main/bindings/local/local_bindings.dart
  • lib/main/localizations/app_localizations.dart
  • model/lib/email/email_action_type.dart
  • test/features/composer/presentation/composer_controller_test.dart
  • test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart
  • test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart
  • test/features/search/verify_before_time_in_search_email_filter_test.dart
💤 Files with no reviewable changes (15)
  • lib/features/mailbox_dashboard/data/model/composer_cache.dart
  • lib/features/mailbox_dashboard/data/repository/composer_cache_repository_impl.dart
  • lib/features/mailbox_dashboard/domain/state/get_composer_cache_state.dart
  • lib/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart
  • lib/features/mailbox_dashboard/domain/state/remove_composer_cache_state.dart
  • lib/features/mailbox_dashboard/domain/usecases/remove_composer_cache_by_id_on_web_interactor.dart
  • lib/features/mailbox_dashboard/presentation/extensions/reopen_composer_cache_extension.dart
  • lib/features/mailbox_dashboard/domain/state/save_composer_cache_state.dart
  • test/features/composer/presentation/composer_controller_test.dart
  • test/features/search/verify_before_time_in_search_email_filter_test.dart
  • test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart
  • lib/features/mailbox_dashboard/data/datasource_impl/session_storage_composer_datasoure_impl.dart
  • test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart
  • lib/features/mailbox_dashboard/domain/usecases/get_composer_cache_on_web_interactor.dart
  • lib/features/composer/domain/usecases/save_composer_cache_on_web_interactor.dart
🧰 Additional context used
🧠 Learnings (14)
📚 Learning: 2025-12-12T04:54:11.121Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4191
File: lib/features/email/presentation/extensions/handle_email_action_extension.dart:37-80
Timestamp: 2025-12-12T04:54:11.121Z
Learning: In lib/features/email/presentation/extensions/handle_email_action_extension.dart, the mailboxDashBoardController.selectedEmail should only be synchronized when isMobileThreadDisabled is true. This is intentional behavior and should not be changed to update selectedEmail in non-mobile or thread-enabled contexts.

Applied to files:

  • lib/features/composer/presentation/view/mobile/mobile_editor_view.dart
  • lib/features/composer/presentation/extensions/setup_selected_identity_extension.dart
  • lib/features/composer/domain/usecases/create_new_and_save_email_to_drafts_interactor.dart
  • lib/features/composer/presentation/extensions/setup_email_content_extension.dart
  • lib/features/composer/presentation/extensions/setup_email_recipients_extension.dart
  • lib/features/mailbox_dashboard/domain/exceptions/local_email_draft_exception.dart
  • lib/features/composer/presentation/extensions/setup_email_template_id_extension.dart
  • lib/features/caching/config/hive_cache_config.dart
  • lib/features/composer/presentation/extensions/create_email_request_extension.dart
  • lib/features/composer/presentation/extensions/setup_email_other_components_extension.dart
  • model/lib/email/email_action_type.dart
  • lib/main/bindings/local/local_bindings.dart
  • lib/features/mailbox_dashboard/data/local/local_email_draft_worker_queue.dart
  • lib/features/composer/presentation/extensions/handle_local_email_draft_extension.dart
  • lib/features/composer/presentation/view/web/web_editor_view.dart
  • lib/features/mailbox_dashboard/domain/repository/local_email_draft_repository.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_list_dialog_builder.dart
  • lib/features/mailbox_dashboard/domain/usecases/remove_local_email_draft_interactor.dart
  • lib/features/composer/presentation/model/create_email_request.dart
  • lib/features/mailbox_dashboard/presentation/extensions/presentation_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/domain/usecases/remove_all_local_email_drafts_interactor.dart
  • lib/features/mailbox_dashboard/data/repository/local_email_draft_repository_impl.dart
  • lib/features/mailbox_dashboard/presentation/extensions/remove_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart
  • lib/features/composer/presentation/manager/composer_manager.dart
  • lib/features/email/presentation/model/composer_arguments.dart
  • lib/features/mailbox_dashboard/presentation/model/presentation_local_email_draft.dart
  • lib/features/mailbox_dashboard/presentation/extensions/local_email_draft_extension.dart
  • lib/features/email/presentation/email_view.dart
  • lib/features/composer/presentation/extensions/setup_email_request_read_receipt_flag_extension.dart
  • lib/features/mailbox_dashboard/data/datasource_impl/local_email_draft_datasource_impl.dart
  • lib/features/composer/presentation/extensions/email_action_type_extension.dart
  • lib/features/email/presentation/widgets/information_sender_and_receiver_builder.dart
  • lib/features/mailbox_dashboard/data/local/local_email_draft_manager.dart
  • lib/features/mailbox_dashboard/data/datasource/local_email_draft_datasource.dart
  • lib/features/composer/presentation/mixin/handle_message_failure_mixin.dart
  • lib/features/mailbox_dashboard/data/model/local_email_draft.dart
  • lib/features/composer/presentation/extensions/setup_email_attachments_extension.dart
  • lib/features/mailbox_dashboard/domain/state/remove_local_email_draft_state.dart
  • lib/features/composer/presentation/extensions/setup_email_important_flag_extension.dart
  • lib/features/composer/presentation/composer_bindings.dart
  • lib/features/mailbox_dashboard/presentation/extensions/restore_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart
  • lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart
  • lib/features/base/widget/email_avatar_builder.dart
  • lib/features/composer/presentation/composer_controller.dart
  • lib/features/composer/presentation/extensions/setup_email_subject_extension.dart
📚 Learning: 2025-12-23T05:27:10.887Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4210
File: model/lib/mailbox/presentation_mailbox.dart:22-34
Timestamp: 2025-12-23T05:27:10.887Z
Learning: In the tmail-flutter codebase, the Action Required folder (actionRequiredFolder) is a virtual folder that is AI-curated and contains emails with the keyword "needs-action". Unlike the Favorites folder, users should NOT be able to drag emails into the Action Required folder. The UI correctly prevents this by not wrapping the Action Required folder item in a DragTarget (see mailbox_item_widget.dart).

Applied to files:

  • lib/features/composer/presentation/view/mobile/mobile_editor_view.dart
  • lib/features/caching/config/hive_cache_config.dart
  • lib/features/composer/presentation/extensions/create_email_request_extension.dart
  • model/lib/email/email_action_type.dart
  • lib/main/bindings/local/local_bindings.dart
  • lib/features/composer/presentation/model/create_email_request.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart
  • lib/features/email/presentation/model/composer_arguments.dart
  • lib/features/email/presentation/email_view.dart
  • lib/features/composer/presentation/extensions/setup_email_important_flag_extension.dart
  • lib/features/composer/presentation/composer_bindings.dart
  • lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart
  • lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart
  • lib/features/composer/presentation/composer_controller.dart
📚 Learning: 2025-12-09T09:36:45.349Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4194
File: lib/features/manage_account/presentation/manage_account_dashboard_controller.dart:174-176
Timestamp: 2025-12-09T09:36:45.349Z
Learning: In Dart/Flutter projects using GetX, do not wrap getBinding<T>() calls in try/catch since they return null when not found. Only wrap Get.find<T>() calls in try/catch because they throw if a dependency is unavailable. When a binding is optional, consider checking Get.isRegistered<T>() or handling the null/exception path gracefully instead of blindly catching, and document the expectation for failure modes where a dependency may not be registered.

Applied to files:

  • lib/features/composer/presentation/view/mobile/mobile_editor_view.dart
  • lib/features/caching/clients/local_email_draft_client.dart
  • lib/features/composer/presentation/extensions/setup_selected_identity_extension.dart
  • lib/features/composer/domain/usecases/create_new_and_save_email_to_drafts_interactor.dart
  • lib/features/composer/presentation/extensions/setup_email_content_extension.dart
  • lib/features/composer/presentation/extensions/setup_email_recipients_extension.dart
  • lib/features/caching/utils/caching_constants.dart
  • lib/features/mailbox_dashboard/domain/exceptions/local_email_draft_exception.dart
  • lib/features/composer/presentation/extensions/setup_email_template_id_extension.dart
  • lib/features/caching/config/hive_cache_config.dart
  • lib/features/composer/presentation/extensions/create_email_request_extension.dart
  • lib/features/mailbox_dashboard/domain/usecases/get_all_local_email_draft_interactor.dart
  • lib/features/composer/presentation/extensions/setup_email_other_components_extension.dart
  • model/lib/email/email_action_type.dart
  • lib/main/bindings/local/local_bindings.dart
  • lib/features/mailbox_dashboard/domain/state/get_all_local_email_draft_state.dart
  • lib/features/mailbox_dashboard/data/local/local_email_draft_worker_queue.dart
  • lib/features/composer/presentation/extensions/handle_local_email_draft_extension.dart
  • lib/features/composer/presentation/view/web/web_editor_view.dart
  • lib/features/composer/presentation/composer_view_web.dart
  • lib/features/composer/domain/usecases/save_local_email_draft_interactor.dart
  • lib/features/mailbox_dashboard/domain/repository/local_email_draft_repository.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_list_dialog_builder.dart
  • lib/features/mailbox_dashboard/domain/usecases/remove_local_email_draft_interactor.dart
  • lib/features/composer/presentation/model/create_email_request.dart
  • lib/features/mailbox_dashboard/presentation/extensions/presentation_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/domain/usecases/remove_all_local_email_drafts_interactor.dart
  • lib/features/composer/domain/state/save_local_email_draft_state.dart
  • lib/features/composer/presentation/manager/composer_timer.dart
  • lib/features/mailbox_dashboard/data/repository/local_email_draft_repository_impl.dart
  • lib/features/composer/domain/usecases/restore_email_inline_images_interactor.dart
  • lib/features/mailbox_dashboard/presentation/extensions/remove_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart
  • lib/features/composer/presentation/manager/composer_manager.dart
  • lib/features/email/presentation/model/composer_arguments.dart
  • lib/main/localizations/app_localizations.dart
  • lib/features/mailbox_dashboard/presentation/model/presentation_local_email_draft.dart
  • lib/features/mailbox_dashboard/presentation/extensions/local_email_draft_extension.dart
  • lib/features/email/presentation/email_view.dart
  • lib/features/composer/presentation/extensions/setup_email_request_read_receipt_flag_extension.dart
  • lib/features/base/widget/dialog_builder/dialog_builder_manager.dart
  • lib/features/mailbox_dashboard/data/datasource_impl/local_email_draft_datasource_impl.dart
  • lib/features/composer/presentation/extensions/email_action_type_extension.dart
  • lib/features/email/presentation/widgets/information_sender_and_receiver_builder.dart
  • lib/features/mailbox_dashboard/data/local/local_email_draft_manager.dart
  • lib/features/mailbox_dashboard/data/datasource/local_email_draft_datasource.dart
  • lib/features/composer/presentation/mixin/handle_message_failure_mixin.dart
  • lib/features/mailbox_dashboard/data/model/local_email_draft.dart
  • lib/features/composer/presentation/extensions/setup_email_attachments_extension.dart
  • lib/features/mailbox_dashboard/domain/state/remove_local_email_draft_state.dart
  • lib/features/composer/presentation/extensions/setup_email_important_flag_extension.dart
  • lib/features/composer/presentation/composer_bindings.dart
  • lib/features/mailbox_dashboard/presentation/extensions/restore_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/domain/state/remove_all_local_email_draft_state.dart
  • lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart
  • lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart
  • lib/features/base/widget/email_avatar_builder.dart
  • lib/features/composer/presentation/composer_controller.dart
  • lib/features/composer/presentation/extensions/setup_email_subject_extension.dart
📚 Learning: 2025-12-09T12:47:45.861Z
Learnt from: zatteo
Repo: linagora/tmail-flutter PR: 4196
File: scribe/lib/scribe/ai/data/datasource_impl/ai_datasource_impl.dart:34-35
Timestamp: 2025-12-09T12:47:45.861Z
Learning: In the tmail-flutter repository, avoid suggesting replacing DioError with DioException unless there is a codebase-wide migration. This pattern applies to all Dart files; when reviewing, only propose a DioError-safe change (e.g., compatible error handling or conversion) unless a global migration is in place. Ensure consistency of DioError usage across modules and flag any deviations for a repo-wide decision.

Applied to files:

  • lib/features/composer/presentation/view/mobile/mobile_editor_view.dart
  • lib/features/caching/clients/local_email_draft_client.dart
  • lib/features/composer/presentation/extensions/setup_selected_identity_extension.dart
  • lib/features/composer/domain/usecases/create_new_and_save_email_to_drafts_interactor.dart
  • lib/features/composer/presentation/extensions/setup_email_content_extension.dart
  • lib/features/composer/presentation/extensions/setup_email_recipients_extension.dart
  • lib/features/caching/utils/caching_constants.dart
  • lib/features/mailbox_dashboard/domain/exceptions/local_email_draft_exception.dart
  • lib/features/composer/presentation/extensions/setup_email_template_id_extension.dart
  • lib/features/caching/config/hive_cache_config.dart
  • lib/features/composer/presentation/extensions/create_email_request_extension.dart
  • lib/features/mailbox_dashboard/domain/usecases/get_all_local_email_draft_interactor.dart
  • lib/features/composer/presentation/extensions/setup_email_other_components_extension.dart
  • model/lib/email/email_action_type.dart
  • lib/main/bindings/local/local_bindings.dart
  • lib/features/mailbox_dashboard/domain/state/get_all_local_email_draft_state.dart
  • lib/features/mailbox_dashboard/data/local/local_email_draft_worker_queue.dart
  • lib/features/composer/presentation/extensions/handle_local_email_draft_extension.dart
  • lib/features/composer/presentation/view/web/web_editor_view.dart
  • lib/features/composer/presentation/composer_view_web.dart
  • lib/features/composer/domain/usecases/save_local_email_draft_interactor.dart
  • lib/features/mailbox_dashboard/domain/repository/local_email_draft_repository.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_list_dialog_builder.dart
  • lib/features/mailbox_dashboard/domain/usecases/remove_local_email_draft_interactor.dart
  • lib/features/composer/presentation/model/create_email_request.dart
  • lib/features/mailbox_dashboard/presentation/extensions/presentation_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/domain/usecases/remove_all_local_email_drafts_interactor.dart
  • lib/features/composer/domain/state/save_local_email_draft_state.dart
  • lib/features/composer/presentation/manager/composer_timer.dart
  • lib/features/mailbox_dashboard/data/repository/local_email_draft_repository_impl.dart
  • lib/features/composer/domain/usecases/restore_email_inline_images_interactor.dart
  • lib/features/mailbox_dashboard/presentation/extensions/remove_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart
  • lib/features/composer/presentation/manager/composer_manager.dart
  • lib/features/email/presentation/model/composer_arguments.dart
  • lib/main/localizations/app_localizations.dart
  • lib/features/mailbox_dashboard/presentation/model/presentation_local_email_draft.dart
  • lib/features/mailbox_dashboard/presentation/extensions/local_email_draft_extension.dart
  • lib/features/email/presentation/email_view.dart
  • lib/features/composer/presentation/extensions/setup_email_request_read_receipt_flag_extension.dart
  • lib/features/base/widget/dialog_builder/dialog_builder_manager.dart
  • lib/features/mailbox_dashboard/data/datasource_impl/local_email_draft_datasource_impl.dart
  • lib/features/composer/presentation/extensions/email_action_type_extension.dart
  • lib/features/email/presentation/widgets/information_sender_and_receiver_builder.dart
  • lib/features/mailbox_dashboard/data/local/local_email_draft_manager.dart
  • lib/features/mailbox_dashboard/data/datasource/local_email_draft_datasource.dart
  • lib/features/composer/presentation/mixin/handle_message_failure_mixin.dart
  • lib/features/mailbox_dashboard/data/model/local_email_draft.dart
  • lib/features/composer/presentation/extensions/setup_email_attachments_extension.dart
  • lib/features/mailbox_dashboard/domain/state/remove_local_email_draft_state.dart
  • lib/features/composer/presentation/extensions/setup_email_important_flag_extension.dart
  • lib/features/composer/presentation/composer_bindings.dart
  • lib/features/mailbox_dashboard/presentation/extensions/restore_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/domain/state/remove_all_local_email_draft_state.dart
  • lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart
  • lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart
  • lib/features/base/widget/email_avatar_builder.dart
  • lib/features/composer/presentation/composer_controller.dart
  • lib/features/composer/presentation/extensions/setup_email_subject_extension.dart
📚 Learning: 2025-12-12T07:56:31.877Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4204
File: lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart:579-582
Timestamp: 2025-12-12T07:56:31.877Z
Learning: In lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart, the addLabelToEmail method intentionally throws UnimplementedError because label synchronization with the email cache is handled separately via extension methods (e.g., _autoSyncLabelToSelectedEmailOnMemory in handle_label_for_email_extension.dart). Implementing it in the cache datasource would cause data conflicts. This differs from other keyword operations like markAsRead, markAsStar, markAsAnswered, and markAsForwarded, which are implemented directly in the cache datasource.

Applied to files:

  • lib/features/caching/clients/local_email_draft_client.dart
  • lib/features/caching/utils/caching_constants.dart
  • lib/features/caching/config/hive_cache_config.dart
  • lib/features/composer/presentation/extensions/create_email_request_extension.dart
  • lib/main/bindings/local/local_bindings.dart
  • lib/features/mailbox_dashboard/data/local/local_email_draft_worker_queue.dart
  • lib/features/mailbox_dashboard/domain/repository/local_email_draft_repository.dart
  • lib/features/mailbox_dashboard/domain/usecases/remove_all_local_email_drafts_interactor.dart
  • lib/features/mailbox_dashboard/data/repository/local_email_draft_repository_impl.dart
  • lib/features/mailbox_dashboard/presentation/extensions/remove_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/data/datasource_impl/local_email_draft_datasource_impl.dart
  • lib/features/mailbox_dashboard/data/datasource/local_email_draft_datasource.dart
  • lib/features/mailbox_dashboard/data/model/local_email_draft.dart
  • lib/features/composer/presentation/extensions/setup_email_attachments_extension.dart
  • lib/features/composer/presentation/extensions/setup_email_important_flag_extension.dart
  • lib/features/composer/presentation/composer_bindings.dart
  • lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart
  • lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart
  • lib/features/composer/presentation/composer_controller.dart
📚 Learning: 2025-12-12T07:43:26.643Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4204
File: lib/features/email/presentation/extensions/handle_label_for_email_extension.dart:94-148
Timestamp: 2025-12-12T07:43:26.643Z
Learning: In lib/features/email/presentation/extensions/handle_label_for_email_extension.dart and similar keyword synchronization code, the addKeyword() function is the preferred method for adding keywords to emails. The resyncKeywords() pattern is being phased out and will be replaced in favor of addKeyword().

Applied to files:

  • lib/features/composer/presentation/extensions/setup_email_recipients_extension.dart
  • lib/features/composer/presentation/extensions/setup_email_template_id_extension.dart
  • lib/features/composer/presentation/extensions/create_email_request_extension.dart
  • lib/features/composer/presentation/extensions/handle_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/presentation/extensions/presentation_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/presentation/extensions/remove_local_email_draft_extension.dart
  • lib/features/composer/presentation/manager/composer_manager.dart
  • lib/features/mailbox_dashboard/presentation/extensions/local_email_draft_extension.dart
  • lib/features/composer/presentation/extensions/email_action_type_extension.dart
  • lib/features/composer/presentation/extensions/setup_email_attachments_extension.dart
  • lib/features/composer/presentation/extensions/setup_email_important_flag_extension.dart
  • lib/features/mailbox_dashboard/presentation/extensions/restore_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart
  • lib/features/composer/presentation/composer_controller.dart
  • lib/features/composer/presentation/extensions/setup_email_subject_extension.dart
📚 Learning: 2025-12-15T06:24:50.369Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4137
File: lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart:32-32
Timestamp: 2025-12-15T06:24:50.369Z
Learning: In lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart and lib/features/manage_account/presentation/manage_account_dashboard_controller.dart, SentryManager.instance.setUser() intentionally sends PII (account ID, display name, username, email) to Sentry for error tracking. The team plans to document this in the app's privacy policy for transparency and compliance.

Applied to files:

  • lib/features/caching/config/hive_cache_config.dart
  • lib/features/composer/presentation/extensions/create_email_request_extension.dart
  • lib/main/bindings/local/local_bindings.dart
  • lib/features/mailbox_dashboard/domain/repository/local_email_draft_repository.dart
  • lib/features/mailbox_dashboard/presentation/extensions/remove_local_email_draft_extension.dart
  • lib/features/email/presentation/model/composer_arguments.dart
  • lib/features/mailbox_dashboard/presentation/model/presentation_local_email_draft.dart
  • lib/features/mailbox_dashboard/data/local/local_email_draft_manager.dart
  • lib/features/mailbox_dashboard/data/datasource/local_email_draft_datasource.dart
  • lib/features/composer/presentation/composer_bindings.dart
  • lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart
  • lib/features/composer/presentation/composer_controller.dart
📚 Learning: 2025-12-18T09:19:32.437Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4203
File: scribe/pubspec.yaml:37-37
Timestamp: 2025-12-18T09:19:32.437Z
Learning: In the tmail-flutter repository, json_annotation version 4.8.0 is the standardized version across modules and should not be upgraded without considering the standardization practice.

Applied to files:

  • lib/features/composer/presentation/extensions/create_email_request_extension.dart
📚 Learning: 2025-12-08T08:11:08.985Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4189
File: lib/features/login/presentation/extensions/handle_company_server_login_info_extension.dart:50-56
Timestamp: 2025-12-08T08:11:08.985Z
Learning: In the tmail-flutter codebase, prefer descriptive variable names that include full context rather than abbreviated names. For example, `removeLoginInfoInteractor` is preferred over shorter alternatives like `removeInteractor` or `interactor`, as it clearly conveys both the action (remove) and the specific data being operated on (loginInfo).

Applied to files:

  • lib/features/mailbox_dashboard/domain/usecases/remove_local_email_draft_interactor.dart
  • lib/features/mailbox_dashboard/domain/usecases/remove_all_local_email_drafts_interactor.dart
  • lib/features/composer/presentation/composer_bindings.dart
  • lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart
  • lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart
  • lib/features/composer/presentation/composer_controller.dart
📚 Learning: 2025-12-12T09:03:05.962Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4204
File: lib/features/email/presentation/model/popup_menu_item_email_action.dart:34-35
Timestamp: 2025-12-12T09:03:05.962Z
Learning: In the tmail-flutter codebase, popup menu items intentionally use a consistent hoverIcon (imagePaths.icThumbsUp) across all popup item types (email actions, mailbox actions, profile settings, etc.), while actionIcon varies per action type. This is a deliberate UI/UX design choice.

Applied to files:

  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart
  • lib/features/email/presentation/email_view.dart
  • lib/features/email/presentation/widgets/information_sender_and_receiver_builder.dart
  • lib/features/base/widget/email_avatar_builder.dart
📚 Learning: 2025-12-23T05:27:40.476Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4210
File: lib/l10n/intl_messages.arb:5151-5182
Timestamp: 2025-12-23T05:27:40.476Z
Learning: In lib/l10n, translations for locale .arb files are updated automatically by an external workflow when new English keys are added to intl_messages.arb. Do not manually modify translated locale .arb files in pull requests; rely on the automated workflow to update translations. If manual edits are necessary, clearly reference the automation workflow and ensure translations remain consistent with the source English keys.

Applied to files:

  • lib/l10n/intl_messages.arb
📚 Learning: 2025-12-19T08:04:06.064Z
Learnt from: hoangdat
Repo: linagora/tmail-flutter PR: 4208
File: lib/l10n/intl_fr.arb:5209-5226
Timestamp: 2025-12-19T08:04:06.064Z
Learning: In all ARB localization files under lib/l10n, AI Scribe translation keys should only be present for the supported locales: en (default), fr, vi, and ru. Do not add AI Scribe keys to other locale files. Ensure fr, vi, and ru ARB files include the same AI Scribe keys as applicable, with en as the source/default. This pattern applies to any ARB file in the lib/l10n directory.

Applied to files:

  • lib/l10n/intl_messages.arb
📚 Learning: 2025-12-23T05:27:47.287Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4210
File: lib/l10n/intl_messages.arb:5151-5182
Timestamp: 2025-12-23T05:27:47.287Z
Learning: In the tmail-flutter repository, localization (l10n) updates for locale files (intl_fr.arb, intl_vi.arb, intl_ru.arb, etc.) are managed automatically through a third-party website/platform. When new English keys are added to intl_messages.arb, translations to other locales are handled by this automated workflow rather than being manually added in pull requests.

Applied to files:

  • lib/main/localizations/app_localizations.dart
📚 Learning: 2025-12-15T06:24:38.823Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4137
File: lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart:32-32
Timestamp: 2025-12-15T06:24:38.823Z
Learning: In lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart and lib/features/manage_account/presentation/manage_account_dashboard_controller.dart, the code calls SentryManager.instance.setUser() with PII fields (account ID, display name, username, email). This is privacy-sensitive. Review and ensure PII handling complies with policy: avoid sending unnecessary PII to Sentry, redact or hash sensitive fields if possible, or document explicit privacy policy and user consent for such data. If PII must be sent, ensure minimal data, secure handling, and add notes to the privacy policy. Consider adding tests or a lint rule to flag setUser calls that include PII.

Applied to files:

  • lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: analyze-test (contact)
  • GitHub Check: analyze-test (server_settings)
  • GitHub Check: analyze-test (email_recovery)
  • GitHub Check: analyze-test (core)
  • GitHub Check: analyze-test (forward)
  • GitHub Check: analyze-test (fcm)
  • GitHub Check: analyze-test (default)
  • GitHub Check: analyze-test (model)
  • GitHub Check: analyze-test (rule_filter)
  • GitHub Check: Build web version and deploy

Copy link

@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: 0

🧹 Nitpick comments (5)
lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart (4)

72-76: Hardcoded Colors.black may break dark theme support.

Using Colors.black directly instead of a theme-aware color (like AppColor constants used elsewhere in this file) could cause visibility issues if dark mode is supported.

🔎 Suggested fix
                         style: Theme.of(context)
                             .textTheme
                             .bodyMedium
-                            ?.copyWith(color: Colors.black),
+                            ?.copyWith(color: AppColor.colorNameEmail),

Alternatively, use Theme.of(context).textTheme.bodyMedium without color override if the theme already defines the appropriate color.


157-161: Consistent theme color concern.

Same as the recipient name, Colors.black is hardcoded here for the "Save as Draft" button text. Consider using a theme-aware color for consistency with the rest of the widget.


181-184: Consider renaming isOldest for clarity.

The name isOldest implies temporal ordering, but it's used to control whether this is the last item (hiding the bottom divider). A name like isLastItem might be clearer, though this is a minor naming suggestion.


44-45: Consider adding semantic label for accessibility.

The InkWell wrapper makes the entire item tappable, but screen readers won't have context about what this action does. Consider wrapping with Semantics or using InkWell's semanticLabel property.

🔎 Suggested improvement
      child: InkWell(
+       semanticLabel: 'Open draft to ${draftLocal.firstRecipientName}',
        onTap: () => onSelectLocalEmailDraftAction?.call(draftLocal),
lib/features/composer/presentation/manager/composer_timer.dart (1)

6-35: LGTM!

The ComposerTimer class cleanly encapsulates periodic timer logic with proper lifecycle management. The 15-second default interval aligns with the PR requirements, and the try-catch in the timer callback provides error isolation.

Consider capturing the stack trace in the catch block for richer debugging:

     } catch (e) {
-      logError('ComposerTimer::start:Exception: $e');
+      logError('ComposerTimer::start:Exception: $e', e, StackTrace.current);
     }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ba978c and 2444c07.

📒 Files selected for processing (15)
  • lib/features/caching/config/hive_cache_config.dart
  • lib/features/composer/presentation/composer_controller.dart
  • lib/features/composer/presentation/extensions/create_email_request_extension.dart
  • lib/features/composer/presentation/extensions/handle_local_email_draft_extension.dart
  • lib/features/composer/presentation/manager/composer_manager.dart
  • lib/features/composer/presentation/manager/composer_timer.dart
  • lib/features/composer/presentation/utils/local_email_draft_helper.dart
  • lib/features/download/presentation/extensions/download_attachment_download_controller_extension.dart
  • lib/features/mailbox_dashboard/data/local/local_email_draft_manager.dart
  • lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart
  • lib/features/mailbox_dashboard/presentation/extensions/local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/presentation/extensions/remove_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/presentation/extensions/restore_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_list_dialog_builder.dart
🚧 Files skipped from review as they are similar to previous changes (5)
  • lib/features/caching/config/hive_cache_config.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_list_dialog_builder.dart
  • lib/features/composer/presentation/extensions/create_email_request_extension.dart
  • lib/features/mailbox_dashboard/data/local/local_email_draft_manager.dart
  • lib/features/mailbox_dashboard/presentation/extensions/local_email_draft_extension.dart
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2025-12-12T07:56:31.877Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4204
File: lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart:579-582
Timestamp: 2025-12-12T07:56:31.877Z
Learning: In lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart, the addLabelToEmail method intentionally throws UnimplementedError because label synchronization with the email cache is handled separately via extension methods (e.g., _autoSyncLabelToSelectedEmailOnMemory in handle_label_for_email_extension.dart). Implementing it in the cache datasource would cause data conflicts. This differs from other keyword operations like markAsRead, markAsStar, markAsAnswered, and markAsForwarded, which are implemented directly in the cache datasource.

Applied to files:

  • lib/features/composer/presentation/utils/local_email_draft_helper.dart
  • lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart
  • lib/features/composer/presentation/composer_controller.dart
📚 Learning: 2025-12-09T09:36:45.349Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4194
File: lib/features/manage_account/presentation/manage_account_dashboard_controller.dart:174-176
Timestamp: 2025-12-09T09:36:45.349Z
Learning: In Dart/Flutter projects using GetX, do not wrap getBinding<T>() calls in try/catch since they return null when not found. Only wrap Get.find<T>() calls in try/catch because they throw if a dependency is unavailable. When a binding is optional, consider checking Get.isRegistered<T>() or handling the null/exception path gracefully instead of blindly catching, and document the expectation for failure modes where a dependency may not be registered.

Applied to files:

  • lib/features/composer/presentation/utils/local_email_draft_helper.dart
  • lib/features/composer/presentation/extensions/handle_local_email_draft_extension.dart
  • lib/features/composer/presentation/manager/composer_manager.dart
  • lib/features/composer/presentation/manager/composer_timer.dart
  • lib/features/download/presentation/extensions/download_attachment_download_controller_extension.dart
  • lib/features/mailbox_dashboard/presentation/extensions/restore_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart
  • lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart
  • lib/features/mailbox_dashboard/presentation/extensions/remove_local_email_draft_extension.dart
  • lib/features/composer/presentation/composer_controller.dart
📚 Learning: 2025-12-09T12:47:45.861Z
Learnt from: zatteo
Repo: linagora/tmail-flutter PR: 4196
File: scribe/lib/scribe/ai/data/datasource_impl/ai_datasource_impl.dart:34-35
Timestamp: 2025-12-09T12:47:45.861Z
Learning: In the tmail-flutter repository, avoid suggesting replacing DioError with DioException unless there is a codebase-wide migration. This pattern applies to all Dart files; when reviewing, only propose a DioError-safe change (e.g., compatible error handling or conversion) unless a global migration is in place. Ensure consistency of DioError usage across modules and flag any deviations for a repo-wide decision.

Applied to files:

  • lib/features/composer/presentation/utils/local_email_draft_helper.dart
  • lib/features/composer/presentation/extensions/handle_local_email_draft_extension.dart
  • lib/features/composer/presentation/manager/composer_manager.dart
  • lib/features/composer/presentation/manager/composer_timer.dart
  • lib/features/download/presentation/extensions/download_attachment_download_controller_extension.dart
  • lib/features/mailbox_dashboard/presentation/extensions/restore_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart
  • lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart
  • lib/features/mailbox_dashboard/presentation/extensions/remove_local_email_draft_extension.dart
  • lib/features/composer/presentation/composer_controller.dart
📚 Learning: 2025-12-12T04:54:11.121Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4191
File: lib/features/email/presentation/extensions/handle_email_action_extension.dart:37-80
Timestamp: 2025-12-12T04:54:11.121Z
Learning: In lib/features/email/presentation/extensions/handle_email_action_extension.dart, the mailboxDashBoardController.selectedEmail should only be synchronized when isMobileThreadDisabled is true. This is intentional behavior and should not be changed to update selectedEmail in non-mobile or thread-enabled contexts.

Applied to files:

  • lib/features/composer/presentation/extensions/handle_local_email_draft_extension.dart
  • lib/features/composer/presentation/manager/composer_manager.dart
  • lib/features/mailbox_dashboard/presentation/extensions/restore_local_email_draft_extension.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart
  • lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart
  • lib/features/mailbox_dashboard/presentation/extensions/remove_local_email_draft_extension.dart
  • lib/features/composer/presentation/composer_controller.dart
📚 Learning: 2025-12-12T07:43:26.643Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4204
File: lib/features/email/presentation/extensions/handle_label_for_email_extension.dart:94-148
Timestamp: 2025-12-12T07:43:26.643Z
Learning: In lib/features/email/presentation/extensions/handle_label_for_email_extension.dart and similar keyword synchronization code, the addKeyword() function is the preferred method for adding keywords to emails. The resyncKeywords() pattern is being phased out and will be replaced in favor of addKeyword().

Applied to files:

  • lib/features/composer/presentation/extensions/handle_local_email_draft_extension.dart
  • lib/features/composer/presentation/manager/composer_manager.dart
  • lib/features/mailbox_dashboard/presentation/extensions/restore_local_email_draft_extension.dart
  • lib/features/composer/presentation/composer_controller.dart
📚 Learning: 2025-12-23T05:27:10.887Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4210
File: model/lib/mailbox/presentation_mailbox.dart:22-34
Timestamp: 2025-12-23T05:27:10.887Z
Learning: In the tmail-flutter codebase, the Action Required folder (actionRequiredFolder) is a virtual folder that is AI-curated and contains emails with the keyword "needs-action". Unlike the Favorites folder, users should NOT be able to drag emails into the Action Required folder. The UI correctly prevents this by not wrapping the Action Required folder item in a DragTarget (see mailbox_item_widget.dart).

Applied to files:

  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart
  • lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart
  • lib/features/composer/presentation/composer_controller.dart
📚 Learning: 2025-12-15T06:24:50.369Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4137
File: lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart:32-32
Timestamp: 2025-12-15T06:24:50.369Z
Learning: In lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart and lib/features/manage_account/presentation/manage_account_dashboard_controller.dart, SentryManager.instance.setUser() intentionally sends PII (account ID, display name, username, email) to Sentry for error tracking. The team plans to document this in the app's privacy policy for transparency and compliance.

Applied to files:

  • lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart
  • lib/features/composer/presentation/composer_controller.dart
📚 Learning: 2025-12-08T08:11:08.985Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4189
File: lib/features/login/presentation/extensions/handle_company_server_login_info_extension.dart:50-56
Timestamp: 2025-12-08T08:11:08.985Z
Learning: In the tmail-flutter codebase, prefer descriptive variable names that include full context rather than abbreviated names. For example, `removeLoginInfoInteractor` is preferred over shorter alternatives like `removeInteractor` or `interactor`, as it clearly conveys both the action (remove) and the specific data being operated on (loginInfo).

Applied to files:

  • lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart
  • lib/features/composer/presentation/composer_controller.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: analyze-test (server_settings)
  • GitHub Check: analyze-test (fcm)
  • GitHub Check: analyze-test (email_recovery)
  • GitHub Check: analyze-test (core)
  • GitHub Check: analyze-test (contact)
  • GitHub Check: analyze-test (forward)
  • GitHub Check: analyze-test (rule_filter)
  • GitHub Check: analyze-test (model)
  • GitHub Check: analyze-test (default)
  • GitHub Check: Build web version and deploy
🔇 Additional comments (29)
lib/features/download/presentation/extensions/download_attachment_download_controller_extension.dart (1)

167-169: LGTM! Correct API usage for CancelToken.

The fix correctly passes a single string argument to cancelToken.cancel() instead of the erroneous list argument. The Dio CancelToken.cancel([dynamic reason]) API accepts an optional reason parameter, and using the localized cancellation message is appropriate.

lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart (2)

20-38: Well-structured widget API.

The constructor clearly separates required dependencies from optional callbacks, making the widget flexible for different use cases while enforcing essential data requirements.


40-193: Overall implementation is clean and well-structured.

The widget follows Flutter best practices with proper use of const constructor, clear separation of layout concerns, and appropriate use of callbacks for actions. The conditional rendering logic for subject, content, and divider is correctly implemented.

lib/features/composer/presentation/utils/local_email_draft_helper.dart (1)

6-19: LGTM!

The utility class is well-designed with a private constructor and a clear static method for generating composite draft IDs. The use of TupleKey ensures consistent key generation across the codebase.

lib/features/composer/presentation/extensions/handle_local_email_draft_extension.dart (2)

8-58: LGTM!

The generateCreateEmailRequest() method has proper null-safety checks and comprehensively constructs the CreateEmailRequest with all necessary fields for local draft persistence.


60-84: LGTM!

The saveLocalEmailDraftAction() method correctly validates prerequisites, auto-creates email tags before saving, and awaits the interactor execution. Error handling is appropriately deferred to the caller (ComposerManager's _handleOnTick).

lib/features/mailbox_dashboard/presentation/extensions/remove_local_email_draft_extension.dart (1)

4-19: LGTM!

The extension correctly mirrors the ID generation logic used in save operations, ensuring consistent key management. The null-safety checks and async handling are properly implemented.

lib/features/composer/presentation/manager/composer_manager.dart (5)

25-43: LGTM!

The isSynchronous parameter provides good flexibility for batch operations vs single additions. The lazy timer initialization pattern is appropriate.


45-52: LGTM!

Using isSynchronous: false for batch additions and deferring timer initialization until after all composers are added prevents redundant timer restarts.


54-67: LGTM!

The timer cleanup logic correctly stops the timer when all composers are removed.


323-347: LGTM!

The timer lifecycle methods are well-implemented:

  • Lazy initialization prevents duplicate timers.
  • Error handling in _handleOnTick isolates failures per composer, ensuring one failing draft save doesn't affect others.
  • Errors are logged with composer ID for debugging visibility.

349-354: LGTM!

Proper cleanup in onClose ensures the timer is stopped when the manager is disposed.

lib/features/mailbox_dashboard/presentation/extensions/restore_local_email_draft_extension.dart (4)

17-30: LGTM!

The method correctly uses getBinding<>() pattern (which returns null when not found, per learnings) and has proper null checks before consuming state.


32-46: LGTM!

The conversion and sorting logic is clean. The in-place sort on listPresentationLocalEmailDraft is fine since it's a locally-created list from .toList().


48-64: LGTM!

The dialog display and single draft editing flow are correctly implemented with proper navigation handling.


66-84: LGTM!

The spread operator copy ([...localDrafts]) correctly prevents mutation of the input parameter before sorting. The toast feedback provides good UX confirmation.

lib/features/composer/presentation/composer_controller.dart (11)

134-141: LGTM!

The addition of HandleMessageFailureMixin follows proper Dart mixin patterns and correctly replaces the previous extension-based approach for message failure handling.


187-188: LGTM!

Making saveLocalEmailDraftInteractor nullable and binding it conditionally for web platform only is appropriate since local draft persistence is a web-specific feature.


247-248: LGTM!

Changing visibility from private (_savedEmailDraftHash) to package-private (savedEmailDraftHash) correctly enables access from the extension methods while maintaining encapsulation.


425-427: LGTM!

Making onBeforeUnloadBrowserListener async and awaiting saveLocalEmailDraftAction() ensures the draft is persisted before the browser unloads.


479-486: LGTM!

Renaming to getUploadUriFromSession (public) enables the extension to access this method while maintaining the same error handling logic.


858-865: LGTM!

Correctly removes the local email draft after a successful send operation. The null check on composerId is appropriate.


1150-1158: LGTM!

The EmailActionType.composeFromLocalEmailDraft correctly replaces the previous reopenComposerBrowser action type, aligning with the new local draft flow.


1186-1195: LGTM!

The draft hash initialization logic correctly handles the new composeFromLocalEmailDraft action type, preserving the original hash for change detection.


1632-1638: LGTM!

The local draft is correctly removed when the user explicitly deletes the composer.


2026-2031: LGTM!

The local draft is correctly removed after successfully saving to the server drafts, preventing duplicate data.


2237-2240: LGTM!

Using saveLocalEmailDraftAction() in onBeforeReconnect ensures drafts are persisted before network reconnection events, providing resilience against data loss.

lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart (2)

257-520: Overall binding structure looks good.

The integration of the local email draft system follows the correct GetX dependency injection pattern:

  • Datasource interfaces and implementations are properly wired
  • Repositories depend on registered datasources
  • Interactors depend on registered repositories
  • The dependency chain is logical and maintainable

The previous duplicate binding issues flagged in past reviews have been successfully addressed.


315-319: All flagged dependencies are properly registered in the codebase. LocalEmailDraftManager and LocalEmailDraftWorkerQueue are registered in lib/main/bindings/local/local_bindings.dart, Uuid and ApplicationManager are in lib/main/bindings/core/core_bindings.dart, FileUploader is in lib/main/bindings/network/network_isolate_binding.dart, and DownloadClient is in lib/main/bindings/network/network_bindings.dart. No registration issues exist.

Likely an incorrect or invalid review comment.

Copy link

@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: 1

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2444c07 and db2157e.

📒 Files selected for processing (4)
  • core/lib/presentation/extensions/color_extension.dart
  • lib/features/composer/presentation/manager/composer_timer.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_list_dialog_builder.dart
🚧 Files skipped from review as they are similar to previous changes (3)
  • lib/features/composer/presentation/manager/composer_timer.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_list_dialog_builder.dart
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-09T09:36:45.349Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4194
File: lib/features/manage_account/presentation/manage_account_dashboard_controller.dart:174-176
Timestamp: 2025-12-09T09:36:45.349Z
Learning: In Dart/Flutter projects using GetX, do not wrap getBinding<T>() calls in try/catch since they return null when not found. Only wrap Get.find<T>() calls in try/catch because they throw if a dependency is unavailable. When a binding is optional, consider checking Get.isRegistered<T>() or handling the null/exception path gracefully instead of blindly catching, and document the expectation for failure modes where a dependency may not be registered.

Applied to files:

  • core/lib/presentation/extensions/color_extension.dart
📚 Learning: 2025-12-09T12:47:45.861Z
Learnt from: zatteo
Repo: linagora/tmail-flutter PR: 4196
File: scribe/lib/scribe/ai/data/datasource_impl/ai_datasource_impl.dart:34-35
Timestamp: 2025-12-09T12:47:45.861Z
Learning: In the tmail-flutter repository, avoid suggesting replacing DioError with DioException unless there is a codebase-wide migration. This pattern applies to all Dart files; when reviewing, only propose a DioError-safe change (e.g., compatible error handling or conversion) unless a global migration is in place. Ensure consistency of DioError usage across modules and flag any deviations for a repo-wide decision.

Applied to files:

  • core/lib/presentation/extensions/color_extension.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: analyze-test (forward)
  • GitHub Check: analyze-test (fcm)
  • GitHub Check: analyze-test (email_recovery)
  • GitHub Check: analyze-test (default)
  • GitHub Check: analyze-test (rule_filter)
  • GitHub Check: analyze-test (contact)
  • GitHub Check: analyze-test (server_settings)
  • GitHub Check: analyze-test (model)
  • GitHub Check: analyze-test (core)

Copy link

@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: 0

🧹 Nitpick comments (2)
core/lib/presentation/extensions/app_text_colors_extension.dart (1)

4-4: Consider singular naming for consistency.

The extension is named AppTextColors (plural) but currently provides only one getter. Consider renaming to AppTextColor (singular) for consistency, unless additional text color getters are planned for future addition.

lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart (1)

140-181: Consider adding semantic labels for better accessibility.

For improved screen reader support, consider wrapping the action buttons with Semantics widgets or adding semantic labels to clarify the action context (e.g., "Edit draft", "Save draft to server", "Discard draft").

Example enhancement
Semantics(
  label: 'Edit draft',
  button: true,
  child: TMailButtonWidget(
    text: AppLocalizations.of(context).edit,
    // ... rest of the properties
  ),
)
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between db2157e and f47e0cf.

📒 Files selected for processing (4)
  • core/lib/core.dart
  • core/lib/presentation/extensions/app_text_colors_extension.dart
  • core/lib/presentation/extensions/color_extension.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/lib/presentation/extensions/color_extension.dart
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-12-09T09:36:45.349Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4194
File: lib/features/manage_account/presentation/manage_account_dashboard_controller.dart:174-176
Timestamp: 2025-12-09T09:36:45.349Z
Learning: In Dart/Flutter projects using GetX, do not wrap getBinding<T>() calls in try/catch since they return null when not found. Only wrap Get.find<T>() calls in try/catch because they throw if a dependency is unavailable. When a binding is optional, consider checking Get.isRegistered<T>() or handling the null/exception path gracefully instead of blindly catching, and document the expectation for failure modes where a dependency may not be registered.

Applied to files:

  • core/lib/presentation/extensions/app_text_colors_extension.dart
  • core/lib/core.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart
📚 Learning: 2025-12-09T12:47:45.861Z
Learnt from: zatteo
Repo: linagora/tmail-flutter PR: 4196
File: scribe/lib/scribe/ai/data/datasource_impl/ai_datasource_impl.dart:34-35
Timestamp: 2025-12-09T12:47:45.861Z
Learning: In the tmail-flutter repository, avoid suggesting replacing DioError with DioException unless there is a codebase-wide migration. This pattern applies to all Dart files; when reviewing, only propose a DioError-safe change (e.g., compatible error handling or conversion) unless a global migration is in place. Ensure consistency of DioError usage across modules and flag any deviations for a repo-wide decision.

Applied to files:

  • core/lib/presentation/extensions/app_text_colors_extension.dart
  • core/lib/core.dart
  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart
📚 Learning: 2025-12-12T04:54:11.121Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4191
File: lib/features/email/presentation/extensions/handle_email_action_extension.dart:37-80
Timestamp: 2025-12-12T04:54:11.121Z
Learning: In lib/features/email/presentation/extensions/handle_email_action_extension.dart, the mailboxDashBoardController.selectedEmail should only be synchronized when isMobileThreadDisabled is true. This is intentional behavior and should not be changed to update selectedEmail in non-mobile or thread-enabled contexts.

Applied to files:

  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart
📚 Learning: 2025-12-23T05:27:10.887Z
Learnt from: dab246
Repo: linagora/tmail-flutter PR: 4210
File: model/lib/mailbox/presentation_mailbox.dart:22-34
Timestamp: 2025-12-23T05:27:10.887Z
Learning: In the tmail-flutter codebase, the Action Required folder (actionRequiredFolder) is a virtual folder that is AI-curated and contains emails with the keyword "needs-action". Unlike the Favorites folder, users should NOT be able to drag emails into the Action Required folder. The UI correctly prevents this by not wrapping the Action Required folder item in a DragTarget (see mailbox_item_widget.dart).

Applied to files:

  • lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: analyze-test (server_settings)
  • GitHub Check: analyze-test (email_recovery)
  • GitHub Check: analyze-test (fcm)
  • GitHub Check: analyze-test (model)
  • GitHub Check: analyze-test (default)
  • GitHub Check: analyze-test (rule_filter)
  • GitHub Check: analyze-test (core)
  • GitHub Check: analyze-test (forward)
  • GitHub Check: analyze-test (contact)
🔇 Additional comments (9)
core/lib/core.dart (1)

21-21: LGTM!

The export addition is appropriately placed in the Extensions section and correctly exposes the new AppTextColors extension to consumers of the core library.

core/lib/presentation/extensions/app_text_colors_extension.dart (2)

1-2: LGTM!

The imports are appropriate and necessary for the extension implementation.


5-10: Implementation is correct.

The primaryTextColor getter properly implements theme-aware text color selection using Theme.of(this).brightness. Both required AppColor constants (primaryDarkThemeTextColor and primaryLightThemeTextColor) are defined in color_extension.dart, so there are no compilation concerns.

lib/features/mailbox_dashboard/presentation/widgets/local_email_draft/local_email_draft_item_widget.dart (6)

1-19: LGTM! Clean imports and well-defined callback types.

The imports are appropriate and the typedef declarations provide clear type safety for the action callbacks.


21-39: LGTM! Well-structured widget with appropriate field declarations.

The class structure is clean with proper separation of required data and optional callbacks.


42-194: LGTM! Solid widget structure with proper tap handling and layout.

The widget properly handles user interaction with the InkWell wrapper and conditionally renders the divider based on item position. The overall layout and spacing are well-structured.


144-144: All localization keys are properly defined: edit, saveAsDraft, and discard exist in intl_messages.arb and are accessible via the generated AppLocalizations class.


57-60: The EmailAvatarBuilder API usage is correct. The constructor accepts avatarText (required) and avatarColors (optional), and the code at lines 57-60 properly passes both parameters matching the current API contract.

Likely an incorrect or invalid review comment.


70-70: All extension methods are properly implemented and handle edge cases correctly.

Verification confirms:

  1. firstRecipientName gracefully handles empty recipients by returning senderName as fallback
  2. AppColor.steelGray200.asFilter() extension method exists in color_extension.dart and properly implements ColorFilter? with default BlendMode.srcIn
  3. getSavedTime(String locale) is properly implemented with locale support via DateFormat

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants