Refactor home & streams/categories list
This commit is contained in:
parent
569d6959d3
commit
8eb7f36ab1
38 changed files with 334 additions and 616 deletions
|
|
@ -4,12 +4,10 @@ import 'dart:io';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:frosty/core/auth/auth_store.dart';
|
||||
import 'package:frosty/core/settings/settings_store.dart';
|
||||
import 'package:frosty/screens/followed_streams/followed_streams_store.dart';
|
||||
import 'package:frosty/screens/home/home.dart';
|
||||
import 'package:frosty/screens/search/search_store.dart';
|
||||
import 'package:frosty/screens/top/categories/categories_store.dart';
|
||||
import 'package:frosty/screens/top/streams/top_streams_store.dart';
|
||||
import 'package:frosty/screens/home/search/stores/search_store.dart';
|
||||
import 'package:frosty/screens/home/stores/list_store.dart';
|
||||
import 'package:frosty/screens/settings/stores/settings_store.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
|
@ -94,9 +92,20 @@ class MyApp extends StatelessWidget {
|
|||
title: 'Frosty',
|
||||
theme: settingsStore.useOledTheme ? oledTheme : defaultTheme,
|
||||
home: Home(
|
||||
topStreamsStore: TopStreamsStore(authStore: authStore),
|
||||
followedStreamsStore: FollowedStreamsStore(authStore: authStore),
|
||||
categoriesStore: CategoriesStore(authStore: authStore),
|
||||
topStreamsStore: ListStore(
|
||||
authStore: authStore,
|
||||
listType: StreamListType.top,
|
||||
),
|
||||
categoriesStore: ListStore(
|
||||
authStore: authStore,
|
||||
listType: StreamListType.categories,
|
||||
),
|
||||
followedStreamsStore: authStore.isLoggedIn
|
||||
? ListStore(
|
||||
authStore: authStore,
|
||||
listType: StreamListType.followed,
|
||||
)
|
||||
: null,
|
||||
searchStore: SearchStore(authStore: authStore),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:frosty/core/settings/settings_store.dart';
|
||||
import 'package:frosty/models/irc_message.dart';
|
||||
import 'package:frosty/screens/channel/chat/chat_assets_store.dart';
|
||||
import 'package:frosty/screens/settings/stores/settings_store.dart';
|
||||
|
||||
class ChatMessage extends StatelessWidget {
|
||||
final IRCMessage ircMessage;
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ import 'dart:async';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:frosty/core/auth/auth_store.dart';
|
||||
import 'package:frosty/core/settings/settings_store.dart';
|
||||
import 'package:frosty/models/irc_message.dart';
|
||||
import 'package:frosty/screens/channel/chat/chat_assets_store.dart';
|
||||
import 'package:frosty/screens/channel/chat/details/chat_details_store.dart';
|
||||
import 'package:frosty/screens/settings/stores/settings_store.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||
|
||||
|
|
@ -140,6 +140,7 @@ abstract class _ChatStoreBase with Store {
|
|||
// The IRC data can contain more than one message separated by CRLF.
|
||||
// To account for this, split by CRLF, then loop and process each message.
|
||||
for (final message in data.trimRight().split('\r\n')) {
|
||||
debugPrint(message);
|
||||
if (message.startsWith('@')) {
|
||||
final parsedIRCMessage = IRCMessage.fromString(message, username: auth.user.details?.login);
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,10 @@ class _ChattersListState extends State<ChattersList> {
|
|||
hintText: 'Filter chatters',
|
||||
contentPadding: const EdgeInsets.all(10.0),
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () => setState(() => _textController.clear()),
|
||||
onPressed: () => setState(() {
|
||||
_textController.clear();
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
}),
|
||||
icon: const Icon(Icons.clear),
|
||||
),
|
||||
border: const OutlineInputBorder(
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import 'dart:io';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:frosty/core/settings/settings.dart';
|
||||
import 'package:frosty/screens/channel/video/video_store.dart';
|
||||
import 'package:frosty/screens/settings/settings.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class VideoOverlay extends StatelessWidget {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import 'dart:async';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:frosty/api/twitch_api.dart';
|
||||
import 'package:frosty/core/auth/auth_store.dart';
|
||||
import 'package:frosty/core/settings/settings_store.dart';
|
||||
import 'package:frosty/models/stream.dart';
|
||||
import 'package:frosty/screens/settings/stores/settings_store.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:frosty/core/auth/auth_store.dart';
|
||||
import 'package:frosty/core/settings/settings.dart';
|
||||
import 'package:frosty/core/settings/settings_store.dart';
|
||||
import 'package:frosty/screens/channel/chat/chat.dart';
|
||||
import 'package:frosty/screens/channel/chat/chat_store.dart';
|
||||
import 'package:frosty/screens/channel/video/video.dart';
|
||||
import 'package:frosty/screens/channel/video/video_store.dart';
|
||||
import 'package:frosty/screens/settings/settings.dart';
|
||||
import 'package:frosty/screens/settings/stores/settings_store.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class VideoChat extends StatelessWidget {
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:frosty/core/settings/settings_store.dart';
|
||||
import 'package:frosty/screens/followed_streams/followed_streams_store.dart';
|
||||
import 'package:frosty/widgets/loading_indicator.dart';
|
||||
import 'package:frosty/widgets/stream_card.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class FollowedStreams extends StatelessWidget {
|
||||
final FollowedStreamsStore store;
|
||||
|
||||
const FollowedStreams({Key? key, required this.store}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RefreshIndicator(
|
||||
onRefresh: store.refresh,
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
if (store.followedStreams.isEmpty && store.isLoading) {
|
||||
return const LoadingIndicator(
|
||||
subtitle: Text('Loading followed streams...'),
|
||||
);
|
||||
}
|
||||
return ListView.builder(
|
||||
itemCount: store.followedStreams.length,
|
||||
itemBuilder: (context, index) {
|
||||
if (index > store.followedStreams.length / 2 && store.hasMore) {
|
||||
store.getFollowedStreams();
|
||||
}
|
||||
return Observer(
|
||||
builder: (context) => StreamCard(
|
||||
streamInfo: store.followedStreams[index],
|
||||
showUptime: context.read<SettingsStore>().showThumbnailUptime,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
import 'package:frosty/api/twitch_api.dart';
|
||||
import 'package:frosty/core/auth/auth_store.dart';
|
||||
import 'package:frosty/models/stream.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'followed_streams_store.g.dart';
|
||||
|
||||
class FollowedStreamsStore = _FollowedStreamsStoreBase with _$FollowedStreamsStore;
|
||||
|
||||
abstract class _FollowedStreamsStoreBase with Store {
|
||||
/// The list of the fetched followed streams.
|
||||
@readonly
|
||||
var _followedStreams = ObservableList<StreamTwitch>();
|
||||
|
||||
/// The loading status for pagination.
|
||||
@readonly
|
||||
bool _isLoading = false;
|
||||
|
||||
/// The pagination cursor for followed streams.
|
||||
String? _followedStreamsCurrentCursor;
|
||||
|
||||
/// The authentication store.
|
||||
final AuthStore authStore;
|
||||
|
||||
/// Returns whether or not there are more streams and loading status for pagination.
|
||||
bool get hasMore => _isLoading == false && _followedStreamsCurrentCursor != null;
|
||||
|
||||
_FollowedStreamsStoreBase({required this.authStore}) {
|
||||
if (authStore.isLoggedIn) getFollowedStreams();
|
||||
}
|
||||
|
||||
/// Fetches the followed streams based on the current cursor.
|
||||
@action
|
||||
Future<void> getFollowedStreams() async {
|
||||
_isLoading = true;
|
||||
|
||||
final newFollowedStreams = await Twitch.getFollowedStreams(
|
||||
id: authStore.user.details!.id,
|
||||
headers: authStore.headersTwitch,
|
||||
cursor: _followedStreamsCurrentCursor,
|
||||
);
|
||||
|
||||
if (newFollowedStreams != null) {
|
||||
if (_followedStreamsCurrentCursor == null) {
|
||||
_followedStreams = newFollowedStreams.data.asObservable();
|
||||
} else {
|
||||
_followedStreams.addAll(newFollowedStreams.data);
|
||||
}
|
||||
_followedStreamsCurrentCursor = newFollowedStreams.pagination['cursor'];
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
}
|
||||
|
||||
/// Resets the cursor and then fetches the followed streams.
|
||||
@action
|
||||
Future<void> refresh() {
|
||||
_followedStreamsCurrentCursor = null;
|
||||
|
||||
return getFollowedStreams();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'followed_streams_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 _$FollowedStreamsStore on _FollowedStreamsStoreBase, Store {
|
||||
final _$_followedStreamsAtom =
|
||||
Atom(name: '_FollowedStreamsStoreBase._followedStreams');
|
||||
|
||||
ObservableList<StreamTwitch> get followedStreams {
|
||||
_$_followedStreamsAtom.reportRead();
|
||||
return super._followedStreams;
|
||||
}
|
||||
|
||||
@override
|
||||
ObservableList<StreamTwitch> get _followedStreams => followedStreams;
|
||||
|
||||
@override
|
||||
set _followedStreams(ObservableList<StreamTwitch> value) {
|
||||
_$_followedStreamsAtom.reportWrite(value, super._followedStreams, () {
|
||||
super._followedStreams = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$_isLoadingAtom = Atom(name: '_FollowedStreamsStoreBase._isLoading');
|
||||
|
||||
bool get isLoading {
|
||||
_$_isLoadingAtom.reportRead();
|
||||
return super._isLoading;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get _isLoading => isLoading;
|
||||
|
||||
@override
|
||||
set _isLoading(bool value) {
|
||||
_$_isLoadingAtom.reportWrite(value, super._isLoading, () {
|
||||
super._isLoading = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$getFollowedStreamsAsyncAction =
|
||||
AsyncAction('_FollowedStreamsStoreBase.getFollowedStreams');
|
||||
|
||||
@override
|
||||
Future<void> getFollowedStreams() {
|
||||
return _$getFollowedStreamsAsyncAction
|
||||
.run(() => super.getFollowedStreams());
|
||||
}
|
||||
|
||||
final _$_FollowedStreamsStoreBaseActionController =
|
||||
ActionController(name: '_FollowedStreamsStoreBase');
|
||||
|
||||
@override
|
||||
Future<void> refresh() {
|
||||
final _$actionInfo = _$_FollowedStreamsStoreBaseActionController
|
||||
.startAction(name: '_FollowedStreamsStoreBase.refresh');
|
||||
try {
|
||||
return super.refresh();
|
||||
} finally {
|
||||
_$_FollowedStreamsStoreBaseActionController.endAction(_$actionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''
|
||||
|
||||
''';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +1,27 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:frosty/core/auth/auth_store.dart';
|
||||
import 'package:frosty/core/settings/settings.dart';
|
||||
import 'package:frosty/core/settings/settings_store.dart';
|
||||
import 'package:frosty/screens/followed_streams/followed_streams.dart';
|
||||
import 'package:frosty/screens/followed_streams/followed_streams_store.dart';
|
||||
import 'package:frosty/screens/search/search.dart';
|
||||
import 'package:frosty/screens/search/search_store.dart';
|
||||
import 'package:frosty/screens/top/categories/categories_store.dart';
|
||||
import 'package:frosty/screens/top/streams/top_streams_store.dart';
|
||||
import 'package:frosty/screens/top/top_section.dart';
|
||||
import 'package:frosty/screens/home/search/search.dart';
|
||||
import 'package:frosty/screens/home/search/stores/search_store.dart';
|
||||
import 'package:frosty/screens/home/stores/list_store.dart';
|
||||
import 'package:frosty/screens/home/streams_list.dart';
|
||||
import 'package:frosty/screens/home/top/top_section.dart';
|
||||
import 'package:frosty/screens/settings/settings.dart';
|
||||
import 'package:frosty/screens/settings/stores/settings_store.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class Home extends StatefulWidget {
|
||||
final TopStreamsStore topStreamsStore;
|
||||
final FollowedStreamsStore followedStreamsStore;
|
||||
final CategoriesStore categoriesStore;
|
||||
final ListStore topStreamsStore;
|
||||
final ListStore categoriesStore;
|
||||
final SearchStore searchStore;
|
||||
final ListStore? followedStreamsStore;
|
||||
|
||||
const Home({
|
||||
Key? key,
|
||||
required this.topStreamsStore,
|
||||
required this.followedStreamsStore,
|
||||
required this.categoriesStore,
|
||||
required this.searchStore,
|
||||
required this.followedStreamsStore,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
|
@ -78,8 +76,11 @@ class _HomeState extends State<Home> {
|
|||
return IndexedStack(
|
||||
index: _selectedIndex,
|
||||
children: [
|
||||
if (authStore.isLoggedIn) FollowedStreams(store: widget.followedStreamsStore),
|
||||
TopSection(topStreamsStore: widget.topStreamsStore, categoriesStore: widget.categoriesStore),
|
||||
if (authStore.isLoggedIn) StreamsList(store: widget.followedStreamsStore!),
|
||||
TopSection(
|
||||
topStreamsStore: widget.topStreamsStore,
|
||||
categoriesStore: widget.categoriesStore,
|
||||
),
|
||||
Search(searchStore: widget.searchStore),
|
||||
],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:frosty/screens/search/search_results_categories.dart';
|
||||
import 'package:frosty/screens/search/search_results_channels.dart';
|
||||
import 'package:frosty/screens/search/search_store.dart';
|
||||
import 'package:frosty/screens/home/search/search_results_categories.dart';
|
||||
import 'package:frosty/screens/home/search/search_results_channels.dart';
|
||||
import 'package:frosty/screens/home/search/stores/search_store.dart';
|
||||
import 'package:frosty/widgets/section_header.dart';
|
||||
|
||||
class Search extends StatefulWidget {
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:frosty/models/category.dart';
|
||||
import 'package:frosty/screens/top/categories/category_card.dart';
|
||||
import 'package:frosty/screens/home/top/categories/category_card.dart';
|
||||
|
||||
class SearchResultsCategories extends StatelessWidget {
|
||||
final List<CategoryTwitch> categories;
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:frosty/screens/channel/video_chat.dart';
|
||||
import 'package:frosty/screens/search/search_store.dart';
|
||||
import 'package:frosty/screens/home/search/stores/search_store.dart';
|
||||
import 'package:frosty/widgets/profile_picture.dart';
|
||||
|
||||
class SearchResultsChannels extends StatelessWidget {
|
||||
130
lib/screens/home/stores/list_store.dart
Normal file
130
lib/screens/home/stores/list_store.dart
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
import 'package:frosty/api/twitch_api.dart';
|
||||
import 'package:frosty/core/auth/auth_store.dart';
|
||||
import 'package:frosty/models/category.dart';
|
||||
import 'package:frosty/models/stream.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'list_store.g.dart';
|
||||
|
||||
class ListStore = _ListStoreBase with _$ListStore;
|
||||
|
||||
abstract class _ListStoreBase with Store {
|
||||
/// The list of the fetched streams.
|
||||
@readonly
|
||||
var _streams = ObservableList<StreamTwitch>();
|
||||
|
||||
@readonly
|
||||
var _categories = ObservableList<CategoryTwitch>();
|
||||
|
||||
/// The loading status for pagination.
|
||||
@readonly
|
||||
bool _isLoading = false;
|
||||
|
||||
/// The pagination cursor for the streams.
|
||||
String? _currentCursor;
|
||||
|
||||
StreamListType listType;
|
||||
|
||||
final CategoryTwitch? categoryInfo;
|
||||
|
||||
/// The authentication store.
|
||||
final AuthStore authStore;
|
||||
|
||||
/// Returns whether or not there are more streams and loading status for pagination.
|
||||
bool get hasMore => _isLoading == false && _currentCursor != null;
|
||||
|
||||
_ListStoreBase({
|
||||
required this.authStore,
|
||||
required this.listType,
|
||||
this.categoryInfo,
|
||||
}) {
|
||||
switch (listType) {
|
||||
case StreamListType.followed:
|
||||
if (listType == StreamListType.followed && authStore.isLoggedIn) {
|
||||
getData();
|
||||
}
|
||||
break;
|
||||
case StreamListType.top:
|
||||
case StreamListType.category:
|
||||
getData();
|
||||
break;
|
||||
case StreamListType.categories:
|
||||
getGames();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetches the streams based on the type and current cursor.
|
||||
@action
|
||||
Future<void> getData() async {
|
||||
_isLoading = true;
|
||||
|
||||
final StreamsTwitch? newStreams;
|
||||
switch (listType) {
|
||||
case StreamListType.followed:
|
||||
newStreams = await Twitch.getFollowedStreams(
|
||||
id: authStore.user.details!.id,
|
||||
headers: authStore.headersTwitch,
|
||||
cursor: _currentCursor,
|
||||
);
|
||||
break;
|
||||
case StreamListType.top:
|
||||
newStreams = await Twitch.getTopStreams(
|
||||
headers: authStore.headersTwitch,
|
||||
cursor: _currentCursor,
|
||||
);
|
||||
break;
|
||||
case StreamListType.category:
|
||||
newStreams = await Twitch.getStreamsUnderGame(
|
||||
gameId: categoryInfo!.id,
|
||||
headers: authStore.headersTwitch,
|
||||
cursor: _currentCursor,
|
||||
);
|
||||
break;
|
||||
case StreamListType.categories:
|
||||
return getGames();
|
||||
}
|
||||
|
||||
if (newStreams != null) {
|
||||
if (_currentCursor == null) {
|
||||
_streams = newStreams.data.asObservable();
|
||||
} else {
|
||||
_streams.addAll(newStreams.data);
|
||||
}
|
||||
_currentCursor = newStreams.pagination['cursor'];
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
}
|
||||
|
||||
// Fetches the top categories based on the current cursor.
|
||||
@action
|
||||
Future<void> getGames() async {
|
||||
final result = await Twitch.getTopGames(headers: authStore.headersTwitch, cursor: _currentCursor);
|
||||
if (result != null) {
|
||||
if (_currentCursor == null) {
|
||||
_categories = result.data.asObservable();
|
||||
} else {
|
||||
_categories.addAll(result.data);
|
||||
}
|
||||
_currentCursor = result.pagination['cursor'];
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
}
|
||||
|
||||
/// Resets the cursor and then fetches the streams.
|
||||
@action
|
||||
Future<void> refresh() {
|
||||
_currentCursor = null;
|
||||
|
||||
return getData();
|
||||
}
|
||||
}
|
||||
|
||||
enum StreamListType {
|
||||
followed,
|
||||
top,
|
||||
category,
|
||||
categories,
|
||||
}
|
||||
97
lib/screens/home/stores/list_store.g.dart
Normal file
97
lib/screens/home/stores/list_store.g.dart
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of '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 _$ListStore on _ListStoreBase, Store {
|
||||
final _$_streamsAtom = Atom(name: '_ListStoreBase._streams');
|
||||
|
||||
ObservableList<StreamTwitch> get streams {
|
||||
_$_streamsAtom.reportRead();
|
||||
return super._streams;
|
||||
}
|
||||
|
||||
@override
|
||||
ObservableList<StreamTwitch> get _streams => streams;
|
||||
|
||||
@override
|
||||
set _streams(ObservableList<StreamTwitch> value) {
|
||||
_$_streamsAtom.reportWrite(value, super._streams, () {
|
||||
super._streams = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$_categoriesAtom = Atom(name: '_ListStoreBase._categories');
|
||||
|
||||
ObservableList<CategoryTwitch> get categories {
|
||||
_$_categoriesAtom.reportRead();
|
||||
return super._categories;
|
||||
}
|
||||
|
||||
@override
|
||||
ObservableList<CategoryTwitch> get _categories => categories;
|
||||
|
||||
@override
|
||||
set _categories(ObservableList<CategoryTwitch> value) {
|
||||
_$_categoriesAtom.reportWrite(value, super._categories, () {
|
||||
super._categories = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$_isLoadingAtom = Atom(name: '_ListStoreBase._isLoading');
|
||||
|
||||
bool get isLoading {
|
||||
_$_isLoadingAtom.reportRead();
|
||||
return super._isLoading;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get _isLoading => isLoading;
|
||||
|
||||
@override
|
||||
set _isLoading(bool value) {
|
||||
_$_isLoadingAtom.reportWrite(value, super._isLoading, () {
|
||||
super._isLoading = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$getDataAsyncAction = AsyncAction('_ListStoreBase.getData');
|
||||
|
||||
@override
|
||||
Future<void> getData() {
|
||||
return _$getDataAsyncAction.run(() => super.getData());
|
||||
}
|
||||
|
||||
final _$getGamesAsyncAction = AsyncAction('_ListStoreBase.getGames');
|
||||
|
||||
@override
|
||||
Future<void> getGames() {
|
||||
return _$getGamesAsyncAction.run(() => super.getGames());
|
||||
}
|
||||
|
||||
final _$_ListStoreBaseActionController =
|
||||
ActionController(name: '_ListStoreBase');
|
||||
|
||||
@override
|
||||
Future<void> refresh() {
|
||||
final _$actionInfo = _$_ListStoreBaseActionController.startAction(
|
||||
name: '_ListStoreBase.refresh');
|
||||
try {
|
||||
return super.refresh();
|
||||
} finally {
|
||||
_$_ListStoreBaseActionController.endAction(_$actionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''
|
||||
|
||||
''';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:frosty/core/settings/settings_store.dart';
|
||||
import 'package:frosty/screens/top/streams/top_streams_store.dart';
|
||||
import 'package:frosty/screens/home/stores/list_store.dart';
|
||||
import 'package:frosty/screens/settings/stores/settings_store.dart';
|
||||
import 'package:frosty/widgets/loading_indicator.dart';
|
||||
import 'package:frosty/widgets/stream_card.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class TopStreams extends StatelessWidget {
|
||||
final TopStreamsStore store;
|
||||
class StreamsList extends StatelessWidget {
|
||||
final ListStore store;
|
||||
|
||||
const TopStreams({Key? key, required this.store}) : super(key: key);
|
||||
const StreamsList({Key? key, required this.store}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -17,18 +17,18 @@ class TopStreams extends StatelessWidget {
|
|||
onRefresh: store.refresh,
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
if (store.topStreams.isEmpty && store.isLoading) {
|
||||
return const LoadingIndicator(subtitle: Text('Loading top streams...'));
|
||||
if (store.streams.isEmpty && store.isLoading) {
|
||||
return const LoadingIndicator(subtitle: Text('Loading streams...'));
|
||||
}
|
||||
return ListView.builder(
|
||||
itemCount: store.topStreams.length,
|
||||
itemCount: store.streams.length,
|
||||
itemBuilder: (context, index) {
|
||||
if (index > store.topStreams.length / 2 && store.hasMore) {
|
||||
store.getTopStreams();
|
||||
if (index > store.streams.length / 2 && store.hasMore) {
|
||||
store.getData();
|
||||
}
|
||||
return Observer(
|
||||
builder: (context) => StreamCard(
|
||||
streamInfo: store.topStreams[index],
|
||||
streamInfo: store.streams[index],
|
||||
showUptime: context.read<SettingsStore>().showThumbnailUptime,
|
||||
),
|
||||
);
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:frosty/screens/top/categories/categories_store.dart';
|
||||
import 'package:frosty/screens/top/categories/category_card.dart';
|
||||
import 'package:frosty/screens/home/stores/list_store.dart';
|
||||
import 'package:frosty/screens/home/top/categories/category_card.dart';
|
||||
|
||||
class Categories extends StatelessWidget {
|
||||
final CategoriesStore store;
|
||||
final ListStore store;
|
||||
|
||||
const Categories({
|
||||
Key? key,
|
||||
|
|
@ -2,8 +2,8 @@ import 'package:cached_network_image/cached_network_image.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:frosty/core/auth/auth_store.dart';
|
||||
import 'package:frosty/models/category.dart';
|
||||
import 'package:frosty/screens/top/categories/category_streams_list/category_streams.dart';
|
||||
import 'package:frosty/screens/top/categories/category_streams_list/category_streams_store.dart';
|
||||
import 'package:frosty/screens/home/stores/list_store.dart';
|
||||
import 'package:frosty/screens/home/top/categories/category_streams.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
/// A tappable card widget that displays a category's box art and name.
|
||||
|
|
@ -19,9 +19,10 @@ class CategoryCard extends StatelessWidget {
|
|||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CategoryStreams(
|
||||
store: CategoryStreamsStore(
|
||||
categoryInfo: category,
|
||||
store: ListStore(
|
||||
authStore: context.read<AuthStore>(),
|
||||
listType: StreamListType.category,
|
||||
categoryInfo: category,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:frosty/core/settings/settings_store.dart';
|
||||
import 'package:frosty/screens/top/categories/category_streams_list/category_streams_store.dart';
|
||||
import 'package:frosty/screens/home/stores/list_store.dart';
|
||||
import 'package:frosty/screens/settings/stores/settings_store.dart';
|
||||
import 'package:frosty/widgets/stream_card.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class CategoryStreams extends StatelessWidget {
|
||||
final CategoryStreamsStore store;
|
||||
final ListStore store;
|
||||
|
||||
const CategoryStreams({Key? key, required this.store}) : super(key: key);
|
||||
|
||||
|
|
@ -27,13 +27,13 @@ class CategoryStreams extends StatelessWidget {
|
|||
expandedHeight: MediaQuery.of(context).size.height / 3,
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
title: Text(
|
||||
store.categoryInfo.name,
|
||||
store.categoryInfo!.name,
|
||||
style: Theme.of(context).textTheme.headline6?.copyWith(fontWeight: FontWeight.bold),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
background: CachedNetworkImage(
|
||||
imageUrl: store.categoryInfo.boxArtUrl.replaceRange(store.categoryInfo.boxArtUrl.lastIndexOf('-') + 1, null, '300x400.jpg'),
|
||||
imageUrl: store.categoryInfo!.boxArtUrl.replaceRange(store.categoryInfo!.boxArtUrl.lastIndexOf('-') + 1, null, '300x400.jpg'),
|
||||
color: const Color.fromRGBO(255, 255, 255, 0.5),
|
||||
colorBlendMode: BlendMode.modulate,
|
||||
fit: BoxFit.cover,
|
||||
|
|
@ -44,7 +44,7 @@ class CategoryStreams extends StatelessWidget {
|
|||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index > store.streams.length / 2 && store.hasMore) {
|
||||
store.getStreams();
|
||||
store.getData();
|
||||
}
|
||||
return Observer(
|
||||
builder: (context) => StreamCard(
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:frosty/screens/top/categories/categories.dart';
|
||||
import 'package:frosty/screens/top/categories/categories_store.dart';
|
||||
import 'package:frosty/screens/top/streams/top_streams.dart';
|
||||
import 'package:frosty/screens/top/streams/top_streams_store.dart';
|
||||
import 'package:frosty/screens/home/stores/list_store.dart';
|
||||
import 'package:frosty/screens/home/streams_list.dart';
|
||||
import 'package:frosty/screens/home/top/categories/categories.dart';
|
||||
|
||||
class TopSection extends StatelessWidget {
|
||||
final TopStreamsStore topStreamsStore;
|
||||
final CategoriesStore categoriesStore;
|
||||
final ListStore topStreamsStore;
|
||||
final ListStore categoriesStore;
|
||||
|
||||
const TopSection({
|
||||
Key? key,
|
||||
|
|
@ -29,7 +28,7 @@ class TopSection extends StatelessWidget {
|
|||
Expanded(
|
||||
child: TabBarView(
|
||||
children: [
|
||||
TopStreams(store: topStreamsStore),
|
||||
StreamsList(store: topStreamsStore),
|
||||
Categories(store: categoriesStore),
|
||||
],
|
||||
),
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:frosty/core/auth/auth_store.dart';
|
||||
import 'package:frosty/core/settings/account/blocked_users.dart';
|
||||
import 'package:frosty/core/settings/account/profile_card.dart';
|
||||
import 'package:frosty/core/settings/settings_store.dart';
|
||||
import 'package:frosty/screens/settings/account/blocked_users.dart';
|
||||
import 'package:frosty/screens/settings/account/profile_card.dart';
|
||||
import 'package:frosty/screens/settings/stores/settings_store.dart';
|
||||
import 'package:frosty/widgets/section_header.dart';
|
||||
|
||||
class AccountSettings extends StatelessWidget {
|
||||
|
|
@ -20,7 +20,10 @@ class AccountSettings extends StatelessWidget {
|
|||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SectionHeader('Account'),
|
||||
const SectionHeader(
|
||||
'Account',
|
||||
padding: EdgeInsets.all(10.0),
|
||||
),
|
||||
ProfileCard(authStore: authStore),
|
||||
if (authStore.isLoggedIn)
|
||||
ListTile(
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:frosty/core/settings/settings_store.dart';
|
||||
import 'package:frosty/screens/settings/stores/settings_store.dart';
|
||||
import 'package:frosty/widgets/section_header.dart';
|
||||
|
||||
class ChatSettings extends StatelessWidget {
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:frosty/core/settings/settings_store.dart';
|
||||
import 'package:frosty/screens/settings/stores/settings_store.dart';
|
||||
import 'package:frosty/widgets/section_header.dart';
|
||||
|
||||
class GeneralSettings extends StatelessWidget {
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:frosty/core/auth/auth_store.dart';
|
||||
import 'package:frosty/core/settings/account/account_settings.dart';
|
||||
import 'package:frosty/core/settings/chat/chat_settings.dart';
|
||||
import 'package:frosty/core/settings/general/general_settings.dart';
|
||||
import 'package:frosty/core/settings/settings_store.dart';
|
||||
import 'package:frosty/core/settings/video/video_settings.dart';
|
||||
import 'package:frosty/screens/settings/account/account_settings.dart';
|
||||
import 'package:frosty/screens/settings/chat_settings.dart';
|
||||
import 'package:frosty/screens/settings/general_settings.dart';
|
||||
import 'package:frosty/screens/settings/stores/settings_store.dart';
|
||||
import 'package:frosty/screens/settings/video_settings.dart';
|
||||
import 'package:frosty/widgets/section_header.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class Settings extends StatelessWidget {
|
||||
|
|
@ -25,6 +26,17 @@ class Settings extends StatelessWidget {
|
|||
GeneralSettings(settingsStore: settingsStore),
|
||||
VideoSettings(settingsStore: settingsStore),
|
||||
ChatSettings(settingsStore: settingsStore),
|
||||
const SectionHeader('Misc'),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.info),
|
||||
title: const Text('About'),
|
||||
onTap: () => showAboutDialog(
|
||||
context: context,
|
||||
applicationName: 'Frosty for Twitch',
|
||||
applicationVersion: '1.0.0',
|
||||
applicationLegalese: '\u{a9} 2021 Tommy Chow',
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:frosty/core/settings/settings_store.dart';
|
||||
import 'package:frosty/screens/settings/stores/settings_store.dart';
|
||||
import 'package:frosty/widgets/section_header.dart';
|
||||
|
||||
class VideoSettings extends StatelessWidget {
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
import 'package:frosty/api/twitch_api.dart';
|
||||
import 'package:frosty/core/auth/auth_store.dart';
|
||||
import 'package:frosty/models/category.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'categories_store.g.dart';
|
||||
|
||||
class CategoriesStore = _CategoriesStoreBase with _$CategoriesStore;
|
||||
|
||||
abstract class _CategoriesStoreBase with Store {
|
||||
final AuthStore authStore;
|
||||
|
||||
@readonly
|
||||
var _categories = ObservableList<CategoryTwitch>();
|
||||
|
||||
String? _currentCursor;
|
||||
|
||||
var _isLoading = false;
|
||||
|
||||
bool get hasMore => _isLoading == false && _currentCursor != null;
|
||||
|
||||
_CategoriesStoreBase({required this.authStore}) {
|
||||
getGames();
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> getGames() async {
|
||||
_isLoading = true;
|
||||
|
||||
final result = await Twitch.getTopGames(headers: authStore.headersTwitch, cursor: _currentCursor);
|
||||
if (result != null) {
|
||||
if (_currentCursor == null) {
|
||||
_categories = result.data.asObservable();
|
||||
} else {
|
||||
_categories.addAll(result.data);
|
||||
}
|
||||
_currentCursor = result.pagination['cursor'];
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> refresh() async {
|
||||
_currentCursor = null;
|
||||
|
||||
return getGames();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'categories_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 _$CategoriesStore on _CategoriesStoreBase, Store {
|
||||
final _$_categoriesAtom = Atom(name: '_CategoriesStoreBase._categories');
|
||||
|
||||
ObservableList<CategoryTwitch> get categories {
|
||||
_$_categoriesAtom.reportRead();
|
||||
return super._categories;
|
||||
}
|
||||
|
||||
@override
|
||||
ObservableList<CategoryTwitch> get _categories => categories;
|
||||
|
||||
@override
|
||||
set _categories(ObservableList<CategoryTwitch> value) {
|
||||
_$_categoriesAtom.reportWrite(value, super._categories, () {
|
||||
super._categories = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$getGamesAsyncAction = AsyncAction('_CategoriesStoreBase.getGames');
|
||||
|
||||
@override
|
||||
Future<void> getGames() {
|
||||
return _$getGamesAsyncAction.run(() => super.getGames());
|
||||
}
|
||||
|
||||
final _$refreshAsyncAction = AsyncAction('_CategoriesStoreBase.refresh');
|
||||
|
||||
@override
|
||||
Future<void> refresh() {
|
||||
return _$refreshAsyncAction.run(() => super.refresh());
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''
|
||||
|
||||
''';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
import 'package:frosty/api/twitch_api.dart';
|
||||
import 'package:frosty/core/auth/auth_store.dart';
|
||||
import 'package:frosty/models/category.dart';
|
||||
import 'package:frosty/models/stream.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'category_streams_store.g.dart';
|
||||
|
||||
class CategoryStreamsStore = _CategoryStreamsStoreBase with _$CategoryStreamsStore;
|
||||
|
||||
abstract class _CategoryStreamsStoreBase with Store {
|
||||
final CategoryTwitch categoryInfo;
|
||||
|
||||
/// The list of the fetched streams under the category.
|
||||
@readonly
|
||||
var _streams = ObservableList<StreamTwitch>();
|
||||
|
||||
/// The loading status for pagination.
|
||||
bool _isLoading = false;
|
||||
|
||||
/// The pagination cursor for streams under the category.
|
||||
String? streamsCurrentCursor;
|
||||
|
||||
/// The authentication store used for obtaining the request headers.
|
||||
final AuthStore authStore;
|
||||
|
||||
/// Returns whether or not there are more streams and loading status for pagination.
|
||||
bool get hasMore => _isLoading == false && streamsCurrentCursor != null;
|
||||
|
||||
_CategoryStreamsStoreBase({
|
||||
required this.categoryInfo,
|
||||
required this.authStore,
|
||||
}) {
|
||||
getStreams();
|
||||
}
|
||||
|
||||
/// Fetches the streams under the category based on the current cursor.
|
||||
@action
|
||||
Future<void> getStreams() async {
|
||||
_isLoading = true;
|
||||
|
||||
final newStreams = await Twitch.getStreamsUnderGame(
|
||||
gameId: categoryInfo.id,
|
||||
headers: authStore.headersTwitch,
|
||||
cursor: streamsCurrentCursor,
|
||||
);
|
||||
|
||||
if (newStreams != null) {
|
||||
if (streamsCurrentCursor == null) {
|
||||
_streams = ObservableList.of(newStreams.data);
|
||||
} else {
|
||||
_streams.addAll(newStreams.data);
|
||||
}
|
||||
streamsCurrentCursor = newStreams.pagination['cursor'];
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
}
|
||||
|
||||
/// Resets the cursor and then fetches the streams under the category.
|
||||
@action
|
||||
Future<void> refresh() {
|
||||
streamsCurrentCursor = null;
|
||||
|
||||
return getStreams();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'category_streams_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 _$CategoryStreamsStore on _CategoryStreamsStoreBase, Store {
|
||||
final _$_streamsAtom = Atom(name: '_CategoryStreamsStoreBase._streams');
|
||||
|
||||
ObservableList<StreamTwitch> get streams {
|
||||
_$_streamsAtom.reportRead();
|
||||
return super._streams;
|
||||
}
|
||||
|
||||
@override
|
||||
ObservableList<StreamTwitch> get _streams => streams;
|
||||
|
||||
@override
|
||||
set _streams(ObservableList<StreamTwitch> value) {
|
||||
_$_streamsAtom.reportWrite(value, super._streams, () {
|
||||
super._streams = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$getStreamsAsyncAction =
|
||||
AsyncAction('_CategoryStreamsStoreBase.getStreams');
|
||||
|
||||
@override
|
||||
Future<void> getStreams() {
|
||||
return _$getStreamsAsyncAction.run(() => super.getStreams());
|
||||
}
|
||||
|
||||
final _$_CategoryStreamsStoreBaseActionController =
|
||||
ActionController(name: '_CategoryStreamsStoreBase');
|
||||
|
||||
@override
|
||||
Future<void> refresh() {
|
||||
final _$actionInfo = _$_CategoryStreamsStoreBaseActionController
|
||||
.startAction(name: '_CategoryStreamsStoreBase.refresh');
|
||||
try {
|
||||
return super.refresh();
|
||||
} finally {
|
||||
_$_CategoryStreamsStoreBaseActionController.endAction(_$actionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''
|
||||
|
||||
''';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
import 'package:frosty/api/twitch_api.dart';
|
||||
import 'package:frosty/core/auth/auth_store.dart';
|
||||
import 'package:frosty/models/stream.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'top_streams_store.g.dart';
|
||||
|
||||
class TopStreamsStore = _TopStreamsStoreBase with _$TopStreamsStore;
|
||||
|
||||
abstract class _TopStreamsStoreBase with Store {
|
||||
/// The list of the fetched top streams.
|
||||
@readonly
|
||||
var _topStreams = ObservableList<StreamTwitch>();
|
||||
|
||||
/// The loading status for pagination.
|
||||
@readonly
|
||||
bool _isLoading = false;
|
||||
|
||||
/// The pagination cursor for top streams.
|
||||
String? _topStreamsCurrentCursor;
|
||||
|
||||
/// The authentication store.
|
||||
final AuthStore authStore;
|
||||
|
||||
/// Returns whether or not there are more streams and loading status for pagination.
|
||||
bool get hasMore => _isLoading == false && _topStreamsCurrentCursor != null;
|
||||
|
||||
_TopStreamsStoreBase({required this.authStore}) {
|
||||
getTopStreams();
|
||||
}
|
||||
|
||||
/// Fetches the top streams based on the current cursor.
|
||||
@action
|
||||
Future<void> getTopStreams() async {
|
||||
_isLoading = true;
|
||||
|
||||
final newTopStreams = await Twitch.getTopStreams(
|
||||
headers: authStore.headersTwitch,
|
||||
cursor: _topStreamsCurrentCursor,
|
||||
);
|
||||
|
||||
if (newTopStreams != null) {
|
||||
if (_topStreamsCurrentCursor == null) {
|
||||
_topStreams = newTopStreams.data.asObservable();
|
||||
} else {
|
||||
_topStreams.addAll(newTopStreams.data);
|
||||
}
|
||||
_topStreamsCurrentCursor = newTopStreams.pagination['cursor'];
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
}
|
||||
|
||||
/// Resets the cursor and then fetches the top streams.
|
||||
@action
|
||||
Future<void> refresh() {
|
||||
_topStreamsCurrentCursor = null;
|
||||
|
||||
return getTopStreams();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'top_streams_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 _$TopStreamsStore on _TopStreamsStoreBase, Store {
|
||||
final _$_topStreamsAtom = Atom(name: '_TopStreamsStoreBase._topStreams');
|
||||
|
||||
ObservableList<StreamTwitch> get topStreams {
|
||||
_$_topStreamsAtom.reportRead();
|
||||
return super._topStreams;
|
||||
}
|
||||
|
||||
@override
|
||||
ObservableList<StreamTwitch> get _topStreams => topStreams;
|
||||
|
||||
@override
|
||||
set _topStreams(ObservableList<StreamTwitch> value) {
|
||||
_$_topStreamsAtom.reportWrite(value, super._topStreams, () {
|
||||
super._topStreams = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$_isLoadingAtom = Atom(name: '_TopStreamsStoreBase._isLoading');
|
||||
|
||||
bool get isLoading {
|
||||
_$_isLoadingAtom.reportRead();
|
||||
return super._isLoading;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get _isLoading => isLoading;
|
||||
|
||||
@override
|
||||
set _isLoading(bool value) {
|
||||
_$_isLoadingAtom.reportWrite(value, super._isLoading, () {
|
||||
super._isLoading = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$getTopStreamsAsyncAction =
|
||||
AsyncAction('_TopStreamsStoreBase.getTopStreams');
|
||||
|
||||
@override
|
||||
Future<void> getTopStreams() {
|
||||
return _$getTopStreamsAsyncAction.run(() => super.getTopStreams());
|
||||
}
|
||||
|
||||
final _$_TopStreamsStoreBaseActionController =
|
||||
ActionController(name: '_TopStreamsStoreBase');
|
||||
|
||||
@override
|
||||
Future<void> refresh() {
|
||||
final _$actionInfo = _$_TopStreamsStoreBaseActionController.startAction(
|
||||
name: '_TopStreamsStoreBase.refresh');
|
||||
try {
|
||||
return super.refresh();
|
||||
} finally {
|
||||
_$_TopStreamsStoreBaseActionController.endAction(_$actionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''
|
||||
|
||||
''';
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue