Add emote autocomplete & tweak emote menu (#101)

* Add emote search/autocomplete & refactor (#61)
- Removed get global emotes future
- Added convenience `addEmote` method
- Added ability to clear recent emotes
- Refactored emote menu

* Fix unusable emotes showing on autocomplete (#61)

* Add option to disable autocomplete (#61)

* Convert autocomplete to builder (#61)

* Fix autocomplete empty & add clear dialog (#61)

* Fix typo

* Fix emote menu removing last word (#61)

* Fix emote menu deadzones & use `InkWell` (#61)

* Improve emote sizing in autocomplete (#61)
This commit is contained in:
Tommy Chow 2022-04-01 16:30:35 -04:00 • committed by GitHub
parent 47239870dc
commit 67a1c338ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 320 additions and 175 deletions

View file

@ -107,10 +107,7 @@ class Chat extends StatelessWidget {
if (chatStore.assetsStore.showEmoteMenu)
SizedBox(
height: MediaQuery.of(context).size.height / 3,
child: EmoteMenu(
assetsStore: chatStore.assetsStore,
textController: chatStore.textController,
),
child: EmoteMenu(chatStore: chatStore),
),
],
),

View file

@ -3,16 +3,14 @@ import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/screens/channel/chat/emote_menu/emote_menu_panel.dart';
import 'package:frosty/screens/channel/chat/emote_menu/recent_emotes_panel.dart';
import 'package:frosty/screens/channel/stores/chat_assets_store.dart';
import 'package:frosty/screens/channel/stores/chat_store.dart';
class EmoteMenu extends StatefulWidget {
final ChatAssetsStore assetsStore;
final TextEditingController textController;
final ChatStore chatStore;
const EmoteMenu({
Key? key,
required this.assetsStore,
required this.textController,
required this.chatStore,
}) : super(key: key);
@override
@ -20,7 +18,7 @@ class EmoteMenu extends StatefulWidget {
}
class _EmoteMenuState extends State<EmoteMenu> {
late final PageController _pageContoller = PageController(initialPage: widget.assetsStore.emoteMenuIndex);
late final PageController _pageContoller = PageController(initialPage: widget.chatStore.assetsStore.emoteMenuIndex);
@override
Widget build(BuildContext context) {
@ -47,9 +45,9 @@ class _EmoteMenuState extends State<EmoteMenu> {
duration: const Duration(milliseconds: 200),
curve: Curves.ease,
);
widget.assetsStore.emoteMenuIndex = index;
widget.chatStore.assetsStore.emoteMenuIndex = index;
},
style: index == widget.assetsStore.emoteMenuIndex ? null : TextButton.styleFrom(primary: Colors.grey),
style: index == widget.chatStore.assetsStore.emoteMenuIndex ? null : TextButton.styleFrom(primary: Colors.grey),
child: Text(section),
),
),
@ -59,39 +57,34 @@ class _EmoteMenuState extends State<EmoteMenu> {
),
Expanded(
child: PageView(
onPageChanged: (index) => widget.assetsStore.emoteMenuIndex = index,
onPageChanged: (index) => widget.chatStore.assetsStore.emoteMenuIndex = index,
controller: _pageContoller,
children: [
RecentEmotesPanel(
assetsStore: widget.assetsStore,
textController: widget.textController,
chatStore: widget.chatStore,
),
Observer(
builder: (_) => EmoteMenuPanel(
assetsStore: widget.assetsStore,
textController: widget.textController,
emotes: widget.assetsStore.userEmoteToObject.values.toList(),
chatStore: widget.chatStore,
emotes: widget.chatStore.assetsStore.userEmoteToObject.values.toList(),
),
),
Observer(
builder: (_) => EmoteMenuPanel(
assetsStore: widget.assetsStore,
textController: widget.textController,
emotes: widget.assetsStore.bttvEmotes,
chatStore: widget.chatStore,
emotes: widget.chatStore.assetsStore.bttvEmotes,
),
),
Observer(
builder: (_) => EmoteMenuPanel(
assetsStore: widget.assetsStore,
textController: widget.textController,
emotes: widget.assetsStore.ffzEmotes,
chatStore: widget.chatStore,
emotes: widget.chatStore.assetsStore.ffzEmotes,
),
),
Observer(
builder: (_) => EmoteMenuPanel(
assetsStore: widget.assetsStore,
textController: widget.textController,
emotes: widget.assetsStore.sevenTVEmotes,
chatStore: widget.chatStore,
emotes: widget.chatStore.assetsStore.sevenTVEmotes,
),
),
],

View file

@ -1,18 +1,16 @@
import 'package:flutter/material.dart';
import 'package:frosty/models/emotes.dart';
import 'package:frosty/screens/channel/chat/emote_menu/emote_menu_section.dart';
import 'package:frosty/screens/channel/stores/chat_assets_store.dart';
import 'package:frosty/screens/channel/stores/chat_store.dart';
import 'package:frosty/widgets/section_header.dart';
class EmoteMenuPanel extends StatelessWidget {
final ChatAssetsStore assetsStore;
final TextEditingController textController;
final ChatStore chatStore;
final List<Emote> emotes;
const EmoteMenuPanel({
Key? key,
required this.assetsStore,
required this.textController,
required this.chatStore,
required this.emotes,
}) : super(key: key);
@ -48,8 +46,7 @@ class EmoteMenuPanel extends StatelessWidget {
),
),
EmoteMenuSection(
assetsStore: assetsStore,
textController: textController,
chatStore: chatStore,
emotes: globalEmotes,
),
],
@ -58,8 +55,7 @@ class EmoteMenuPanel extends StatelessWidget {
child: SectionHeader('Channel Emotes'),
),
EmoteMenuSection(
assetsStore: assetsStore,
textController: textController,
chatStore: chatStore,
emotes: channelEmotes,
),
],
@ -68,8 +64,7 @@ class EmoteMenuPanel extends StatelessWidget {
child: SectionHeader('Subscribed Emotes'),
),
EmoteMenuSection(
assetsStore: assetsStore,
textController: textController,
chatStore: chatStore,
emotes: subEmotes,
),
],
@ -78,8 +73,7 @@ class EmoteMenuPanel extends StatelessWidget {
child: SectionHeader('Unlocked Emotes'),
),
EmoteMenuSection(
assetsStore: assetsStore,
textController: textController,
chatStore: chatStore,
emotes: miscEmotes,
),
],

View file

@ -2,46 +2,38 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:frosty/constants/constants.dart';
import 'package:frosty/models/emotes.dart';
import 'package:frosty/screens/channel/stores/chat_assets_store.dart';
import 'package:frosty/screens/channel/stores/chat_store.dart';
import 'package:frosty/screens/settings/stores/settings_store.dart';
import 'package:provider/provider.dart';
class EmoteMenuSection extends StatelessWidget {
final ChatAssetsStore assetsStore;
final TextEditingController textController;
final ChatStore chatStore;
final List<Emote> emotes;
const EmoteMenuSection({
Key? key,
required this.assetsStore,
required this.textController,
required this.chatStore,
required this.emotes,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return SliverPadding(
padding: const EdgeInsets.all(10.0),
sliver: SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: MediaQuery.of(context).orientation == Orientation.portrait
? 8
: context.read<SettingsStore>().showVideo
? 6
: 16,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
),
delegate: SliverChildBuilderDelegate(
(context, index) => GestureDetector(
onTap: () {
textController.text += ' ' + emotes[index].name;
assetsStore.recentEmotes.remove(emotes[index]);
assetsStore.recentEmotes.insert(0, emotes[index]);
},
child: Tooltip(
message: emotes[index].name,
preferBelow: false,
return SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: MediaQuery.of(context).orientation == Orientation.portrait
? 8
: context.read<SettingsStore>().showVideo
? 6
: 16,
),
delegate: SliverChildBuilderDelegate(
(context, index) => InkWell(
onTap: () => chatStore.addEmote(emotes[index]),
child: Tooltip(
message: emotes[index].name,
preferBelow: false,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Center(
child: CachedNetworkImage(
imageUrl: emotes[index].url,
@ -51,8 +43,8 @@ class EmoteMenuSection extends StatelessWidget {
),
),
),
childCount: emotes.length,
),
childCount: emotes.length,
),
);
}

View file

@ -1,60 +1,91 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:frosty/constants/constants.dart';
import 'package:frosty/screens/channel/stores/chat_assets_store.dart';
import 'package:frosty/screens/channel/stores/chat_store.dart';
import 'package:frosty/screens/settings/stores/settings_store.dart';
import 'package:frosty/widgets/section_header.dart';
import 'package:provider/provider.dart';
class RecentEmotesPanel extends StatelessWidget {
final ChatAssetsStore assetsStore;
final TextEditingController textController;
class RecentEmotesPanel extends StatefulWidget {
final ChatStore chatStore;
const RecentEmotesPanel({
Key? key,
required this.assetsStore,
required this.textController,
required this.chatStore,
}) : super(key: key);
@override
State<RecentEmotesPanel> createState() => _RecentEmotesPanelState();
}
class _RecentEmotesPanelState extends State<RecentEmotesPanel> {
Future<void> _showClearDialog() {
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Clear Recent Emotes'),
content: const Text('Are you sure you want to clear your recent emotes?'),
actions: [
TextButton(
onPressed: Navigator.of(context).pop,
child: const Text('Cancel'),
style: TextButton.styleFrom(primary: Colors.red),
),
ElevatedButton(
onPressed: () {
setState(widget.chatStore.assetsStore.recentEmotes.clear);
Navigator.pop(context);
},
child: const Text('Yes'),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: [
const SliverToBoxAdapter(
child: SectionHeader(
'Recent Emotes',
padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0),
SliverToBoxAdapter(
child: Row(
children: [
const SectionHeader(
'Recent Emotes',
padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0),
),
const Spacer(),
SizedBox(
height: 30,
child: TextButton(
onPressed: widget.chatStore.assetsStore.recentEmotes.isEmpty ? null : _showClearDialog,
child: const Text('Clear'),
),
),
],
),
),
SliverPadding(
padding: const EdgeInsets.all(10.0),
sliver: SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: MediaQuery.of(context).orientation == Orientation.portrait
? 8
: context.read<SettingsStore>().showVideo
? 6
: 16,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
),
delegate: SliverChildBuilderDelegate(
(context, index) {
final emote = assetsStore.recentEmotes[index];
final validEmotes = [...assetsStore.emoteToObject.values, ...assetsStore.userEmoteToObject.values];
final matchingEmotes = validEmotes.where((existingEmote) => existingEmote.name == emote.name && existingEmote.type == emote.type);
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: MediaQuery.of(context).orientation == Orientation.portrait
? 8
: context.read<SettingsStore>().showVideo
? 6
: 16,
),
delegate: SliverChildBuilderDelegate(
(context, index) {
final emote = widget.chatStore.assetsStore.recentEmotes[index];
final validEmotes = [...widget.chatStore.assetsStore.emoteToObject.values, ...widget.chatStore.assetsStore.userEmoteToObject.values];
final matchingEmotes = validEmotes.where((existingEmote) => existingEmote.name == emote.name && existingEmote.type == emote.type);
return GestureDetector(
onTap: matchingEmotes.isNotEmpty
? () {
textController.text += ' ' + emote.name;
assetsStore.recentEmotes.removeWhere((recentEmote) => recentEmote.name == matchingEmotes.first.name);
assetsStore.recentEmotes.insert(0, matchingEmotes.first);
}
: null,
child: Tooltip(
message: emote.name,
preferBelow: false,
return InkWell(
onTap: matchingEmotes.isNotEmpty ? () => widget.chatStore.addEmote(emote) : null,
child: Tooltip(
message: emote.name,
preferBelow: false,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Center(
child: CachedNetworkImage(
imageUrl: matchingEmotes.isNotEmpty ? matchingEmotes.first.url : emote.url,
@ -65,10 +96,10 @@ class RecentEmotesPanel extends StatelessWidget {
),
),
),
);
},
childCount: assetsStore.recentEmotes.length,
),
),
);
},
childCount: widget.chatStore.assetsStore.recentEmotes.length,
),
),
],

View file

@ -1,5 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/constants/constants.dart';
import 'package:frosty/screens/channel/chat/details/chat_details.dart';
import 'package:frosty/screens/channel/stores/chat_store.dart';
@ -11,61 +13,105 @@ class ChatBottomBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Observer(
builder: (context) => Row(
children: [
IconButton(
icon: Icon(Icons.adaptive.more),
tooltip: 'Chat Details',
onPressed: () => showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (_) => SizedBox(
height: MediaQuery.of(context).size.height * 0.8,
child: ChatDetails(
chatDetails: chatStore.chatDetailsStore,
chatStore: chatStore,
userLogin: chatStore.channelName,
),
builder: (context) {
final emotes = [
...chatStore.assetsStore.userEmoteToObject.values,
...chatStore.assetsStore.bttvEmotes,
...chatStore.assetsStore.ffzEmotes,
...chatStore.assetsStore.sevenTVEmotes
].where((emote) => emote.name.toLowerCase().contains(chatStore.textController.text.split(' ').last.toLowerCase())).toList();
return Column(
children: [
if (chatStore.settings.emoteAutocomplete && chatStore.showAutocomplete && emotes.isNotEmpty) ...[
const Divider(
height: 1.0,
thickness: 1.0,
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0),
child: TextField(
focusNode: chatStore.textFieldFocusNode,
minLines: 1,
maxLines: 5,
enabled: chatStore.auth.isLoggedIn ? true : false,
decoration: InputDecoration(
suffixIcon: IconButton(
tooltip: 'Emote Menu',
icon: const Icon(Icons.emoji_emotions_outlined),
onPressed: () {
FocusScope.of(context).unfocus();
chatStore.assetsStore.showEmoteMenu = !chatStore.assetsStore.showEmoteMenu;
},
SizedBox(
height: 50,
child: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 5.0),
itemCount: emotes.length,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) => InkWell(
onTap: () => chatStore.addEmote(emotes[index], autocompleteMode: true),
child: Tooltip(
message: emotes[index].name,
preferBelow: false,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 5.0),
child: Center(
child: CachedNetworkImage(
imageUrl: emotes[index].url,
fadeInDuration: const Duration(),
height: emotes[index].height?.toDouble() ?? defaultEmoteSize,
width: emotes[index].width?.toDouble(),
),
),
),
),
),
isDense: true,
hintMaxLines: 1,
contentPadding: const EdgeInsets.all(10.0),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
),
hintText: chatStore.auth.isLoggedIn ? 'Send a message' : 'Log in to chat',
),
controller: chatStore.textController,
onSubmitted: chatStore.sendMessage,
),
)
],
Row(
children: [
IconButton(
icon: Icon(Icons.adaptive.more),
tooltip: 'Chat Details',
onPressed: () => showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (_) => SizedBox(
height: MediaQuery.of(context).size.height * 0.8,
child: ChatDetails(
chatDetails: chatStore.chatDetailsStore,
chatStore: chatStore,
userLogin: chatStore.channelName,
),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0),
child: TextField(
focusNode: chatStore.textFieldFocusNode,
minLines: 1,
maxLines: 5,
enabled: chatStore.auth.isLoggedIn ? true : false,
decoration: InputDecoration(
suffixIcon: IconButton(
tooltip: 'Emote Menu',
icon: const Icon(Icons.emoji_emotions_outlined),
onPressed: () {
FocusScope.of(context).unfocus();
chatStore.assetsStore.showEmoteMenu = !chatStore.assetsStore.showEmoteMenu;
},
),
hintMaxLines: 1,
contentPadding: const EdgeInsets.all(10.0),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
),
hintText: chatStore.auth.isLoggedIn ? 'Send a message' : 'Log in to chat',
),
controller: chatStore.textController,
onSubmitted: chatStore.sendMessage,
),
),
),
IconButton(
tooltip: 'Send',
icon: const Icon(Icons.send),
onPressed: chatStore.auth.isLoggedIn ? () => chatStore.sendMessage(chatStore.textController.text) : null,
)
],
),
),
IconButton(
tooltip: 'Send',
icon: const Icon(Icons.send),
onPressed: chatStore.auth.isLoggedIn ? () => chatStore.sendMessage(chatStore.textController.text) : null,
)
],
),
],
);
},
);
}
}

View file

@ -72,6 +72,9 @@ abstract class _ChatStoreBase with Store {
@readonly
var _autoScroll = true;
@readonly
var _showAutocomplete = false;
/// The logged-in user's appearance in chat.
@readonly
var _userState = const USERSTATE();
@ -129,7 +132,7 @@ abstract class _ChatStoreBase with Store {
}
});
// Add a listener to the textfield that will do the following when focused:
// Add a listener to the textfield focus that will do the following when focused:
// 1. Hide the emote menu if it is currently shown
// 2. Scroll to the latest message (after a brief delay)
textFieldFocusNode.addListener(() {
@ -142,6 +145,11 @@ abstract class _ChatStoreBase with Store {
);
}
});
// Add a listener to the textfield that will show/hide the autocomplete bar if focused.
// Will also rebuild the autocomplete bar when typing, refreshing the results as the user types.
textController
.addListener(() => _showAutocomplete = textFieldFocusNode.hasFocus && textController.text.split(' ').last.isNotEmpty ? true : false);
}
/// Handle and process the provided string-representation of the IRC data.
@ -344,6 +352,28 @@ abstract class _ChatStoreBase with Store {
textController.clear();
}
/// Adds the given [emote] to the chat textfield.
@action
void addEmote(Emote emote, {bool autocompleteMode = false}) {
if (textController.text.isEmpty || textController.text.endsWith(' ')) {
textController.text += emote.name + ' ';
} else if (autocompleteMode && _showAutocomplete && textController.text.endsWith('')) {
final split = textController.text.split(' ')
..removeLast()
..add(emote.name + ' ');
textController.text = split.join(' ');
} else {
textController.text += ' ' + emote.name + ' ';
}
assetsStore.recentEmotes
..removeWhere((recentEmote) => recentEmote.name == emote.name && recentEmote.type == emote.type)
..insert(0, emote);
textController.selection = TextSelection.fromPosition(TextPosition(offset: textController.text.length));
}
/// Closes and disposes all the channels and controllers used by the store.
void dispose() {
_channel?.sink.close(1001);

View file

@ -43,6 +43,24 @@ mixin _$ChatStore on _ChatStoreBase, Store {
});
}
final _$_showAutocompleteAtom =
Atom(name: '_ChatStoreBase._showAutocomplete');
bool get showAutocomplete {
_$_showAutocompleteAtom.reportRead();
return super._showAutocomplete;
}
@override
bool get _showAutocomplete => showAutocomplete;
@override
set _showAutocomplete(bool value) {
_$_showAutocompleteAtom.reportWrite(value, super._showAutocomplete, () {
super._showAutocomplete = value;
});
}
final _$_userStateAtom = Atom(name: '_ChatStoreBase._userState');
USERSTATE get userState {
@ -107,6 +125,17 @@ mixin _$ChatStore on _ChatStoreBase, Store {
}
}
@override
void addEmote(Emote emote, {bool autocompleteMode = false}) {
final _$actionInfo = _$_ChatStoreBaseActionController.startAction(
name: '_ChatStoreBase.addEmote');
try {
return super.addEmote(emote, autocompleteMode: autocompleteMode);
} finally {
_$_ChatStoreBaseActionController.endAction(_$actionInfo);
}
}
@override
String toString() {
return '''

View file

@ -36,10 +36,24 @@ class _ChatSettingsState extends State<ChatSettings> {
children: [
SwitchListTile.adaptive(
title: const Text('Bottom Bar'),
subtitle: const Text('Displays the bottom text field bar.'),
subtitle: const Text('Shows the bottom text field bar.'),
value: settingsStore.showBottomBar,
onChanged: (newValue) => settingsStore.showBottomBar = newValue,
),
SwitchListTile.adaptive(
isThreeLine: true,
title: const Text('Emote Autocomplete'),
subtitle: const Text('Shows a bar that suggests matching emotes when typing.'),
value: settingsStore.emoteAutocomplete,
onChanged: settingsStore.showBottomBar ? (newValue) => settingsStore.emoteAutocomplete = newValue : null,
),
SwitchListTile.adaptive(
isThreeLine: true,
title: const Text('Zero-Width Emotes'),
subtitle: const Text('Shows "stacked" emotes from BetterTTV and 7TV.'),
value: settingsStore.showZeroWidth,
onChanged: (newValue) => settingsStore.showZeroWidth = newValue,
),
SwitchListTile.adaptive(
isThreeLine: true,
title: const Text('Readable Name Colors'),
@ -47,13 +61,6 @@ class _ChatSettingsState extends State<ChatSettings> {
value: settingsStore.useReadableColors,
onChanged: (newValue) => settingsStore.useReadableColors = newValue,
),
SwitchListTile.adaptive(
isThreeLine: true,
title: const Text('Zero-Width Emotes'),
subtitle: const Text('Displays "stacked" emotes from BetterTTV and 7TV.'),
value: settingsStore.showZeroWidth,
onChanged: (newValue) => settingsStore.showZeroWidth = newValue,
),
SwitchListTile.adaptive(
isThreeLine: true,
title: const Text('Show Deleted Messages'),
@ -64,14 +71,14 @@ class _ChatSettingsState extends State<ChatSettings> {
SwitchListTile.adaptive(
isThreeLine: true,
title: const Text('Message Dividers'),
subtitle: const Text('Displays a subtle divider between each message.'),
subtitle: const Text('Shows a subtle divider between each message.'),
value: settingsStore.showChatMessageDividers,
onChanged: (newValue) => settingsStore.showChatMessageDividers = newValue,
),
ListTile(
isThreeLine: true,
title: const Text('Message Timestamps'),
subtitle: const Text('Displays timestamps for when a message was sent.'),
subtitle: const Text('Shows timestamps for when a message was sent.'),
trailing: DropdownButton(
value: settingsStore.timestampType,
onChanged: (TimestampType? newTimestamp) => settingsStore.timestampType = newTimestamp!,

View file

@ -43,7 +43,7 @@ class GeneralSettings extends StatelessWidget {
SwitchListTile.adaptive(
isThreeLine: true,
title: const Text('Stream Uptime on Thumbnails'),
subtitle: const Text('Displays the uptime of the stream in the HH:MM:SS format.'),
subtitle: const Text('Shows the uptime of the stream in the HH:MM:SS format.'),
value: settingsStore.showThumbnailUptime,
onChanged: settingsStore.showThumbnails ? (newValue) => settingsStore.showThumbnailUptime = newValue : null,
),

View file

@ -95,7 +95,7 @@ class OtherSettings extends StatelessWidget {
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
padding: const EdgeInsets.symmetric(horizontal: 15.0),
width: double.infinity,
child: OutlinedButton.icon(
icon: const Icon(Icons.delete_sweep),
@ -112,7 +112,7 @@ class OtherSettings extends StatelessWidget {
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
padding: const EdgeInsets.symmetric(horizontal: 15.0),
width: double.infinity,
child: OutlinedButton.icon(
icon: const Icon(Icons.restore),

View file

@ -79,6 +79,8 @@ abstract class _SettingsStoreBase with Store {
static const defaultEmoteScale = 1.0;
static const defaultEmoteAutocomplete = true;
@JsonKey(defaultValue: defaultShowBottomBar)
@observable
var showBottomBar = defaultShowBottomBar;
@ -123,6 +125,10 @@ abstract class _SettingsStoreBase with Store {
@observable
var emoteScale = defaultEmoteScale;
@JsonKey(defaultValue: defaultEmoteAutocomplete)
@observable
var emoteAutocomplete = defaultEmoteAutocomplete;
// * Other settings
static const defaultSendCrashLogs = true;
@ -168,6 +174,7 @@ abstract class _SettingsStoreBase with Store {
messageSpacing = defaultMessageSpacing;
badgeScale = defaultBadgeScale;
emoteScale = defaultEmoteScale;
emoteAutocomplete = defaultEmoteAutocomplete;
// * Other settings
sendCrashLogs = defaultSendCrashLogs;

View file

@ -32,6 +32,7 @@ SettingsStore _$SettingsStoreFromJson(Map<String, dynamic> json) =>
..messageScale = (json['messageScale'] as num?)?.toDouble() ?? 1.0
..badgeScale = (json['badgeScale'] as num?)?.toDouble() ?? 1.0
..emoteScale = (json['emoteScale'] as num?)?.toDouble() ?? 1.0
..emoteAutocomplete = json['emoteAutocomplete'] as bool? ?? true
..sendCrashLogs = json['sendCrashLogs'] as bool? ?? true
..fullScreen = json['fullScreen'] as bool? ?? false
..expandInfo = json['expandInfo'] as bool? ?? true;
@ -56,6 +57,7 @@ Map<String, dynamic> _$SettingsStoreToJson(SettingsStore instance) =>
'messageScale': instance.messageScale,
'badgeScale': instance.badgeScale,
'emoteScale': instance.emoteScale,
'emoteAutocomplete': instance.emoteAutocomplete,
'sendCrashLogs': instance.sendCrashLogs,
'fullScreen': instance.fullScreen,
'expandInfo': instance.expandInfo,
@ -358,6 +360,22 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$emoteAutocompleteAtom =
Atom(name: '_SettingsStoreBase.emoteAutocomplete');
@override
bool get emoteAutocomplete {
_$emoteAutocompleteAtom.reportRead();
return super.emoteAutocomplete;
}
@override
set emoteAutocomplete(bool value) {
_$emoteAutocompleteAtom.reportWrite(value, super.emoteAutocomplete, () {
super.emoteAutocomplete = value;
});
}
final _$sendCrashLogsAtom = Atom(name: '_SettingsStoreBase.sendCrashLogs');
@override
@ -438,6 +456,7 @@ messageSpacing: ${messageSpacing},
messageScale: ${messageScale},
badgeScale: ${badgeScale},
emoteScale: ${emoteScale},
emoteAutocomplete: ${emoteAutocomplete},
sendCrashLogs: ${sendCrashLogs},
fullScreen: ${fullScreen},
expandInfo: ${expandInfo}