From f081bd7c439ff91e6d9d43c3e683a3ccf72d734e Mon Sep 17 00:00:00 2001 From: "Alexander \"PapaTutuWawa" Date: Sun, 16 Jul 2023 17:37:47 +0200 Subject: [PATCH] fix: Multiple fixes - Improve performance when a lot of items are "unknown". Thanks to https://github.com/dattran-pt19/group_grid_view - Fix some behaviour in the refresh code --- lib/src/ui/bloc/anime_list_bloc.dart | 2 + lib/src/ui/bloc/calendar_bloc.dart | 26 +- lib/src/ui/pages/calendar.dart | 425 ++++++++++++++------------- 3 files changed, 238 insertions(+), 215 deletions(-) diff --git a/lib/src/ui/bloc/anime_list_bloc.dart b/lib/src/ui/bloc/anime_list_bloc.dart index 6262519..ba453e6 100644 --- a/lib/src/ui/bloc/anime_list_bloc.dart +++ b/lib/src/ui/bloc/anime_list_bloc.dart @@ -36,6 +36,8 @@ class AnimeListBloc extends Bloc { final List _mangas = List.empty(growable: true); + List get unfilteredAnime => _animes; + List _getFilteredAnime({ MediumTrackingState? trackingState, }) { diff --git a/lib/src/ui/bloc/calendar_bloc.dart b/lib/src/ui/bloc/calendar_bloc.dart index 332f7af..ae85633 100644 --- a/lib/src/ui/bloc/calendar_bloc.dart +++ b/lib/src/ui/bloc/calendar_bloc.dart @@ -28,7 +28,7 @@ class CalendarBloc extends Bloc { ); final al = GetIt.I.get(); - final animes = al.state.animes.where((anime) => anime.airing); + final animes = al.unfilteredAnime.where((anime) => anime.airing); emit( state.copyWith( refreshing: true, @@ -40,21 +40,33 @@ class CalendarBloc extends Bloc { for (final anime in animes) { emit(state.copyWith(refreshingCount: state.refreshingCount + 1)); - Anime apiData; + String? broadcastDay; + bool airing; try { - apiData = await Jikan().getAnime(int.parse(anime.id)); - } catch (_) { - print('API request for anime ${anime.id} failed'); - continue; + final apiData = await Jikan().getAnime(int.parse(anime.id)); + airing = apiData.airing; + broadcastDay = apiData.broadcast?.split(' ').first; + } catch (ex) { + print('API request for anime ${anime.id} failed: $ex'); + airing = false; } - if (!apiData.airing) { + print('Anime "${anime.title}": airing=${airing}'); + if (!airing) { al.add( AnimeUpdatedEvent( anime.copyWith(airing: false, broadcastDay: null), commit: true, ), ); + } else if (anime.broadcastDay != broadcastDay) { + print('Updating Anime "${anime.title}": broadcastDay=$broadcastDay'); + al.add( + AnimeUpdatedEvent( + anime.copyWith(airing: true, broadcastDay: broadcastDay), + commit: true, + ), + ); } // Prevent hammering Jikan diff --git a/lib/src/ui/pages/calendar.dart b/lib/src/ui/pages/calendar.dart index 9f387bb..10c3f6d 100644 --- a/lib/src/ui/pages/calendar.dart +++ b/lib/src/ui/pages/calendar.dart @@ -9,6 +9,7 @@ import 'package:anitrack/src/ui/widgets/grid_item.dart'; import 'package:anitrack/src/ui/widgets/image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:get_it/get_it.dart'; enum Weekday { monday, @@ -52,7 +53,7 @@ extension AddIfExists on Map> { } } -class CalendarPage extends StatelessWidget { +class CalendarPage extends StatefulWidget { const CalendarPage({super.key}); static MaterialPageRoute get route => MaterialPageRoute( @@ -62,6 +63,11 @@ class CalendarPage extends StatelessWidget { ), ); + @override + CalendarPageState createState() => CalendarPageState(); +} + +class CalendarPageState extends State { List _renderWeekdayList( BuildContext context, Weekday day, @@ -73,231 +79,234 @@ class CalendarPage extends StatelessWidget { assert(data[day]!.isNotEmpty, 'There should be at least one anime'); return [ - Padding( - padding: const EdgeInsets.only( - left: 16, - right: 16, - top: 16, - bottom: 4, - ), - child: Text( - day.toName(), - style: Theme.of(context).textTheme.titleLarge, + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.only( + left: 0, + right: 16, + top: 20, + bottom: 4, + ), + child: Text( + day.toName(), + style: Theme.of(context).textTheme.titleLarge, + ), ), ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: GridView.builder( - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, - mainAxisSpacing: 8, - crossAxisSpacing: 8, - childAspectRatio: 120 / (100 * (16 / 9)), - ), - itemCount: data[day]!.length, - itemBuilder: (context, index) { - final anime = data[day]![index]; - return GridItem( - child: AnimeCoverImage( - url: anime.thumbnailUrl, - hero: 'calendar_${anime.id}', - onTap: () { - context.read().add( - AnimeDetailsRequestedEvent( - anime, - heroImagePrefix: 'calendar_', - ), - ); - }, - ), - ); - }, + SliverGrid.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + mainAxisSpacing: 8, + crossAxisSpacing: 8, + childAspectRatio: 120 / (100 * (16 / 9)), ), + itemCount: data[day]!.length, + itemBuilder: (context, index) { + final anime = data[day]![index]; + return GridItem( + child: AnimeCoverImage( + url: anime.thumbnailUrl, + hero: 'calendar_${anime.id}', + onTap: () { + context.read().add( + AnimeDetailsRequestedEvent( + anime, + heroImagePrefix: 'calendar_', + ), + ); + }, + ), + ); + }, ), ]; } @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, calendarState) => - BlocBuilder( - buildWhen: (previous, current) => !calendarState.refreshing, - builder: (context, state) { - final airingAnimeMap = >{}; - for (final anime in state.animes) { - if (!anime.airing) continue; + final airingAnimeMap = >{}; + for (final anime in GetIt.I.get().unfilteredAnime) { + if (!anime.airing) continue; - final Weekday day; - switch (anime.broadcastDay) { - case 'Mondays': - day = Weekday.monday; - break; - case 'Tuesdays': - day = Weekday.tuesday; - break; - case 'Wednesdays': - day = Weekday.wednesday; - break; - case 'Thursdays': - day = Weekday.thursday; - break; - case 'Fridays': - day = Weekday.friday; - break; - case 'Saturdays': - day = Weekday.saturday; - break; - case 'Sundays': - day = Weekday.sunday; - break; - default: - day = Weekday.unknown; - break; - } + final Weekday day; + switch (anime.broadcastDay) { + case 'Mondays': + day = Weekday.monday; + break; + case 'Tuesdays': + day = Weekday.tuesday; + break; + case 'Wednesdays': + day = Weekday.wednesday; + break; + case 'Thursdays': + day = Weekday.thursday; + break; + case 'Fridays': + day = Weekday.friday; + break; + case 'Saturdays': + day = Weekday.saturday; + break; + case 'Sundays': + day = Weekday.sunday; + break; + default: + day = Weekday.unknown; + break; + } - airingAnimeMap.addOrSet(day, anime); - } + airingAnimeMap.addOrSet(day, anime); + } - return Scaffold( - appBar: AppBar( - title: Text(t.calendar.calendar), - actions: [ - IconButton( - onPressed: () { - context.read().add(RefreshPerformedEvent()); - }, - icon: const Icon(Icons.refresh), - ), - ], + return BlocListener( + listenWhen: (previous, current) => + previous.refreshing != current.refreshing, + listener: (context, state) { + // Force an update + if (!state.refreshing) { + setState(() {}); + } + }, + child: Scaffold( + appBar: AppBar( + title: Text(t.calendar.calendar), + actions: [ + IconButton( + onPressed: () { + context.read().add(RefreshPerformedEvent()); + }, + icon: const Icon(Icons.refresh), ), - drawer: getDrawer(context), - body: Stack( - children: [ - Positioned( - left: 0, - right: 0, - top: 0, - bottom: 0, - child: ListView( - children: [ - // Render all available weekdays - ..._renderWeekdayList( - context, - Weekday.unknown, - airingAnimeMap, - ), - ..._renderWeekdayList( - context, - Weekday.monday, - airingAnimeMap, - ), - ..._renderWeekdayList( - context, - Weekday.tuesday, - airingAnimeMap, - ), - ..._renderWeekdayList( - context, - Weekday.wednesday, - airingAnimeMap, - ), - ..._renderWeekdayList( - context, - Weekday.thursday, - airingAnimeMap, - ), - ..._renderWeekdayList( - context, - Weekday.friday, - airingAnimeMap, - ), - ..._renderWeekdayList( - context, - Weekday.saturday, - airingAnimeMap, - ), - ..._renderWeekdayList( - context, - Weekday.sunday, - airingAnimeMap, - ), - - // Provide a nice bottom padding, while keeping the elastic effect attached - // to the bottom-most edge. - const SizedBox( - height: 16, - ), - ], + ], + ), + drawer: getDrawer(context), + body: Stack( + children: [ + Positioned( + left: 8, + right: 8, + top: 0, + bottom: 0, + child: CustomScrollView( + slivers: [ + // Render all available weekdays + ..._renderWeekdayList( + context, + Weekday.unknown, + airingAnimeMap, ), - ), - Positioned( - left: 0, - right: 0, - top: 0, - bottom: 0, - child: BlocBuilder( - builder: (context, state) { - if (!state.refreshing) { - return const SizedBox(); - } - - return const ModalBarrier( - dismissible: false, - color: Colors.black54, - ); - }, + ..._renderWeekdayList( + context, + Weekday.monday, + airingAnimeMap, + ), + ..._renderWeekdayList( + context, + Weekday.tuesday, + airingAnimeMap, + ), + ..._renderWeekdayList( + context, + Weekday.wednesday, + airingAnimeMap, + ), + ..._renderWeekdayList( + context, + Weekday.thursday, + airingAnimeMap, + ), + ..._renderWeekdayList( + context, + Weekday.friday, + airingAnimeMap, + ), + ..._renderWeekdayList( + context, + Weekday.saturday, + airingAnimeMap, + ), + ..._renderWeekdayList( + context, + Weekday.sunday, + airingAnimeMap, ), - ), - Positioned( - left: 0, - right: 0, - top: 0, - bottom: 0, - child: BlocBuilder( - builder: (context, state) { - if (!state.refreshing) { - return const SizedBox(); - } - return Center( - child: SizedBox( - width: 150, - height: 150, - child: DecoratedBox( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: Colors.grey.shade800, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Padding( - padding: EdgeInsets.all(25), - child: CircularProgressIndicator(), - ), - Text( - t.settings.importIndicator( - current: state.refreshingCount, - total: state.refreshingTotal, - ), - style: Theme.of(context).textTheme.bodyMedium, - ), - ], - ), - ), + // Provide a nice bottom padding, while keeping the elastic effect attached + // to the bottom-most edge. + const SliverToBoxAdapter( + child: SizedBox( + height: 16, + ), + ), + ], + ), + ), + Positioned( + left: 0, + right: 0, + top: 0, + bottom: 0, + child: BlocBuilder( + buildWhen: (previous, current) => + previous.refreshing != current.refreshing, + builder: (context, state) { + if (!state.refreshing) { + return const SizedBox(); + } + + return const ModalBarrier( + dismissible: false, + color: Colors.black54, + ); + }, + ), + ), + Positioned( + left: 0, + right: 0, + top: 0, + bottom: 0, + child: BlocBuilder( + builder: (context, state) { + if (!state.refreshing) { + return const SizedBox(); + } + + return Center( + child: SizedBox( + width: 150, + height: 150, + child: DecoratedBox( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: Colors.grey.shade800, ), - ); - }, - ), - ), - ], + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Padding( + padding: EdgeInsets.all(25), + child: CircularProgressIndicator(), + ), + Text( + t.settings.importIndicator( + current: state.refreshingCount, + total: state.refreshingTotal, + ), + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + ), + ), + ); + }, + ), ), - ); - }, + ], + ), ), ); }