From 8eb7f36ab169c34242fb2b829f937e19eba50d73 Mon Sep 17 00:00:00 2001 From: Tommy Chow Date: Tue, 4 Jan 2022 02:58:02 -0500 Subject: [PATCH] Refactor home & streams/categories list --- lib/main.dart | 25 ++-- lib/screens/channel/chat/chat_message.dart | 2 +- lib/screens/channel/chat/chat_store.dart | 3 +- .../channel/chat/details/chat_users_list.dart | 5 +- lib/screens/channel/video/video_overlay.dart | 2 +- lib/screens/channel/video/video_store.dart | 2 +- lib/screens/channel/video_chat.dart | 4 +- .../followed_streams/followed_streams.dart | 43 ------ .../followed_streams_store.dart | 62 --------- .../followed_streams_store.g.dart | 76 ---------- lib/screens/home/home.dart | 31 +++-- lib/screens/{ => home}/search/search.dart | 6 +- .../search/search_results_categories.dart | 2 +- .../search/search_results_channels.dart | 2 +- .../search/stores}/search_store.dart | 0 .../search/stores}/search_store.g.dart | 0 lib/screens/home/stores/list_store.dart | 130 ++++++++++++++++++ lib/screens/home/stores/list_store.g.dart | 97 +++++++++++++ .../streams_list.dart} | 22 +-- .../{ => home}/top/categories/categories.dart | 6 +- .../top/categories/category_card.dart | 9 +- .../top/categories}/category_streams.dart | 12 +- lib/screens/{ => home}/top/top_section.dart | 13 +- .../settings/account/account_settings.dart | 11 +- .../settings/account/blocked_users.dart | 0 .../settings/account/profile_card.dart | 0 .../settings}/chat_settings.dart | 2 +- .../settings}/general_settings.dart | 2 +- lib/{core => screens}/settings/settings.dart | 22 ++- .../settings/stores}/settings_store.dart | 0 .../settings/stores}/settings_store.g.dart | 0 .../settings}/video_settings.dart | 2 +- .../top/categories/categories_store.dart | 49 ------- .../top/categories/categories_store.g.dart | 49 ------- .../category_streams_store.dart | 67 --------- .../category_streams_store.g.dart | 57 -------- .../top/streams/top_streams_store.dart | 61 -------- .../top/streams/top_streams_store.g.dart | 74 ---------- 38 files changed, 334 insertions(+), 616 deletions(-) delete mode 100644 lib/screens/followed_streams/followed_streams.dart delete mode 100644 lib/screens/followed_streams/followed_streams_store.dart delete mode 100644 lib/screens/followed_streams/followed_streams_store.g.dart rename lib/screens/{ => home}/search/search.dart (95%) rename lib/screens/{ => home}/search/search_results_categories.dart (87%) rename lib/screens/{ => home}/search/search_results_channels.dart (97%) rename lib/screens/{search => home/search/stores}/search_store.dart (100%) rename lib/screens/{search => home/search/stores}/search_store.g.dart (100%) create mode 100644 lib/screens/home/stores/list_store.dart create mode 100644 lib/screens/home/stores/list_store.g.dart rename lib/screens/{top/streams/top_streams.dart => home/streams_list.dart} (59%) rename lib/screens/{ => home}/top/categories/categories.dart (84%) rename lib/screens/{ => home}/top/categories/category_card.dart (87%) rename lib/screens/{top/categories/category_streams_list => home/top/categories}/category_streams.dart (83%) rename lib/screens/{ => home}/top/top_section.dart (64%) rename lib/{core => screens}/settings/account/account_settings.dart (85%) rename lib/{core => screens}/settings/account/blocked_users.dart (100%) rename lib/{core => screens}/settings/account/profile_card.dart (100%) rename lib/{core/settings/chat => screens/settings}/chat_settings.dart (96%) rename lib/{core/settings/general => screens/settings}/general_settings.dart (94%) rename lib/{core => screens}/settings/settings.dart (50%) rename lib/{core/settings => screens/settings/stores}/settings_store.dart (100%) rename lib/{core/settings => screens/settings/stores}/settings_store.g.dart (100%) rename lib/{core/settings/video => screens/settings}/video_settings.dart (94%) delete mode 100644 lib/screens/top/categories/categories_store.dart delete mode 100644 lib/screens/top/categories/categories_store.g.dart delete mode 100644 lib/screens/top/categories/category_streams_list/category_streams_store.dart delete mode 100644 lib/screens/top/categories/category_streams_list/category_streams_store.g.dart delete mode 100644 lib/screens/top/streams/top_streams_store.dart delete mode 100644 lib/screens/top/streams/top_streams_store.g.dart diff --git a/lib/main.dart b/lib/main.dart index 0bbf410e..a73f133e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -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), ), ); diff --git a/lib/screens/channel/chat/chat_message.dart b/lib/screens/channel/chat/chat_message.dart index ff647894..847c0b53 100644 --- a/lib/screens/channel/chat/chat_message.dart +++ b/lib/screens/channel/chat/chat_message.dart @@ -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; diff --git a/lib/screens/channel/chat/chat_store.dart b/lib/screens/channel/chat/chat_store.dart index 9e395424..d3ab3573 100644 --- a/lib/screens/channel/chat/chat_store.dart +++ b/lib/screens/channel/chat/chat_store.dart @@ -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); diff --git a/lib/screens/channel/chat/details/chat_users_list.dart b/lib/screens/channel/chat/details/chat_users_list.dart index 5e28a895..abf367d4 100644 --- a/lib/screens/channel/chat/details/chat_users_list.dart +++ b/lib/screens/channel/chat/details/chat_users_list.dart @@ -73,7 +73,10 @@ class _ChattersListState extends State { 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( diff --git a/lib/screens/channel/video/video_overlay.dart b/lib/screens/channel/video/video_overlay.dart index 0518c60d..e32227c4 100644 --- a/lib/screens/channel/video/video_overlay.dart +++ b/lib/screens/channel/video/video_overlay.dart @@ -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 { diff --git a/lib/screens/channel/video/video_store.dart b/lib/screens/channel/video/video_store.dart index 38adc5c9..aadd1480 100644 --- a/lib/screens/channel/video/video_store.dart +++ b/lib/screens/channel/video/video_store.dart @@ -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'; diff --git a/lib/screens/channel/video_chat.dart b/lib/screens/channel/video_chat.dart index 6ed0e934..e1321f3c 100644 --- a/lib/screens/channel/video_chat.dart +++ b/lib/screens/channel/video_chat.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 { diff --git a/lib/screens/followed_streams/followed_streams.dart b/lib/screens/followed_streams/followed_streams.dart deleted file mode 100644 index d2ad338d..00000000 --- a/lib/screens/followed_streams/followed_streams.dart +++ /dev/null @@ -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().showThumbnailUptime, - ), - ); - }, - ); - }, - ), - ); - } -} diff --git a/lib/screens/followed_streams/followed_streams_store.dart b/lib/screens/followed_streams/followed_streams_store.dart deleted file mode 100644 index 12bb0ebc..00000000 --- a/lib/screens/followed_streams/followed_streams_store.dart +++ /dev/null @@ -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(); - - /// 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 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 refresh() { - _followedStreamsCurrentCursor = null; - - return getFollowedStreams(); - } -} diff --git a/lib/screens/followed_streams/followed_streams_store.g.dart b/lib/screens/followed_streams/followed_streams_store.g.dart deleted file mode 100644 index b1f011eb..00000000 --- a/lib/screens/followed_streams/followed_streams_store.g.dart +++ /dev/null @@ -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 get followedStreams { - _$_followedStreamsAtom.reportRead(); - return super._followedStreams; - } - - @override - ObservableList get _followedStreams => followedStreams; - - @override - set _followedStreams(ObservableList 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 getFollowedStreams() { - return _$getFollowedStreamsAsyncAction - .run(() => super.getFollowedStreams()); - } - - final _$_FollowedStreamsStoreBaseActionController = - ActionController(name: '_FollowedStreamsStoreBase'); - - @override - Future refresh() { - final _$actionInfo = _$_FollowedStreamsStoreBaseActionController - .startAction(name: '_FollowedStreamsStoreBase.refresh'); - try { - return super.refresh(); - } finally { - _$_FollowedStreamsStoreBaseActionController.endAction(_$actionInfo); - } - } - - @override - String toString() { - return ''' - - '''; - } -} diff --git a/lib/screens/home/home.dart b/lib/screens/home/home.dart index fab456cc..4d5adbdf 100644 --- a/lib/screens/home/home.dart +++ b/lib/screens/home/home.dart @@ -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 { 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), ], ); diff --git a/lib/screens/search/search.dart b/lib/screens/home/search/search.dart similarity index 95% rename from lib/screens/search/search.dart rename to lib/screens/home/search/search.dart index 2abd9843..1a14dafa 100644 --- a/lib/screens/search/search.dart +++ b/lib/screens/home/search/search.dart @@ -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 { diff --git a/lib/screens/search/search_results_categories.dart b/lib/screens/home/search/search_results_categories.dart similarity index 87% rename from lib/screens/search/search_results_categories.dart rename to lib/screens/home/search/search_results_categories.dart index bb8559e9..b7cd377f 100644 --- a/lib/screens/search/search_results_categories.dart +++ b/lib/screens/home/search/search_results_categories.dart @@ -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 categories; diff --git a/lib/screens/search/search_results_channels.dart b/lib/screens/home/search/search_results_channels.dart similarity index 97% rename from lib/screens/search/search_results_channels.dart rename to lib/screens/home/search/search_results_channels.dart index f0078b83..a692d2d6 100644 --- a/lib/screens/search/search_results_channels.dart +++ b/lib/screens/home/search/search_results_channels.dart @@ -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 { diff --git a/lib/screens/search/search_store.dart b/lib/screens/home/search/stores/search_store.dart similarity index 100% rename from lib/screens/search/search_store.dart rename to lib/screens/home/search/stores/search_store.dart diff --git a/lib/screens/search/search_store.g.dart b/lib/screens/home/search/stores/search_store.g.dart similarity index 100% rename from lib/screens/search/search_store.g.dart rename to lib/screens/home/search/stores/search_store.g.dart diff --git a/lib/screens/home/stores/list_store.dart b/lib/screens/home/stores/list_store.dart new file mode 100644 index 00000000..2ea1acf7 --- /dev/null +++ b/lib/screens/home/stores/list_store.dart @@ -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(); + + @readonly + var _categories = ObservableList(); + + /// 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 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 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 refresh() { + _currentCursor = null; + + return getData(); + } +} + +enum StreamListType { + followed, + top, + category, + categories, +} diff --git a/lib/screens/home/stores/list_store.g.dart b/lib/screens/home/stores/list_store.g.dart new file mode 100644 index 00000000..1e2f6a31 --- /dev/null +++ b/lib/screens/home/stores/list_store.g.dart @@ -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 get streams { + _$_streamsAtom.reportRead(); + return super._streams; + } + + @override + ObservableList get _streams => streams; + + @override + set _streams(ObservableList value) { + _$_streamsAtom.reportWrite(value, super._streams, () { + super._streams = value; + }); + } + + final _$_categoriesAtom = Atom(name: '_ListStoreBase._categories'); + + ObservableList get categories { + _$_categoriesAtom.reportRead(); + return super._categories; + } + + @override + ObservableList get _categories => categories; + + @override + set _categories(ObservableList 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 getData() { + return _$getDataAsyncAction.run(() => super.getData()); + } + + final _$getGamesAsyncAction = AsyncAction('_ListStoreBase.getGames'); + + @override + Future getGames() { + return _$getGamesAsyncAction.run(() => super.getGames()); + } + + final _$_ListStoreBaseActionController = + ActionController(name: '_ListStoreBase'); + + @override + Future refresh() { + final _$actionInfo = _$_ListStoreBaseActionController.startAction( + name: '_ListStoreBase.refresh'); + try { + return super.refresh(); + } finally { + _$_ListStoreBaseActionController.endAction(_$actionInfo); + } + } + + @override + String toString() { + return ''' + + '''; + } +} diff --git a/lib/screens/top/streams/top_streams.dart b/lib/screens/home/streams_list.dart similarity index 59% rename from lib/screens/top/streams/top_streams.dart rename to lib/screens/home/streams_list.dart index 8855b8ad..0ac727c3 100644 --- a/lib/screens/top/streams/top_streams.dart +++ b/lib/screens/home/streams_list.dart @@ -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().showThumbnailUptime, ), ); diff --git a/lib/screens/top/categories/categories.dart b/lib/screens/home/top/categories/categories.dart similarity index 84% rename from lib/screens/top/categories/categories.dart rename to lib/screens/home/top/categories/categories.dart index 8ef77673..27c70ef3 100644 --- a/lib/screens/top/categories/categories.dart +++ b/lib/screens/home/top/categories/categories.dart @@ -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, diff --git a/lib/screens/top/categories/category_card.dart b/lib/screens/home/top/categories/category_card.dart similarity index 87% rename from lib/screens/top/categories/category_card.dart rename to lib/screens/home/top/categories/category_card.dart index aa22fa91..94bad921 100644 --- a/lib/screens/top/categories/category_card.dart +++ b/lib/screens/home/top/categories/category_card.dart @@ -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(), + listType: StreamListType.category, + categoryInfo: category, ), ), ), diff --git a/lib/screens/top/categories/category_streams_list/category_streams.dart b/lib/screens/home/top/categories/category_streams.dart similarity index 83% rename from lib/screens/top/categories/category_streams_list/category_streams.dart rename to lib/screens/home/top/categories/category_streams.dart index 56e89617..faec1747 100644 --- a/lib/screens/top/categories/category_streams_list/category_streams.dart +++ b/lib/screens/home/top/categories/category_streams.dart @@ -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( diff --git a/lib/screens/top/top_section.dart b/lib/screens/home/top/top_section.dart similarity index 64% rename from lib/screens/top/top_section.dart rename to lib/screens/home/top/top_section.dart index 9a0cb77e..8866d318 100644 --- a/lib/screens/top/top_section.dart +++ b/lib/screens/home/top/top_section.dart @@ -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), ], ), diff --git a/lib/core/settings/account/account_settings.dart b/lib/screens/settings/account/account_settings.dart similarity index 85% rename from lib/core/settings/account/account_settings.dart rename to lib/screens/settings/account/account_settings.dart index a976a6c3..f19bf33a 100644 --- a/lib/core/settings/account/account_settings.dart +++ b/lib/screens/settings/account/account_settings.dart @@ -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( diff --git a/lib/core/settings/account/blocked_users.dart b/lib/screens/settings/account/blocked_users.dart similarity index 100% rename from lib/core/settings/account/blocked_users.dart rename to lib/screens/settings/account/blocked_users.dart diff --git a/lib/core/settings/account/profile_card.dart b/lib/screens/settings/account/profile_card.dart similarity index 100% rename from lib/core/settings/account/profile_card.dart rename to lib/screens/settings/account/profile_card.dart diff --git a/lib/core/settings/chat/chat_settings.dart b/lib/screens/settings/chat_settings.dart similarity index 96% rename from lib/core/settings/chat/chat_settings.dart rename to lib/screens/settings/chat_settings.dart index 67fbf05c..f556765b 100644 --- a/lib/core/settings/chat/chat_settings.dart +++ b/lib/screens/settings/chat_settings.dart @@ -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 { diff --git a/lib/core/settings/general/general_settings.dart b/lib/screens/settings/general_settings.dart similarity index 94% rename from lib/core/settings/general/general_settings.dart rename to lib/screens/settings/general_settings.dart index 4f1c6275..fbb4f77d 100644 --- a/lib/core/settings/general/general_settings.dart +++ b/lib/screens/settings/general_settings.dart @@ -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 { diff --git a/lib/core/settings/settings.dart b/lib/screens/settings/settings.dart similarity index 50% rename from lib/core/settings/settings.dart rename to lib/screens/settings/settings.dart index f5a2521d..0e1ae6bf 100644 --- a/lib/core/settings/settings.dart +++ b/lib/screens/settings/settings.dart @@ -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', + ), + ) ], ), ), diff --git a/lib/core/settings/settings_store.dart b/lib/screens/settings/stores/settings_store.dart similarity index 100% rename from lib/core/settings/settings_store.dart rename to lib/screens/settings/stores/settings_store.dart diff --git a/lib/core/settings/settings_store.g.dart b/lib/screens/settings/stores/settings_store.g.dart similarity index 100% rename from lib/core/settings/settings_store.g.dart rename to lib/screens/settings/stores/settings_store.g.dart diff --git a/lib/core/settings/video/video_settings.dart b/lib/screens/settings/video_settings.dart similarity index 94% rename from lib/core/settings/video/video_settings.dart rename to lib/screens/settings/video_settings.dart index 907d79f4..1b0f2a1f 100644 --- a/lib/core/settings/video/video_settings.dart +++ b/lib/screens/settings/video_settings.dart @@ -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 { diff --git a/lib/screens/top/categories/categories_store.dart b/lib/screens/top/categories/categories_store.dart deleted file mode 100644 index 634df618..00000000 --- a/lib/screens/top/categories/categories_store.dart +++ /dev/null @@ -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(); - - String? _currentCursor; - - var _isLoading = false; - - bool get hasMore => _isLoading == false && _currentCursor != null; - - _CategoriesStoreBase({required this.authStore}) { - getGames(); - } - - @action - Future 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 refresh() async { - _currentCursor = null; - - return getGames(); - } -} diff --git a/lib/screens/top/categories/categories_store.g.dart b/lib/screens/top/categories/categories_store.g.dart deleted file mode 100644 index abcf364e..00000000 --- a/lib/screens/top/categories/categories_store.g.dart +++ /dev/null @@ -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 get categories { - _$_categoriesAtom.reportRead(); - return super._categories; - } - - @override - ObservableList get _categories => categories; - - @override - set _categories(ObservableList value) { - _$_categoriesAtom.reportWrite(value, super._categories, () { - super._categories = value; - }); - } - - final _$getGamesAsyncAction = AsyncAction('_CategoriesStoreBase.getGames'); - - @override - Future getGames() { - return _$getGamesAsyncAction.run(() => super.getGames()); - } - - final _$refreshAsyncAction = AsyncAction('_CategoriesStoreBase.refresh'); - - @override - Future refresh() { - return _$refreshAsyncAction.run(() => super.refresh()); - } - - @override - String toString() { - return ''' - - '''; - } -} diff --git a/lib/screens/top/categories/category_streams_list/category_streams_store.dart b/lib/screens/top/categories/category_streams_list/category_streams_store.dart deleted file mode 100644 index 1cd9206e..00000000 --- a/lib/screens/top/categories/category_streams_list/category_streams_store.dart +++ /dev/null @@ -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(); - - /// 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 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 refresh() { - streamsCurrentCursor = null; - - return getStreams(); - } -} diff --git a/lib/screens/top/categories/category_streams_list/category_streams_store.g.dart b/lib/screens/top/categories/category_streams_list/category_streams_store.g.dart deleted file mode 100644 index 475eb5ab..00000000 --- a/lib/screens/top/categories/category_streams_list/category_streams_store.g.dart +++ /dev/null @@ -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 get streams { - _$_streamsAtom.reportRead(); - return super._streams; - } - - @override - ObservableList get _streams => streams; - - @override - set _streams(ObservableList value) { - _$_streamsAtom.reportWrite(value, super._streams, () { - super._streams = value; - }); - } - - final _$getStreamsAsyncAction = - AsyncAction('_CategoryStreamsStoreBase.getStreams'); - - @override - Future getStreams() { - return _$getStreamsAsyncAction.run(() => super.getStreams()); - } - - final _$_CategoryStreamsStoreBaseActionController = - ActionController(name: '_CategoryStreamsStoreBase'); - - @override - Future refresh() { - final _$actionInfo = _$_CategoryStreamsStoreBaseActionController - .startAction(name: '_CategoryStreamsStoreBase.refresh'); - try { - return super.refresh(); - } finally { - _$_CategoryStreamsStoreBaseActionController.endAction(_$actionInfo); - } - } - - @override - String toString() { - return ''' - - '''; - } -} diff --git a/lib/screens/top/streams/top_streams_store.dart b/lib/screens/top/streams/top_streams_store.dart deleted file mode 100644 index 32d14551..00000000 --- a/lib/screens/top/streams/top_streams_store.dart +++ /dev/null @@ -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(); - - /// 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 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 refresh() { - _topStreamsCurrentCursor = null; - - return getTopStreams(); - } -} diff --git a/lib/screens/top/streams/top_streams_store.g.dart b/lib/screens/top/streams/top_streams_store.g.dart deleted file mode 100644 index 1acdf600..00000000 --- a/lib/screens/top/streams/top_streams_store.g.dart +++ /dev/null @@ -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 get topStreams { - _$_topStreamsAtom.reportRead(); - return super._topStreams; - } - - @override - ObservableList get _topStreams => topStreams; - - @override - set _topStreams(ObservableList 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 getTopStreams() { - return _$getTopStreamsAsyncAction.run(() => super.getTopStreams()); - } - - final _$_TopStreamsStoreBaseActionController = - ActionController(name: '_TopStreamsStoreBase'); - - @override - Future refresh() { - final _$actionInfo = _$_TopStreamsStoreBaseActionController.startAction( - name: '_TopStreamsStoreBase.refresh'); - try { - return super.refresh(); - } finally { - _$_TopStreamsStoreBaseActionController.endAction(_$actionInfo); - } - } - - @override - String toString() { - return ''' - - '''; - } -}