From 9119a69b17d22a2cbecb2254e618c3a31a27d7a2 Mon Sep 17 00:00:00 2001 From: Tommy Chow Date: Sun, 8 Feb 2026 20:34:49 -0800 Subject: [PATCH] fix 7TV emote mutations on computed map, fix search store disposer leak, fix typo Co-Authored-By: Claude Opus 4.6 --- .../chat/stores/chat_assets_store.dart | 14 +++++++++ .../chat/stores/chat_assets_store.g.dart | 29 +++++++++++++++++++ .../channel/chat/stores/chat_store.dart | 6 ++-- lib/screens/home/search/search_store.dart | 5 +++- .../home/stream_list/stream_list_store.dart | 6 ++-- 5 files changed, 52 insertions(+), 8 deletions(-) diff --git a/lib/screens/channel/chat/stores/chat_assets_store.dart b/lib/screens/channel/chat/stores/chat_assets_store.dart index 5c820479..c1ec05f7 100644 --- a/lib/screens/channel/chat/stores/chat_assets_store.dart +++ b/lib/screens/channel/chat/stores/chat_assets_store.dart @@ -443,5 +443,19 @@ abstract class ChatAssetsStoreBase with Store { } } + /// Adds a channel emote (e.g., from a live 7TV event). + /// Mutates the observable `_channelEmoteToObject` so `emoteToObject` recomputes. + @action + void addChannelEmote(Emote emote) { + _channelEmoteToObject[emote.name] = emote; + } + + /// Removes a channel emote by name (e.g., from a live 7TV event). + /// Mutates the observable `_channelEmoteToObject` so `emoteToObject` recomputes. + @action + void removeChannelEmote(String emoteName) { + _channelEmoteToObject.remove(emoteName); + } + void dispose() => _disposeReaction(); } diff --git a/lib/screens/channel/chat/stores/chat_assets_store.g.dart b/lib/screens/channel/chat/stores/chat_assets_store.g.dart index 9067032c..1c1e1b51 100644 --- a/lib/screens/channel/chat/stores/chat_assets_store.g.dart +++ b/lib/screens/channel/chat/stores/chat_assets_store.g.dart @@ -320,6 +320,35 @@ mixin _$ChatAssetsStore on ChatAssetsStoreBase, Store { ); } + late final _$ChatAssetsStoreBaseActionController = ActionController( + name: 'ChatAssetsStoreBase', + context: context, + ); + + @override + void addChannelEmote(Emote emote) { + final _$actionInfo = _$ChatAssetsStoreBaseActionController.startAction( + name: 'ChatAssetsStoreBase.addChannelEmote', + ); + try { + return super.addChannelEmote(emote); + } finally { + _$ChatAssetsStoreBaseActionController.endAction(_$actionInfo); + } + } + + @override + void removeChannelEmote(String emoteName) { + final _$actionInfo = _$ChatAssetsStoreBaseActionController.startAction( + name: 'ChatAssetsStoreBase.removeChannelEmote', + ); + try { + return super.removeChannelEmote(emoteName); + } finally { + _$ChatAssetsStoreBaseActionController.endAction(_$actionInfo); + } + } + @override String toString() { return ''' diff --git a/lib/screens/channel/chat/stores/chat_store.dart b/lib/screens/channel/chat/stores/chat_store.dart index b75422c1..2ea1c347 100644 --- a/lib/screens/channel/chat/stores/chat_store.dart +++ b/lib/screens/channel/chat/stores/chat_store.dart @@ -750,7 +750,7 @@ abstract class ChatStoreBase with Store { final emote = Emote.from7TV(pushedEmote, EmoteType.sevenTVChannel); - assetsStore.emoteToObject[emote.name] = emote; + assetsStore.addChannelEmote(emote); messageBuffer.add( IRCMessage.createNotice( @@ -763,9 +763,7 @@ abstract class ChatStoreBase with Store { if (pulledEmote == null) return; - assetsStore.emoteToObject.removeWhere( - (name, _) => name == pulledEmote.name, - ); + assetsStore.removeChannelEmote(pulledEmote.name); messageBuffer.add( IRCMessage.createNotice( diff --git a/lib/screens/home/search/search_store.dart b/lib/screens/home/search/search_store.dart index 7afe3318..0e3df531 100644 --- a/lib/screens/home/search/search_store.dart +++ b/lib/screens/home/search/search_store.dart @@ -23,6 +23,8 @@ abstract class SearchStoreBase with Store { Timer? _debounceTimer; + ReactionDisposer? _disposeReaction; + @readonly var _searchText = ''; @@ -53,7 +55,7 @@ abstract class SearchStoreBase with Store { ObservableList(); // Create a reaction that will limit the history to 8 entries and update it to local storage automatically. - autorun((_) { + _disposeReaction = autorun((_) { if (_searchHistory.length > 8) _searchHistory.removeLast(); prefs.setStringList('search_history', _searchHistory); }); @@ -136,6 +138,7 @@ abstract class SearchStoreBase with Store { } void dispose() { + _disposeReaction?.call(); _debounceTimer?.cancel(); textEditingController.dispose(); textFieldFocusNode.dispose(); diff --git a/lib/screens/home/stream_list/stream_list_store.dart b/lib/screens/home/stream_list/stream_list_store.dart index c7a6e4ad..94efe2f1 100644 --- a/lib/screens/home/stream_list/stream_list_store.dart +++ b/lib/screens/home/stream_list/stream_list_store.dart @@ -143,7 +143,7 @@ abstract class ListStoreBase with Store { @readonly String? _error; - ReactionDisposer? _pinnedStreamsReactioniDisposer; + ReactionDisposer? _pinnedStreamsReactionDisposer; ReactionDisposer? _offlineChannelsExpansionDisposer; ListStoreBase({ @@ -166,7 +166,7 @@ abstract class ListStoreBase with Store { } if (listType == ListType.followed) { - _pinnedStreamsReactioniDisposer = reaction( + _pinnedStreamsReactionDisposer = reaction( (_) => settingsStore.pinnedChannelIds, (_) => getPinnedStreams(), ); @@ -347,7 +347,7 @@ abstract class ListStoreBase with Store { } void dispose() { - _pinnedStreamsReactioniDisposer?.call(); + _pinnedStreamsReactionDisposer?.call(); _offlineChannelsExpansionDisposer?.call(); scrollController?.dispose();