diff --git a/lib/models/irc_message.dart b/lib/models/irc_message.dart index 8c9abbef..1e394b3f 100644 --- a/lib/models/irc_message.dart +++ b/lib/models/irc_message.dart @@ -78,15 +78,14 @@ class IRCMessage { List generateSpan({ required TextStyle? style, required ChatAssetsStore assetsStore, + required double badgeHeight, + required double emoteHeight, bool showMessage = true, bool useZeroWidth = false, bool useReadableColors = false, bool? isLightTheme, TimestampType timestamp = TimestampType.disabled, }) { - const badgeHeight = 20.0; - const emoteHeight = 30.0; - final emoteToObject = assetsStore.emoteToObject; final twitchBadgeToObject = assetsStore.twitchBadgesToObject; final ffzUserToBadges = assetsStore.userToFFZBadges; @@ -307,20 +306,6 @@ class IRCMessage { )) .toList(); - // Regex from dart_emoji package; used for emoji compatibility with zero-width - final regexEmoji = RegExp( - r'[\u{1f300}-\u{1f5ff}\u{1f900}-\u{1f9ff}\u{1f600}-\u{1f64f}' - r'\u{1f680}-\u{1f6ff}\u{2600}-\u{26ff}\u{2700}' - r'-\u{27bf}\u{1f1e6}-\u{1f1ff}\u{1f191}-\u{1f251}' - r'\u{1f004}\u{1f0cf}\u{1f170}-\u{1f171}\u{1f17e}' - r'-\u{1f17f}\u{1f18e}\u{3030}\u{2b50}\u{2b55}' - r'\u{2934}-\u{2935}\u{2b05}-\u{2b07}\u{2b1b}' - r'-\u{2b1c}\u{3297}\u{3299}\u{303d}\u{00a9}' - r'\u{00ae}\u{2122}\u{23f3}\u{24c2}\u{23e9}' - r'-\u{23ef}\u{25b6}\u{23f8}-\u{23fa}\u{200d}]+', - unicode: true, - ); - var nextWordIsEmoji = false; if (regexEmoji.hasMatch(words[index])) nextWordIsEmoji = true; @@ -332,7 +317,15 @@ class IRCMessage { preferBelow: false, child: Stack( alignment: AlignmentDirectional.center, - children: nextWordIsEmoji ? [Text(words[index]), ...stack] : stack, + children: nextWordIsEmoji + ? [ + Text( + words[index], + style: textStyle?.copyWith(fontSize: emoteHeight), + ), + ...stack + ] + : stack, ), ), ), @@ -352,7 +345,11 @@ class IRCMessage { ); } } else { - localSpan.add(_createTextSpan(text: word, style: textStyle)); + if (regexEmoji.hasMatch(word)) { + localSpan.add(_createTextSpan(text: word, style: textStyle?.copyWith(fontSize: emoteHeight))); + } else { + localSpan.add(_createTextSpan(text: word, style: textStyle)); + } } localSpan.add(const TextSpan(text: ' ')); index--; @@ -372,7 +369,11 @@ class IRCMessage { ), ); } else { - span.add(_createTextSpan(text: word, style: textStyle)); + if (regexEmoji.hasMatch(word)) { + span.add(_createTextSpan(text: word, style: textStyle?.copyWith(fontSize: emoteHeight))); + } else { + span.add(_createTextSpan(text: word, style: textStyle)); + } } } } @@ -676,3 +677,17 @@ enum Command { globalUserState, none, } + +// Regex from dart_emoji package; used for emoji compatibility with zero-width +final regexEmoji = RegExp( + r'[\u{1f300}-\u{1f5ff}\u{1f900}-\u{1f9ff}\u{1f600}-\u{1f64f}' + r'\u{1f680}-\u{1f6ff}\u{2600}-\u{26ff}\u{2700}' + r'-\u{27bf}\u{1f1e6}-\u{1f1ff}\u{1f191}-\u{1f251}' + r'\u{1f004}\u{1f0cf}\u{1f170}-\u{1f171}\u{1f17e}' + r'-\u{1f17f}\u{1f18e}\u{3030}\u{2b50}\u{2b55}' + r'\u{2934}-\u{2935}\u{2b05}-\u{2b07}\u{2b1b}' + r'-\u{2b1c}\u{3297}\u{3299}\u{303d}\u{00a9}' + r'\u{00ae}\u{2122}\u{23f3}\u{24c2}\u{23e9}' + r'-\u{23ef}\u{25b6}\u{23f8}-\u{23fa}\u{200d}]+', + unicode: true, +); diff --git a/lib/screens/channel/chat/widgets/chat_message.dart b/lib/screens/channel/chat/widgets/chat_message.dart index f2b4b05e..5355a2d0 100644 --- a/lib/screens/channel/chat/widgets/chat_message.dart +++ b/lib/screens/channel/chat/widgets/chat_message.dart @@ -31,6 +31,8 @@ class ChatMessage extends StatelessWidget { children: ircMessage.generateSpan( style: DefaultTextStyle.of(context).style, assetsStore: assetsStore, + emoteHeight: settingsStore.emoteHeight, + badgeHeight: settingsStore.badgeHeight, useZeroWidth: settingsStore.showZeroWidth, useReadableColors: settingsStore.useReadableColors, isLightTheme: Theme.of(context).brightness == Brightness.light, @@ -68,6 +70,8 @@ class ChatMessage extends StatelessWidget { children: ircMessage.generateSpan( style: DefaultTextStyle.of(context).style, assetsStore: assetsStore, + emoteHeight: settingsStore.emoteHeight, + badgeHeight: settingsStore.badgeHeight, showMessage: settingsStore.showDeletedMessages, useZeroWidth: settingsStore.showZeroWidth, useReadableColors: settingsStore.useReadableColors, @@ -122,6 +126,8 @@ class ChatMessage extends StatelessWidget { children: ircMessage.generateSpan( style: DefaultTextStyle.of(context).style, assetsStore: assetsStore, + emoteHeight: settingsStore.emoteHeight, + badgeHeight: settingsStore.badgeHeight, useZeroWidth: settingsStore.showZeroWidth, useReadableColors: settingsStore.useReadableColors, isLightTheme: Theme.of(context).brightness == Brightness.light, diff --git a/lib/screens/settings/sections/chat_settings.dart b/lib/screens/settings/sections/chat_settings.dart index 94d041b4..73c785f4 100644 --- a/lib/screens/settings/sections/chat_settings.dart +++ b/lib/screens/settings/sections/chat_settings.dart @@ -80,13 +80,21 @@ class _ChatSettingsState extends State { Text.rich( TextSpan( children: [ - const TextSpan(text: 'Hello! This is a text preview message '), + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: CachedNetworkImage( + imageUrl: 'https://static-cdn.jtvnw.net/badges/v1/bbbe0db0-a598-423e-86d0-f9fb98ca1933/3', + placeholder: (context, url) => const SizedBox(), + height: settingsStore.badgeHeight, + ), + ), + const TextSpan(text: ' Badge and emote preview '), WidgetSpan( alignment: PlaceholderAlignment.middle, child: CachedNetworkImage( imageUrl: 'https://static-cdn.jtvnw.net/emoticons/v2/425618/default/dark/3.0', placeholder: (context, url) => const SizedBox(), - height: 30.0, + height: settingsStore.emoteHeight, ), ), ], @@ -95,12 +103,12 @@ class _ChatSettingsState extends State { ), SizedBox(height: settingsStore.messageSpacing), Text( - 'Here is a another message to visualize spacing!', + 'Hello! Here\'s a text preview.', textScaleFactor: settingsStore.fontScale, ), SizedBox(height: settingsStore.messageSpacing), Text( - 'And one more for spacing without an emote!', + 'And another for spacing without an emote!', textScaleFactor: settingsStore.fontScale, ), ], @@ -129,6 +137,26 @@ class _ChatSettingsState extends State { onChanged: (newValue) => settingsStore.messageSpacing = newValue, ), ), + ListTile( + title: Text('Badge height: ${settingsStore.badgeHeight.toStringAsFixed(0)}'), + subtitle: Slider.adaptive( + value: settingsStore.badgeHeight, + min: 10.0, + max: 50.0, + divisions: 8, + onChanged: (newValue) => settingsStore.badgeHeight = newValue, + ), + ), + ListTile( + title: Text('Emote height: ${settingsStore.emoteHeight.toStringAsFixed(0)}'), + subtitle: Slider.adaptive( + value: settingsStore.emoteHeight, + min: 10.0, + max: 50.0, + divisions: 8, + onChanged: (newValue) => settingsStore.emoteHeight = newValue, + ), + ), Container( padding: const EdgeInsets.all(10.0), width: double.infinity, diff --git a/lib/screens/settings/stores/settings_store.dart b/lib/screens/settings/stores/settings_store.dart index 7370866e..8c86a865 100644 --- a/lib/screens/settings/stores/settings_store.dart +++ b/lib/screens/settings/stores/settings_store.dart @@ -64,6 +64,14 @@ abstract class _SettingsStoreBase with Store { @observable var messageSpacing = 10.0; + @JsonKey(defaultValue: 20.0) + @observable + var badgeHeight = 20.0; + + @JsonKey(defaultValue: 30.0) + @observable + var emoteHeight = 30.0; + // Global configs @JsonKey(defaultValue: false) @observable diff --git a/lib/screens/settings/stores/settings_store.g.dart b/lib/screens/settings/stores/settings_store.g.dart index c92e5fab..070065c8 100644 --- a/lib/screens/settings/stores/settings_store.g.dart +++ b/lib/screens/settings/stores/settings_store.g.dart @@ -25,6 +25,8 @@ SettingsStore _$SettingsStoreFromJson(Map json) => ..useReadableColors = json['useReadableColors'] as bool? ?? true ..fontScale = (json['fontScale'] as num?)?.toDouble() ?? 1.0 ..messageSpacing = (json['messageSpacing'] as num?)?.toDouble() ?? 10.0 + ..badgeHeight = (json['badgeHeight'] as num?)?.toDouble() ?? 20.0 + ..emoteHeight = (json['emoteHeight'] as num?)?.toDouble() ?? 30.0 ..fullScreen = json['fullScreen'] as bool? ?? false ..expandInfo = json['expandInfo'] as bool? ?? true; @@ -42,6 +44,8 @@ Map _$SettingsStoreToJson(SettingsStore instance) => 'useReadableColors': instance.useReadableColors, 'fontScale': instance.fontScale, 'messageSpacing': instance.messageSpacing, + 'badgeHeight': instance.badgeHeight, + 'emoteHeight': instance.emoteHeight, 'fullScreen': instance.fullScreen, 'expandInfo': instance.expandInfo, }; @@ -251,6 +255,36 @@ mixin _$SettingsStore on _SettingsStoreBase, Store { }); } + final _$badgeHeightAtom = Atom(name: '_SettingsStoreBase.badgeHeight'); + + @override + double get badgeHeight { + _$badgeHeightAtom.reportRead(); + return super.badgeHeight; + } + + @override + set badgeHeight(double value) { + _$badgeHeightAtom.reportWrite(value, super.badgeHeight, () { + super.badgeHeight = value; + }); + } + + final _$emoteHeightAtom = Atom(name: '_SettingsStoreBase.emoteHeight'); + + @override + double get emoteHeight { + _$emoteHeightAtom.reportRead(); + return super.emoteHeight; + } + + @override + set emoteHeight(double value) { + _$emoteHeightAtom.reportWrite(value, super.emoteHeight, () { + super.emoteHeight = value; + }); + } + final _$fullScreenAtom = Atom(name: '_SettingsStoreBase.fullScreen'); @override @@ -296,6 +330,8 @@ timestampType: ${timestampType}, useReadableColors: ${useReadableColors}, fontScale: ${fontScale}, messageSpacing: ${messageSpacing}, +badgeHeight: ${badgeHeight}, +emoteHeight: ${emoteHeight}, fullScreen: ${fullScreen}, expandInfo: ${expandInfo} ''';