format
This commit is contained in:
parent
45306dc387
commit
3fe40e8273
2 changed files with 279 additions and 284 deletions
|
|
@ -77,270 +77,265 @@ class ChatBottomBar extends StatelessWidget {
|
|||
final isFullscreenOverlay = chatStore.settings.fullScreen;
|
||||
|
||||
final bottomBarContent = Column(
|
||||
children: [
|
||||
AnimatedScrollBorder(
|
||||
scrollController: chatStore.scrollController,
|
||||
),
|
||||
if (chatStore.replyingToMessage != null) ...[
|
||||
const Divider(),
|
||||
ListTile(
|
||||
contentPadding: const EdgeInsets.only(left: 16),
|
||||
leading: const Icon(Icons.reply),
|
||||
title: Tooltip(
|
||||
message: chatStore.replyingToMessage!.message,
|
||||
preferBelow: false,
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: chatStore.replyingToMessage!.generateSpan(
|
||||
context,
|
||||
assetsStore: chatStore.assetsStore,
|
||||
emoteScale: chatStore.settings.emoteScale,
|
||||
badgeScale: chatStore.settings.badgeScale,
|
||||
launchExternal: chatStore.settings.launchUrlExternal,
|
||||
timestamp: chatStore.settings.timestampType,
|
||||
currentChannelId: chatStore.channelId,
|
||||
),
|
||||
),
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: context.defaultTextStyle.copyWith(
|
||||
fontSize: chatStore.settings.fontSize,
|
||||
children: [
|
||||
AnimatedScrollBorder(scrollController: chatStore.scrollController),
|
||||
if (chatStore.replyingToMessage != null) ...[
|
||||
const Divider(),
|
||||
ListTile(
|
||||
contentPadding: const EdgeInsets.only(left: 16),
|
||||
leading: const Icon(Icons.reply),
|
||||
title: Tooltip(
|
||||
message: chatStore.replyingToMessage!.message,
|
||||
preferBelow: false,
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: chatStore.replyingToMessage!.generateSpan(
|
||||
context,
|
||||
assetsStore: chatStore.assetsStore,
|
||||
emoteScale: chatStore.settings.emoteScale,
|
||||
badgeScale: chatStore.settings.badgeScale,
|
||||
launchExternal: chatStore.settings.launchUrlExternal,
|
||||
timestamp: chatStore.settings.timestampType,
|
||||
currentChannelId: chatStore.channelId,
|
||||
),
|
||||
),
|
||||
),
|
||||
trailing: IconButton(
|
||||
tooltip: 'Cancel reply',
|
||||
onPressed: () {
|
||||
chatStore.replyingToMessage = null;
|
||||
},
|
||||
icon: const Icon(Icons.close),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (chatStore.settings.autocomplete &&
|
||||
chatStore.showEmoteAutocomplete &&
|
||||
matchingEmotes.isNotEmpty) ...[
|
||||
const Divider(),
|
||||
SizedBox(
|
||||
height: 50,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.all(4),
|
||||
itemCount: matchingEmotes.length,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, index) => InkWell(
|
||||
onTap: () => chatStore.addEmote(
|
||||
matchingEmotes[index],
|
||||
autocompleteMode: true,
|
||||
),
|
||||
onLongPress: () {
|
||||
HapticFeedback.lightImpact();
|
||||
|
||||
IRCMessage.showEmoteDetailsBottomSheet(
|
||||
context,
|
||||
emote: matchingEmotes[index],
|
||||
launchExternal: chatStore.settings.launchUrlExternal,
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Center(
|
||||
child: FrostyCachedNetworkImage(
|
||||
imageUrl: matchingEmotes[index].url,
|
||||
useFade: false,
|
||||
height:
|
||||
matchingEmotes[index].height?.toDouble() ??
|
||||
defaultEmoteSize,
|
||||
width: matchingEmotes[index].width?.toDouble(),
|
||||
),
|
||||
),
|
||||
),
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: context.defaultTextStyle.copyWith(
|
||||
fontSize: chatStore.settings.fontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (chatStore.settings.autocomplete &&
|
||||
chatStore.showMentionAutocomplete &&
|
||||
matchingChatters.isNotEmpty) ...[
|
||||
const Divider(),
|
||||
SizedBox(
|
||||
height: 50,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.all(4),
|
||||
itemCount: matchingChatters.length,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, index) => TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
),
|
||||
onPressed: () {
|
||||
final split = chatStore.textController.text.split(' ')
|
||||
..removeLast()
|
||||
..add('@${matchingChatters[index]} ');
|
||||
|
||||
chatStore.textController.text = split.join(' ');
|
||||
chatStore.textController.selection =
|
||||
TextSelection.fromPosition(
|
||||
TextPosition(
|
||||
offset: chatStore.textController.text.length,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Text(matchingChatters[index]),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 12, top: 8, bottom: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
if (!chatStore.expandChat &&
|
||||
chatStore.settings.chatWidth < 0.3 &&
|
||||
chatStore.settings.showVideo &&
|
||||
context.isLandscape)
|
||||
IconButton(
|
||||
tooltip: 'Enter a message',
|
||||
onPressed: () {
|
||||
chatStore.expandChat = true;
|
||||
chatStore.safeRequestFocus();
|
||||
},
|
||||
icon: const Icon(Icons.edit),
|
||||
)
|
||||
else
|
||||
Expanded(
|
||||
child: Observer(
|
||||
builder: (context) {
|
||||
final isDisabledDueToDelay =
|
||||
chatStore.settings.showVideo &&
|
||||
chatStore.settings.chatDelay > 0;
|
||||
final isDisabled =
|
||||
!chatStore.auth.isLoggedIn ||
|
||||
chatStore.isSendingMessage ||
|
||||
isDisabledDueToDelay;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
// Show notification when trying to tap disabled input due to chat delay
|
||||
if (isDisabledDueToDelay) {
|
||||
chatStore.updateNotification(
|
||||
'Chatting is disabled due to message delay',
|
||||
);
|
||||
}
|
||||
},
|
||||
child: TextField(
|
||||
textInputAction: TextInputAction.send,
|
||||
focusNode: chatStore.textFieldFocusNode,
|
||||
minLines: 1,
|
||||
maxLines: 3,
|
||||
// Disable text field when sending message, when not logged in, or when chat delay is active
|
||||
enabled: !isDisabled,
|
||||
decoration: InputDecoration(
|
||||
prefixIcon:
|
||||
chatStore.settings.emoteMenuButtonOnLeft
|
||||
? emoteMenuButton
|
||||
: null,
|
||||
suffixIcon: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 8,
|
||||
children: [
|
||||
if (!chatStore
|
||||
.settings
|
||||
.emoteMenuButtonOnLeft &&
|
||||
emoteMenuButton != null)
|
||||
emoteMenuButton,
|
||||
],
|
||||
),
|
||||
hintMaxLines: 1,
|
||||
hintText: chatStore.auth.isLoggedIn
|
||||
? chatStore.isSendingMessage
|
||||
? 'Sending...'
|
||||
: chatStore.replyingToMessage !=
|
||||
null
|
||||
? 'Reply'
|
||||
: 'Chat'
|
||||
: 'Log in',
|
||||
),
|
||||
controller: chatStore.textController,
|
||||
onSubmitted: chatStore.sendMessage,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (chatStore.settings.showVideo &&
|
||||
chatStore.settings.chatDelay > 0)
|
||||
Tooltip(
|
||||
message:
|
||||
'Message delay: ${chatStore.settings.chatDelay.toInt()} seconds${chatStore.settings.autoSyncChatDelay ? ' (auto-synced)' : ''}',
|
||||
preferBelow: false,
|
||||
triggerMode: TooltipTriggerMode.tap,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(left: 12),
|
||||
width: 38,
|
||||
child: Text(
|
||||
'${chatStore.settings.chatDelay.toInt()}s',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
fontFeatures: [
|
||||
const FontFeature.tabularFigures(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (chatStore.showSendButton &&
|
||||
(chatStore.settings.chatWidth >= 0.3 ||
|
||||
chatStore.expandChat ||
|
||||
context.isPortrait))
|
||||
Observer(
|
||||
builder: (context) {
|
||||
return IconButton(
|
||||
tooltip: chatStore.isSendingMessage
|
||||
? 'Sending...'
|
||||
: 'Send',
|
||||
icon: chatStore.isSendingMessage
|
||||
? const SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
)
|
||||
: const Icon(Icons.send_rounded),
|
||||
onPressed:
|
||||
chatStore.auth.isLoggedIn &&
|
||||
!chatStore.isSendingMessage &&
|
||||
!(chatStore.settings.showVideo &&
|
||||
chatStore.settings.chatDelay > 0)
|
||||
? () => chatStore.sendMessage(
|
||||
chatStore.textController.text,
|
||||
)
|
||||
: null,
|
||||
);
|
||||
},
|
||||
)
|
||||
else
|
||||
IconButton(
|
||||
icon: Icon(Icons.adaptive.more_rounded),
|
||||
tooltip: 'More',
|
||||
onPressed: () => showModalBottomSheetWithProperFocus(
|
||||
isScrollControlled: true,
|
||||
context: context,
|
||||
builder: (_) => ChatDetails(
|
||||
chatDetailsStore: chatStore.chatDetailsStore,
|
||||
chatStore: chatStore,
|
||||
userLogin: chatStore.channelName,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
trailing: IconButton(
|
||||
tooltip: 'Cancel reply',
|
||||
onPressed: () {
|
||||
chatStore.replyingToMessage = null;
|
||||
},
|
||||
icon: const Icon(Icons.close),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
if (chatStore.settings.autocomplete &&
|
||||
chatStore.showEmoteAutocomplete &&
|
||||
matchingEmotes.isNotEmpty) ...[
|
||||
const Divider(),
|
||||
SizedBox(
|
||||
height: 50,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.all(4),
|
||||
itemCount: matchingEmotes.length,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, index) => InkWell(
|
||||
onTap: () => chatStore.addEmote(
|
||||
matchingEmotes[index],
|
||||
autocompleteMode: true,
|
||||
),
|
||||
onLongPress: () {
|
||||
HapticFeedback.lightImpact();
|
||||
|
||||
IRCMessage.showEmoteDetailsBottomSheet(
|
||||
context,
|
||||
emote: matchingEmotes[index],
|
||||
launchExternal: chatStore.settings.launchUrlExternal,
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Center(
|
||||
child: FrostyCachedNetworkImage(
|
||||
imageUrl: matchingEmotes[index].url,
|
||||
useFade: false,
|
||||
height:
|
||||
matchingEmotes[index].height?.toDouble() ??
|
||||
defaultEmoteSize,
|
||||
width: matchingEmotes[index].width?.toDouble(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (chatStore.settings.autocomplete &&
|
||||
chatStore.showMentionAutocomplete &&
|
||||
matchingChatters.isNotEmpty) ...[
|
||||
const Divider(),
|
||||
SizedBox(
|
||||
height: 50,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.all(4),
|
||||
itemCount: matchingChatters.length,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, index) => TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
),
|
||||
onPressed: () {
|
||||
final split = chatStore.textController.text.split(' ')
|
||||
..removeLast()
|
||||
..add('@${matchingChatters[index]} ');
|
||||
|
||||
chatStore.textController.text = split.join(' ');
|
||||
chatStore.textController.selection =
|
||||
TextSelection.fromPosition(
|
||||
TextPosition(
|
||||
offset: chatStore.textController.text.length,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Text(matchingChatters[index]),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 12, top: 8, bottom: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
if (!chatStore.expandChat &&
|
||||
chatStore.settings.chatWidth < 0.3 &&
|
||||
chatStore.settings.showVideo &&
|
||||
context.isLandscape)
|
||||
IconButton(
|
||||
tooltip: 'Enter a message',
|
||||
onPressed: () {
|
||||
chatStore.expandChat = true;
|
||||
chatStore.safeRequestFocus();
|
||||
},
|
||||
icon: const Icon(Icons.edit),
|
||||
)
|
||||
else
|
||||
Expanded(
|
||||
child: Observer(
|
||||
builder: (context) {
|
||||
final isDisabledDueToDelay =
|
||||
chatStore.settings.showVideo &&
|
||||
chatStore.settings.chatDelay > 0;
|
||||
final isDisabled =
|
||||
!chatStore.auth.isLoggedIn ||
|
||||
chatStore.isSendingMessage ||
|
||||
isDisabledDueToDelay;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
// Show notification when trying to tap disabled input due to chat delay
|
||||
if (isDisabledDueToDelay) {
|
||||
chatStore.updateNotification(
|
||||
'Chatting is disabled due to message delay',
|
||||
);
|
||||
}
|
||||
},
|
||||
child: TextField(
|
||||
textInputAction: TextInputAction.send,
|
||||
focusNode: chatStore.textFieldFocusNode,
|
||||
minLines: 1,
|
||||
maxLines: 3,
|
||||
// Disable text field when sending message, when not logged in, or when chat delay is active
|
||||
enabled: !isDisabled,
|
||||
decoration: InputDecoration(
|
||||
prefixIcon:
|
||||
chatStore.settings.emoteMenuButtonOnLeft
|
||||
? emoteMenuButton
|
||||
: null,
|
||||
suffixIcon: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 8,
|
||||
children: [
|
||||
if (!chatStore
|
||||
.settings
|
||||
.emoteMenuButtonOnLeft &&
|
||||
emoteMenuButton != null)
|
||||
emoteMenuButton,
|
||||
],
|
||||
),
|
||||
hintMaxLines: 1,
|
||||
hintText: chatStore.auth.isLoggedIn
|
||||
? chatStore.isSendingMessage
|
||||
? 'Sending...'
|
||||
: chatStore.replyingToMessage != null
|
||||
? 'Reply'
|
||||
: 'Chat'
|
||||
: 'Log in',
|
||||
),
|
||||
controller: chatStore.textController,
|
||||
onSubmitted: chatStore.sendMessage,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (chatStore.settings.showVideo &&
|
||||
chatStore.settings.chatDelay > 0)
|
||||
Tooltip(
|
||||
message:
|
||||
'Message delay: ${chatStore.settings.chatDelay.toInt()} seconds${chatStore.settings.autoSyncChatDelay ? ' (auto-synced)' : ''}',
|
||||
preferBelow: false,
|
||||
triggerMode: TooltipTriggerMode.tap,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(left: 12),
|
||||
width: 38,
|
||||
child: Text(
|
||||
'${chatStore.settings.chatDelay.toInt()}s',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
fontFeatures: [const FontFeature.tabularFigures()],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (chatStore.showSendButton &&
|
||||
(chatStore.settings.chatWidth >= 0.3 ||
|
||||
chatStore.expandChat ||
|
||||
context.isPortrait))
|
||||
Observer(
|
||||
builder: (context) {
|
||||
return IconButton(
|
||||
tooltip: chatStore.isSendingMessage
|
||||
? 'Sending...'
|
||||
: 'Send',
|
||||
icon: chatStore.isSendingMessage
|
||||
? const SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
)
|
||||
: const Icon(Icons.send_rounded),
|
||||
onPressed:
|
||||
chatStore.auth.isLoggedIn &&
|
||||
!chatStore.isSendingMessage &&
|
||||
!(chatStore.settings.showVideo &&
|
||||
chatStore.settings.chatDelay > 0)
|
||||
? () => chatStore.sendMessage(
|
||||
chatStore.textController.text,
|
||||
)
|
||||
: null,
|
||||
);
|
||||
},
|
||||
)
|
||||
else
|
||||
IconButton(
|
||||
icon: Icon(Icons.adaptive.more_rounded),
|
||||
tooltip: 'More',
|
||||
onPressed: () => showModalBottomSheetWithProperFocus(
|
||||
isScrollControlled: true,
|
||||
context: context,
|
||||
builder: (_) => ChatDetails(
|
||||
chatDetailsStore: chatStore.chatDetailsStore,
|
||||
chatStore: chatStore,
|
||||
userLogin: chatStore.channelName,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return isFullscreenOverlay
|
||||
? Padding(
|
||||
|
|
|
|||
|
|
@ -130,40 +130,40 @@ class _SearchState extends State<Search> {
|
|||
child: CustomScrollView(
|
||||
controller: widget.scrollController,
|
||||
slivers: [
|
||||
// Add padding for app bar and search bar
|
||||
_SearchTopPadding(),
|
||||
SliverToBoxAdapter(
|
||||
child: Builder(
|
||||
builder: (context) => SectionHeader(
|
||||
'Channels',
|
||||
isFirst: true,
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
16 + MediaQuery.of(context).padding.left,
|
||||
12,
|
||||
16 + MediaQuery.of(context).padding.right,
|
||||
8,
|
||||
// Add padding for app bar and search bar
|
||||
_SearchTopPadding(),
|
||||
SliverToBoxAdapter(
|
||||
child: Builder(
|
||||
builder: (context) => SectionHeader(
|
||||
'Channels',
|
||||
isFirst: true,
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
16 + MediaQuery.of(context).padding.left,
|
||||
12,
|
||||
16 + MediaQuery.of(context).padding.right,
|
||||
8,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SearchResultsChannels(
|
||||
searchStore: _searchStore,
|
||||
query: _searchStore.searchText,
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Builder(
|
||||
builder: (context) => SectionHeader(
|
||||
'Categories',
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
16 + MediaQuery.of(context).padding.left,
|
||||
8,
|
||||
16 + MediaQuery.of(context).padding.right,
|
||||
8,
|
||||
SearchResultsChannels(
|
||||
searchStore: _searchStore,
|
||||
query: _searchStore.searchText,
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Builder(
|
||||
builder: (context) => SectionHeader(
|
||||
'Categories',
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
16 + MediaQuery.of(context).padding.left,
|
||||
8,
|
||||
16 + MediaQuery.of(context).padding.right,
|
||||
8,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SearchResultsCategories(searchStore: _searchStore),
|
||||
SearchResultsCategories(searchStore: _searchStore),
|
||||
// Add padding for bottom navigation bar
|
||||
const SliverBottomPadding(),
|
||||
],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue