Skip to content

TF-4284 Fix refreshing causes "virtual mailboxes" to be hidden#4290

Open
dab246 wants to merge 4 commits intomasterfrom
bugfix/tf-4284-refreshing-causes-virtual-mailboxes-to-be-hidden
Open

TF-4284 Fix refreshing causes "virtual mailboxes" to be hidden#4290
dab246 wants to merge 4 commits intomasterfrom
bugfix/tf-4284-refreshing-causes-virtual-mailboxes-to-be-hidden

Conversation

@dab246
Copy link
Member

@dab246 dab246 commented Jan 29, 2026

Issue

#4284

Root cause

Because each refresh involves rebuilding the tree before adding the virtual folders (Favorite/Action Required), the mailbox list view is re-rendered.

Solution

Add virtual folders when creating the mailbox tree. Only then trigger the rendering to the UI.

Resolved

Screen.Recording.2026-01-29.at.17.49.38.mov

Summary by CodeRabbit

  • New Features

    • Added a consolidated MailboxCollection model and a single update entry point that can apply optional transformation hooks; AI-driven actions are supported but off by default.
  • Refactor

    • Mailbox tree and list updates moved to an immutable, functional flow with a centralized routine to keep default, personal, team and “all” views synchronized.
    • Favorite and action-required folder flows now return updated collections instead of mutating state.
  • Tests

    • Tests updated to use the new MailboxCollection shape.

@coderabbitai
Copy link

coderabbitai bot commented Jan 29, 2026

Walkthrough

Introduces a new immutable MailboxCollection aggregating allMailboxes, defaultTree, personalTree, and teamTree. Refactors mailbox tree generation (mailbox_tree_builder) to return MailboxCollection and updates callers. BaseMailboxController.buildTree and refreshTree accept an optional OnUpdateMailboxCollectionCallback; add updateMailboxTree to apply a MailboxCollection to controller state and reset rebuild flags. Extensions for favorite/action-required folders were converted from mutating void methods to pure functions returning MailboxCollection. Controllers (mailbox, search) and tests were updated to construct and propagate MailboxCollection; added isAINeedsActionEnabled getter and autoCreateVirtualFolder/updateMailboxCollection flow.

Possibly related PRs

  • TF-4141 Expected actions #4210: Overlapping changes to mailbox virtual-folder/action-required behavior and migration toward MailboxCollection-based update APIs.

Suggested labels

Label

Suggested reviewers

  • tddang-linagora
  • hoangdat
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly and specifically identifies the issue being fixed: virtual mailboxes being hidden during refresh operations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bugfix/tf-4284-refreshing-causes-virtual-mailboxes-to-be-hidden

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: 2

🤖 Fix all issues with AI agents
In
`@lib/features/mailbox/presentation/extensions/handle_action_required_tab_extension.dart`:
- Around line 52-64: The helper _addToAllMailboxes mutates the input list by
calling currentAllMailboxes.add(folder); change it to be non-mutating: when the
id is not present, return a new list containing the existing entries plus folder
(e.g., copy currentAllMailboxes and append folder) instead of modifying
currentAllMailboxes in place so callers' original MailboxCollection.allMailboxes
is not altered.

In
`@lib/features/mailbox/presentation/extensions/handle_favorite_tab_extension.dart`:
- Around line 55-67: The function _addFavoriteFolderToAllMailboxes currently
mutates the input list allMailboxes by calling .add(favoriteFolder); change it
to return a new list instead to preserve immutability (e.g., create a copy of
allMailboxes and append favoriteFolder or return a new list combining
allMailboxes and favoriteFolder) while keeping the existing early-exit when the
id already exists; update only inside _addFavoriteFolderToAllMailboxes and
preserve parameter names favoriteFolder and allMailboxes.
🧹 Nitpick comments (1)
lib/features/search/mailbox/presentation/search_mailbox_controller.dart (1)

177-183: Consider passing callback to refreshTree for consistency.

The buildTree call (lines 170-173) passes onUpdateMailboxCollectionCallback: updateMailboxCollection, but the refreshTree call here doesn't pass the callback. In MailboxController, the equivalent refreshTree call (line 629-632) does pass the callback. This inconsistency might cause virtual folders not to be added after a refresh in the search mailbox context.

♻️ Proposed fix
     } else if (success is RefreshChangesAllMailboxSuccess) {
       currentMailboxState = success.currentMailboxState;
-      await refreshTree(success.mailboxList);
+      await refreshTree(
+        success.mailboxList,
+        onUpdateMailboxCollectionCallback: updateMailboxCollection,
+      );
       if (currentContext != null) {
         syncAllMailboxWithDisplayName(currentContext!);
       }

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: 2

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/presentation/extensions/handle_favorite_tab_extension.dart (1)

36-52: Avoid mutating the existing default tree list.

currentDefaultFolders aliases defaultTree.root.childrenItems. The subsequent insert mutates the original list, undermining the new immutable flow and potentially causing side effects elsewhere. Clone the list before inserting.

🔧 Suggested fix
-    List<MailboxNode> currentDefaultFolders =
-        defaultMailboxNode.childrenItems ?? [];
+    final currentDefaultFolders =
+        List<MailboxNode>.from(defaultMailboxNode.childrenItems ?? []);
🤖 Fix all issues with AI agents
In
`@lib/features/mailbox/presentation/extensions/handle_action_required_tab_extension.dart`:
- Around line 40-49: The _addToDefaultMailboxTree function unconditionally
inserts a virtual Action Required node which can create duplicates; before
calling children.insertAfterStarredOrInbox(MailboxNode(folder)) check the
existing children (root.childrenItems) for an existing MailboxNode representing
the same virtual folder (e.g., compare mailbox id or a distinguishing flag on
PresentationMailbox) and skip insertion (return currentDefaultTree or
root.copyWith(children: children)) if a match is found; use the same identifiers
present in this file (function _addToDefaultMailboxTree, PresentationMailbox,
MailboxTree, MailboxNode, insertAfterStarredOrInbox, root.childrenItems) so the
guard prevents inserting a duplicate node.

In `@lib/features/search/mailbox/presentation/search_mailbox_controller.dart`:
- Around line 224-235: The failure branch is bypassing virtual-folder gating by
calling addActionRequiredFolder directly; instead, construct the
MailboxCollection (using MailboxCollection with
allMailboxes/defaultTree/personalTree/teamTree) and pass it into
updateMailboxCollection so updateMailboxCollection → autoCreateVirtualFolder
applies feature-flag checks (isAINeedsActionEnabled) and also ensures Favorites
get created; replace the direct addActionRequiredFolder call with a call to
updateMailboxCollection(mailboxCollection: MailboxCollection(...),
isRefreshTrigger: false) to unify behavior with the success path.

@github-actions
Copy link

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

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.

1 participant