From 4a9678bb152bb89f965a0f16f9bad938bb3eb374 Mon Sep 17 00:00:00 2001 From: Tommy Chow Date: Sun, 4 Jan 2026 20:36:53 -0500 Subject: [PATCH] fix autocomplete bar blocking bottom of chat --- lib/screens/channel/chat/chat.dart | 6 +-- .../channel/chat/stores/chat_store.dart | 47 +++++++++++++++++++ .../channel/chat/stores/chat_store.g.dart | 28 ++++++++++- .../channel/chat/widgets/chat_bottom_bar.dart | 27 +---------- 4 files changed, 79 insertions(+), 29 deletions(-) diff --git a/lib/screens/channel/chat/chat.dart b/lib/screens/channel/chat/chat.dart index 944ba3d7..18ec8e3e 100644 --- a/lib/screens/channel/chat/chat.dart +++ b/lib/screens/channel/chat/chat.dart @@ -58,7 +58,7 @@ class Chat extends StatelessWidget { padding: EdgeInsets.only( top: MediaQuery.of(context).padding.top, bottom: - 68 + + chatStore.bottomBarHeight + (chatStore.assetsStore.showEmoteMenu ? 0 : MediaQuery.of(context).padding.bottom), @@ -70,7 +70,7 @@ class Chat extends StatelessWidget { padding: (listPadding ?? EdgeInsets.zero).add( EdgeInsets.only( bottom: - 68 + + chatStore.bottomBarHeight + (chatStore.assetsStore.showEmoteMenu ? 0 : MediaQuery.of( @@ -126,7 +126,7 @@ class Chat extends StatelessWidget { top: 4, right: 4, bottom: - 68 + + chatStore.bottomBarHeight + (chatStore.assetsStore.showEmoteMenu ? 0 : MediaQuery.of(context).padding.bottom), diff --git a/lib/screens/channel/chat/stores/chat_store.dart b/lib/screens/channel/chat/stores/chat_store.dart index b46e6eb2..da6268fb 100644 --- a/lib/screens/channel/chat/stores/chat_store.dart +++ b/lib/screens/channel/chat/stores/chat_store.dart @@ -36,6 +36,12 @@ abstract class ChatStoreBase with Store { 'ROOMSTATE', }; + /// Base height of the bottom bar (input field area). + static const _baseBottomBarHeight = 68.0; + + /// Height of the autocomplete bar (SizedBox + Divider). + static const _autocompleteHeight = 51.0; + /// Checks if IRC data contains a command that should bypass the chat delay. /// Returns true if any message in the data contains a bypass command. bool _shouldBypassDelay(String data) { @@ -203,6 +209,47 @@ abstract class ChatStoreBase with Store { /// Public getter for whether the chat is in shared chat mode. bool get isInSharedChatMode => _isInSharedChatMode; + /// Emotes matching the current autocomplete search term. + @computed + List get matchingEmotes { + if (!_showEmoteAutocomplete) return const []; + final searchTerm = _inputText.split(' ').last.toLowerCase(); + if (searchTerm.isEmpty) return const []; + + return [ + ...assetsStore.userEmoteToObject.values, + ...assetsStore.bttvEmotes, + ...assetsStore.ffzEmotes, + ...assetsStore.sevenTVEmotes, + ].where((emote) => emote.name.toLowerCase().contains(searchTerm)).toList(); + } + + /// Chatters matching the current mention autocomplete search term. + @computed + List get matchingChatters { + if (!_showMentionAutocomplete) return const []; + final searchTerm = + _inputText.split(' ').last.replaceFirst('@', '').toLowerCase(); + return chatDetailsStore.chatUsers + .where((chatter) => chatter.contains(searchTerm)) + .toList(); + } + + /// Current bottom bar height based on visible overlays. + /// Used to dynamically adjust chat message list padding. + @computed + double get bottomBarHeight { + var height = _baseBottomBarHeight; + + if (settings.autocomplete && + ((_showEmoteAutocomplete && matchingEmotes.isNotEmpty) || + (_showMentionAutocomplete && matchingChatters.isNotEmpty))) { + height += _autocompleteHeight; + } + + return height; + } + ChatStoreBase({ required this.twitchApi, required this.auth, diff --git a/lib/screens/channel/chat/stores/chat_store.g.dart b/lib/screens/channel/chat/stores/chat_store.g.dart index 9292169e..7e956e78 100644 --- a/lib/screens/channel/chat/stores/chat_store.g.dart +++ b/lib/screens/channel/chat/stores/chat_store.g.dart @@ -17,6 +17,29 @@ mixin _$ChatStore on ChatStoreBase, Store { () => super.renderMessages, name: 'ChatStoreBase.renderMessages', )).value; + Computed>? _$matchingEmotesComputed; + + @override + List get matchingEmotes => + (_$matchingEmotesComputed ??= Computed>( + () => super.matchingEmotes, + name: 'ChatStoreBase.matchingEmotes', + )).value; + Computed>? _$matchingChattersComputed; + + @override + List get matchingChatters => + (_$matchingChattersComputed ??= Computed>( + () => super.matchingChatters, + name: 'ChatStoreBase.matchingChatters', + )).value; + Computed? _$bottomBarHeightComputed; + + @override + double get bottomBarHeight => (_$bottomBarHeightComputed ??= Computed( + () => super.bottomBarHeight, + name: 'ChatStoreBase.bottomBarHeight', + )).value; late final _$timeRemainingAtom = Atom( name: 'ChatStoreBase.timeRemaining', @@ -489,7 +512,10 @@ mixin _$ChatStore on ChatStoreBase, Store { timeRemaining: ${timeRemaining}, expandChat: ${expandChat}, replyingToMessage: ${replyingToMessage}, -renderMessages: ${renderMessages} +renderMessages: ${renderMessages}, +matchingEmotes: ${matchingEmotes}, +matchingChatters: ${matchingChatters}, +bottomBarHeight: ${bottomBarHeight} '''; } } diff --git a/lib/screens/channel/chat/widgets/chat_bottom_bar.dart b/lib/screens/channel/chat/widgets/chat_bottom_bar.dart index e74ccd02..dd0c9304 100644 --- a/lib/screens/channel/chat/widgets/chat_bottom_bar.dart +++ b/lib/screens/channel/chat/widgets/chat_bottom_bar.dart @@ -57,31 +57,8 @@ class ChatBottomBar extends StatelessWidget { return Observer( builder: (context) { - final matchingEmotes = - [ - ...chatStore.assetsStore.userEmoteToObject.values, - ...chatStore.assetsStore.bttvEmotes, - ...chatStore.assetsStore.ffzEmotes, - ...chatStore.assetsStore.sevenTVEmotes, - ] - .where( - (emote) => emote.name.toLowerCase().contains( - chatStore.inputText.split(' ').last.toLowerCase(), - ), - ) - .toList(); - - final matchingChatters = chatStore.chatDetailsStore.chatUsers - .where( - (chatter) => chatter.contains( - chatStore.inputText - .split(' ') - .last - .replaceFirst('@', '') - .toLowerCase(), - ), - ) - .toList(); + final matchingEmotes = chatStore.matchingEmotes; + final matchingChatters = chatStore.matchingChatters; final isFullscreenOverlay = chatStore.settings.fullScreen && context.isLandscape;