Add support for FFZ dev, bot, and supporter badges
This commit is contained in:
parent
d9bca9cc59
commit
c86b0f8c8f
6 changed files with 159 additions and 9 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:convert';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:frosty/models/badges.dart';
|
||||
import 'package:frosty/models/emotes.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
|
|
@ -11,7 +12,7 @@ class FFZ {
|
|||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body) as List;
|
||||
final emotes = decoded.map((emote) => EmoteFFZ.fromJson(emote)).toList();
|
||||
final emotes = decoded.map((emote) => EmoteFFZ.fromJson(emote));
|
||||
|
||||
return emotes.map((emote) => Emote.fromFFZ(emote, EmoteType.ffzGlobal)).toList();
|
||||
} else {
|
||||
|
|
@ -27,7 +28,7 @@ class FFZ {
|
|||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body) as List;
|
||||
final emotes = decoded.map((emote) => EmoteFFZ.fromJson(emote)).toList();
|
||||
final emotes = decoded.map((emote) => EmoteFFZ.fromJson(emote));
|
||||
|
||||
return emotes.map((emote) => Emote.fromFFZ(emote, EmoteType.ffzChannel)).toList();
|
||||
} else {
|
||||
|
|
@ -35,4 +36,50 @@ class FFZ {
|
|||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static Future<Map<String, List<BadgeInfoFFZ>>?> getBadges() async {
|
||||
final url = Uri.parse('https://api.frankerfacez.com/v1/badges');
|
||||
final response = await http.get(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body);
|
||||
final badges = decoded['badges'] as List;
|
||||
final users = decoded['users'];
|
||||
|
||||
final badgeObjects = badges.map((badge) => BadgeInfoFFZ.fromJson(badge)).toList();
|
||||
|
||||
final result = <String, List<BadgeInfoFFZ>>{};
|
||||
final developers = users['1'] as List;
|
||||
final bots = users['2'] as List;
|
||||
final supporters = users['3'] as List;
|
||||
|
||||
for (var username in developers) {
|
||||
final entry = result[username];
|
||||
if (entry != null) {
|
||||
entry.add(badgeObjects[0]);
|
||||
} else {
|
||||
result[username] = [badgeObjects[0]];
|
||||
}
|
||||
}
|
||||
for (var username in bots) {
|
||||
final entry = result[username];
|
||||
if (entry != null) {
|
||||
entry.add(badgeObjects[1]);
|
||||
} else {
|
||||
result[username] = [badgeObjects[1]];
|
||||
}
|
||||
}
|
||||
for (var username in supporters) {
|
||||
final entry = result[username];
|
||||
if (entry != null) {
|
||||
entry.add(badgeObjects[2]);
|
||||
} else {
|
||||
result[username] = [badgeObjects[2]];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
debugPrint('Failed to get FFZ badges');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,3 +29,46 @@ class BadgeInfoTwitch {
|
|||
|
||||
factory BadgeInfoTwitch.fromJson(Map<String, dynamic> json) => _$BadgeInfoTwitchFromJson(json);
|
||||
}
|
||||
|
||||
@JsonSerializable(createToJson: false, fieldRename: FieldRename.snake)
|
||||
class BadgeInfoFFZ {
|
||||
final int id;
|
||||
final String name;
|
||||
final String title;
|
||||
final int slot;
|
||||
final String? replaces;
|
||||
final String color;
|
||||
final String image;
|
||||
final BadgeUrlsFFZ urls;
|
||||
|
||||
const BadgeInfoFFZ(
|
||||
this.id,
|
||||
this.name,
|
||||
this.title,
|
||||
this.slot,
|
||||
this.replaces,
|
||||
this.color,
|
||||
this.image,
|
||||
this.urls,
|
||||
);
|
||||
|
||||
factory BadgeInfoFFZ.fromJson(Map<String, dynamic> json) => _$BadgeInfoFFZFromJson(json);
|
||||
}
|
||||
|
||||
@JsonSerializable(createToJson: false)
|
||||
class BadgeUrlsFFZ {
|
||||
@JsonKey(name: '1')
|
||||
final String url1x;
|
||||
@JsonKey(name: '2')
|
||||
final String url2x;
|
||||
@JsonKey(name: '4')
|
||||
final String url4x;
|
||||
|
||||
const BadgeUrlsFFZ(
|
||||
this.url1x,
|
||||
this.url2x,
|
||||
this.url4x,
|
||||
);
|
||||
|
||||
factory BadgeUrlsFFZ.fromJson(Map<String, dynamic> json) => _$BadgeUrlsFFZFromJson(json);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,3 +16,20 @@ BadgeInfoTwitch _$BadgeInfoTwitchFromJson(Map<String, dynamic> json) =>
|
|||
json['click_action'] as String,
|
||||
json['click_url'] as String,
|
||||
);
|
||||
|
||||
BadgeInfoFFZ _$BadgeInfoFFZFromJson(Map<String, dynamic> json) => BadgeInfoFFZ(
|
||||
json['id'] as int,
|
||||
json['name'] as String,
|
||||
json['title'] as String,
|
||||
json['slot'] as int,
|
||||
json['replaces'] as String?,
|
||||
json['color'] as String,
|
||||
json['image'] as String,
|
||||
BadgeUrlsFFZ.fromJson(json['urls'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
BadgeUrlsFFZ _$BadgeUrlsFFZFromJson(Map<String, dynamic> json) => BadgeUrlsFFZ(
|
||||
json['1'] as String,
|
||||
json['2'] as String,
|
||||
json['4'] as String,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -71,7 +71,8 @@ class IRCMessage {
|
|||
List<InlineSpan> generateSpan({
|
||||
required TextStyle? style,
|
||||
required Map<String, Emote> emoteToObject,
|
||||
required Map<String, BadgeInfoTwitch> badgeToObject,
|
||||
required Map<String, BadgeInfoTwitch> twitchBadges,
|
||||
required Map<String, List<BadgeInfoFFZ>> ffzBadges,
|
||||
bool hideMessage = false,
|
||||
bool zeroWidthEnabled = false,
|
||||
Timestamp timestamp = Timestamp.none,
|
||||
|
|
@ -103,10 +104,43 @@ class IRCMessage {
|
|||
}
|
||||
|
||||
// Add any badges to the span.
|
||||
|
||||
// FFZ badges
|
||||
var ignoreMod = false;
|
||||
final ffzUserBadges = ffzBadges[user];
|
||||
if (ffzUserBadges != null) {
|
||||
for (final ffzBadge in ffzUserBadges) {
|
||||
if (ffzBadge.replaces != null) ignoreMod = true;
|
||||
|
||||
span.add(
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: Tooltip(
|
||||
message: ffzBadge.title,
|
||||
preferBelow: false,
|
||||
child: ColoredBox(
|
||||
color: ffzBadge.name == 'bot' ? HexColor.fromHex('#00AD03') : HexColor.fromHex(ffzBadge.color),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: 'https:' + ffzBadge.urls.url4x,
|
||||
placeholder: (context, url) => const SizedBox(),
|
||||
fadeInDuration: const Duration(seconds: 0),
|
||||
height: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
span.add(const TextSpan(text: ' '));
|
||||
}
|
||||
}
|
||||
|
||||
// Twitch badges
|
||||
final badges = tags['badges'];
|
||||
if (badges != null) {
|
||||
for (final badge in badges.split(',')) {
|
||||
final badgeInfo = badgeToObject[badge];
|
||||
if (badge == 'moderator/1' && ignoreMod) continue;
|
||||
|
||||
final badgeInfo = twitchBadges[badge];
|
||||
if (badgeInfo != null) {
|
||||
span.add(
|
||||
WidgetSpan(
|
||||
|
|
|
|||
|
|
@ -28,7 +28,10 @@ abstract class _ChatAssetsStoreBase with Store {
|
|||
var _userEmoteToObject = ObservableMap<String, Emote>();
|
||||
|
||||
/// The map of badges ids to their object representation.
|
||||
final badgesToObject = <String, BadgeInfoTwitch>{};
|
||||
final twitchBadgesToObject = <String, BadgeInfoTwitch>{};
|
||||
|
||||
/// The map of usernames to their FFZ badges.
|
||||
final userToFFZBadges = <String, List<BadgeInfoFFZ>>{};
|
||||
|
||||
/// The current index of the emote menu stack.
|
||||
@observable
|
||||
|
|
@ -81,9 +84,12 @@ abstract class _ChatAssetsStoreBase with Store {
|
|||
|
||||
for (final map in badges) {
|
||||
if (map != null) {
|
||||
badgesToObject.addAll(map);
|
||||
twitchBadgesToObject.addAll(map);
|
||||
}
|
||||
}
|
||||
|
||||
final ffzBadges = await FFZ.getBadges();
|
||||
if (ffzBadges != null) userToFFZBadges.addAll(ffzBadges);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ class ChatMessage extends StatelessWidget {
|
|||
children: ircMessage.generateSpan(
|
||||
style: DefaultTextStyle.of(context).style,
|
||||
emoteToObject: assetsStore.emoteToObject,
|
||||
badgeToObject: assetsStore.badgesToObject,
|
||||
twitchBadges: assetsStore.twitchBadgesToObject,
|
||||
ffzBadges: assetsStore.userToFFZBadges,
|
||||
zeroWidthEnabled: zeroWidth,
|
||||
timestamp: timestamp,
|
||||
),
|
||||
|
|
@ -56,7 +57,8 @@ class ChatMessage extends StatelessWidget {
|
|||
children: ircMessage.generateSpan(
|
||||
style: DefaultTextStyle.of(context).style,
|
||||
emoteToObject: assetsStore.emoteToObject,
|
||||
badgeToObject: assetsStore.badgesToObject,
|
||||
twitchBadges: assetsStore.twitchBadgesToObject,
|
||||
ffzBadges: assetsStore.userToFFZBadges,
|
||||
hideMessage: hideMessageIfBanned,
|
||||
zeroWidthEnabled: zeroWidth,
|
||||
timestamp: timestamp,
|
||||
|
|
@ -105,7 +107,8 @@ class ChatMessage extends StatelessWidget {
|
|||
children: ircMessage.generateSpan(
|
||||
style: DefaultTextStyle.of(context).style,
|
||||
emoteToObject: assetsStore.emoteToObject,
|
||||
badgeToObject: assetsStore.badgesToObject,
|
||||
twitchBadges: assetsStore.twitchBadgesToObject,
|
||||
ffzBadges: assetsStore.userToFFZBadges,
|
||||
zeroWidthEnabled: zeroWidth,
|
||||
timestamp: timestamp,
|
||||
),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue