Combine top and categories stores into one

This commit is contained in:
Tommy Chow 2022-01-04 04:08:00 -05:00
parent 8eb7f36ab1
commit 1f2b99dfce
9 changed files with 78 additions and 69 deletions

View file

@ -92,18 +92,14 @@ class MyApp extends StatelessWidget {
title: 'Frosty',
theme: settingsStore.useOledTheme ? oledTheme : defaultTheme,
home: Home(
topStreamsStore: ListStore(
topSectionStore: ListStore(
authStore: authStore,
listType: StreamListType.top,
),
categoriesStore: ListStore(
authStore: authStore,
listType: StreamListType.categories,
listType: ListType.top,
),
followedStreamsStore: authStore.isLoggedIn
? ListStore(
authStore: authStore,
listType: StreamListType.followed,
listType: ListType.followed,
)
: null,
searchStore: SearchStore(authStore: authStore),

View file

@ -11,15 +11,13 @@ import 'package:frosty/screens/settings/stores/settings_store.dart';
import 'package:provider/provider.dart';
class Home extends StatefulWidget {
final ListStore topStreamsStore;
final ListStore categoriesStore;
final ListStore topSectionStore;
final SearchStore searchStore;
final ListStore? followedStreamsStore;
const Home({
Key? key,
required this.topStreamsStore,
required this.categoriesStore,
required this.topSectionStore,
required this.searchStore,
required this.followedStreamsStore,
}) : super(key: key);
@ -77,10 +75,7 @@ class _HomeState extends State<Home> {
index: _selectedIndex,
children: [
if (authStore.isLoggedIn) StreamsList(store: widget.followedStreamsStore!),
TopSection(
topStreamsStore: widget.topStreamsStore,
categoriesStore: widget.categoriesStore,
),
TopSection(topSectionStore: widget.topSectionStore),
Search(searchStore: widget.searchStore),
],
);

View file

@ -21,17 +21,20 @@ abstract class _ListStoreBase with Store {
bool _isLoading = false;
/// The pagination cursor for the streams.
String? _currentCursor;
String? _streamsCursor;
StreamListType listType;
/// The pagination cursor for the categories.
String? _categoriesCursor;
final CategoryTwitch? categoryInfo;
final ListType listType;
/// 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;
bool get hasMore => _isLoading == false && _streamsCursor != null;
_ListStoreBase({
required this.authStore,
@ -39,59 +42,55 @@ abstract class _ListStoreBase with Store {
this.categoryInfo,
}) {
switch (listType) {
case StreamListType.followed:
if (listType == StreamListType.followed && authStore.isLoggedIn) {
getData();
}
case ListType.followed:
if (listType == ListType.followed && authStore.isLoggedIn) getStreams();
break;
case StreamListType.top:
case StreamListType.category:
getData();
case ListType.top:
getStreams();
getCategories();
break;
case StreamListType.categories:
getGames();
case ListType.category:
getStreams();
break;
}
}
/// Fetches the streams based on the type and current cursor.
@action
Future<void> getData() async {
Future<void> getStreams() async {
_isLoading = true;
final StreamsTwitch? newStreams;
switch (listType) {
case StreamListType.followed:
case ListType.followed:
newStreams = await Twitch.getFollowedStreams(
id: authStore.user.details!.id,
headers: authStore.headersTwitch,
cursor: _currentCursor,
cursor: _streamsCursor,
);
break;
case StreamListType.top:
case ListType.top:
newStreams = await Twitch.getTopStreams(
headers: authStore.headersTwitch,
cursor: _currentCursor,
cursor: _streamsCursor,
);
break;
case StreamListType.category:
case ListType.category:
newStreams = await Twitch.getStreamsUnderGame(
gameId: categoryInfo!.id,
headers: authStore.headersTwitch,
cursor: _currentCursor,
cursor: _streamsCursor,
);
break;
case StreamListType.categories:
return getGames();
}
if (newStreams != null) {
if (_currentCursor == null) {
if (_streamsCursor == null) {
_streams = newStreams.data.asObservable();
} else {
_streams.addAll(newStreams.data);
}
_currentCursor = newStreams.pagination['cursor'];
_streamsCursor = newStreams.pagination['cursor'];
}
_isLoading = false;
@ -99,15 +98,17 @@ abstract class _ListStoreBase with Store {
// Fetches the top categories based on the current cursor.
@action
Future<void> getGames() async {
final result = await Twitch.getTopGames(headers: authStore.headersTwitch, cursor: _currentCursor);
Future<void> getCategories() async {
_isLoading = true;
final result = await Twitch.getTopGames(headers: authStore.headersTwitch, cursor: _categoriesCursor);
if (result != null) {
if (_currentCursor == null) {
if (_categoriesCursor == null) {
_categories = result.data.asObservable();
} else {
_categories.addAll(result.data);
}
_currentCursor = result.pagination['cursor'];
_categoriesCursor = result.pagination['cursor'];
}
_isLoading = false;
@ -115,16 +116,23 @@ abstract class _ListStoreBase with Store {
/// Resets the cursor and then fetches the streams.
@action
Future<void> refresh() {
_currentCursor = null;
Future<void> refreshStreams() {
_streamsCursor = null;
return getData();
return getStreams();
}
/// Resets the cursor and then fetches the categories.
@action
Future<void> refreshCategories() {
_categoriesCursor = null;
return getCategories();
}
}
enum StreamListType {
enum ListType {
followed,
top,
category,
categories,
}

View file

@ -60,29 +60,41 @@ mixin _$ListStore on _ListStoreBase, Store {
});
}
final _$getDataAsyncAction = AsyncAction('_ListStoreBase.getData');
final _$getStreamsAsyncAction = AsyncAction('_ListStoreBase.getStreams');
@override
Future<void> getData() {
return _$getDataAsyncAction.run(() => super.getData());
Future<void> getStreams() {
return _$getStreamsAsyncAction.run(() => super.getStreams());
}
final _$getGamesAsyncAction = AsyncAction('_ListStoreBase.getGames');
final _$getCategoriesAsyncAction =
AsyncAction('_ListStoreBase.getCategories');
@override
Future<void> getGames() {
return _$getGamesAsyncAction.run(() => super.getGames());
Future<void> getCategories() {
return _$getCategoriesAsyncAction.run(() => super.getCategories());
}
final _$_ListStoreBaseActionController =
ActionController(name: '_ListStoreBase');
@override
Future<void> refresh() {
Future<void> refreshStreams() {
final _$actionInfo = _$_ListStoreBaseActionController.startAction(
name: '_ListStoreBase.refresh');
name: '_ListStoreBase.refreshStreams');
try {
return super.refresh();
return super.refreshStreams();
} finally {
_$_ListStoreBaseActionController.endAction(_$actionInfo);
}
}
@override
Future<void> refreshCategories() {
final _$actionInfo = _$_ListStoreBaseActionController.startAction(
name: '_ListStoreBase.refreshCategories');
try {
return super.refreshCategories();
} finally {
_$_ListStoreBaseActionController.endAction(_$actionInfo);
}

View file

@ -14,7 +14,7 @@ class StreamsList extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: store.refresh,
onRefresh: store.refreshStreams,
child: Observer(
builder: (_) {
if (store.streams.isEmpty && store.isLoading) {
@ -24,7 +24,7 @@ class StreamsList extends StatelessWidget {
itemCount: store.streams.length,
itemBuilder: (context, index) {
if (index > store.streams.length / 2 && store.hasMore) {
store.getData();
store.getStreams();
}
return Observer(
builder: (context) => StreamCard(

View file

@ -14,7 +14,7 @@ class Categories extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: store.refresh,
onRefresh: store.refreshCategories,
child: Observer(
builder: (_) {
return GridView.builder(
@ -24,7 +24,7 @@ class Categories extends StatelessWidget {
itemCount: store.categories.length,
itemBuilder: (context, index) {
if (index > store.categories.length / 2 && store.hasMore) {
store.getGames();
store.getCategories();
}
return CategoryCard(category: store.categories[index]);
},

View file

@ -21,7 +21,7 @@ class CategoryCard extends StatelessWidget {
builder: (context) => CategoryStreams(
store: ListStore(
authStore: context.read<AuthStore>(),
listType: StreamListType.category,
listType: ListType.category,
categoryInfo: category,
),
),

View file

@ -15,7 +15,7 @@ class CategoryStreams extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
body: RefreshIndicator(
onRefresh: store.refresh,
onRefresh: store.refreshStreams,
child: Observer(
builder: (context) {
return CustomScrollView(
@ -44,7 +44,7 @@ class CategoryStreams extends StatelessWidget {
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index > store.streams.length / 2 && store.hasMore) {
store.getData();
store.getStreams();
}
return Observer(
builder: (context) => StreamCard(

View file

@ -4,13 +4,11 @@ import 'package:frosty/screens/home/streams_list.dart';
import 'package:frosty/screens/home/top/categories/categories.dart';
class TopSection extends StatelessWidget {
final ListStore topStreamsStore;
final ListStore categoriesStore;
final ListStore topSectionStore;
const TopSection({
Key? key,
required this.topStreamsStore,
required this.categoriesStore,
required this.topSectionStore,
}) : super(key: key);
@override
@ -28,8 +26,8 @@ class TopSection extends StatelessWidget {
Expanded(
child: TabBarView(
children: [
StreamsList(store: topStreamsStore),
Categories(store: categoriesStore),
StreamsList(store: topSectionStore),
Categories(store: topSectionStore),
],
),
),