Skip to content

TF-4660 Drive loading#4699

Merged
hoangdat merged 9 commits into
masterfrom
feature/TF-4660-drive-loading
Jul 24, 2026
Merged

TF-4660 Drive loading#4699
hoangdat merged 9 commits into
masterfrom
feature/TF-4660-drive-loading

Conversation

@tddang-linagora

@tddang-linagora tddang-linagora commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Issue

Demo

Web

Screen.Recording.2026-07-22.at.3.47.19.PM.mov

Mobile

Mobile has a bug where drive is stuck at login stage, cannot end the loading, but as soon as it is fixed, the loading will go away without any further changes.

drive-mobile-loading.webm

Summary by CodeRabbit

  • New Features

    • Added placeholder UI while Drive intent web content initializes.
    • Updated Drive intent modals to resolve intents asynchronously before loading.
    • Added a readiness timeout safeguard and new hooks for “ready” and timeout handling.
  • Bug Fixes

    • Prevented premature message handling before intent setup completes.
    • Improved Drive picker failure handling and ensured modal cleanup on timeout/error.
    • Enhanced token exchange requests with an explicit JSON response header.
    • Updated attachment picker taps to close the current route before opening the picker.
  • Refactor

    • Made the intent modal shell layout more configurable (padding/shape/optional close button).
  • Tests

    • Expanded Drive intent message-handler timeout coverage and improved isolation.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The Drive attachment flow now resolves intent data asynchronously in web and mobile modals, displays platform-specific skeleton loaders until readiness, and reports intent errors through callbacks. Modal layout options were expanded, picker error handling was centralized, and context-menu navigation closes before opening the picker. Message handling now gates early events and adds readiness timeout hooks and tests. The exchange-token request sends an Accept: application/json header.

Possibly related PRs

Suggested labels: attach-from-drive

Suggested reviewers: dab246, hoangdat, zatteo, codescene-delta-analysis

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and clearly related to the Drive loading work in this changeset.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/TF-4660-drive-loading

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.

codescene-delta-analysis[bot]

This comment was marked as outdated.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@workplace/lib/presentation/mixin/drive_picker_state_mixin.dart`:
- Around line 58-64: Capture the localized attach-from-Drive failure message
while the picker context is still mounted, before Navigator.pop(context)
disposes the state, and pass that value through the picker flow into
_onIntentError or DrivePickFailure instead of resolving it there via
mounted/context.

In `@workplace/lib/presentation/view/drive_intent_web_view_modal_mobile.dart`:
- Around line 39-58: Ensure resolved intents are not dropped when the WebView is
created after the future completes: store the resolved intent in the state, then
centralize the data/URL loading logic and invoke it from both the future
completion handler and onWebViewCreated. Update the relevant intent state and
WebView initialization methods so whichever completes last performs the load
once the controller is available.

In `@workplace/lib/presentation/view/drive_intent_web_view_modal_web.dart`:
- Around line 48-60: Store the resolved intent in state when the
`widget.intentFuture.then` callback runs, rather than relying only on
`_iframeElement`. Update `onIframeCreated` to check for the buffered intent and
perform the `srcdoc` removal and URL navigation there; reuse a helper if
appropriate so both paths apply the same intent once the iframe exists.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0ebca8cc-2854-48d1-b829-f1459ea637d6

📥 Commits

Reviewing files that changed from the base of the PR and between 00d1e05 and f6f019a.

📒 Files selected for processing (9)
  • workplace/lib/data/datasource_impl/workplace_datasource_impl.dart
  • workplace/lib/presentation/mixin/drive_intent_message_handler_mixin.dart
  • workplace/lib/presentation/mixin/drive_picker_state_mixin.dart
  • workplace/lib/presentation/notifier/drive_attachment_state.dart
  • workplace/lib/presentation/view/drive_intent_skeleton_page.dart
  • workplace/lib/presentation/view/drive_intent_web_view_modal_mobile.dart
  • workplace/lib/presentation/view/drive_intent_web_view_modal_shell.dart
  • workplace/lib/presentation/view/drive_intent_web_view_modal_web.dart
  • workplace/lib/presentation/widget/drive_attachment_context_menu_tile.dart
💤 Files with no reviewable changes (1)
  • workplace/lib/presentation/notifier/drive_attachment_state.dart

Comment thread workplace/lib/presentation/mixin/drive_picker_state_mixin.dart Outdated
Comment thread workplace/lib/presentation/view/drive_intent_web_view_modal_mobile.dart Outdated
Comment thread workplace/lib/presentation/view/drive_intent_web_view_modal_web.dart Outdated
@github-actions

Copy link
Copy Markdown

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

@Arsnael

Arsnael commented Jul 10, 2026

Copy link
Copy Markdown
Member

Docker image published for this PR: linagora/tmail-web-pr:4699

@dab246

dab246 commented Jul 10, 2026

Copy link
Copy Markdown
Member
  • If a white screen still appears, we should also handle it using a loading view.
Screenshot 2026-07-10 at 13 54 02

Comment thread workplace/lib/presentation/view/drive_intent_web_view_modal_web.dart Outdated
@tddang-linagora

Copy link
Copy Markdown
Collaborator Author

If a white screen still appears, we should also handle it using a loading view.

The only cue for that is ready postMessage, but that would be web only at the moment, as mobile is still stuck with login screen

Comment thread workplace/lib/presentation/view/drive_intent_web_view_modal_web.dart Outdated
codescene-delta-analysis[bot]

This comment was marked as outdated.

@tddang-linagora
tddang-linagora requested a review from dab246 July 10, 2026 07:21
Comment thread workplace/lib/presentation/notifier/drive_attachment_state.dart
Comment thread workplace/lib/presentation/model/drive_intent_timing.dart Outdated
Comment thread workplace/lib/presentation/mixin/drive_picker_state_mixin.dart
@dab246

dab246 commented Jul 10, 2026

Copy link
Copy Markdown
Member

If a white screen still appears, we should also handle it using a loading view.

The only cue for that is ready postMessage, but that would be web only at the moment, as mobile is still stuck with login screen

So let's fix it for the web version first; for the mobile version, please comment so we can fix it later. Also, update the demo to work correctly for the web version.

@tddang-linagora

Copy link
Copy Markdown
Collaborator Author

If a white screen still appears, we should also handle it using a loading view.

The only cue for that is ready postMessage, but that would be web only at the moment, as mobile is still stuck with login screen

So let's fix it for the web version first; for the mobile version, please comment so we can fix it later. Also, update the demo to work correctly for the web version.

Done and demos updated

codescene-delta-analysis[bot]

This comment was marked as outdated.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
workplace/lib/presentation/view/drive_intent_web_view_modal_web.dart (1)

49-58: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Intent URL can be silently dropped if the future resolves before onIframeCreated.

_handleIntentResolved sets _iframeElement?.src, but _iframeElement is only assigned in onIframeCreated. If intentFuture settles first, the ?.src assignment is a no-op and nothing reapplies the URL afterward — the skeleton stays visible indefinitely. This was flagged in a prior review and remains unresolved.

🔒 Proposed fix: buffer the resolved intent and apply from both paths
 class _DriveIntentWebViewModalState extends State<DriveIntentWebViewModal>
     with DriveIntentMessageHandlerMixin, WebWindowMessageMixin<DriveIntentWebViewModal> {
   html.IFrameElement? _iframeElement;
   String _intentOrigin = 'null';
   bool _showSkeleton = true;
+  WorkplaceIntent? _resolvedIntent;
   void _handleIntentResolved(WorkplaceIntent intent) {
     if (!mounted) return;
-    final origin = intent.intentUrl.scheme == 'data' ? 'null' : intent.intentUrl.origin;
-    _intentOrigin = origin;
-    initMessageHandler(intentId: intent.intentId, intentOrigin: origin);
-    _iframeElement?.src = intent.intentUrl.toString();
+    _resolvedIntent = intent;
+    _applyIntent();
+  }
+
+  void _applyIntent() {
+    final intent = _resolvedIntent;
+    final iframe = _iframeElement;
+    if (intent == null || iframe == null) return;
+    final origin = intent.intentUrl.scheme == 'data' ? 'null' : intent.intentUrl.origin;
+    _intentOrigin = origin;
+    initMessageHandler(intentId: intent.intentId, intentOrigin: origin);
+    iframe.src = intent.intentUrl.toString();
   }
         HtmlIframeWidget(
           key: const ValueKey('drive-intent-webview'),
           onIframeCreated: (iframe) {
             _iframeElement = iframe;
+            _applyIntent();
           },
         ),

Also applies to: 94-96

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workplace/lib/presentation/view/drive_intent_web_view_modal_web.dart` around
lines 49 - 58, Buffer the resolved WorkplaceIntent in _handleIntentResolved
instead of relying on _iframeElement being available, and add a shared helper to
apply the buffered intent URL when the iframe exists. Invoke that helper from
both _handleIntentResolved and onIframeCreated, ensuring the iframe receives the
URL regardless of which callback runs first and preserving the existing
origin/message-handler setup.
♻️ Duplicate comments (1)
workplace/lib/presentation/view/drive_intent_web_view_modal_mobile.dart (1)

40-58: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Intent load can be dropped if the future resolves before onWebViewCreated fires.

The .then callback calls _webViewController?.loadData/loadUrl, but _webViewController is only assigned in onWebViewCreated (line 79). On mobile, InAppWebView creation is asynchronous via platform channels, so the race is more likely than on web. If the controller isn't ready, the load is a no-op and the skeleton stays forever. This was flagged in a prior review and remains unresolved.

🔒 Proposed fix: buffer the resolved intent and load from both paths
 class _DriveIntentWebViewModalState extends State<DriveIntentWebViewModal>
     with DriveIntentMessageHandlerMixin {
   InAppWebViewController? _webViewController;
   String _intentOrigin = 'null';
   bool _showSkeleton = true;
+  WorkplaceIntent? _resolvedIntent;
   widget.intentFuture.then((intent) {
     if (!mounted) return;
-    final isDataUri = intent.intentUrl.scheme == 'data';
-    final origin = isDataUri ? 'null' : intent.intentUrl.origin;
-    _intentOrigin = origin;
-    initMessageHandler(intentId: intent.intentId, intentOrigin: origin);
-    if (isDataUri) {
-      _webViewController?.loadData(
-        data: DriveIntentFakePage.buildHtml(intent.intentId),
-      );
-    } else {
-      _webViewController?.loadUrl(
-        urlRequest: URLRequest(url: WebUri.uri(intent.intentUrl)),
-      );
-    }
+    _resolvedIntent = intent;
+    _applyIntent();
   }).catchError((Object e) {
+  void _applyIntent() {
+    final intent = _resolvedIntent;
+    final controller = _webViewController;
+    if (intent == null || controller == null) return;
+    final isDataUri = intent.intentUrl.scheme == 'data';
+    final origin = isDataUri ? 'null' : intent.intentUrl.origin;
+    _intentOrigin = origin;
+    initMessageHandler(intentId: intent.intentId, intentOrigin: origin);
+    if (isDataUri) {
+      controller.loadData(data: DriveIntentFakePage.buildHtml(intent.intentId));
+    } else {
+      controller.loadUrl(urlRequest: URLRequest(url: WebUri.uri(intent.intentUrl)));
+    }
+  }
+
   `@override`
   Widget build(BuildContext context) {
             onWebViewCreated: (controller) {
               _webViewController = controller;
               controller.addJavaScriptHandler(
                 handlerName: DriveIntentShims.handlerName,
                 callback: (args) => onMessage(
                   raw: args[0] as String,
                   origin: args.length > 1 ? args[1] as String? : null,
                 ),
               );
+              _applyIntent();
             },

Also applies to: 78-79

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workplace/lib/presentation/view/drive_intent_web_view_modal_mobile.dart`
around lines 40 - 58, Buffer the resolved intent when the future completes
before the WebView controller exists, instead of dropping the load. Update the
intent-loading logic and onWebViewCreated callback to retain the resolved intent
and invoke a shared load routine from both paths, ensuring it runs only when
mounted and _webViewController is available while preserving error handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@workplace/lib/presentation/view/drive_intent_web_view_modal_web.dart`:
- Around line 49-58: Buffer the resolved WorkplaceIntent in
_handleIntentResolved instead of relying on _iframeElement being available, and
add a shared helper to apply the buffered intent URL when the iframe exists.
Invoke that helper from both _handleIntentResolved and onIframeCreated, ensuring
the iframe receives the URL regardless of which callback runs first and
preserving the existing origin/message-handler setup.

---

Duplicate comments:
In `@workplace/lib/presentation/view/drive_intent_web_view_modal_mobile.dart`:
- Around line 40-58: Buffer the resolved intent when the future completes before
the WebView controller exists, instead of dropping the load. Update the
intent-loading logic and onWebViewCreated callback to retain the resolved intent
and invoke a shared load routine from both paths, ensuring it runs only when
mounted and _webViewController is available while preserving error handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a2e3b5f6-07a6-46cc-a918-5d79176dd37c

📥 Commits

Reviewing files that changed from the base of the PR and between 7299c5e and 8ac6eeb.

📒 Files selected for processing (5)
  • workplace/lib/presentation/mixin/drive_intent_message_handler_mixin.dart
  • workplace/lib/presentation/mixin/drive_picker_state_mixin.dart
  • workplace/lib/presentation/view/drive_intent_skeleton_loader.dart
  • workplace/lib/presentation/view/drive_intent_web_view_modal_mobile.dart
  • workplace/lib/presentation/view/drive_intent_web_view_modal_web.dart
💤 Files with no reviewable changes (1)
  • workplace/lib/presentation/mixin/drive_picker_state_mixin.dart

@dab246

dab246 commented Jul 10, 2026

Copy link
Copy Markdown
Member
  • The corners aren't rounded yet, it doesn't quite match the design. Please check it again.
Screenshot 2026-07-10 at 15 04 11

Currently:

Screenshot 2026-07-10 at 15 04 38

@Arsnael

Arsnael commented Jul 10, 2026

Copy link
Copy Markdown
Member

Docker image published for this PR: linagora/tmail-web-pr:4699

@tddang-linagora

Copy link
Copy Markdown
Collaborator Author

The corners aren't rounded yet, it doesn't quite match the design. Please check it again.

Radius added

@tddang-linagora
tddang-linagora requested a review from dab246 July 10, 2026 08:14
codescene-delta-analysis[bot]

This comment was marked as outdated.

@tddang-linagora

Copy link
Copy Markdown
Collaborator Author

The search box is overflowing the iframe in mobile responsive design when loading view

Screen.Recording.2026-07-23.at.2.48.34.PM.mov

codescene-delta-analysis[bot]

This comment was marked as outdated.

@tddang-linagora

Copy link
Copy Markdown
Collaborator Author
Screen.Recording.2026-07-23.at.3.13.03.PM.mov

@tddang-linagora
tddang-linagora requested a review from dab246 July 23, 2026 08:14
dab246
dab246 previously approved these changes Jul 23, 2026

@dab246 dab246 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@Arsnael

Arsnael commented Jul 23, 2026

Copy link
Copy Markdown
Member

Docker image published for this PR: linagora/tmail-web-pr:4699

codescene-delta-analysis[bot]

This comment was marked as outdated.

@Arsnael

Arsnael commented Jul 23, 2026

Copy link
Copy Markdown
Member

Docker image published for this PR: linagora/tmail-web-pr:4699

@hoangdat

hoangdat commented Jul 23, 2026

Copy link
Copy Markdown
Member
  • I still prefer the close button should work, agree not close by touch outside in mid-run, but close button should work (especially in mobile)

@hoangdat

hoangdat commented Jul 24, 2026

Copy link
Copy Markdown
Member
  • log error in failed states

codescene-delta-analysis[bot]

This comment was marked as outdated.

@tddang-linagora

Copy link
Copy Markdown
Collaborator Author

log error in failed states

Done

I still prefer the close button should work, agree not close by touch outside in mid-run, but close button should work (especially in mobile)

untitled.webm

@Arsnael

Arsnael commented Jul 24, 2026

Copy link
Copy Markdown
Member

Docker image published for this PR: linagora/tmail-web-pr:4699

@hoangdat

hoangdat commented Jul 24, 2026

Copy link
Copy Markdown
Member
  • in mobile Attach from Drive instead of Browse

@tddang-linagora

Copy link
Copy Markdown
Collaborator Author

in mobile Attach from Drive instead of Browse

Done

@codescene-delta-analysis codescene-delta-analysis Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Our agent can fix these. Install it.

Gates Passed
3 Quality Gates Passed

Quality Gate Profile: The Bare Minimum
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.

@Arsnael

Arsnael commented Jul 24, 2026

Copy link
Copy Markdown
Member

Docker image published for this PR: linagora/tmail-web-pr:4699

@hoangdat
hoangdat merged commit a8eaf6e into master Jul 24, 2026
23 checks passed
@hoangdat
hoangdat deleted the feature/TF-4660-drive-loading branch July 24, 2026 07:43
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.

4 participants