From 386701dc45742df789bf609774463bd91eda67cd Mon Sep 17 00:00:00 2001 From: Castropy Date: Wed, 4 Feb 2026 19:50:55 -0400 Subject: [PATCH] details in pr --- .../Flutter/ephemeral/flutter_lldb_helper.py | 32 +++++++++++++++++ .../ios/Flutter/ephemeral/flutter_lldbinit | 5 +++ .../Flutter/ephemeral/flutter_lldb_helper.py | 32 +++++++++++++++++ .../ios/Flutter/ephemeral/flutter_lldbinit | 5 +++ .../message_list_view/message_list_view.dart | 8 +++-- .../unread_indicator_button.dart | 11 ++++-- .../Flutter/ephemeral/flutter_lldb_helper.py | 32 +++++++++++++++++ .../ios/Flutter/ephemeral/flutter_lldbinit | 5 +++ .../Flutter/ephemeral/flutter_lldb_helper.py | 32 +++++++++++++++++ .../ios/Flutter/ephemeral/flutter_lldbinit | 5 +++ .../Flutter/ephemeral/flutter_lldb_helper.py | 32 +++++++++++++++++ .../ios/Flutter/ephemeral/flutter_lldbinit | 5 +++ sample_app/lib/app.dart | 35 ++++++++++++------- 13 files changed, 222 insertions(+), 17 deletions(-) create mode 100644 packages/stream_chat/example/ios/Flutter/ephemeral/flutter_lldb_helper.py create mode 100644 packages/stream_chat/example/ios/Flutter/ephemeral/flutter_lldbinit create mode 100644 packages/stream_chat_flutter/example/ios/Flutter/ephemeral/flutter_lldb_helper.py create mode 100644 packages/stream_chat_flutter/example/ios/Flutter/ephemeral/flutter_lldbinit create mode 100644 packages/stream_chat_flutter_core/example/ios/Flutter/ephemeral/flutter_lldb_helper.py create mode 100644 packages/stream_chat_flutter_core/example/ios/Flutter/ephemeral/flutter_lldbinit create mode 100644 packages/stream_chat_persistence/example/ios/Flutter/ephemeral/flutter_lldb_helper.py create mode 100644 packages/stream_chat_persistence/example/ios/Flutter/ephemeral/flutter_lldbinit create mode 100644 sample_app/ios/Flutter/ephemeral/flutter_lldb_helper.py create mode 100644 sample_app/ios/Flutter/ephemeral/flutter_lldbinit diff --git a/packages/stream_chat/example/ios/Flutter/ephemeral/flutter_lldb_helper.py b/packages/stream_chat/example/ios/Flutter/ephemeral/flutter_lldb_helper.py new file mode 100644 index 0000000000..a88caf99df --- /dev/null +++ b/packages/stream_chat/example/ios/Flutter/ephemeral/flutter_lldb_helper.py @@ -0,0 +1,32 @@ +# +# Generated file, do not edit. +# + +import lldb + +def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict): + """Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages.""" + base = frame.register["x0"].GetValueAsAddress() + page_len = frame.register["x1"].GetValueAsUnsigned() + + # Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the + # first page to see if handled it correctly. This makes diagnosing + # misconfiguration (e.g. missing breakpoint) easier. + data = bytearray(page_len) + data[0:8] = b'IHELPED!' + + error = lldb.SBError() + frame.GetThread().GetProcess().WriteMemory(base, data, error) + if not error.Success(): + print(f'Failed to write into {base}[+{page_len}]', error) + return + +def __lldb_init_module(debugger: lldb.SBDebugger, _): + target = debugger.GetDummyTarget() + # Caveat: must use BreakpointCreateByRegEx here and not + # BreakpointCreateByName. For some reasons callback function does not + # get carried over from dummy target for the later. + bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$") + bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__)) + bp.SetAutoContinue(True) + print("-- LLDB integration loaded --") diff --git a/packages/stream_chat/example/ios/Flutter/ephemeral/flutter_lldbinit b/packages/stream_chat/example/ios/Flutter/ephemeral/flutter_lldbinit new file mode 100644 index 0000000000..e3ba6fbedc --- /dev/null +++ b/packages/stream_chat/example/ios/Flutter/ephemeral/flutter_lldbinit @@ -0,0 +1,5 @@ +# +# Generated file, do not edit. +# + +command script import --relative-to-command-file flutter_lldb_helper.py diff --git a/packages/stream_chat_flutter/example/ios/Flutter/ephemeral/flutter_lldb_helper.py b/packages/stream_chat_flutter/example/ios/Flutter/ephemeral/flutter_lldb_helper.py new file mode 100644 index 0000000000..a88caf99df --- /dev/null +++ b/packages/stream_chat_flutter/example/ios/Flutter/ephemeral/flutter_lldb_helper.py @@ -0,0 +1,32 @@ +# +# Generated file, do not edit. +# + +import lldb + +def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict): + """Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages.""" + base = frame.register["x0"].GetValueAsAddress() + page_len = frame.register["x1"].GetValueAsUnsigned() + + # Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the + # first page to see if handled it correctly. This makes diagnosing + # misconfiguration (e.g. missing breakpoint) easier. + data = bytearray(page_len) + data[0:8] = b'IHELPED!' + + error = lldb.SBError() + frame.GetThread().GetProcess().WriteMemory(base, data, error) + if not error.Success(): + print(f'Failed to write into {base}[+{page_len}]', error) + return + +def __lldb_init_module(debugger: lldb.SBDebugger, _): + target = debugger.GetDummyTarget() + # Caveat: must use BreakpointCreateByRegEx here and not + # BreakpointCreateByName. For some reasons callback function does not + # get carried over from dummy target for the later. + bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$") + bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__)) + bp.SetAutoContinue(True) + print("-- LLDB integration loaded --") diff --git a/packages/stream_chat_flutter/example/ios/Flutter/ephemeral/flutter_lldbinit b/packages/stream_chat_flutter/example/ios/Flutter/ephemeral/flutter_lldbinit new file mode 100644 index 0000000000..e3ba6fbedc --- /dev/null +++ b/packages/stream_chat_flutter/example/ios/Flutter/ephemeral/flutter_lldbinit @@ -0,0 +1,5 @@ +# +# Generated file, do not edit. +# + +command script import --relative-to-command-file flutter_lldb_helper.py diff --git a/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart index 24fcaa3725..c099e36ed4 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart @@ -1,4 +1,4 @@ -// ignore_for_file: lines_longer_than_80_chars +// ignore_for_file: lines_longer_than_80_chars import 'dart:async'; import 'dart:math'; @@ -538,7 +538,11 @@ class _StreamMessageListViewState extends State { final newMessagesListLength = messages.length; if (_messageListLength != null) { - if (_bottomPaginationActive || (_inBetweenList && _upToDate)) { +// MODIFICATION: Channel status stability check added. + // The scrolling is adjusted if the SDK confirms that the data is synchronized. (isUpToDate). + final isChannelStable = streamChannel?.channel.state?.isUpToDate ?? false; + + if (isChannelStable && (_bottomPaginationActive || (_inBetweenList && _upToDate))) { if (_itemPositionListener.itemPositions.value.isNotEmpty) { final first = _itemPositionListener.itemPositions.value.first; final diff = newMessagesListLength - _messageListLength!; diff --git a/packages/stream_chat_flutter/lib/src/message_list_view/unread_indicator_button.dart b/packages/stream_chat_flutter/lib/src/message_list_view/unread_indicator_button.dart index 6c66c118ce..b5d09b6e34 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view/unread_indicator_button.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view/unread_indicator_button.dart @@ -5,7 +5,7 @@ import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/utils/extensions.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:svg_icon_widget/svg_icon_widget.dart'; - + /// Function signature for handling the dismiss action on the unread indicator. typedef OnUnreadIndicatorDismissTap = Future Function(); @@ -60,10 +60,16 @@ class UnreadIndicatorButton extends StatelessWidget { final channel = StreamChannel.of(context).channel; if (channel.state == null) return const Empty(); - return BetterStreamBuilder( + return BetterStreamBuilder( initialData: channel.state!.currentUserRead, stream: channel.state!.currentUserReadStream, builder: (context, currentUserRead) { + // CHANGE: Unnecessary comparison with null is removed. + // Only channel.state is checked for security purposes. + if (channel.state == null) { + return const Empty(); + } + final unreadCount = currentUserRead.unreadMessages; if (unreadCount <= 0) return const Empty(); @@ -87,6 +93,7 @@ class UnreadIndicatorButton extends StatelessWidget { child: Padding( padding: const EdgeInsets.fromLTRB(16, 2, 8, 2), child: Row( + mainAxisSize: MainAxisSize.min, children: [ Text( context.translations.unreadCountIndicatorLabel( diff --git a/packages/stream_chat_flutter_core/example/ios/Flutter/ephemeral/flutter_lldb_helper.py b/packages/stream_chat_flutter_core/example/ios/Flutter/ephemeral/flutter_lldb_helper.py new file mode 100644 index 0000000000..a88caf99df --- /dev/null +++ b/packages/stream_chat_flutter_core/example/ios/Flutter/ephemeral/flutter_lldb_helper.py @@ -0,0 +1,32 @@ +# +# Generated file, do not edit. +# + +import lldb + +def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict): + """Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages.""" + base = frame.register["x0"].GetValueAsAddress() + page_len = frame.register["x1"].GetValueAsUnsigned() + + # Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the + # first page to see if handled it correctly. This makes diagnosing + # misconfiguration (e.g. missing breakpoint) easier. + data = bytearray(page_len) + data[0:8] = b'IHELPED!' + + error = lldb.SBError() + frame.GetThread().GetProcess().WriteMemory(base, data, error) + if not error.Success(): + print(f'Failed to write into {base}[+{page_len}]', error) + return + +def __lldb_init_module(debugger: lldb.SBDebugger, _): + target = debugger.GetDummyTarget() + # Caveat: must use BreakpointCreateByRegEx here and not + # BreakpointCreateByName. For some reasons callback function does not + # get carried over from dummy target for the later. + bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$") + bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__)) + bp.SetAutoContinue(True) + print("-- LLDB integration loaded --") diff --git a/packages/stream_chat_flutter_core/example/ios/Flutter/ephemeral/flutter_lldbinit b/packages/stream_chat_flutter_core/example/ios/Flutter/ephemeral/flutter_lldbinit new file mode 100644 index 0000000000..e3ba6fbedc --- /dev/null +++ b/packages/stream_chat_flutter_core/example/ios/Flutter/ephemeral/flutter_lldbinit @@ -0,0 +1,5 @@ +# +# Generated file, do not edit. +# + +command script import --relative-to-command-file flutter_lldb_helper.py diff --git a/packages/stream_chat_persistence/example/ios/Flutter/ephemeral/flutter_lldb_helper.py b/packages/stream_chat_persistence/example/ios/Flutter/ephemeral/flutter_lldb_helper.py new file mode 100644 index 0000000000..a88caf99df --- /dev/null +++ b/packages/stream_chat_persistence/example/ios/Flutter/ephemeral/flutter_lldb_helper.py @@ -0,0 +1,32 @@ +# +# Generated file, do not edit. +# + +import lldb + +def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict): + """Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages.""" + base = frame.register["x0"].GetValueAsAddress() + page_len = frame.register["x1"].GetValueAsUnsigned() + + # Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the + # first page to see if handled it correctly. This makes diagnosing + # misconfiguration (e.g. missing breakpoint) easier. + data = bytearray(page_len) + data[0:8] = b'IHELPED!' + + error = lldb.SBError() + frame.GetThread().GetProcess().WriteMemory(base, data, error) + if not error.Success(): + print(f'Failed to write into {base}[+{page_len}]', error) + return + +def __lldb_init_module(debugger: lldb.SBDebugger, _): + target = debugger.GetDummyTarget() + # Caveat: must use BreakpointCreateByRegEx here and not + # BreakpointCreateByName. For some reasons callback function does not + # get carried over from dummy target for the later. + bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$") + bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__)) + bp.SetAutoContinue(True) + print("-- LLDB integration loaded --") diff --git a/packages/stream_chat_persistence/example/ios/Flutter/ephemeral/flutter_lldbinit b/packages/stream_chat_persistence/example/ios/Flutter/ephemeral/flutter_lldbinit new file mode 100644 index 0000000000..e3ba6fbedc --- /dev/null +++ b/packages/stream_chat_persistence/example/ios/Flutter/ephemeral/flutter_lldbinit @@ -0,0 +1,5 @@ +# +# Generated file, do not edit. +# + +command script import --relative-to-command-file flutter_lldb_helper.py diff --git a/sample_app/ios/Flutter/ephemeral/flutter_lldb_helper.py b/sample_app/ios/Flutter/ephemeral/flutter_lldb_helper.py new file mode 100644 index 0000000000..a88caf99df --- /dev/null +++ b/sample_app/ios/Flutter/ephemeral/flutter_lldb_helper.py @@ -0,0 +1,32 @@ +# +# Generated file, do not edit. +# + +import lldb + +def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict): + """Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages.""" + base = frame.register["x0"].GetValueAsAddress() + page_len = frame.register["x1"].GetValueAsUnsigned() + + # Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the + # first page to see if handled it correctly. This makes diagnosing + # misconfiguration (e.g. missing breakpoint) easier. + data = bytearray(page_len) + data[0:8] = b'IHELPED!' + + error = lldb.SBError() + frame.GetThread().GetProcess().WriteMemory(base, data, error) + if not error.Success(): + print(f'Failed to write into {base}[+{page_len}]', error) + return + +def __lldb_init_module(debugger: lldb.SBDebugger, _): + target = debugger.GetDummyTarget() + # Caveat: must use BreakpointCreateByRegEx here and not + # BreakpointCreateByName. For some reasons callback function does not + # get carried over from dummy target for the later. + bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$") + bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__)) + bp.SetAutoContinue(True) + print("-- LLDB integration loaded --") diff --git a/sample_app/ios/Flutter/ephemeral/flutter_lldbinit b/sample_app/ios/Flutter/ephemeral/flutter_lldbinit new file mode 100644 index 0000000000..e3ba6fbedc --- /dev/null +++ b/sample_app/ios/Flutter/ephemeral/flutter_lldbinit @@ -0,0 +1,5 @@ +# +# Generated file, do not edit. +# + +command script import --relative-to-command-file flutter_lldb_helper.py diff --git a/sample_app/lib/app.dart b/sample_app/lib/app.dart index 7fc8ba93b8..e22416ad3e 100644 --- a/sample_app/lib/app.dart +++ b/sample_app/lib/app.dart @@ -358,28 +358,37 @@ class _StreamChatSampleAppState extends State } /// Constructs callback for notification click event. - OnRemoteMessage _onFirebaseMessageOpenedApp(StreamChatClient client) { +OnRemoteMessage _onFirebaseMessageOpenedApp(StreamChatClient client) { return (message) async { debugPrint('[onMessageOpenedApp] #firebase; message: ${message.toMap()}'); - // This callback is getting invoked when the user clicks - // on the notification in case if notification was shown by OS. + final channelCid = (message.data['cid'] as String?) ?? ''; + if (channelCid.isEmpty) return; + final parts = channelCid.split(':'); final channelType = parts[0]; final channelId = parts[1]; + + // Assigns the channel if it exists in the state, + // otherwise creates a new channel instance. var channel = client.state.channels[channelCid]; - if (channel == null) { - channel = client.channel( - channelType, - id: channelId, - ); + channel ??= client.channel(channelType, id: channelId); + + // Synchronizes the channel state before navigating to the UI. + // This ensures that unread counts and message lists are accurate + // upon the first frame of the ChannelPage. + try { await channel.watch(); + } catch (e) { + debugPrint('Error synchronizing channel: $e'); + } + + if (_navigatorKey.currentContext != null) { + GoRouter.of(_navigatorKey.currentContext!).pushNamed( + Routes.CHANNEL_PAGE.name, + pathParameters: Routes.CHANNEL_PAGE.params(channel), + ); } - // Navigates to Channel page, which is associated with the notification. - GoRouter.of(_navigatorKey.currentContext!).pushNamed( - Routes.CHANNEL_PAGE.name, - pathParameters: Routes.CHANNEL_PAGE.params(channel), - ); }; }