Skip to content

TW-2952 Scroll glitch#2956

Open
tddang-linagora wants to merge 5 commits intomainfrom
TW-2952/Scroll-glitch
Open

TW-2952 Scroll glitch#2956
tddang-linagora wants to merge 5 commits intomainfrom
TW-2952/Scroll-glitch

Conversation

@tddang-linagora
Copy link
Copy Markdown
Collaborator

@tddang-linagora tddang-linagora commented Mar 25, 2026

Ticket

Reproduce

reproduce.mp4

Resolved

resolved.mp4

Summary by CodeRabbit

  • Bug Fixes

    • Messages containing URLs now consistently force a proper line break for improved readability.
  • Refactor

    • Link preview flow simplified: previews render with a more direct lifecycle and updated loading/placeholder behavior.
    • Context-menu state handling simplified so closing the menu no longer toggles extra popup state.
  • Tests

    • Added and updated unit/widget tests for message line breaks and link preview behaviors.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 25, 2026

Walkthrough

The PR adds URL-detection to MessageContentBuilderMixin._checkNeedAddNewLine so a found URL forces a newline; removes the popup-menu ValueNotifier and related methods from LinkifyMixin; refactors GetPreviewUrlMixin to be mixin GetPreviewUrlMixin<T extends StatefulWidget> on State<T> with subscription tracking, disposal, and a new clearPreviewUrlState(); converts TwakeLinkPreview from a StatefulWidget+controller into a StatelessWidget that no longer performs async preview fetching; converts TwakeLinkPreviewItem into a StatefulWidget that fetches previews via GetPreviewUrlMixin with keep-alive and ValueListenable-driven rendering; and updates tests to cover URL line-break logic and preview styling/behavior.

Possibly related PRs

Suggested reviewers

  • dab246
  • hoangdat
  • nqhhdev
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is largely incomplete, providing only ticket reference and video links while omitting critical sections: root cause analysis, solution details, impact description, test recommendations, and pre-merge requirements. Complete the description template by adding root cause explanation, detailed solution outline, impact description, test recommendations, and pre-merge checklist. The video links alone do not provide sufficient context.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'TW-2952 Scroll glitch' directly maps to the ticket and describes the primary issue addressed, but lacks specificity about the technical solution implemented.
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
  • Commit unit tests in branch TW-2952/Scroll-glitch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@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

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

Inline comments:
In
`@lib/widgets/twake_components/twake_preview_link/twake_link_preview_item.dart`:
- Around line 78-88: The outer InkWell in TwakeLinkPreviewItem is duplicating
the tap handler already provided inside LinkPreviewBuilder, causing nested tap
events; remove the outer InkWell wrapper (the InkWell in TwakeLinkPreviewItem
that calls UrlLauncher(context, url: widget.previewLink).launchUrl()) so only
the InkWell inside LinkPreviewBuilder handles taps, or alternatively remove the
InkWell inside LinkPreviewBuilder and keep the outer one—choose one consistent
place for the onTap (reference: TwakeLinkPreviewItem, LinkPreviewBuilder,
UrlLauncher.launchUrl, previewLink).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f4a3b5fc-2020-4718-a1c0-6af58e0dbbf8

📥 Commits

Reviewing files that changed from the base of the PR and between acfa947 and 54ae0dc.

📒 Files selected for processing (7)
  • lib/pages/chat/events/message/message_content_builder_mixin.dart
  • lib/presentation/mixins/linkify_mixin.dart
  • lib/widgets/mixins/get_preview_url_mixin.dart
  • lib/widgets/twake_components/twake_preview_link/twake_link_preview.dart
  • lib/widgets/twake_components/twake_preview_link/twake_link_preview_item.dart
  • test/pages/chat/events/message/message_content_builder_mixin_test.dart
  • test/widget/message/twake_link_preview_item_test.dart
💤 Files with no reviewable changes (1)
  • lib/presentation/mixins/linkify_mixin.dart

@github-actions
Copy link
Copy Markdown
Contributor

This PR has been deployed to https://linagora.github.io/twake-on-matrix/2956

Copy link
Copy Markdown
Contributor

@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

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

Inline comments:
In
`@lib/widgets/twake_components/twake_preview_link/twake_link_preview_item.dart`:
- Around line 32-38: When previewLink becomes null/empty or Uri.tryParse returns
null, the widget currently returns early and leaves the previous preview/state
and any in-flight subscription active; update _getPreviewInfo() to call the
mixin reset routine instead of returning—specifically invoke
clearPreviewUrlState() (which cancels _getPreviewUrlSubscription and sets
getPreviewUrlStateNotifier to Right(GetPreviewUrlInitial())) before returning,
and do the same for the other early-return branch that handles invalid links so
any previous preview is cleared and in-flight requests are cancelled before
exiting; ensure you reference _getPreviewInfo(), previewLink, getPreviewUrl(),
clearPreviewUrlState(), _getPreviewUrlSubscription, getPreviewUrlStateNotifier
and GetPreviewUrlInitial() when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bb53c8eb-d000-437c-aeee-bfc496cb6e13

📥 Commits

Reviewing files that changed from the base of the PR and between 54ae0dc and 9141648.

📒 Files selected for processing (2)
  • lib/widgets/twake_components/twake_preview_link/twake_link_preview_item.dart
  • test/pages/chat/events/message/message_content_builder_mixin_test.dart
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/pages/chat/events/message/message_content_builder_mixin_test.dart

@tddang-linagora
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 25, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@9clg6
Copy link
Copy Markdown
Collaborator

9clg6 commented Mar 25, 2026

LGTM

Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (1)
lib/widgets/twake_components/twake_preview_link/twake_link_preview_item.dart (1)

68-81: Verify color consistency between skeleton and success states.

The container background colors differ between states:

  • Success: primaryContainer / onSurface.withOpacity(0.08)
  • Skeleton: primary[95] / LinagoraStateLayer(...).opacityLayer1

This may cause a visual flicker when transitioning from loading to loaded. Consider using the same colors for both states, or confirm this difference is intentional.

Also applies to: 92-107

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

In `@lib/widgets/twake_components/twake_preview_link/twake_link_preview_item.dart`
around lines 68 - 81, The success and skeleton background colors in
TwakeLinkPreviewItem differ and cause flicker; update the skeleton rendering
(the block around lines 92-107) and the main Container (the block using
TwakeLinkPreviewItem.linkPreviewBodyKey and TwakeLinkPreviewItemStyle) to reuse
a single background color expression based on widget.ownMessage (e.g., compute a
local backgroundColor variable once and use it in both the ShapeDecoration and
the skeleton LinagoraStateLayer/primary[95] usage) so both loading and loaded
states share the same color logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/widgets/mixins/get_preview_url_mixin.dart`:
- Around line 23-28: Dispose currently cancels _getPreviewUrlSubscription after
disposing getPreviewUrlStateNotifier which allows a stream callback
(_handleGetPreviewUrlOnData) to run and touch a disposed ValueNotifier; swap the
order so _getPreviewUrlSubscription?.cancel() is called before
getPreviewUrlStateNotifier.dispose() inside the dispose() override to prevent
callbacks from writing to a disposed notifier (refer to dispose(),
_getPreviewUrlSubscription, getPreviewUrlStateNotifier, and
_handleGetPreviewUrlOnData).

---

Nitpick comments:
In
`@lib/widgets/twake_components/twake_preview_link/twake_link_preview_item.dart`:
- Around line 68-81: The success and skeleton background colors in
TwakeLinkPreviewItem differ and cause flicker; update the skeleton rendering
(the block around lines 92-107) and the main Container (the block using
TwakeLinkPreviewItem.linkPreviewBodyKey and TwakeLinkPreviewItemStyle) to reuse
a single background color expression based on widget.ownMessage (e.g., compute a
local backgroundColor variable once and use it in both the ShapeDecoration and
the skeleton LinagoraStateLayer/primary[95] usage) so both loading and loaded
states share the same color logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0b33795e-ca2f-42f8-bfe5-ab6f343c7db7

📥 Commits

Reviewing files that changed from the base of the PR and between 9141648 and 58357f1.

📒 Files selected for processing (2)
  • lib/widgets/mixins/get_preview_url_mixin.dart
  • lib/widgets/twake_components/twake_preview_link/twake_link_preview_item.dart

Comment on lines +23 to +28
@override
void dispose() {
getPreviewUrlStateNotifier.dispose();
_getPreviewUrlSubscription?.cancel();
super.dispose();
}
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.

⚠️ Potential issue | 🟡 Minor

Cancel subscription before disposing the notifier.

There's a subtle timing issue: if a stream event is delivered between disposing the notifier (line 25) and canceling the subscription (line 26), _handleGetPreviewUrlOnData would attempt to set a value on a disposed ValueNotifier, potentially throwing an error.

🔧 Proposed fix: Swap the order
 `@override`
 void dispose() {
-  getPreviewUrlStateNotifier.dispose();
   _getPreviewUrlSubscription?.cancel();
+  getPreviewUrlStateNotifier.dispose();
   super.dispose();
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@override
void dispose() {
getPreviewUrlStateNotifier.dispose();
_getPreviewUrlSubscription?.cancel();
super.dispose();
}
`@override`
void dispose() {
_getPreviewUrlSubscription?.cancel();
getPreviewUrlStateNotifier.dispose();
super.dispose();
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/widgets/mixins/get_preview_url_mixin.dart` around lines 23 - 28, Dispose
currently cancels _getPreviewUrlSubscription after disposing
getPreviewUrlStateNotifier which allows a stream callback
(_handleGetPreviewUrlOnData) to run and touch a disposed ValueNotifier; swap the
order so _getPreviewUrlSubscription?.cancel() is called before
getPreviewUrlStateNotifier.dispose() inside the dispose() override to prevent
callbacks from writing to a disposed notifier (refer to dispose(),
_getPreviewUrlSubscription, getPreviewUrlStateNotifier, and
_handleGetPreviewUrlOnData).

Copy link
Copy Markdown
Member

@nqhhdev nqhhdev left a comment

Choose a reason for hiding this comment

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

LGTM

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.

3 participants