Add options to disable emotes and badges (#371)

* Add options to toggle emotes and badges

* Hide emote menu section when emotes disabled

* Hide emote menu button when emotes are disabled
This commit is contained in:
Tommy Chow 2024-08-10 20:22:57 -07:00 • committed by GitHub
parent e0c8959727
commit 6c49c0b3a8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 394 additions and 148 deletions

View file

@ -128,26 +128,31 @@ class Chat extends StatelessWidget {
const Divider(),
Expanded(
child: FrostyPageView(
headers: const [
headers: [
'Recent',
'Twitch',
'7TV',
'BTTV',
'FFZ',
if (chatStore.settings.showTwitchEmotes)
'Twitch',
if (chatStore.settings.show7TVEmotes) '7TV',
if (chatStore.settings.showBTTVEmotes) 'BTTV',
if (chatStore.settings.showFFZEmotes) 'FFZ',
],
children: [
RecentEmotesPanel(
chatStore: chatStore,
),
EmoteMenuPanel(
chatStore: chatStore,
twitchEmotes: chatStore
.assetsStore.userEmoteSectionToEmotes,
),
if (chatStore.settings.showTwitchEmotes)
EmoteMenuPanel(
chatStore: chatStore,
twitchEmotes: chatStore
.assetsStore.userEmoteSectionToEmotes,
),
...[
chatStore.assetsStore.sevenTVEmotes,
chatStore.assetsStore.bttvEmotes,
chatStore.assetsStore.ffzEmotes,
if (chatStore.settings.show7TVEmotes)
chatStore.assetsStore.sevenTVEmotes,
if (chatStore.settings.showBTTVEmotes)
chatStore.assetsStore.bttvEmotes,
if (chatStore.settings.showFFZEmotes)
chatStore.assetsStore.ffzEmotes,
].map(
(emotes) => EmoteMenuPanel(
chatStore: chatStore,

View file

@ -125,91 +125,87 @@ abstract class ChatAssetsStoreBase with Store {
required Map<String, String> headers,
required Function onEmoteError,
required Function onBadgeError,
bool showTwitchEmotes = true,
bool showTwitchBadges = true,
bool show7TVEmotes = true,
bool showBTTVEmotes = true,
bool showBTTVBadges = true,
bool showFFZEmotes = true,
bool showFFZBadges = true,
}) =>
// Fetch the global and channel's assets (emotes & badges).
// Async awaits are placed in a list so they are performed in parallel.
//
// Emotes
Future.wait([
emotesFuture(
channelId: channelId,
headers: headers,
onError: onEmoteError,
),
badgesFuture(
channelId: channelId,
headers: headers,
onError: onBadgeError,
),
]);
Future.wait([
if (showTwitchEmotes) ...[
twitchApi
.getEmotesGlobal(headers: headers)
.catchError(onEmoteError),
twitchApi
.getEmotesChannel(id: channelId, headers: headers)
.then((emotes) {
_userEmoteSectionToEmotes.update(
'Channel Emotes',
(existingEmoteSet) => [...existingEmoteSet, ...emotes],
ifAbsent: () => emotes.toList(),
);
@action
Future<void> emotesFuture({
required String channelId,
required Map<String, String> headers,
required Function onError,
}) =>
// Parallel futures look ugly, but are worth the performance improvement.
// Each future has it's own catchError so that the entire future is not ended on an error.
// This will ensure that other futures complete even if one fails.
Future.wait([
ffzApi.getEmotesGlobal().catchError(onError),
bttvApi.getEmotesGlobal().catchError(onError),
bttvApi.getEmotesChannel(id: channelId).catchError(onError),
twitchApi.getEmotesGlobal(headers: headers).catchError(onError),
twitchApi
.getEmotesChannel(id: channelId, headers: headers)
.then((emotes) {
_userEmoteSectionToEmotes.update(
'Channel Emotes',
(existingEmoteSet) => [...existingEmoteSet, ...emotes],
ifAbsent: () => emotes.toList(),
);
return emotes;
}).catchError(onEmoteError),
],
if (show7TVEmotes) ...[
sevenTVApi.getEmotesGlobal().catchError(onEmoteError),
sevenTVApi.getEmotesChannel(id: channelId).then((data) {
final (setId, emotes) = data;
sevenTvEmoteSetId = setId;
return emotes;
}).catchError(onEmoteError),
],
if (showBTTVEmotes) ...[
bttvApi.getEmotesGlobal().catchError(onEmoteError),
bttvApi.getEmotesChannel(id: channelId).catchError(onEmoteError),
],
if (showFFZEmotes) ...[
ffzApi.getEmotesGlobal().catchError(onEmoteError),
ffzApi.getRoomInfo(id: channelId).then((ffzRoom) {
final (roomInfo, emotes) = ffzRoom;
return emotes;
}).catchError(onError),
sevenTVApi.getEmotesGlobal().catchError(onError),
sevenTVApi.getEmotesChannel(id: channelId).then((data) {
final (setId, emotes) = data;
sevenTvEmoteSetId = setId;
return emotes;
}).catchError(onError),
ffzApi.getRoomInfo(id: channelId).then((ffzRoom) {
final (roomInfo, emotes) = ffzRoom;
ffzRoomInfo = roomInfo;
return emotes;
}).catchError(onError),
]).then((assets) => assets.expand((list) => list)).then(
(emotes) => _emoteToObject = {
for (final emote in emotes) emote.name: emote,
}.asObservable(),
);
@action
Future<void> badgesFuture({
required String channelId,
required Map<String, String> headers,
required Function onError,
}) =>
Future.wait([
// Get global badges first, then channel badges to avoid badge conflicts.
// We want the channel badges to override the global badges.
twitchApi
.getBadgesGlobal(headers: headers)
.then((badges) => twitchBadgesToObject.addAll(badges))
.then(
(_) => twitchApi
.getBadgesChannel(id: channelId, headers: headers)
.then((badges) => twitchBadgesToObject.addAll(badges))
.catchError(onError),
ffzRoomInfo = roomInfo;
return emotes;
}).catchError(onEmoteError),
],
]).then((assets) => assets.expand((list) => list)).then(
(emotes) => _emoteToObject = {
for (final emote in emotes) emote.name: emote,
}.asObservable(),
),
ffzApi
.getBadges()
.then((badges) => _userToFFZBadges = badges)
.catchError(onError),
bttvApi
.getBadges()
.then((badges) => _userToBTTVBadges = badges)
.catchError(onError),
//Emotes
Future.wait([
// Get global badges first, then channel badges to avoid badge conflicts.
// We want the channel badges to override the global badges.
if (showTwitchBadges)
twitchApi
.getBadgesGlobal(headers: headers)
.then((badges) => twitchBadgesToObject.addAll(badges))
.then(
(_) => twitchApi
.getBadgesChannel(id: channelId, headers: headers)
.then((badges) => twitchBadgesToObject.addAll(badges))
.catchError(onBadgeError),
),
if (showFFZBadges)
ffzApi
.getBadges()
.then((badges) => _userToFFZBadges = badges)
.catchError(onBadgeError),
if (showBTTVBadges)
bttvApi
.getBadges()
.then((badges) => _userToBTTVBadges = badges)
.catchError(onBadgeError),
]),
]);
@action

View file

@ -199,11 +199,19 @@ mixin _$ChatAssetsStore on ChatAssetsStoreBase, Store {
ActionController(name: 'ChatAssetsStoreBase', context: context);
@override
Future<void> assetsFuture(
{required String channelId,
required Map<String, String> headers,
required Function onEmoteError,
required Function onBadgeError}) {
Future<void> assetsFuture({
required String channelId,
required Map<String, String> headers,
required Function onEmoteError,
required Function onBadgeError,
bool showTwitchEmotes = true,
bool showTwitchBadges = true,
bool show7TVEmotes = true,
bool showBTTVEmotes = true,
bool showBTTVBadges = true,
bool showFFZEmotes = true,
bool showFFZBadges = true,
}) {
final _$actionInfo = _$ChatAssetsStoreBaseActionController.startAction(
name: 'ChatAssetsStoreBase.assetsFuture');
try {
@ -211,37 +219,14 @@ mixin _$ChatAssetsStore on ChatAssetsStoreBase, Store {
channelId: channelId,
headers: headers,
onEmoteError: onEmoteError,
onBadgeError: onBadgeError);
} finally {
_$ChatAssetsStoreBaseActionController.endAction(_$actionInfo);
}
}
@override
Future<void> emotesFuture(
{required String channelId,
required Map<String, String> headers,
required Function onError}) {
final _$actionInfo = _$ChatAssetsStoreBaseActionController.startAction(
name: 'ChatAssetsStoreBase.emotesFuture');
try {
return super.emotesFuture(
channelId: channelId, headers: headers, onError: onError);
} finally {
_$ChatAssetsStoreBaseActionController.endAction(_$actionInfo);
}
}
@override
Future<void> badgesFuture(
{required String channelId,
required Map<String, String> headers,
required Function onError}) {
final _$actionInfo = _$ChatAssetsStoreBaseActionController.startAction(
name: 'ChatAssetsStoreBase.badgesFuture');
try {
return super.badgesFuture(
channelId: channelId, headers: headers, onError: onError);
onBadgeError: onBadgeError,
showTwitchEmotes: showTwitchEmotes,
showTwitchBadges: showTwitchBadges,
show7TVEmotes: show7TVEmotes,
showBTTVEmotes: showBTTVEmotes,
showBTTVBadges: showBTTVBadges,
showFFZEmotes: showFFZEmotes,
showFFZBadges: showFFZBadges);
} finally {
_$ChatAssetsStoreBaseActionController.endAction(_$actionInfo);
}

View file

@ -170,6 +170,34 @@ abstract class ChatStoreBase with Store {
),
);
reactions.add(
reaction(
(_) => [
settings.showTwitchEmotes,
settings.showTwitchBadges,
settings.show7TVEmotes,
settings.showBTTVEmotes,
settings.showBTTVBadges,
settings.showFFZEmotes,
settings.showFFZBadges,
],
(_) {
if (!settings.show7TVEmotes) {
_sevenTVChannel?.sink.close(1000);
}
getAssets().then((_) {
if (settings.show7TVEmotes) {
final emoteSetId = assetsStore.sevenTvEmoteSetId;
if (emoteSetId != null) {
listenToSevenTVEmoteSet(emoteSetId: emoteSetId);
}
}
});
},
),
);
// Create a timer that will add messages from the buffer every 200 milliseconds.
_messageBufferTimer = Timer.periodic(
const Duration(milliseconds: 200),
@ -353,8 +381,11 @@ abstract class ChatStoreBase with Store {
);
getAssets().then((_) {
if (assetsStore.sevenTvEmoteSetId != null) {
listenToSevenTVEmoteSet(emoteSetId: assetsStore.sevenTvEmoteSetId!);
if (!settings.show7TVEmotes) return;
final emoteSetId = assetsStore.sevenTvEmoteSetId;
if (emoteSetId != null) {
listenToSevenTVEmoteSet(emoteSetId: emoteSetId);
}
});
@ -378,6 +409,13 @@ abstract class ChatStoreBase with Store {
debugPrint(error.toString());
return <Badge>[];
},
showTwitchEmotes: settings.showTwitchEmotes,
showTwitchBadges: settings.showTwitchBadges,
show7TVEmotes: settings.show7TVEmotes,
showBTTVEmotes: settings.showBTTVEmotes,
showBTTVBadges: settings.showBTTVBadges,
showFFZEmotes: settings.showFFZEmotes,
showFFZBadges: settings.showFFZBadges,
);
/// Re-enables [_autoScroll] and jumps to the latest message.

View file

@ -14,25 +14,32 @@ class ChatBottomBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
final emoteMenuButton = Tooltip(
message: 'Emote menu',
preferBelow: false,
child: IconButton(
color: chatStore.assetsStore.showEmoteMenu
? Theme.of(context).colorScheme.secondary
: null,
icon: Icon(
chatStore.assetsStore.showEmoteMenu
? Icons.emoji_emotions_rounded
: Icons.emoji_emotions_outlined,
),
onPressed: () {
FocusScope.of(context).unfocus();
chatStore.assetsStore.showEmoteMenu =
!chatStore.assetsStore.showEmoteMenu;
},
),
);
final isEmotesEnabled = chatStore.settings.showTwitchEmotes ||
chatStore.settings.show7TVEmotes ||
chatStore.settings.showBTTVEmotes ||
chatStore.settings.showFFZEmotes;
final emoteMenuButton = isEmotesEnabled
? Tooltip(
message: 'Emote menu',
preferBelow: false,
child: IconButton(
color: chatStore.assetsStore.showEmoteMenu
? Theme.of(context).colorScheme.secondary
: null,
icon: Icon(
chatStore.assetsStore.showEmoteMenu
? Icons.emoji_emotions_rounded
: Icons.emoji_emotions_outlined,
),
onPressed: () {
FocusScope.of(context).unfocus();
chatStore.assetsStore.showEmoteMenu =
!chatStore.assetsStore.showEmoteMenu;
},
),
)
: null;
return Observer(
builder: (context) {

View file

@ -288,6 +288,42 @@ class _ChatSettingsState extends State<ChatSettings> {
value: settingsStore.autocomplete,
onChanged: (newValue) => settingsStore.autocomplete = newValue,
),
const SectionHeader('Emotes and badges', showDivider: true),
SettingsListSwitch(
title: 'Show Twitch emotes',
value: settingsStore.showTwitchEmotes,
onChanged: (newValue) => settingsStore.showTwitchEmotes = newValue,
),
SettingsListSwitch(
title: 'Show Twitch badges',
value: settingsStore.showTwitchBadges,
onChanged: (newValue) => settingsStore.showTwitchBadges = newValue,
),
SettingsListSwitch(
title: 'Show 7TV emotes',
value: settingsStore.show7TVEmotes,
onChanged: (newValue) => settingsStore.show7TVEmotes = newValue,
),
SettingsListSwitch(
title: 'Show BTTV emotes',
value: settingsStore.showBTTVEmotes,
onChanged: (newValue) => settingsStore.showBTTVEmotes = newValue,
),
SettingsListSwitch(
title: 'Show BTTV badges',
value: settingsStore.showBTTVBadges,
onChanged: (newValue) => settingsStore.showBTTVBadges = newValue,
),
SettingsListSwitch(
title: 'Show FFZ emotes',
value: settingsStore.showFFZEmotes,
onChanged: (newValue) => settingsStore.showFFZEmotes = newValue,
),
SettingsListSwitch(
title: 'Show FFZ badges',
value: settingsStore.showFFZBadges,
onChanged: (newValue) => settingsStore.showFFZBadges = newValue,
),
],
),
);

View file

@ -141,6 +141,15 @@ abstract class _SettingsStoreBase with Store {
// Autocomplete defaults
static const defaultAutocomplete = true;
// Emotes and badges defaults
static const defaultShowTwitchEmotes = true;
static const defaultShowTwitchBadges = true;
static const defaultShow7TVEmotes = true;
static const defaultShowBTTVEmotes = true;
static const defaultShowBTTVBadges = true;
static const defaultShowFFZEmotes = true;
static const defaultShowFFZBadges = true;
// Message sizing options
@JsonKey(defaultValue: defaultBadgeScale)
@observable
@ -244,6 +253,35 @@ abstract class _SettingsStoreBase with Store {
@observable
var autocomplete = defaultAutocomplete;
// Emotes and badges
@JsonKey(defaultValue: defaultShowTwitchEmotes)
@observable
var showTwitchEmotes = defaultShowTwitchEmotes;
@JsonKey(defaultValue: defaultShowTwitchBadges)
@observable
var showTwitchBadges = defaultShowTwitchBadges;
@JsonKey(defaultValue: defaultShow7TVEmotes)
@observable
var show7TVEmotes = defaultShow7TVEmotes;
@JsonKey(defaultValue: defaultShowBTTVEmotes)
@observable
var showBTTVEmotes = defaultShowBTTVEmotes;
@JsonKey(defaultValue: defaultShowBTTVBadges)
@observable
var showBTTVBadges = defaultShowBTTVBadges;
@JsonKey(defaultValue: defaultShowFFZEmotes)
@observable
var showFFZEmotes = defaultShowFFZEmotes;
@JsonKey(defaultValue: defaultShowFFZBadges)
@observable
var showFFZBadges = defaultShowFFZBadges;
@action
void resetChatSettings() {
badgeScale = defaultBadgeScale;
@ -275,6 +313,14 @@ abstract class _SettingsStoreBase with Store {
chatOnlyPreventSleep = defaultChatOnlyPreventSleep;
autocomplete = defaultAutocomplete;
showTwitchEmotes = defaultShowTwitchEmotes;
showTwitchBadges = defaultShowTwitchBadges;
show7TVEmotes = defaultShow7TVEmotes;
showBTTVEmotes = defaultShowBTTVEmotes;
showBTTVBadges = defaultShowBTTVBadges;
showFFZEmotes = defaultShowFFZEmotes;
showFFZBadges = defaultShowFFZBadges;
}
// * Other settings

View file

@ -54,6 +54,13 @@ SettingsStore _$SettingsStoreFromJson(Map<String, dynamic> json) =>
(json['fullScreenChatOverlayOpacity'] as num?)?.toDouble() ?? 0.5
..chatOnlyPreventSleep = json['chatOnlyPreventSleep'] as bool? ?? false
..autocomplete = json['autocomplete'] as bool? ?? true
..showTwitchEmotes = json['showTwitchEmotes'] as bool? ?? true
..showTwitchBadges = json['showTwitchBadges'] as bool? ?? true
..show7TVEmotes = json['show7TVEmotes'] as bool? ?? true
..showBTTVEmotes = json['showBTTVEmotes'] as bool? ?? true
..showBTTVBadges = json['showBTTVBadges'] as bool? ?? true
..showFFZEmotes = json['showFFZEmotes'] as bool? ?? true
..showFFZBadges = json['showFFZBadges'] as bool? ?? true
..shareCrashLogsAndAnalytics =
json['shareCrashLogsAndAnalytics'] as bool? ?? true
..fullScreen = json['fullScreen'] as bool? ?? false
@ -95,6 +102,13 @@ Map<String, dynamic> _$SettingsStoreToJson(SettingsStore instance) =>
'fullScreenChatOverlayOpacity': instance.fullScreenChatOverlayOpacity,
'chatOnlyPreventSleep': instance.chatOnlyPreventSleep,
'autocomplete': instance.autocomplete,
'showTwitchEmotes': instance.showTwitchEmotes,
'showTwitchBadges': instance.showTwitchBadges,
'show7TVEmotes': instance.show7TVEmotes,
'showBTTVEmotes': instance.showBTTVEmotes,
'showBTTVBadges': instance.showBTTVBadges,
'showFFZEmotes': instance.showFFZEmotes,
'showFFZBadges': instance.showFFZBadges,
'shareCrashLogsAndAnalytics': instance.shareCrashLogsAndAnalytics,
'fullScreen': instance.fullScreen,
'fullScreenChatOverlay': instance.fullScreenChatOverlay,
@ -665,6 +679,118 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
late final _$showTwitchEmotesAtom =
Atom(name: '_SettingsStoreBase.showTwitchEmotes', context: context);
@override
bool get showTwitchEmotes {
_$showTwitchEmotesAtom.reportRead();
return super.showTwitchEmotes;
}
@override
set showTwitchEmotes(bool value) {
_$showTwitchEmotesAtom.reportWrite(value, super.showTwitchEmotes, () {
super.showTwitchEmotes = value;
});
}
late final _$showTwitchBadgesAtom =
Atom(name: '_SettingsStoreBase.showTwitchBadges', context: context);
@override
bool get showTwitchBadges {
_$showTwitchBadgesAtom.reportRead();
return super.showTwitchBadges;
}
@override
set showTwitchBadges(bool value) {
_$showTwitchBadgesAtom.reportWrite(value, super.showTwitchBadges, () {
super.showTwitchBadges = value;
});
}
late final _$show7TVEmotesAtom =
Atom(name: '_SettingsStoreBase.show7TVEmotes', context: context);
@override
bool get show7TVEmotes {
_$show7TVEmotesAtom.reportRead();
return super.show7TVEmotes;
}
@override
set show7TVEmotes(bool value) {
_$show7TVEmotesAtom.reportWrite(value, super.show7TVEmotes, () {
super.show7TVEmotes = value;
});
}
late final _$showBTTVEmotesAtom =
Atom(name: '_SettingsStoreBase.showBTTVEmotes', context: context);
@override
bool get showBTTVEmotes {
_$showBTTVEmotesAtom.reportRead();
return super.showBTTVEmotes;
}
@override
set showBTTVEmotes(bool value) {
_$showBTTVEmotesAtom.reportWrite(value, super.showBTTVEmotes, () {
super.showBTTVEmotes = value;
});
}
late final _$showBTTVBadgesAtom =
Atom(name: '_SettingsStoreBase.showBTTVBadges', context: context);
@override
bool get showBTTVBadges {
_$showBTTVBadgesAtom.reportRead();
return super.showBTTVBadges;
}
@override
set showBTTVBadges(bool value) {
_$showBTTVBadgesAtom.reportWrite(value, super.showBTTVBadges, () {
super.showBTTVBadges = value;
});
}
late final _$showFFZEmotesAtom =
Atom(name: '_SettingsStoreBase.showFFZEmotes', context: context);
@override
bool get showFFZEmotes {
_$showFFZEmotesAtom.reportRead();
return super.showFFZEmotes;
}
@override
set showFFZEmotes(bool value) {
_$showFFZEmotesAtom.reportWrite(value, super.showFFZEmotes, () {
super.showFFZEmotes = value;
});
}
late final _$showFFZBadgesAtom =
Atom(name: '_SettingsStoreBase.showFFZBadges', context: context);
@override
bool get showFFZBadges {
_$showFFZBadgesAtom.reportRead();
return super.showFFZBadges;
}
@override
set showFFZBadges(bool value) {
_$showFFZBadgesAtom.reportWrite(value, super.showFFZBadges, () {
super.showFFZBadges = value;
});
}
late final _$shareCrashLogsAndAnalyticsAtom = Atom(
name: '_SettingsStoreBase.shareCrashLogsAndAnalytics', context: context);
@ -820,6 +946,13 @@ chatWidth: ${chatWidth},
fullScreenChatOverlayOpacity: ${fullScreenChatOverlayOpacity},
chatOnlyPreventSleep: ${chatOnlyPreventSleep},
autocomplete: ${autocomplete},
showTwitchEmotes: ${showTwitchEmotes},
showTwitchBadges: ${showTwitchBadges},
show7TVEmotes: ${show7TVEmotes},
showBTTVEmotes: ${showBTTVEmotes},
showBTTVBadges: ${showBTTVBadges},
showFFZEmotes: ${showFFZEmotes},
showFFZBadges: ${showFFZBadges},
shareCrashLogsAndAnalytics: ${shareCrashLogsAndAnalytics},
fullScreen: ${fullScreen},
fullScreenChatOverlay: ${fullScreenChatOverlay}