Refactor APIs to use a persistent client
This commit is contained in:
parent
91d70fffae
commit
34263fbf94
19 changed files with 256 additions and 127 deletions
|
|
@ -1,14 +1,19 @@
|
|||
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;
|
||||
import 'package:http/http.dart';
|
||||
|
||||
class BTTVApi {
|
||||
final Client _client;
|
||||
|
||||
BTTVApi(this._client);
|
||||
|
||||
class BTTV {
|
||||
/// Returns a map of global BTTV emotes to their URL.
|
||||
static Future<List<Emote>> getEmotesGlobal() async {
|
||||
Future<List<Emote>> getEmotesGlobal() async {
|
||||
final url = Uri.parse('https://api.betterttv.net/3/cached/emotes/global');
|
||||
final response = await http.get(url);
|
||||
final response = await _client.get(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body) as List;
|
||||
|
|
@ -22,9 +27,9 @@ class BTTV {
|
|||
}
|
||||
|
||||
/// Returns a map of a channel's BTTV emotes to their URL.
|
||||
static Future<List<Emote>> getEmotesChannel({required String id}) async {
|
||||
Future<List<Emote>> getEmotesChannel({required String id}) async {
|
||||
final url = Uri.parse('https://api.betterttv.net/3/cached/users/twitch/$id');
|
||||
final response = await http.get(url);
|
||||
final response = await _client.get(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body);
|
||||
|
|
@ -41,9 +46,9 @@ class BTTV {
|
|||
}
|
||||
}
|
||||
|
||||
static Future<Map<String, Badge>?> getBadges() async {
|
||||
Future<Map<String, Badge>?> getBadges() async {
|
||||
final url = Uri.parse('https://api.betterttv.net/3/cached/badges');
|
||||
final response = await http.get(url);
|
||||
final response = await _client.get(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final badges = jsonDecode(response.body) as List;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
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;
|
||||
import 'package:http/http.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class FFZ {
|
||||
class FFZApi {
|
||||
final Client _client;
|
||||
|
||||
FFZApi(this._client);
|
||||
|
||||
/// Returns a map of global FFZ emotes to their URL.
|
||||
static Future<List<Emote>> getEmotesGlobal() async {
|
||||
Future<List<Emote>> getEmotesGlobal() async {
|
||||
final url = Uri.parse('https://api.frankerfacez.com/v1/set/global');
|
||||
final response = await http.get(url);
|
||||
final response = await _client.get(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body);
|
||||
|
|
@ -29,9 +34,9 @@ class FFZ {
|
|||
}
|
||||
|
||||
/// Returns a channel's FFZ room info including custom badges and emote used.
|
||||
static Future<Tuple2<RoomFFZ, List<Emote>>?> getRoomInfo({required String name}) async {
|
||||
Future<Tuple2<RoomFFZ, List<Emote>>?> getRoomInfo({required String name}) async {
|
||||
final url = Uri.parse('https://api.frankerfacez.com/v1/room/$name');
|
||||
final response = await http.get(url);
|
||||
final response = await _client.get(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body);
|
||||
|
|
@ -46,9 +51,9 @@ class FFZ {
|
|||
}
|
||||
}
|
||||
|
||||
static Future<Map<String, List<Badge>>?> getBadges() async {
|
||||
Future<Map<String, List<Badge>>?> getBadges() async {
|
||||
final url = Uri.parse('https://api.frankerfacez.com/v1/badges/ids');
|
||||
final response = await http.get(url);
|
||||
final response = await _client.get(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body);
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
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;
|
||||
import 'package:http/http.dart';
|
||||
|
||||
// A class for general requests.
|
||||
class SevenTV {
|
||||
class SevenTVAPI {
|
||||
final Client _client;
|
||||
|
||||
SevenTVAPI(this._client);
|
||||
|
||||
/// Returns a map of global 7TV emotes to their URL.
|
||||
static Future<List<Emote>> getEmotesGlobal() async {
|
||||
Future<List<Emote>> getEmotesGlobal() async {
|
||||
final url = Uri.parse('https://api.7tv.app/v2/emotes/global');
|
||||
final response = await http.get(url);
|
||||
final response = await _client.get(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body) as List;
|
||||
|
|
@ -23,9 +28,9 @@ class SevenTV {
|
|||
}
|
||||
|
||||
/// Returns a map of a channel's 7TV emotes to their URL.
|
||||
static Future<List<Emote>> getEmotesChannel({required String user}) async {
|
||||
Future<List<Emote>> getEmotesChannel({required String user}) async {
|
||||
final url = Uri.parse('https://api.7tv.app/v2/users/$user/emotes');
|
||||
final response = await http.get(url);
|
||||
final response = await _client.get(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body) as List;
|
||||
|
|
@ -38,9 +43,9 @@ class SevenTV {
|
|||
}
|
||||
}
|
||||
|
||||
static Future<Map<String, List<Badge>>?> getBadges() async {
|
||||
Future<Map<String, List<Badge>>?> getBadges() async {
|
||||
final url = Uri.parse('https://api.7tv.app/v2/badges?user_identifier=twitch_id');
|
||||
final response = await http.get(url);
|
||||
final response = await _client.get(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body)['badges'] as List;
|
||||
|
|
|
|||
|
|
@ -9,12 +9,16 @@ import 'package:frosty/models/chatters.dart';
|
|||
import 'package:frosty/models/emotes.dart';
|
||||
import 'package:frosty/models/stream.dart';
|
||||
import 'package:frosty/models/user.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:http/http.dart';
|
||||
|
||||
class Twitch {
|
||||
static Future<List<Emote>> getEmotesGlobal({required Map<String, String>? headers}) async {
|
||||
class TwitchApi {
|
||||
final Client _client;
|
||||
|
||||
TwitchApi(this._client);
|
||||
|
||||
Future<List<Emote>> getEmotesGlobal({required Map<String, String>? headers}) async {
|
||||
final url = Uri.parse('https://api.twitch.tv/helix/chat/emotes/global');
|
||||
final response = await http.get(url, headers: headers);
|
||||
final response = await _client.get(url, headers: headers);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body)['data'] as List;
|
||||
|
|
@ -28,9 +32,9 @@ class Twitch {
|
|||
}
|
||||
|
||||
/// Returns a map of a channel's Twitch emotes to their URL.
|
||||
static Future<List<Emote>> getEmotesChannel({required String id, required Map<String, String>? headers}) async {
|
||||
Future<List<Emote>> getEmotesChannel({required String id, required Map<String, String>? headers}) async {
|
||||
final url = Uri.parse('https://api.twitch.tv/helix/chat/emotes?broadcaster_id=$id');
|
||||
final response = await http.get(url, headers: headers);
|
||||
final response = await _client.get(url, headers: headers);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body)['data'] as List;
|
||||
|
|
@ -44,9 +48,9 @@ class Twitch {
|
|||
}
|
||||
|
||||
/// Returns a map of a channel's Twitch emotes to their URL.
|
||||
static Future<List<Emote>> getEmotesSets({required String setId, required Map<String, String>? headers, sub = false}) async {
|
||||
Future<List<Emote>> getEmotesSets({required String setId, required Map<String, String>? headers, sub = false}) async {
|
||||
final url = Uri.parse('https://api.twitch.tv/helix/chat/emotes/set?emote_set_id=$setId');
|
||||
final response = await http.get(url, headers: headers);
|
||||
final response = await _client.get(url, headers: headers);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body)['data'] as List;
|
||||
|
|
@ -69,9 +73,9 @@ class Twitch {
|
|||
}
|
||||
|
||||
/// Returns a map of global Twitch badges to their URL.
|
||||
static Future<Map<String, Badge>> getBadgesGlobal() async {
|
||||
Future<Map<String, Badge>> getBadgesGlobal() async {
|
||||
final url = Uri.parse('https://badges.twitch.tv/v1/badges/global/display');
|
||||
final response = await http.get(url);
|
||||
final response = await _client.get(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final result = <String, Badge>{};
|
||||
|
|
@ -89,9 +93,9 @@ class Twitch {
|
|||
}
|
||||
|
||||
/// Returns a map of a channel's Twitch badges to their URL.
|
||||
static Future<Map<String, Badge>> getBadgesChannel({required String id}) async {
|
||||
Future<Map<String, Badge>> getBadgesChannel({required String id}) async {
|
||||
final url = Uri.parse('https://badges.twitch.tv/v1/badges/channels/$id/display');
|
||||
final response = await http.get(url);
|
||||
final response = await _client.get(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final result = <String, Badge>{};
|
||||
|
|
@ -109,8 +113,8 @@ class Twitch {
|
|||
}
|
||||
|
||||
/// Returns the user's info given their token (headers)
|
||||
static Future<UserTwitch?> getUserInfo({required Map<String, String>? headers}) async {
|
||||
final response = await http.get(Uri.parse('https://api.twitch.tv/helix/users'), headers: headers);
|
||||
Future<UserTwitch?> getUserInfo({required Map<String, String>? headers}) async {
|
||||
final response = await _client.get(Uri.parse('https://api.twitch.tv/helix/users'), headers: headers);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final userData = jsonDecode(response.body)['data'] as List;
|
||||
|
|
@ -122,7 +126,7 @@ class Twitch {
|
|||
}
|
||||
|
||||
/// Returns a token for an anonymous user.
|
||||
static Future<String?> getDefaultToken() async {
|
||||
Future<String?> getDefaultToken() async {
|
||||
debugPrint('Getting default token...');
|
||||
|
||||
final url = Uri(
|
||||
|
|
@ -136,7 +140,7 @@ class Twitch {
|
|||
},
|
||||
);
|
||||
|
||||
final response = await http.post(url);
|
||||
final response = await _client.post(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return jsonDecode(response.body)['access_token'];
|
||||
|
|
@ -146,9 +150,9 @@ class Twitch {
|
|||
}
|
||||
|
||||
/// Returns the validity of the given token
|
||||
static Future<bool> validateToken({required String token}) async {
|
||||
Future<bool> validateToken({required String token}) async {
|
||||
debugPrint('Validating token...');
|
||||
final response = await http.get(
|
||||
final response = await _client.get(
|
||||
Uri.parse('https://id.twitch.tv/oauth2/validate'),
|
||||
headers: {'Authorization': 'Bearer $token'},
|
||||
);
|
||||
|
|
@ -163,7 +167,7 @@ class Twitch {
|
|||
}
|
||||
|
||||
/// Returns a map containing top 20 streams and a cursor for further requests.
|
||||
static Future<StreamsTwitch?> getTopStreams({required Map<String, String>? headers, String? cursor}) async {
|
||||
Future<StreamsTwitch?> getTopStreams({required Map<String, String>? headers, String? cursor}) async {
|
||||
final Uri uri;
|
||||
|
||||
if (cursor == null) {
|
||||
|
|
@ -172,7 +176,7 @@ class Twitch {
|
|||
uri = Uri.parse('https://api.twitch.tv/helix/streams?after=$cursor');
|
||||
}
|
||||
|
||||
final response = await http.get(uri, headers: headers);
|
||||
final response = await _client.get(uri, headers: headers);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body);
|
||||
|
|
@ -184,7 +188,7 @@ class Twitch {
|
|||
}
|
||||
|
||||
/// Returns a map with the given user's top 20 followed streams and a cursor for further requests.
|
||||
static Future<StreamsTwitch?> getFollowedStreams({
|
||||
Future<StreamsTwitch?> getFollowedStreams({
|
||||
required String id,
|
||||
required Map<String, String>? headers,
|
||||
String? cursor,
|
||||
|
|
@ -197,7 +201,7 @@ class Twitch {
|
|||
uri = Uri.parse('https://api.twitch.tv/helix/streams/followed?user_id=$id&after=$cursor');
|
||||
}
|
||||
|
||||
final response = await http.get(uri, headers: headers);
|
||||
final response = await _client.get(uri, headers: headers);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body);
|
||||
|
|
@ -209,7 +213,7 @@ class Twitch {
|
|||
}
|
||||
|
||||
/// Returns the list of streams under the given game/category ID.
|
||||
static Future<StreamsTwitch?> getStreamsUnderGame({
|
||||
Future<StreamsTwitch?> getStreamsUnderGame({
|
||||
required String gameId,
|
||||
required Map<String, String>? headers,
|
||||
String? cursor,
|
||||
|
|
@ -218,7 +222,7 @@ class Twitch {
|
|||
? Uri.parse('https://api.twitch.tv/helix/streams?game_id=$gameId')
|
||||
: Uri.parse('https://api.twitch.tv/helix/streams?game_id=$gameId&after=$cursor');
|
||||
|
||||
final response = await http.get(uri, headers: headers);
|
||||
final response = await _client.get(uri, headers: headers);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body);
|
||||
|
|
@ -230,10 +234,10 @@ class Twitch {
|
|||
}
|
||||
|
||||
/// Returns the stream info given the user login.
|
||||
static Future<StreamTwitch?> getStream({required String userLogin, required Map<String, String>? headers}) async {
|
||||
Future<StreamTwitch?> getStream({required String userLogin, required Map<String, String>? headers}) async {
|
||||
final uri = Uri.parse('https://api.twitch.tv/helix/streams?user_login=$userLogin');
|
||||
|
||||
final response = await http.get(uri, headers: headers);
|
||||
final response = await _client.get(uri, headers: headers);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body);
|
||||
|
|
@ -248,10 +252,10 @@ class Twitch {
|
|||
}
|
||||
|
||||
/// Returns a user's info given their login name.
|
||||
static Future<UserTwitch?> getUser({String? userLogin, String? id, required Map<String, String>? headers}) async {
|
||||
Future<UserTwitch?> getUser({String? userLogin, String? id, required Map<String, String>? headers}) async {
|
||||
final uri = id != null ? Uri.parse('https://api.twitch.tv/helix/users?id=$id') : Uri.parse('https://api.twitch.tv/helix/users?login=$userLogin');
|
||||
|
||||
final response = await http.get(uri, headers: headers);
|
||||
final response = await _client.get(uri, headers: headers);
|
||||
if (response.statusCode == 200) {
|
||||
final userData = jsonDecode(response.body)['data'] as List;
|
||||
|
||||
|
|
@ -266,8 +270,8 @@ class Twitch {
|
|||
}
|
||||
|
||||
/// Returns a channels's info associated with the given ID.
|
||||
static Future<Channel?> getChannel({required String userId, required Map<String, String>? headers}) async {
|
||||
final response = await http.get(Uri.parse('https://api.twitch.tv/helix/channels?broadcaster_id=$userId'), headers: headers);
|
||||
Future<Channel?> getChannel({required String userId, required Map<String, String>? headers}) async {
|
||||
final response = await _client.get(Uri.parse('https://api.twitch.tv/helix/channels?broadcaster_id=$userId'), headers: headers);
|
||||
if (response.statusCode == 200) {
|
||||
final channelData = jsonDecode(response.body)['data'] as List;
|
||||
|
||||
|
|
@ -282,8 +286,8 @@ class Twitch {
|
|||
}
|
||||
|
||||
/// Returns a list of channel query objects closest matching the given query string.
|
||||
static Future<List<ChannelQuery>> searchChannels({required String query, required Map<String, String>? headers}) async {
|
||||
final response = await http.get(Uri.parse('https://api.twitch.tv/helix/search/channels?first=8&query=$query'), headers: headers);
|
||||
Future<List<ChannelQuery>> searchChannels({required String query, required Map<String, String>? headers}) async {
|
||||
final response = await _client.get(Uri.parse('https://api.twitch.tv/helix/search/channels?first=8&query=$query'), headers: headers);
|
||||
if (response.statusCode == 200) {
|
||||
final channelData = jsonDecode(response.body)['data'] as List;
|
||||
|
||||
|
|
@ -294,7 +298,7 @@ class Twitch {
|
|||
}
|
||||
|
||||
/// Returns a map containing top 20 categories/games and a cursor for further requests.
|
||||
static Future<CategoriesTwitch?> getTopGames({required Map<String, String>? headers, String? cursor}) async {
|
||||
Future<CategoriesTwitch?> getTopGames({required Map<String, String>? headers, String? cursor}) async {
|
||||
final Uri uri;
|
||||
|
||||
if (cursor == null) {
|
||||
|
|
@ -303,7 +307,7 @@ class Twitch {
|
|||
uri = Uri.parse('https://api.twitch.tv/helix/games/top?after=$cursor');
|
||||
}
|
||||
|
||||
final response = await http.get(uri, headers: headers);
|
||||
final response = await _client.get(uri, headers: headers);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body);
|
||||
|
|
@ -315,12 +319,12 @@ class Twitch {
|
|||
}
|
||||
|
||||
/// Returns a map containing top 20 categories/games and a cursor for further requests.
|
||||
static Future<CategoriesTwitch?> searchCategories({required Map<String, String>? headers, required String query, String? cursor}) async {
|
||||
Future<CategoriesTwitch?> searchCategories({required Map<String, String>? headers, required String query, String? cursor}) async {
|
||||
final uri = cursor == null
|
||||
? Uri.parse('https://api.twitch.tv/helix/search/categories?first=8&query=$query')
|
||||
: Uri.parse('https://api.twitch.tv/helix/search/categories?first=8&query=$query&after=$cursor');
|
||||
|
||||
final response = await http.get(uri, headers: headers);
|
||||
final response = await _client.get(uri, headers: headers);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body);
|
||||
|
|
@ -332,10 +336,10 @@ class Twitch {
|
|||
}
|
||||
|
||||
/// Returns the sub count for a user.
|
||||
static Future<int?> getSubscriberCount({required String userId, required Map<String, String>? headers}) async {
|
||||
Future<int?> getSubscriberCount({required String userId, required Map<String, String>? headers}) async {
|
||||
final uri = Uri.parse('https://api.twitch.tv/helix/subscriptions?broadcaster_id=$userId');
|
||||
|
||||
final response = await http.get(uri, headers: headers);
|
||||
final response = await _client.get(uri, headers: headers);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body);
|
||||
|
|
@ -346,10 +350,10 @@ class Twitch {
|
|||
}
|
||||
}
|
||||
|
||||
static Future<ChatUsers?> getChatters({required String userLogin}) async {
|
||||
Future<ChatUsers?> getChatters({required String userLogin}) async {
|
||||
final uri = Uri.parse('https://tmi.twitch.tv/group/user/$userLogin/chatters');
|
||||
|
||||
final response = await http.get(uri);
|
||||
final response = await _client.get(uri);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body);
|
||||
|
|
@ -361,7 +365,7 @@ class Twitch {
|
|||
}
|
||||
|
||||
/// Returns a user's list of blocked users given their id.
|
||||
static Future<List<UserBlockedTwitch>> getUserBlockedList({
|
||||
Future<List<UserBlockedTwitch>> getUserBlockedList({
|
||||
required String id,
|
||||
required Map<String, String>? headers,
|
||||
String? cursor,
|
||||
|
|
@ -370,7 +374,7 @@ class Twitch {
|
|||
? Uri.parse('https://api.twitch.tv/helix/users/blocks?first=100&broadcaster_id=$id')
|
||||
: Uri.parse('https://api.twitch.tv/helix/users/blocks?first=100&broadcaster_id=$id&after=$cursor');
|
||||
|
||||
final response = await http.get(uri, headers: headers);
|
||||
final response = await _client.get(uri, headers: headers);
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body);
|
||||
|
||||
|
|
@ -396,10 +400,10 @@ class Twitch {
|
|||
}
|
||||
|
||||
// Blocks the user with the given ID and returns true on success or false on failure.
|
||||
static Future<bool> blockUser({required String userId, required Map<String, String> headers}) async {
|
||||
Future<bool> blockUser({required String userId, required Map<String, String> headers}) async {
|
||||
final uri = Uri.parse('https://api.twitch.tv/helix/users/blocks?target_user_id=$userId');
|
||||
|
||||
final response = await http.put(uri, headers: headers);
|
||||
final response = await _client.put(uri, headers: headers);
|
||||
if (response.statusCode == 204) {
|
||||
return true;
|
||||
} else {
|
||||
|
|
@ -408,10 +412,10 @@ class Twitch {
|
|||
}
|
||||
|
||||
// Unblocks the user with the given ID and returns true on success or false on failure.
|
||||
static Future<bool> unblockUser({required String userId, required Map<String, String> headers}) async {
|
||||
Future<bool> unblockUser({required String userId, required Map<String, String> headers}) async {
|
||||
final uri = Uri.parse('https://api.twitch.tv/helix/users/blocks?target_user_id=$userId');
|
||||
|
||||
final response = await http.delete(uri, headers: headers);
|
||||
final response = await _client.delete(uri, headers: headers);
|
||||
if (response.statusCode == 204) {
|
||||
return true;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -14,11 +14,13 @@ abstract class _AuthBase with Store {
|
|||
/// Secure storage to store tokens.
|
||||
final _storage = const FlutterSecureStorage();
|
||||
|
||||
final TwitchApi twitchApi;
|
||||
|
||||
/// Whether the token is valid or not.
|
||||
var _tokenIsValid = false;
|
||||
|
||||
/// The store containing information relevant to the current user.
|
||||
final user = UserStore();
|
||||
final UserStore user;
|
||||
|
||||
/// The current token.
|
||||
@readonly
|
||||
|
|
@ -32,6 +34,8 @@ abstract class _AuthBase with Store {
|
|||
@computed
|
||||
Map<String, String> get headersTwitch => {'Authorization': 'Bearer $_token', 'Client-Id': clientId};
|
||||
|
||||
_AuthBase({required this.twitchApi}) : user = UserStore(twitchApi: twitchApi);
|
||||
|
||||
/// Initialize by retrieving a token if it does not already exist.
|
||||
@action
|
||||
Future<void> init() async {
|
||||
|
|
@ -45,7 +49,7 @@ abstract class _AuthBase with Store {
|
|||
_token = await _storage.read(key: 'DEFAULT_TOKEN');
|
||||
// If the token does not exist, get a new token and store it.
|
||||
if (_token == null) {
|
||||
_token = await Twitch.getDefaultToken();
|
||||
_token = await twitchApi.getDefaultToken();
|
||||
await _storage.write(key: 'DEFAULT_TOKEN', value: _token);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -56,7 +60,7 @@ abstract class _AuthBase with Store {
|
|||
}
|
||||
|
||||
// Validate the token.
|
||||
_tokenIsValid = await Twitch.validateToken(token: _token!);
|
||||
_tokenIsValid = await twitchApi.validateToken(token: _token!);
|
||||
|
||||
// If the token is invalid, logout.
|
||||
if (!_tokenIsValid) {
|
||||
|
|
@ -98,7 +102,7 @@ abstract class _AuthBase with Store {
|
|||
_token = customToken;
|
||||
}
|
||||
|
||||
_tokenIsValid = await Twitch.validateToken(token: _token!);
|
||||
_tokenIsValid = await twitchApi.validateToken(token: _token!);
|
||||
if (!_tokenIsValid) return;
|
||||
|
||||
// Store the user token.
|
||||
|
|
@ -133,18 +137,18 @@ abstract class _AuthBase with Store {
|
|||
// If the default token does not already exist, get the new default token and store it.
|
||||
// Else, validate the existing token.
|
||||
if (_token == null) {
|
||||
_token = await Twitch.getDefaultToken();
|
||||
_token = await twitchApi.getDefaultToken();
|
||||
await _storage.write(key: 'DEFAULT_TOKEN', value: _token);
|
||||
_tokenIsValid = await Twitch.validateToken(token: _token!);
|
||||
_tokenIsValid = await twitchApi.validateToken(token: _token!);
|
||||
} else {
|
||||
// Validate the stored token.
|
||||
_tokenIsValid = await Twitch.validateToken(token: _token!);
|
||||
_tokenIsValid = await twitchApi.validateToken(token: _token!);
|
||||
|
||||
// If the stored token is invalid, get a new default token and store it.
|
||||
if (!_tokenIsValid) {
|
||||
_token = await Twitch.getDefaultToken();
|
||||
_token = await twitchApi.getDefaultToken();
|
||||
await _storage.write(key: 'DEFAULT_TOKEN', value: _token);
|
||||
_tokenIsValid = await Twitch.validateToken(token: _token!);
|
||||
_tokenIsValid = await twitchApi.validateToken(token: _token!);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ part 'user_store.g.dart';
|
|||
class UserStore = _UserStoreBase with _$UserStore;
|
||||
|
||||
abstract class _UserStoreBase with Store {
|
||||
final TwitchApi twitchApi;
|
||||
|
||||
/// The current user's info.
|
||||
@readonly
|
||||
UserTwitch? _details;
|
||||
|
|
@ -17,20 +19,22 @@ abstract class _UserStoreBase with Store {
|
|||
|
||||
ReactionDisposer? disposeReaction;
|
||||
|
||||
_UserStoreBase({required this.twitchApi});
|
||||
|
||||
@action
|
||||
Future<void> init({required Map<String, String> headers}) async {
|
||||
// Get and update the current user's info.
|
||||
_details = await Twitch.getUserInfo(headers: headers);
|
||||
_details = await twitchApi.getUserInfo(headers: headers);
|
||||
|
||||
// Get and update the current user's list of blocked users.
|
||||
if (_details?.id != null) _blockedUsers = (await Twitch.getUserBlockedList(id: _details!.id, headers: headers)).asObservable();
|
||||
if (_details?.id != null) _blockedUsers = (await twitchApi.getUserBlockedList(id: _details!.id, headers: headers)).asObservable();
|
||||
|
||||
disposeReaction = autorun((_) => _blockedUsers.sort((a, b) => a.userLogin.compareTo(b.userLogin)));
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> block({required String targetId, required Map<String, String> headers}) async {
|
||||
final success = await Twitch.blockUser(userId: targetId, headers: headers);
|
||||
final success = await twitchApi.blockUser(userId: targetId, headers: headers);
|
||||
// Add a slight delay between requests, otherwise the blocked list won't properly update.
|
||||
// Weird behavior, might be something to do with PUT request and time to create and update resource?
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
|
@ -39,13 +43,13 @@ abstract class _UserStoreBase with Store {
|
|||
|
||||
@action
|
||||
Future<void> unblock({required String targetId, required Map<String, String> headers}) async {
|
||||
final success = await Twitch.unblockUser(userId: targetId, headers: headers);
|
||||
final success = await twitchApi.unblockUser(userId: targetId, headers: headers);
|
||||
if (success) await refreshBlockedUsers(headers: headers);
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> refreshBlockedUsers({required Map<String, String> headers}) async =>
|
||||
_blockedUsers = (await Twitch.getUserBlockedList(id: _details!.id, headers: headers)).asObservable();
|
||||
_blockedUsers = (await twitchApi.getUserBlockedList(id: _details!.id, headers: headers)).asObservable();
|
||||
|
||||
@action
|
||||
void dispose() {
|
||||
|
|
|
|||
|
|
@ -3,14 +3,20 @@ import 'dart:io';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:frosty/api/bttv_api.dart';
|
||||
import 'package:frosty/api/ffz_api.dart';
|
||||
import 'package:frosty/api/seventv_api.dart';
|
||||
import 'package:frosty/api/twitch_api.dart';
|
||||
import 'package:frosty/constants/constants.dart';
|
||||
import 'package:frosty/core/auth/auth_store.dart';
|
||||
import 'package:frosty/core/user/user_store.dart';
|
||||
import 'package:frosty/screens/home/home.dart';
|
||||
import 'package:frosty/screens/home/stores/categories_store.dart';
|
||||
import 'package:frosty/screens/home/stores/home_store.dart';
|
||||
import 'package:frosty/screens/home/stores/list_store.dart';
|
||||
import 'package:frosty/screens/home/stores/search_store.dart';
|
||||
import 'package:frosty/screens/settings/stores/settings_store.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
|
@ -19,10 +25,6 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Create and initialize the authentication store
|
||||
final authStore = AuthStore();
|
||||
await authStore.init();
|
||||
|
||||
// Get the shared preferences instance and obtain the existing user settings if it exists.
|
||||
// If default settings don't exist, use an empty JSON string to use the default values.
|
||||
final preferences = await SharedPreferences.getInstance();
|
||||
|
|
@ -38,11 +40,25 @@ Future<void> main() async {
|
|||
await SentryFlutter.init((options) => options.tracesSampleRate = sampleRate);
|
||||
}
|
||||
|
||||
final client = Client();
|
||||
final twitchApiService = TwitchApi(client);
|
||||
final bttvApiService = BTTVApi(client);
|
||||
final ffzApiService = FFZApi(client);
|
||||
final sevenTvApiService = SevenTVAPI(client);
|
||||
|
||||
// Create and initialize the authentication store
|
||||
final authStore = AuthStore(twitchApi: twitchApiService);
|
||||
await authStore.init();
|
||||
|
||||
runApp(
|
||||
MultiProvider(
|
||||
providers: [
|
||||
Provider<AuthStore>(create: (_) => authStore),
|
||||
Provider<SettingsStore>(create: (_) => settingsStore),
|
||||
Provider<TwitchApi>(create: (_) => twitchApiService),
|
||||
Provider<BTTVApi>(create: (_) => bttvApiService),
|
||||
Provider<FFZApi>(create: (_) => ffzApiService),
|
||||
Provider<SevenTVAPI>(create: (_) => sevenTvApiService),
|
||||
],
|
||||
child: const MyApp(),
|
||||
),
|
||||
|
|
@ -54,9 +70,6 @@ class MyApp extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final authStore = context.read<AuthStore>();
|
||||
final settingsStore = context.read<SettingsStore>();
|
||||
|
||||
final lightTheme = ThemeData(
|
||||
scaffoldBackgroundColor: Colors.white,
|
||||
splashFactory: Platform.isIOS ? NoSplash.splashFactory : null,
|
||||
|
|
@ -130,6 +143,10 @@ class MyApp extends StatelessWidget {
|
|||
|
||||
return Observer(
|
||||
builder: (context) {
|
||||
final authStore = context.read<AuthStore>();
|
||||
final settingsStore = context.read<SettingsStore>();
|
||||
final twitchApi = context.read<TwitchApi>();
|
||||
|
||||
return MaterialApp(
|
||||
title: 'Frosty',
|
||||
theme: lightTheme,
|
||||
|
|
@ -143,16 +160,24 @@ class MyApp extends StatelessWidget {
|
|||
homeStore: HomeStore(),
|
||||
topSectionStore: ListStore(
|
||||
authStore: authStore,
|
||||
twitchApi: twitchApi,
|
||||
listType: ListType.top,
|
||||
),
|
||||
categoriesSectionStore: CategoriesStore(authStore: authStore),
|
||||
categoriesSectionStore: CategoriesStore(
|
||||
authStore: authStore,
|
||||
twitchApi: twitchApi,
|
||||
),
|
||||
followedStreamsStore: authStore.isLoggedIn
|
||||
? ListStore(
|
||||
authStore: authStore,
|
||||
twitchApi: twitchApi,
|
||||
listType: ListType.followed,
|
||||
)
|
||||
: null,
|
||||
searchStore: SearchStore(authStore: authStore),
|
||||
searchStore: SearchStore(
|
||||
authStore: authStore,
|
||||
twitchApi: twitchApi,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -12,6 +12,11 @@ part 'chat_assets_store.g.dart';
|
|||
class ChatAssetsStore = _ChatAssetsStoreBase with _$ChatAssetsStore;
|
||||
|
||||
abstract class _ChatAssetsStoreBase with Store {
|
||||
final TwitchApi twitchApi;
|
||||
final BTTVApi bttvApi;
|
||||
final FFZApi ffzApi;
|
||||
final SevenTVAPI sevenTvApi;
|
||||
|
||||
/// Contains any custom FFZ mod and vip badges for the channel.
|
||||
RoomFFZ? ffzRoomInfo;
|
||||
|
||||
|
|
@ -67,11 +72,18 @@ abstract class _ChatAssetsStoreBase with Store {
|
|||
return emote.type == EmoteType.sevenTvChannel || emote.type == EmoteType.sevenTvGlobal;
|
||||
}
|
||||
|
||||
_ChatAssetsStoreBase({
|
||||
required this.twitchApi,
|
||||
required this.bttvApi,
|
||||
required this.ffzApi,
|
||||
required this.sevenTvApi,
|
||||
});
|
||||
|
||||
/// Fetches global and channel assets (badges and emotes) and stores them in [_emoteToUrl]
|
||||
@action
|
||||
Future<void> getAssets({required String channelName, required Map<String, String> headers}) async {
|
||||
// Fetch the desired channel/user's information.
|
||||
final channelInfo = await Twitch.getUser(userLogin: channelName, headers: headers);
|
||||
final channelInfo = await twitchApi.getUser(userLogin: channelName, headers: headers);
|
||||
|
||||
if (channelInfo != null) {
|
||||
// Fetch the global and channel's assets (emotes & badges).
|
||||
|
|
@ -87,14 +99,14 @@ abstract class _ChatAssetsStoreBase with Store {
|
|||
@action
|
||||
Future<void> getEmotes({required UserTwitch channelInfo, required Map<String, String> headers}) async {
|
||||
final assets = await Future.wait([
|
||||
FFZ.getEmotesGlobal(),
|
||||
BTTV.getEmotesGlobal(),
|
||||
BTTV.getEmotesChannel(id: channelInfo.id),
|
||||
Twitch.getEmotesGlobal(headers: headers),
|
||||
Twitch.getEmotesChannel(id: channelInfo.id, headers: headers),
|
||||
SevenTV.getEmotesGlobal(),
|
||||
SevenTV.getEmotesChannel(user: channelInfo.login),
|
||||
FFZ.getRoomInfo(name: channelInfo.login).then((ffzRoom) {
|
||||
ffzApi.getEmotesGlobal(),
|
||||
bttvApi.getEmotesGlobal(),
|
||||
bttvApi.getEmotesChannel(id: channelInfo.id),
|
||||
twitchApi.getEmotesGlobal(headers: headers),
|
||||
twitchApi.getEmotesChannel(id: channelInfo.id, headers: headers),
|
||||
sevenTvApi.getEmotesGlobal(),
|
||||
sevenTvApi.getEmotesChannel(user: channelInfo.login),
|
||||
ffzApi.getRoomInfo(name: channelInfo.login).then((ffzRoom) {
|
||||
ffzRoomInfo = ffzRoom?.item1;
|
||||
return ffzRoom?.item2 ?? <Emote>[];
|
||||
}),
|
||||
|
|
@ -107,17 +119,18 @@ abstract class _ChatAssetsStoreBase with Store {
|
|||
|
||||
@action
|
||||
Future<void> getBadges({required UserTwitch channelInfo, required Map<String, String> headers}) async => await Future.wait([
|
||||
Twitch.getBadgesGlobal()
|
||||
twitchApi
|
||||
.getBadgesGlobal()
|
||||
.then((badges) => twitchBadgesToObject.addAll(badges))
|
||||
.then((_) => Twitch.getBadgesChannel(id: channelInfo.id).then((badges) => twitchBadgesToObject.addAll(badges))),
|
||||
FFZ.getBadges().then((badges) => _userToFFZBadges = badges ?? {}),
|
||||
SevenTV.getBadges().then((badges) => _userTo7TVBadges = badges ?? {}),
|
||||
BTTV.getBadges().then((badges) => _userToBTTVBadges = badges ?? {}),
|
||||
.then((_) => twitchApi.getBadgesChannel(id: channelInfo.id).then((badges) => twitchBadgesToObject.addAll(badges))),
|
||||
ffzApi.getBadges().then((badges) => _userToFFZBadges = badges ?? {}),
|
||||
sevenTvApi.getBadges().then((badges) => _userTo7TVBadges = badges ?? {}),
|
||||
bttvApi.getBadges().then((badges) => _userToBTTVBadges = badges ?? {}),
|
||||
]);
|
||||
|
||||
@action
|
||||
Future<void> getUserEmotes({required List<String> emoteSets, required Map<String, String> headers}) async =>
|
||||
await Future.wait(emoteSets.map((setId) => Twitch.getEmotesSets(setId: setId, headers: headers)))
|
||||
await Future.wait(emoteSets.map((setId) => twitchApi.getEmotesSets(setId: setId, headers: headers)))
|
||||
.then((emotes) => emotes.expand((list) => list).toList())
|
||||
.then((userEmotes) => _userEmoteToObject = {for (final emote in userEmotes) emote.name: emote}.asObservable());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ part 'chat_details_store.g.dart';
|
|||
class ChatDetailsStore = _ChatDetailsStoreBase with _$ChatDetailsStore;
|
||||
|
||||
abstract class _ChatDetailsStoreBase with Store {
|
||||
final TwitchApi twitchApi;
|
||||
|
||||
/// The rules and modes being used in the chat.
|
||||
@observable
|
||||
var roomState = const ROOMSTATE();
|
||||
|
|
@ -33,6 +35,8 @@ abstract class _ChatDetailsStoreBase with Store {
|
|||
_chatUsers!.chatters.viewers,
|
||||
].map((e) => e.where((user) => user.contains(filterText)).toList());
|
||||
|
||||
_ChatDetailsStoreBase({required this.twitchApi});
|
||||
|
||||
@action
|
||||
Future<void> updateChatters(String userLogin) async => _chatUsers = await Twitch.getChatters(userLogin: userLogin);
|
||||
Future<void> updateChatters(String userLogin) async => _chatUsers = await twitchApi.getChatters(userLogin: userLogin);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,10 +46,10 @@ abstract class _ChatStoreBase with Store {
|
|||
final textController = TextEditingController();
|
||||
|
||||
/// The chat details store responsible for the chat modes and users in chat.
|
||||
final chatDetailsStore = ChatDetailsStore();
|
||||
final ChatDetailsStore chatDetailsStore;
|
||||
|
||||
/// The assets store responsible for badges, emotes, and the emote menu.
|
||||
final assetsStore = ChatAssetsStore();
|
||||
final ChatAssetsStore assetsStore;
|
||||
|
||||
/// Requested message to be sent by the user. Will only be sent on receipt of a USERNOTICE command.
|
||||
IRCMessage? toSend;
|
||||
|
|
@ -70,6 +70,8 @@ abstract class _ChatStoreBase with Store {
|
|||
|
||||
_ChatStoreBase({
|
||||
required this.auth,
|
||||
required this.chatDetailsStore,
|
||||
required this.assetsStore,
|
||||
required this.settings,
|
||||
required this.channelName,
|
||||
}) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ part 'video_store.g.dart';
|
|||
class VideoStore = _VideoStoreBase with _$VideoStore;
|
||||
|
||||
abstract class _VideoStoreBase with Store {
|
||||
final TwitchApi twitchApi;
|
||||
|
||||
WebViewController? controller;
|
||||
|
||||
late Timer overlayTimer;
|
||||
|
|
@ -32,6 +34,7 @@ abstract class _VideoStoreBase with Store {
|
|||
final SettingsStore settingsStore;
|
||||
|
||||
_VideoStoreBase({
|
||||
required this.twitchApi,
|
||||
required this.userLogin,
|
||||
required this.authStore,
|
||||
required this.settingsStore,
|
||||
|
|
@ -69,7 +72,7 @@ abstract class _VideoStoreBase with Store {
|
|||
|
||||
@action
|
||||
Future<void> updateStreamInfo() async {
|
||||
final updatedStreamInfo = await Twitch.getStream(userLogin: userLogin, headers: authStore.headersTwitch);
|
||||
final updatedStreamInfo = await twitchApi.getStream(userLogin: userLogin, headers: authStore.headersTwitch);
|
||||
if (updatedStreamInfo != null) {
|
||||
_streamInfo = updatedStreamInfo;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:frosty/api/twitch_api.dart';
|
||||
import 'package:frosty/screens/channel/chat/chat.dart';
|
||||
import 'package:frosty/screens/channel/stores/chat_store.dart';
|
||||
import 'package:frosty/screens/channel/stores/video_store.dart';
|
||||
import 'package:frosty/screens/channel/video/video.dart';
|
||||
import 'package:frosty/screens/settings/settings.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class VideoChat extends StatefulWidget {
|
||||
final String displayName;
|
||||
|
|
@ -31,6 +33,7 @@ class _VideoChatState extends State<VideoChat> {
|
|||
key: GlobalKey(),
|
||||
userLogin: chatStore.channelName,
|
||||
videoStore: VideoStore(
|
||||
twitchApi: context.read<TwitchApi>(),
|
||||
userLogin: chatStore.channelName,
|
||||
authStore: chatStore.auth,
|
||||
settingsStore: chatStore.settings,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:frosty/api/bttv_api.dart';
|
||||
import 'package:frosty/api/ffz_api.dart';
|
||||
import 'package:frosty/api/seventv_api.dart';
|
||||
import 'package:frosty/api/twitch_api.dart';
|
||||
import 'package:frosty/models/channel.dart';
|
||||
import 'package:frosty/screens/channel/stores/chat_assets_store.dart';
|
||||
import 'package:frosty/screens/channel/stores/chat_details_store.dart';
|
||||
import 'package:frosty/screens/channel/stores/chat_store.dart';
|
||||
import 'package:frosty/screens/channel/video_chat.dart';
|
||||
import 'package:frosty/screens/home/stores/search_store.dart';
|
||||
|
|
@ -29,6 +35,15 @@ class SearchResultsChannels extends StatelessWidget {
|
|||
builder: (context) => VideoChat(
|
||||
displayName: channelInfo!.broadcasterName,
|
||||
chatStore: ChatStore(
|
||||
chatDetailsStore: ChatDetailsStore(
|
||||
twitchApi: context.read<TwitchApi>(),
|
||||
),
|
||||
assetsStore: ChatAssetsStore(
|
||||
twitchApi: context.read<TwitchApi>(),
|
||||
ffzApi: context.read<FFZApi>(),
|
||||
bttvApi: context.read<BTTVApi>(),
|
||||
sevenTvApi: context.read<SevenTVAPI>(),
|
||||
),
|
||||
channelName: channelInfo.broadcasterLogin,
|
||||
auth: searchStore.authStore,
|
||||
settings: context.read<SettingsStore>(),
|
||||
|
|
@ -93,6 +108,15 @@ class SearchResultsChannels extends StatelessWidget {
|
|||
builder: (context) => VideoChat(
|
||||
displayName: channel.displayName,
|
||||
chatStore: ChatStore(
|
||||
chatDetailsStore: ChatDetailsStore(
|
||||
twitchApi: context.read<TwitchApi>(),
|
||||
),
|
||||
assetsStore: ChatAssetsStore(
|
||||
twitchApi: context.read<TwitchApi>(),
|
||||
ffzApi: context.read<FFZApi>(),
|
||||
bttvApi: context.read<BTTVApi>(),
|
||||
sevenTvApi: context.read<SevenTVAPI>(),
|
||||
),
|
||||
channelName: channel.broadcasterLogin,
|
||||
auth: searchStore.authStore,
|
||||
settings: context.read<SettingsStore>(),
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@ abstract class _CategoriesStoreBase with Store {
|
|||
/// Returns whether or not there are more streams and loading status for pagination.
|
||||
bool get hasMore => _isLoading == false && _categoriesCursor != null;
|
||||
|
||||
_CategoriesStoreBase({required this.authStore}) {
|
||||
final TwitchApi twitchApi;
|
||||
|
||||
_CategoriesStoreBase({required this.authStore, required this.twitchApi}) {
|
||||
scrollController.addListener(() {
|
||||
if (scrollController.position.atEdge || scrollController.position.outOfRange) {
|
||||
showJumpButton = false;
|
||||
|
|
@ -45,7 +47,7 @@ abstract class _CategoriesStoreBase with Store {
|
|||
Future<void> getCategories() async {
|
||||
_isLoading = true;
|
||||
|
||||
final result = await Twitch.getTopGames(headers: authStore.headersTwitch, cursor: _categoriesCursor);
|
||||
final result = await twitchApi.getTopGames(headers: authStore.headersTwitch, cursor: _categoriesCursor);
|
||||
if (result != null) {
|
||||
if (_categoriesCursor == null) {
|
||||
_categories = result.data.asObservable();
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ abstract class _ListStoreBase with Store {
|
|||
/// The authentication store.
|
||||
final AuthStore authStore;
|
||||
|
||||
final TwitchApi twitchApi;
|
||||
|
||||
final ListType listType;
|
||||
|
||||
final CategoryTwitch? categoryInfo;
|
||||
|
|
@ -38,6 +40,7 @@ abstract class _ListStoreBase with Store {
|
|||
|
||||
_ListStoreBase({
|
||||
required this.authStore,
|
||||
required this.twitchApi,
|
||||
required this.listType,
|
||||
this.categoryInfo,
|
||||
}) {
|
||||
|
|
@ -68,20 +71,20 @@ abstract class _ListStoreBase with Store {
|
|||
final StreamsTwitch? newStreams;
|
||||
switch (listType) {
|
||||
case ListType.followed:
|
||||
newStreams = await Twitch.getFollowedStreams(
|
||||
newStreams = await twitchApi.getFollowedStreams(
|
||||
id: authStore.user.details!.id,
|
||||
headers: authStore.headersTwitch,
|
||||
cursor: _streamsCursor,
|
||||
);
|
||||
break;
|
||||
case ListType.top:
|
||||
newStreams = await Twitch.getTopStreams(
|
||||
newStreams = await twitchApi.getTopStreams(
|
||||
headers: authStore.headersTwitch,
|
||||
cursor: _streamsCursor,
|
||||
);
|
||||
break;
|
||||
case ListType.category:
|
||||
newStreams = await Twitch.getStreamsUnderGame(
|
||||
newStreams = await twitchApi.getStreamsUnderGame(
|
||||
gameId: categoryInfo!.id,
|
||||
headers: authStore.headersTwitch,
|
||||
cursor: _streamsCursor,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ class SearchStore = _SearchStoreBase with _$SearchStore;
|
|||
abstract class _SearchStoreBase with Store {
|
||||
final AuthStore authStore;
|
||||
|
||||
final TwitchApi twitchApi;
|
||||
|
||||
@readonly
|
||||
var _searchHistory = ObservableList<String>();
|
||||
|
||||
|
|
@ -22,7 +24,7 @@ abstract class _SearchStoreBase with Store {
|
|||
@readonly
|
||||
ObservableFuture<CategoriesTwitch?>? _categoryFuture;
|
||||
|
||||
_SearchStoreBase({required this.authStore}) {
|
||||
_SearchStoreBase({required this.authStore, required this.twitchApi}) {
|
||||
init();
|
||||
}
|
||||
|
||||
|
|
@ -46,26 +48,30 @@ abstract class _SearchStoreBase with Store {
|
|||
_searchHistory.remove(query);
|
||||
_searchHistory.insert(0, query);
|
||||
|
||||
_channelFuture = Twitch.searchChannels(
|
||||
_channelFuture = twitchApi
|
||||
.searchChannels(
|
||||
query: query,
|
||||
headers: authStore.headersTwitch,
|
||||
).then(
|
||||
)
|
||||
.then(
|
||||
(channels) {
|
||||
channels.sort((c1, c2) => c2.isLive ? 1 : -1);
|
||||
return channels;
|
||||
},
|
||||
).asObservable();
|
||||
|
||||
_categoryFuture = Twitch.searchCategories(
|
||||
query: query,
|
||||
headers: authStore.headersTwitch,
|
||||
).asObservable();
|
||||
_categoryFuture = twitchApi
|
||||
.searchCategories(
|
||||
query: query,
|
||||
headers: authStore.headersTwitch,
|
||||
)
|
||||
.asObservable();
|
||||
}
|
||||
|
||||
Future<Channel?> searchChannel(String query) async {
|
||||
try {
|
||||
final user = await Twitch.getUser(userLogin: query, headers: authStore.headersTwitch);
|
||||
return await Twitch.getChannel(userId: user!.id, headers: authStore.headersTwitch);
|
||||
final user = await twitchApi.getUser(userLogin: query, headers: authStore.headersTwitch);
|
||||
return await twitchApi.getChannel(userId: user!.id, headers: authStore.headersTwitch);
|
||||
} catch (e) {
|
||||
debugPrint(e.toString());
|
||||
rethrow;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:frosty/api/twitch_api.dart';
|
||||
import 'package:frosty/core/auth/auth_store.dart';
|
||||
import 'package:frosty/models/category.dart';
|
||||
import 'package:frosty/screens/home/stores/list_store.dart';
|
||||
|
|
@ -20,6 +21,7 @@ class CategoryCard extends StatelessWidget {
|
|||
MaterialPageRoute(
|
||||
builder: (context) => CategoryStreams(
|
||||
store: ListStore(
|
||||
twitchApi: context.read<TwitchApi>(),
|
||||
authStore: context.read<AuthStore>(),
|
||||
listType: ListType.category,
|
||||
categoryInfo: category,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:frosty/api/bttv_api.dart';
|
||||
import 'package:frosty/api/ffz_api.dart';
|
||||
import 'package:frosty/api/seventv_api.dart';
|
||||
import 'package:frosty/api/twitch_api.dart';
|
||||
import 'package:frosty/core/auth/auth_store.dart';
|
||||
import 'package:frosty/models/stream.dart';
|
||||
import 'package:frosty/screens/channel/stores/chat_assets_store.dart';
|
||||
import 'package:frosty/screens/channel/stores/chat_details_store.dart';
|
||||
import 'package:frosty/screens/channel/stores/chat_store.dart';
|
||||
import 'package:frosty/screens/channel/video_chat.dart';
|
||||
import 'package:frosty/screens/settings/stores/settings_store.dart';
|
||||
|
|
@ -37,6 +43,15 @@ class StreamCard extends StatelessWidget {
|
|||
channelName: streamInfo.userLogin,
|
||||
auth: context.read<AuthStore>(),
|
||||
settings: context.read<SettingsStore>(),
|
||||
chatDetailsStore: ChatDetailsStore(
|
||||
twitchApi: context.read<TwitchApi>(),
|
||||
),
|
||||
assetsStore: ChatAssetsStore(
|
||||
twitchApi: context.read<TwitchApi>(),
|
||||
ffzApi: context.read<FFZApi>(),
|
||||
bttvApi: context.read<BTTVApi>(),
|
||||
sevenTvApi: context.read<SevenTVAPI>(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class ProfilePicture extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: Twitch.getUser(userLogin: userLogin, headers: context.read<AuthStore>().headersTwitch),
|
||||
future: context.read<TwitchApi>().getUser(userLogin: userLogin, headers: context.read<AuthStore>().headersTwitch),
|
||||
builder: (context, AsyncSnapshot<UserTwitch?> snapshot) {
|
||||
return CircleAvatar(
|
||||
radius: radius,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue