Convert providers to MobX stores
This commit is contained in:
parent
2c72d41e78
commit
b86b989507
7 changed files with 320 additions and 64 deletions
|
|
@ -1,3 +0,0 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class SettingsProvider extends ChangeNotifier {}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:mobx/mobx.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:frosty/constants.dart';
|
||||
|
|
@ -6,7 +7,11 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|||
import 'package:http/http.dart' as http;
|
||||
import 'package:flutter_web_auth/flutter_web_auth.dart';
|
||||
|
||||
class AuthenticationProvider extends ChangeNotifier {
|
||||
part 'auth_store.g.dart';
|
||||
|
||||
class AuthStore = AuthBase with _$AuthStore;
|
||||
|
||||
abstract class AuthBase with Store {
|
||||
static String? _token;
|
||||
static String? get token => _token;
|
||||
|
||||
|
|
@ -16,7 +21,9 @@ class AuthenticationProvider extends ChangeNotifier {
|
|||
final _secret = secret;
|
||||
final _clientId = clientId;
|
||||
|
||||
@observable
|
||||
var _isLoggedIn = false;
|
||||
|
||||
bool get isLoggedIn => _isLoggedIn;
|
||||
|
||||
var _tokenIsValid = false;
|
||||
|
|
@ -85,7 +92,7 @@ class AuthenticationProvider extends ChangeNotifier {
|
|||
debugPrint('Token invalidated :(');
|
||||
_tokenIsValid = false;
|
||||
|
||||
notifyListeners();
|
||||
// notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> _getUserInfo() async {
|
||||
|
|
@ -121,7 +128,7 @@ class AuthenticationProvider extends ChangeNotifier {
|
|||
|
||||
await _getUserInfo();
|
||||
|
||||
notifyListeners();
|
||||
// notifyListeners();
|
||||
} catch (error) {
|
||||
debugPrint('Login failed due to $error');
|
||||
}
|
||||
|
|
@ -133,6 +140,6 @@ class AuthenticationProvider extends ChangeNotifier {
|
|||
|
||||
await _storage.delete(key: 'USER_TOKEN');
|
||||
debugPrint('Succesfully logged out');
|
||||
notifyListeners();
|
||||
// notifyListeners();
|
||||
}
|
||||
}
|
||||
33
lib/stores/auth_store.g.dart
Normal file
33
lib/stores/auth_store.g.dart
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'auth_store.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// StoreGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
|
||||
|
||||
mixin _$AuthStore on AuthBase, Store {
|
||||
final _$_isLoggedInAtom = Atom(name: 'AuthBase._isLoggedIn');
|
||||
|
||||
@override
|
||||
bool get _isLoggedIn {
|
||||
_$_isLoggedInAtom.reportRead();
|
||||
return super._isLoggedIn;
|
||||
}
|
||||
|
||||
@override
|
||||
set _isLoggedIn(bool value) {
|
||||
_$_isLoggedInAtom.reportWrite(value, super._isLoggedIn, () {
|
||||
super._isLoggedIn = value;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''
|
||||
|
||||
''';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +1,35 @@
|
|||
import 'package:mobx/mobx.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:frosty/constants.dart';
|
||||
import 'package:frosty/providers/authentication_provider.dart';
|
||||
import 'package:frosty/stores/auth_store.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:frosty/models/channel.dart';
|
||||
|
||||
class ChannelListProvider extends ChangeNotifier {
|
||||
var topChannels = <Channel>[];
|
||||
var followedChannels = <Channel>[];
|
||||
var isLoading = false;
|
||||
part 'channel_list_store.g.dart';
|
||||
|
||||
class ChannelListStore = _ChannelListBase with _$ChannelListStore;
|
||||
|
||||
abstract class _ChannelListBase with Store {
|
||||
@observable
|
||||
ObservableList<Channel> topChannels = ObservableList<Channel>();
|
||||
|
||||
@observable
|
||||
ObservableList<Channel> followedChannels = ObservableList<Channel>();
|
||||
|
||||
@observable
|
||||
bool isLoading = false;
|
||||
|
||||
@observable
|
||||
String? topChannelsCurrentCursor;
|
||||
|
||||
@observable
|
||||
String? followedChannelsCurrentCursor;
|
||||
|
||||
final token = AuthenticationProvider.token;
|
||||
final token = AuthBase.token;
|
||||
final String? id;
|
||||
|
||||
ChannelListProvider({this.id}) {
|
||||
_ChannelListBase({this.id}) {
|
||||
if (token != null) {
|
||||
updateTopChannels();
|
||||
}
|
||||
|
|
@ -43,6 +56,7 @@ class ChannelListProvider extends ChangeNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> update({required Category category}) async {
|
||||
switch (category) {
|
||||
case Category.top:
|
||||
|
|
@ -55,6 +69,7 @@ class ChannelListProvider extends ChangeNotifier {
|
|||
}
|
||||
|
||||
/// Returns the top 10 streamers and a cursor for further requests.
|
||||
@action
|
||||
Future<void> updateTopChannels() async {
|
||||
final url = Uri.parse('https://api.twitch.tv/helix/streams?first=10');
|
||||
final headers = {'Authorization': 'Bearer $token', 'Client-Id': clientId};
|
||||
|
|
@ -65,26 +80,32 @@ class ChannelListProvider extends ChangeNotifier {
|
|||
final decoded = jsonDecode(response.body);
|
||||
final data = decoded['data'] as List;
|
||||
|
||||
final result = {'channels': data.map((channel) => Channel.fromJson(channel)).toList(), 'cursor': decoded['pagination']['cursor']};
|
||||
final result = {
|
||||
'channels': data.map((channel) => Channel.fromJson(channel)).toList(),
|
||||
'cursor': decoded['pagination']['cursor']
|
||||
};
|
||||
|
||||
topChannels = result['channels'];
|
||||
topChannels = ObservableList.of(result['channels']);
|
||||
topChannelsCurrentCursor = result['cursor'];
|
||||
} else {
|
||||
debugPrint('Failed to update top channels');
|
||||
}
|
||||
notifyListeners();
|
||||
// notifyListeners();
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> getMoreChannels({required Category category}) async {
|
||||
isLoading = true;
|
||||
Uri url;
|
||||
|
||||
switch (category) {
|
||||
case Category.top:
|
||||
url = Uri.parse('https://api.twitch.tv/helix/streams?first=10&after=$topChannelsCurrentCursor');
|
||||
url = Uri.parse(
|
||||
'https://api.twitch.tv/helix/streams?first=10&after=$topChannelsCurrentCursor');
|
||||
break;
|
||||
case Category.followed:
|
||||
url = Uri.parse('https://api.twitch.tv/helix/streams/followed?user_id=$id&first=10&after=$followedChannelsCurrentCursor');
|
||||
url = Uri.parse(
|
||||
'https://api.twitch.tv/helix/streams/followed?user_id=$id&first=10&after=$followedChannelsCurrentCursor');
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +116,10 @@ class ChannelListProvider extends ChangeNotifier {
|
|||
final decoded = jsonDecode(response.body);
|
||||
final data = decoded['data'] as List;
|
||||
|
||||
final result = {'channels': data.map((channel) => Channel.fromJson(channel)).toList(), 'cursor': decoded['pagination']['cursor']};
|
||||
final result = {
|
||||
'channels': data.map((channel) => Channel.fromJson(channel)).toList(),
|
||||
'cursor': decoded['pagination']['cursor']
|
||||
};
|
||||
|
||||
switch (category) {
|
||||
case Category.top:
|
||||
|
|
@ -112,11 +136,13 @@ class ChannelListProvider extends ChangeNotifier {
|
|||
debugPrint('Failed to get more channels');
|
||||
}
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
// notifyListeners();
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> updateFollowedChannels() async {
|
||||
final url = Uri.parse('https://api.twitch.tv/helix/streams/followed?first=10&user_id=$id');
|
||||
final url = Uri.parse(
|
||||
'https://api.twitch.tv/helix/streams/followed?first=10&user_id=$id');
|
||||
final headers = {'Authorization': 'Bearer $token', 'Client-Id': clientId};
|
||||
|
||||
final response = await http.get(url, headers: headers);
|
||||
|
|
@ -125,14 +151,17 @@ class ChannelListProvider extends ChangeNotifier {
|
|||
final decoded = jsonDecode(response.body);
|
||||
final data = decoded['data'] as List;
|
||||
|
||||
final result = {'channels': data.map((channel) => Channel.fromJson(channel)).toList(), 'cursor': decoded['pagination']['cursor']};
|
||||
final result = {
|
||||
'channels': data.map((channel) => Channel.fromJson(channel)).toList(),
|
||||
'cursor': decoded['pagination']['cursor']
|
||||
};
|
||||
|
||||
followedChannels = result['channels'];
|
||||
followedChannels = ObservableList.of(result['channels']);
|
||||
followedChannelsCurrentCursor = result['cursor'];
|
||||
} else {
|
||||
debugPrint('Failed to update followed channels');
|
||||
}
|
||||
notifyListeners();
|
||||
// notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
135
lib/stores/channel_list_store.g.dart
Normal file
135
lib/stores/channel_list_store.g.dart
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'channel_list_store.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// StoreGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
|
||||
|
||||
mixin _$ChannelListStore on _ChannelListBase, Store {
|
||||
final _$topChannelsAtom = Atom(name: '_ChannelListBase.topChannels');
|
||||
|
||||
@override
|
||||
ObservableList<Channel> get topChannels {
|
||||
_$topChannelsAtom.reportRead();
|
||||
return super.topChannels;
|
||||
}
|
||||
|
||||
@override
|
||||
set topChannels(ObservableList<Channel> value) {
|
||||
_$topChannelsAtom.reportWrite(value, super.topChannels, () {
|
||||
super.topChannels = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$followedChannelsAtom =
|
||||
Atom(name: '_ChannelListBase.followedChannels');
|
||||
|
||||
@override
|
||||
ObservableList<Channel> get followedChannels {
|
||||
_$followedChannelsAtom.reportRead();
|
||||
return super.followedChannels;
|
||||
}
|
||||
|
||||
@override
|
||||
set followedChannels(ObservableList<Channel> value) {
|
||||
_$followedChannelsAtom.reportWrite(value, super.followedChannels, () {
|
||||
super.followedChannels = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$isLoadingAtom = Atom(name: '_ChannelListBase.isLoading');
|
||||
|
||||
@override
|
||||
bool get isLoading {
|
||||
_$isLoadingAtom.reportRead();
|
||||
return super.isLoading;
|
||||
}
|
||||
|
||||
@override
|
||||
set isLoading(bool value) {
|
||||
_$isLoadingAtom.reportWrite(value, super.isLoading, () {
|
||||
super.isLoading = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$topChannelsCurrentCursorAtom =
|
||||
Atom(name: '_ChannelListBase.topChannelsCurrentCursor');
|
||||
|
||||
@override
|
||||
String? get topChannelsCurrentCursor {
|
||||
_$topChannelsCurrentCursorAtom.reportRead();
|
||||
return super.topChannelsCurrentCursor;
|
||||
}
|
||||
|
||||
@override
|
||||
set topChannelsCurrentCursor(String? value) {
|
||||
_$topChannelsCurrentCursorAtom
|
||||
.reportWrite(value, super.topChannelsCurrentCursor, () {
|
||||
super.topChannelsCurrentCursor = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$followedChannelsCurrentCursorAtom =
|
||||
Atom(name: '_ChannelListBase.followedChannelsCurrentCursor');
|
||||
|
||||
@override
|
||||
String? get followedChannelsCurrentCursor {
|
||||
_$followedChannelsCurrentCursorAtom.reportRead();
|
||||
return super.followedChannelsCurrentCursor;
|
||||
}
|
||||
|
||||
@override
|
||||
set followedChannelsCurrentCursor(String? value) {
|
||||
_$followedChannelsCurrentCursorAtom
|
||||
.reportWrite(value, super.followedChannelsCurrentCursor, () {
|
||||
super.followedChannelsCurrentCursor = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$updateAsyncAction = AsyncAction('_ChannelListBase.update');
|
||||
|
||||
@override
|
||||
Future<void> update({required Category category}) {
|
||||
return _$updateAsyncAction.run(() => super.update(category: category));
|
||||
}
|
||||
|
||||
final _$updateTopChannelsAsyncAction =
|
||||
AsyncAction('_ChannelListBase.updateTopChannels');
|
||||
|
||||
@override
|
||||
Future<void> updateTopChannels() {
|
||||
return _$updateTopChannelsAsyncAction.run(() => super.updateTopChannels());
|
||||
}
|
||||
|
||||
final _$getMoreChannelsAsyncAction =
|
||||
AsyncAction('_ChannelListBase.getMoreChannels');
|
||||
|
||||
@override
|
||||
Future<void> getMoreChannels({required Category category}) {
|
||||
return _$getMoreChannelsAsyncAction
|
||||
.run(() => super.getMoreChannels(category: category));
|
||||
}
|
||||
|
||||
final _$updateFollowedChannelsAsyncAction =
|
||||
AsyncAction('_ChannelListBase.updateFollowedChannels');
|
||||
|
||||
@override
|
||||
Future<void> updateFollowedChannels() {
|
||||
return _$updateFollowedChannelsAsyncAction
|
||||
.run(() => super.updateFollowedChannels());
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''
|
||||
topChannels: ${topChannels},
|
||||
followedChannels: ${followedChannels},
|
||||
isLoading: ${isLoading},
|
||||
topChannelsCurrentCursor: ${topChannelsCurrentCursor},
|
||||
followedChannelsCurrentCursor: ${followedChannelsCurrentCursor}
|
||||
''';
|
||||
}
|
||||
}
|
||||
|
|
@ -2,50 +2,30 @@ import 'package:cached_network_image/cached_network_image.dart';
|
|||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:frosty/models/channel.dart';
|
||||
import 'package:frosty/providers/authentication_provider.dart';
|
||||
import 'package:frosty/stores/auth_store.dart';
|
||||
import 'package:frosty/utility/request.dart';
|
||||
import 'package:frosty/widgets/chat_message.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||
|
||||
class ChatProvider extends ChangeNotifier {
|
||||
final Channel channelInfo;
|
||||
part 'chat_store.g.dart';
|
||||
|
||||
class ChatStore = _ChatStoreBase with _$ChatStore;
|
||||
|
||||
abstract class _ChatStoreBase with Store {
|
||||
@observable
|
||||
bool autoScroll = true;
|
||||
|
||||
List<String> messages = [];
|
||||
|
||||
final channel = WebSocketChannel.connect(Uri.parse('wss://irc-ws.chat.twitch.tv:443'));
|
||||
final messages = <String>[];
|
||||
final scrollController = ScrollController();
|
||||
|
||||
final _assetToUrl = <String, String>{};
|
||||
final _emoteIdToWord = <String, String>{};
|
||||
|
||||
var autoScroll = true;
|
||||
final scrollController = ScrollController();
|
||||
|
||||
ChatProvider({required this.channelInfo}) {
|
||||
final commands = [
|
||||
'PASS oauth:${AuthenticationProvider.token}',
|
||||
'NICK justinfan888',
|
||||
'CAP REQ :twitch.tv/tags',
|
||||
'CAP REQ :twitch.tv/commands',
|
||||
// 'CAP REQ :twitch.tv/membership',
|
||||
'CAP END',
|
||||
'JOIN #${channelInfo.userLogin}',
|
||||
];
|
||||
|
||||
for (final command in commands) {
|
||||
channel.sink.add(command);
|
||||
}
|
||||
|
||||
scrollController.addListener(() {
|
||||
if (!scrollController.position.atEdge) {
|
||||
autoScroll = false;
|
||||
notifyListeners();
|
||||
} else if (scrollController.position.atEdge && scrollController.position.pixels != scrollController.position.minScrollExtent) {
|
||||
autoScroll = true;
|
||||
notifyListeners();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> getEmotes() async {
|
||||
Future<void> start(Channel channelInfo) async {
|
||||
final assets = [
|
||||
await Request.getEmotesBTTVGlobal(),
|
||||
await Request.getEmotesBTTVChannel(id: channelInfo.userId),
|
||||
|
|
@ -64,8 +44,30 @@ class ChatProvider extends ChangeNotifier {
|
|||
_assetToUrl.addAll(map);
|
||||
}
|
||||
}
|
||||
final commands = [
|
||||
'PASS oauth:${AuthBase.token}',
|
||||
'NICK justinfan888',
|
||||
'CAP REQ :twitch.tv/tags',
|
||||
'CAP REQ :twitch.tv/commands',
|
||||
// 'CAP REQ :twitch.tv/membership',
|
||||
'CAP END',
|
||||
'JOIN #${channelInfo.userLogin}',
|
||||
];
|
||||
|
||||
for (final command in commands) {
|
||||
channel.sink.add(command);
|
||||
}
|
||||
|
||||
scrollController.addListener(() {
|
||||
if (!scrollController.position.atEdge) {
|
||||
autoScroll = false;
|
||||
} else if (scrollController.position.atEdge && scrollController.position.pixels != scrollController.position.minScrollExtent) {
|
||||
autoScroll = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
void handleWebsocketData(Object? data) {
|
||||
for (final message in data.toString().split('\r\n')) {
|
||||
if (message.startsWith('@')) {
|
||||
|
|
@ -223,6 +225,7 @@ class ChatProvider extends ChangeNotifier {
|
|||
return result;
|
||||
}
|
||||
|
||||
@action
|
||||
void resumeScroll() {
|
||||
autoScroll = true;
|
||||
scrollController.jumpTo(scrollController.position.maxScrollExtent);
|
||||
|
|
@ -230,12 +233,6 @@ class ChatProvider extends ChangeNotifier {
|
|||
scrollController.jumpTo(scrollController.position.maxScrollExtent);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
channel.sink.close();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/50081213/how-do-i-use-hexadecimal-color-strings-in-flutter
|
||||
58
lib/stores/chat_store.g.dart
Normal file
58
lib/stores/chat_store.g.dart
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'chat_store.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// StoreGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
|
||||
|
||||
mixin _$ChatStore on _ChatStoreBase, Store {
|
||||
final _$autoScrollAtom = Atom(name: '_ChatStoreBase.autoScroll');
|
||||
|
||||
@override
|
||||
bool get autoScroll {
|
||||
_$autoScrollAtom.reportRead();
|
||||
return super.autoScroll;
|
||||
}
|
||||
|
||||
@override
|
||||
set autoScroll(bool value) {
|
||||
_$autoScrollAtom.reportWrite(value, super.autoScroll, () {
|
||||
super.autoScroll = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$_ChatStoreBaseActionController =
|
||||
ActionController(name: '_ChatStoreBase');
|
||||
|
||||
@override
|
||||
void handleWebsocketData(Object? data) {
|
||||
final _$actionInfo = _$_ChatStoreBaseActionController.startAction(
|
||||
name: '_ChatStoreBase.handleWebsocketData');
|
||||
try {
|
||||
return super.handleWebsocketData(data);
|
||||
} finally {
|
||||
_$_ChatStoreBaseActionController.endAction(_$actionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void resumeScroll() {
|
||||
final _$actionInfo = _$_ChatStoreBaseActionController.startAction(
|
||||
name: '_ChatStoreBase.resumeScroll');
|
||||
try {
|
||||
return super.resumeScroll();
|
||||
} finally {
|
||||
_$_ChatStoreBaseActionController.endAction(_$actionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''
|
||||
autoScroll: ${autoScroll}
|
||||
''';
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue