Add tooltips to emotes & badges, update API
This commit is contained in:
parent
81c18feb45
commit
a95c3eb7ae
9 changed files with 67 additions and 106 deletions
|
|
@ -11,7 +11,7 @@ class BTTV {
|
|||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body) as List;
|
||||
final List<EmoteBTTVGlobal> emotes = decoded.map((emote) => EmoteBTTVGlobal.fromJson(emote)).toList();
|
||||
final emotes = decoded.map((emote) => EmoteBTTVGlobal.fromJson(emote)).toList();
|
||||
|
||||
final emoteToUrl = <String, String>{};
|
||||
for (final emote in emotes) {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ class FFZ {
|
|||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body) as List;
|
||||
final List<EmoteFFZ> emotes = decoded.map((emote) => EmoteFFZ.fromJson(emote)).toList();
|
||||
final emotes = decoded.map((emote) => EmoteFFZ.fromJson(emote)).toList();
|
||||
|
||||
final emoteToUrl = <String, String>{};
|
||||
for (final emote in emotes) {
|
||||
|
|
@ -31,7 +31,7 @@ class FFZ {
|
|||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body) as List;
|
||||
final List<EmoteFFZ> emotes = decoded.map((emote) => EmoteFFZ.fromJson(emote)).toList();
|
||||
final emotes = decoded.map((emote) => EmoteFFZ.fromJson(emote)).toList();
|
||||
|
||||
final emoteToUrl = <String, String>{};
|
||||
for (final emote in emotes) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:frosty/models/irc.dart';
|
||||
|
||||
class IRC {
|
||||
|
|
@ -97,11 +97,15 @@ class IRC {
|
|||
span.add(
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: badgeUrl,
|
||||
placeholder: (context, url) => const SizedBox(),
|
||||
fadeInDuration: const Duration(seconds: 0),
|
||||
height: 20,
|
||||
child: Tooltip(
|
||||
message: badge,
|
||||
preferBelow: false,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: badgeUrl,
|
||||
placeholder: (context, url) => const SizedBox(),
|
||||
fadeInDuration: const Duration(seconds: 0),
|
||||
height: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
@ -153,11 +157,15 @@ class IRC {
|
|||
span.add(
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: emoteUrl,
|
||||
placeholder: (context, url) => const SizedBox(),
|
||||
fadeInDuration: const Duration(seconds: 0),
|
||||
height: 25,
|
||||
child: Tooltip(
|
||||
message: word,
|
||||
preferBelow: false,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: emoteUrl,
|
||||
placeholder: (context, url) => const SizedBox(),
|
||||
fadeInDuration: const Duration(seconds: 0),
|
||||
height: 25,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class SevenTV {
|
|||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body) as List;
|
||||
final List<Emote7TV> emotes = decoded.map((emote) => Emote7TV.fromJson(emote)).toList();
|
||||
final emotes = decoded.map((emote) => Emote7TV.fromJson(emote)).toList();
|
||||
|
||||
final emoteToUrl = <String, String>{};
|
||||
for (final emote in emotes) {
|
||||
|
|
@ -31,7 +31,7 @@ class SevenTV {
|
|||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body) as List;
|
||||
final List<Emote7TV> emotes = decoded.map((emote) => Emote7TV.fromJson(emote)).toList();
|
||||
final emotes = decoded.map((emote) => Emote7TV.fromJson(emote)).toList();
|
||||
|
||||
final emoteToUrl = <String, String>{};
|
||||
for (final emote in emotes) {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class Twitch {
|
|||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body)['data'] as List;
|
||||
final List<EmoteTwitch> emotes = decoded.map((emote) => EmoteTwitch.fromJson(emote)).toList();
|
||||
final emotes = decoded.map((emote) => EmoteTwitch.fromJson(emote)).toList();
|
||||
|
||||
final emoteToUrl = <String, String>{};
|
||||
for (final emote in emotes) {
|
||||
|
|
@ -37,7 +37,7 @@ class Twitch {
|
|||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body)['data'] as List;
|
||||
final List<EmoteTwitch> emotes = decoded.map((emote) => EmoteTwitch.fromJson(emote)).toList();
|
||||
final emotes = decoded.map((emote) => EmoteTwitch.fromJson(emote)).toList();
|
||||
|
||||
final emoteToUrl = <String, String>{};
|
||||
for (final emote in emotes) {
|
||||
|
|
@ -51,44 +51,39 @@ class Twitch {
|
|||
}
|
||||
|
||||
/// Returns a map of global Twitch badges to their URL.
|
||||
static Future<Map<String, String>?> getBadgesGlobal({required Map<String, String>? headers}) async {
|
||||
final url = Uri.parse('https://api.twitch.tv/helix/chat/badges/global');
|
||||
final response = await http.get(url, headers: headers);
|
||||
static Future<Map<String, String>?> getBadgesGlobal() async {
|
||||
final url = Uri.parse('https://badges.twitch.tv/v1/badges/global/display');
|
||||
final response = await http.get(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body)['data'] as List;
|
||||
final List<BadgesTwitch> badges = decoded.map((emote) => BadgesTwitch.fromJson(emote)).toList();
|
||||
final result = <String, String>{};
|
||||
|
||||
final badgeToUrl = <String, String>{};
|
||||
for (final badge in badges) {
|
||||
for (final badgeVersion in badge.versions) {
|
||||
badgeToUrl['${badge.setId}/${badgeVersion.id}'] = badgeVersion.imageUrl4x;
|
||||
}
|
||||
}
|
||||
final decoded = jsonDecode(response.body)['badge_sets'] as Map;
|
||||
|
||||
return badgeToUrl;
|
||||
// TODO: Figure out cleaner way to decode badge JSON.
|
||||
decoded.forEach((id, versions) =>
|
||||
(versions['versions'] as Map).forEach((version, badgeInfo) => result['$id/$version'] = BadgeInfoTwitch.fromJson(badgeInfo).imageUrl4x));
|
||||
|
||||
return result;
|
||||
} else {
|
||||
debugPrint('Failed to get global Twitch badges. Error code: ${response.statusCode}');
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a map of a channel's Twitch badges to their URL.
|
||||
static Future<Map<String, String>?> getBadgesChannel({required String id, required Map<String, String>? headers}) async {
|
||||
final url = Uri.parse('https://api.twitch.tv/helix/chat/badges?broadcaster_id=$id');
|
||||
final response = await http.get(url, headers: headers);
|
||||
static Future<Map<String, String>?> getBadgesChannel({required String id}) async {
|
||||
final url = Uri.parse('https://badges.twitch.tv/v1/badges/channels/$id/display');
|
||||
final response = await http.get(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body)['data'] as List;
|
||||
final List<BadgesTwitch> badges = decoded.map((emote) => BadgesTwitch.fromJson(emote)).toList();
|
||||
final result = <String, String>{};
|
||||
|
||||
final badgeToUrl = <String, String>{};
|
||||
for (final badge in badges) {
|
||||
for (final badgeVersion in badge.versions) {
|
||||
badgeToUrl['${badge.setId}/${badgeVersion.id}'] = badgeVersion.imageUrl4x;
|
||||
}
|
||||
}
|
||||
final decoded = jsonDecode(response.body)['badge_sets'] as Map;
|
||||
|
||||
return badgeToUrl;
|
||||
decoded.forEach((id, versions) =>
|
||||
(versions['versions'] as Map).forEach((version, badgeInfo) => result['$id/$version'] = BadgeInfoTwitch.fromJson(badgeInfo).imageUrl4x));
|
||||
|
||||
return result;
|
||||
} else {
|
||||
debugPrint('Failed to get Twitch badges for id: $id. Error code: ${response.statusCode}');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@ import 'package:json_annotation/json_annotation.dart';
|
|||
part 'badges.g.dart';
|
||||
|
||||
// Twitch Badges
|
||||
@JsonSerializable(createToJson: false)
|
||||
class BadgeImagesTwitch {
|
||||
final String id;
|
||||
@JsonSerializable(createToJson: false, fieldRename: FieldRename.snake)
|
||||
class BadgeInfoTwitch {
|
||||
@JsonKey(name: 'image_url_1x')
|
||||
final String imageUrl1x;
|
||||
@JsonKey(name: 'image_url_2x')
|
||||
|
|
@ -13,25 +12,20 @@ class BadgeImagesTwitch {
|
|||
@JsonKey(name: 'image_url_4x')
|
||||
final String imageUrl4x;
|
||||
|
||||
const BadgeImagesTwitch(
|
||||
this.id,
|
||||
final String description;
|
||||
final String title;
|
||||
final String clickAction;
|
||||
final String clickUrl;
|
||||
|
||||
const BadgeInfoTwitch(
|
||||
this.imageUrl1x,
|
||||
this.imageUrl2x,
|
||||
this.imageUrl4x,
|
||||
this.description,
|
||||
this.title,
|
||||
this.clickAction,
|
||||
this.clickUrl,
|
||||
);
|
||||
|
||||
factory BadgeImagesTwitch.fromJson(Map<String, dynamic> json) => _$BadgeImagesTwitchFromJson(json);
|
||||
}
|
||||
|
||||
@JsonSerializable(createToJson: false, fieldRename: FieldRename.snake)
|
||||
class BadgesTwitch {
|
||||
final String setId;
|
||||
final List<BadgeImagesTwitch> versions;
|
||||
|
||||
const BadgesTwitch(
|
||||
this.setId,
|
||||
this.versions,
|
||||
);
|
||||
|
||||
factory BadgesTwitch.fromJson(Map<String, dynamic> json) => _$BadgesTwitchFromJson(json);
|
||||
factory BadgeInfoTwitch.fromJson(Map<String, dynamic> json) => _$BadgeInfoTwitchFromJson(json);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,17 +6,13 @@ part of 'badges.dart';
|
|||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
BadgeImagesTwitch _$BadgeImagesTwitchFromJson(Map<String, dynamic> json) =>
|
||||
BadgeImagesTwitch(
|
||||
json['id'] as String,
|
||||
BadgeInfoTwitch _$BadgeInfoTwitchFromJson(Map<String, dynamic> json) =>
|
||||
BadgeInfoTwitch(
|
||||
json['image_url_1x'] as String,
|
||||
json['image_url_2x'] as String,
|
||||
json['image_url_4x'] as String,
|
||||
);
|
||||
|
||||
BadgesTwitch _$BadgesTwitchFromJson(Map<String, dynamic> json) => BadgesTwitch(
|
||||
json['set_id'] as String,
|
||||
(json['versions'] as List<dynamic>)
|
||||
.map((e) => BadgeImagesTwitch.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
json['description'] as String,
|
||||
json['title'] as String,
|
||||
json['click_action'] as String,
|
||||
json['click_url'] as String,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ abstract class _ChatStoreBase with Store {
|
|||
if (message.startsWith('@')) {
|
||||
final parsedIRCMessage = IRCMessage.fromString(message);
|
||||
|
||||
// Filter messages from any blocked users if not moderating and not the channel owner
|
||||
// Filter messages from any blocked users if not a moderator or not the channel owner.
|
||||
if (!_userState.mod &&
|
||||
channelName != auth.user?.login &&
|
||||
auth.blockedUsers.where((blockedUser) => blockedUser.userLogin == parsedIRCMessage.user).isNotEmpty) continue;
|
||||
|
|
@ -228,8 +228,8 @@ abstract class _ChatStoreBase with Store {
|
|||
await BTTV.getEmotesChannel(id: channelInfo.id),
|
||||
await Twitch.getEmotesGlobal(headers: auth.headersTwitch),
|
||||
await Twitch.getEmotesChannel(id: channelInfo.id, headers: auth.headersTwitch),
|
||||
await Twitch.getBadgesGlobal(headers: auth.headersTwitch),
|
||||
await Twitch.getBadgesChannel(id: channelInfo.id, headers: auth.headersTwitch),
|
||||
await Twitch.getBadgesGlobal(),
|
||||
await Twitch.getBadgesChannel(id: channelInfo.id),
|
||||
await SevenTV.getEmotesGlobal(),
|
||||
await SevenTV.getEmotesChannel(user: channelInfo.login)
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
import "dart:convert";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
import 'package:frosty/models/badges.dart';
|
||||
|
||||
void main() {
|
||||
group('Twitch', () {
|
||||
test('badge should parse correctly', () {
|
||||
const sampleJson = '''{
|
||||
"set_id": "vip",
|
||||
"versions": [
|
||||
{
|
||||
"id": "1",
|
||||
"image_url_1x": "https://static-cdn.jtvnw.net/badges/v1/b817aba4-fad8-49e2-b88a-7cc744dfa6ec/1",
|
||||
"image_url_2x": "https://static-cdn.jtvnw.net/badges/v1/b817aba4-fad8-49e2-b88a-7cc744dfa6ec/2",
|
||||
"image_url_4x": "https://static-cdn.jtvnw.net/badges/v1/b817aba4-fad8-49e2-b88a-7cc744dfa6ec/3"
|
||||
}
|
||||
]
|
||||
}''';
|
||||
|
||||
final decoded = jsonDecode(sampleJson);
|
||||
final badge = BadgesTwitch.fromJson(decoded);
|
||||
|
||||
expect(badge.setId, "vip");
|
||||
expect(badge.versions.first.id, "1");
|
||||
expect(badge.versions.length, 1);
|
||||
expect(badge.versions.first.id, "1");
|
||||
expect(badge.versions.first.imageUrl1x, "https://static-cdn.jtvnw.net/badges/v1/b817aba4-fad8-49e2-b88a-7cc744dfa6ec/1");
|
||||
expect(badge.versions.first.imageUrl2x, "https://static-cdn.jtvnw.net/badges/v1/b817aba4-fad8-49e2-b88a-7cc744dfa6ec/2");
|
||||
expect(badge.versions.first.imageUrl4x, "https://static-cdn.jtvnw.net/badges/v1/b817aba4-fad8-49e2-b88a-7cc744dfa6ec/3");
|
||||
});
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue