feat(ui): add snackbar feedback for user actions - #2872
Conversation
Wire user-facing success/error snackbars (via StreamSnackbar) for message actions (copy, pin/unpin, delete, flag, mark unread, mute/unmute user), ending a poll, audio-recording permission denial, and message send/edit failures. Adds the supporting translations across all locales. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Show success/error snackbars for muting/pinning/leaving/deleting a channel and muting/blocking a member, across the channel list swipe action, the channel detail sheet, and the chat/group info screens. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
packages/stream_chat_flutter/test/src/message_widget/stream_message_item_test.dart (2)
55-117: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueGeneralize the long-press helper to remove the duplicated gesture block.
openActionsAndTapPinperforms the long press and then taps "Pin to Conversation". The copy test repeats the same long-press sequence inline. Parameterize the helper with the action label.♻️ Proposed refactor
- Future<void> openActionsAndTapPin(WidgetTester tester) async { + Future<void> openActionsAndTap(WidgetTester tester, String actionLabel) async { final gesture = await tester.startGesture( tester.getCenter(find.byType(StreamMessageItem).first), ); await tester.pump(const Duration(milliseconds: 700)); await gesture.up(); await tester.pumpAndSettle(); - await tester.tap(find.text('Pin to Conversation')); + await tester.tap(find.text(actionLabel)); await tester.pumpAndSettle(); }Then call
openActionsAndTap(tester, 'Pin to Conversation')in the pin tests andopenActionsAndTap(tester, 'Copy Message')in the copy test.🤖 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 `@packages/stream_chat_flutter/test/src/message_widget/stream_message_item_test.dart` around lines 55 - 117, Generalize openActionsAndTapPin into an action-label-based helper, such as openActionsAndTap, while preserving its existing long-press and settling behavior. Pass the requested label to the tap lookup, update both pin tests to use “Pin to Conversation,” and replace the duplicated gesture sequence in the copy test with the helper using “Copy Message.”
79-88: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAdd a call verification to the pin error test.
The success test verifies
channel.pinMessage. The error test asserts only the snackbar text. Add the same verification so a regression that skips the call is detected.💚 Proposed addition
await openActionsAndTapPin(tester); + verify(() => channel.pinMessage(message)).called(1); expect(find.text('Error pinning message'), findsOneWidget);🤖 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 `@packages/stream_chat_flutter/test/src/message_widget/stream_message_item_test.dart` around lines 79 - 88, Add a verification in the “shows an error snackbar when pinning fails” test that confirms channel.pinMessage was called with message after openActionsAndTapPin, matching the success test’s interaction assertion while preserving the existing snackbar expectation.packages/stream_chat_flutter/lib/src/attachment/poll_attachment.dart (1)
12-12: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winImport
poll_attachment.dartthrough the barrel instead of directly fromsrc/.
package:stream_chat_flutter/src/utils/extensions.dartis already exported bypackages/stream_chat_flutter/lib/stream_chat_flutter.dart. Move or add this import through the barrel sopoll_attachment.dartstill follows the package guideline for src files.🤖 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 `@packages/stream_chat_flutter/lib/src/attachment/poll_attachment.dart` at line 12, Update the imports in poll_attachment.dart to reference the public stream_chat_flutter.dart barrel instead of importing src/utils/extensions.dart directly, while preserving access to the same extensions.Source: Learnings
🤖 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
`@packages/stream_chat_flutter/lib/src/message_input/audio_recorder/audio_recorder_controller.dart`:
- Around line 73-75: In the permission-denied branch of the audio recorder
controller, cancel the pending info timer before assigning
RecordStateIdle(message: permissionDeniedMessage). Reuse the existing
timer-management mechanism associated with showInfo/onLongPressCancel so the
callback cannot later clear the denial message.
In `@sample_app/lib/utils/action_feedback.dart`:
- Around line 4-5: Update the documentation for the action feedback helper to
clarify that when messenger is null, action still executes and only success or
error snackbar feedback is omitted.
---
Nitpick comments:
In `@packages/stream_chat_flutter/lib/src/attachment/poll_attachment.dart`:
- Line 12: Update the imports in poll_attachment.dart to reference the public
stream_chat_flutter.dart barrel instead of importing src/utils/extensions.dart
directly, while preserving access to the same extensions.
In
`@packages/stream_chat_flutter/test/src/message_widget/stream_message_item_test.dart`:
- Around line 55-117: Generalize openActionsAndTapPin into an action-label-based
helper, such as openActionsAndTap, while preserving its existing long-press and
settling behavior. Pass the requested label to the tap lookup, update both pin
tests to use “Pin to Conversation,” and replace the duplicated gesture sequence
in the copy test with the helper using “Copy Message.”
- Around line 79-88: Add a verification in the “shows an error snackbar when
pinning fails” test that confirms channel.pinMessage was called with message
after openActionsAndTapPin, matching the success test’s interaction assertion
while preserving the existing snackbar expectation.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: ac428ac5-a463-4658-a20d-62792ef2b401
📒 Files selected for processing (25)
packages/stream_chat_flutter/CHANGELOG.mdpackages/stream_chat_flutter/lib/src/attachment/poll_attachment.dartpackages/stream_chat_flutter/lib/src/localization/translations.dartpackages/stream_chat_flutter/lib/src/message_input/audio_recorder/audio_recorder_controller.dartpackages/stream_chat_flutter/lib/src/message_input/stream_chat_message_input.dartpackages/stream_chat_flutter/lib/src/message_input/stream_message_composer.dartpackages/stream_chat_flutter/lib/src/message_widget/stream_message_item.dartpackages/stream_chat_flutter/test/src/message_widget/stream_message_item_test.dartpackages/stream_chat_localizations/CHANGELOG.mdpackages/stream_chat_localizations/lib/src/stream_chat_localizations_ca.dartpackages/stream_chat_localizations/lib/src/stream_chat_localizations_de.dartpackages/stream_chat_localizations/lib/src/stream_chat_localizations_en.dartpackages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dartpackages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dartpackages/stream_chat_localizations/lib/src/stream_chat_localizations_hi.dartpackages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dartpackages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dartpackages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dartpackages/stream_chat_localizations/lib/src/stream_chat_localizations_no.dartpackages/stream_chat_localizations/lib/src/stream_chat_localizations_pt.dartpackages/stream_chat_localizations/test/translations_test.dartsample_app/lib/pages/chat_info_screen.dartsample_app/lib/pages/group_info_screen.dartsample_app/lib/utils/action_feedback.dartsample_app/lib/widgets/channel_list.dart
- Cancel the pending audio info timer before showing the permission-denied message so a stale timer can't clear it. - Narrow the feedback try/catch to `on Exception` so programming errors surface instead of being masked by a friendly snackbar. - Clarify the `pinned` param convention on the pin snackbar getters. - Fix the null-messenger doc contract (the action still runs). - Add delete success/error tests through the confirmation dialog; generalize the test action helper and verify the call in the pin error test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Convert message action `switch` statement to a switch expression in `StreamMessageItem`. - Replace `if (mounted)` block with a guard clause in `StreamMessageComposer` and extract error message selection into a local variable. - Inline `StreamSnackbar` definitions and method arguments in `PollAttachment` and `StreamChatMessageInput` for cleaner code formatting.
Linear: FLU-682
🎯 Goal
Give users success/error snackbar feedback for actions that were previously fire-and-forget (
.ignore()), silently swallowing both success and failures. RN is the reference here — itsaddNotificationsystem surfaces feedback for ~66 call sites; the Flutter SDK surfaced almost none. The snackbar infrastructure (StreamSnackbar/StreamSnackbarMessenger) already exists instream_core_flutter, so this is a wiring effort.Also incorporates the cross-SDK decision (Slack thread, Aug 2026) to add a "Message copied to clipboard" snackbar — none of the SDKs had one.
🛠 What changed
SDK (
stream_chat_flutter) — success/error snackbars for:_onActionTap)RecordStateIdlebridge)onErrorhandler is suppliedThe messenger is resolved from the page context before any
await, so the snackbar still shows across confirmation dialogs and even if the message is removed by the action (e.g. delete).Localizations (
stream_chat_localizations) — 13 new keys implemented across all 11 locales.Sample app — channel/member action snackbars (mute/pin/leave/delete channel, mute/block member) across the channel-list swipe, detail sheet, and chat/group info screens.
🧪 Testing
flutter analyzeclean;stream_chat_flutterfull non-golden suite 993 passing;stream_chat_localizations23/23 (every locale implements all new keys).📝 Notes / follow-ups
✅ Contributor checklist
stream_chat_flutter,stream_chat_localizations)melos run analyzepassesmelos run formatpasses🤖 Generated with Claude Code
Summary by CodeRabbit