From cb90751330033e45f28cc6897cd675075e7bb4fa Mon Sep 17 00:00:00 2001 From: Jojo Feng Date: Thu, 7 Dec 2023 23:24:43 -0800 Subject: [PATCH] fix flickering image. (#343) --- lib/blocs/stories/stories_bloc.dart | 2 +- lib/cubits/fav/fav_cubit.dart | 30 +++++++++++-------- lib/extensions/context_extension.dart | 16 ++++++++-- lib/models/app_exception.dart | 15 ++++++++++ lib/models/models.dart | 1 + .../hacker_news_web_repository.dart | 12 +++++++- lib/screens/profile/profile_screen.dart | 6 +++- .../widgets/link_preview/link_view.dart | 10 +++++-- 8 files changed, 71 insertions(+), 21 deletions(-) create mode 100644 lib/models/app_exception.dart diff --git a/lib/blocs/stories/stories_bloc.dart b/lib/blocs/stories/stories_bloc.dart index 4251884..442ed80 100644 --- a/lib/blocs/stories/stories_bloc.dart +++ b/lib/blocs/stories/stories_bloc.dart @@ -35,7 +35,7 @@ class StoriesBloc extends Bloc { super(const StoriesState.init()) { on( onLoadStories, - transformer: sequential(), + transformer: concurrent(), ); on(onInitialize); on(onRefresh); diff --git a/lib/cubits/fav/fav_cubit.dart b/lib/cubits/fav/fav_cubit.dart index 162e6f2..3ba97bf 100644 --- a/lib/cubits/fav/fav_cubit.dart +++ b/lib/cubits/fav/fav_cubit.dart @@ -169,20 +169,26 @@ class FavCubit extends Cubit { emit(FavState.init()); } - Future merge() async { + Future merge({required AppExceptionHandler onError}) async { if (_authBloc.state.isLoggedIn) { emit(state.copyWith(mergeStatus: Status.inProgress)); - final Iterable ids = await _hackerNewsWebRepository.fetchFavorites( - of: _authBloc.state.username, - ); - final List combinedIds = [...ids, ...state.favIds]; - final LinkedHashSet mergedIds = LinkedHashSet.from(combinedIds); - await _preferenceRepository.overwriteFav( - username: username, - ids: mergedIds, - ); - emit(state.copyWith(mergeStatus: Status.success)); - refresh(); + try { + final Iterable ids = await _hackerNewsWebRepository.fetchFavorites( + of: _authBloc.state.username, + ); + final List combinedIds = [...ids, ...state.favIds]; + final LinkedHashSet mergedIds = + LinkedHashSet.from(combinedIds); + await _preferenceRepository.overwriteFav( + username: username, + ids: mergedIds, + ); + emit(state.copyWith(mergeStatus: Status.success)); + refresh(); + } on RateLimitedException catch (e) { + onError(e); + emit(state.copyWith(mergeStatus: Status.failure)); + } } } diff --git a/lib/extensions/context_extension.dart b/lib/extensions/context_extension.dart index 9289ca4..b2cca51 100644 --- a/lib/extensions/context_extension.dart +++ b/lib/extensions/context_extension.dart @@ -38,9 +38,19 @@ extension ContextExtension on BuildContext { ); } - void showErrorSnackBar() => showSnackBar( - content: Constants.errorMessage, - ); + void showErrorSnackBar([String? message]) { + ScaffoldMessenger.of(this).showSnackBar( + SnackBar( + backgroundColor: Theme.of(this).colorScheme.errorContainer, + content: Text( + message ?? Constants.errorMessage, + style: TextStyle( + color: Theme.of(this).colorScheme.onErrorContainer, + ), + ), + ), + ); + } Rect? get rect { final RenderBox? box = findRenderObject() as RenderBox?; diff --git a/lib/models/app_exception.dart b/lib/models/app_exception.dart new file mode 100644 index 0000000..fb0ae1c --- /dev/null +++ b/lib/models/app_exception.dart @@ -0,0 +1,15 @@ +typedef AppExceptionHandler = void Function(AppException); + +class AppException implements Exception { + AppException({ + required this.message, + this.stackTrace, + }); + + final String message; + final StackTrace? stackTrace; +} + +class RateLimitedException extends AppException { + RateLimitedException() : super(message: 'Rate limited...'); +} diff --git a/lib/models/models.dart b/lib/models/models.dart index 7b791f6..01fe1a8 100644 --- a/lib/models/models.dart +++ b/lib/models/models.dart @@ -1,3 +1,4 @@ +export 'app_exception.dart'; export 'comments_order.dart'; export 'discoverable_feature.dart'; export 'export_destination.dart'; diff --git a/lib/repositories/hacker_news_web_repository.dart b/lib/repositories/hacker_news_web_repository.dart index 3ec34bd..05062ec 100644 --- a/lib/repositories/hacker_news_web_repository.dart +++ b/lib/repositories/hacker_news_web_repository.dart @@ -1,6 +1,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; -import 'package:hacki/models/item/comment.dart'; +import 'package:hacki/config/constants.dart'; +import 'package:hacki/models/models.dart'; import 'package:html/dom.dart' hide Comment; import 'package:html/parser.dart'; import 'package:html_unescape/html_unescape.dart'; @@ -25,6 +26,14 @@ class HackerNewsWebRepository { '''$_favoritesBaseUrl$username${isComment ? '&comments=t' : ''}&p=$page''', ); final Response response = await get(url); + + if (response.body.contains('Sorry')) { + throw RateLimitedException(); + } + + /// Due to rate limiting, we have a short break here. + await Future.delayed(AppDurations.oneSecond); + final Document document = parse(response.body); final List elements = document.querySelectorAll(_aThingSelector); final Iterable parsedIds = elements @@ -45,6 +54,7 @@ class HackerNewsWebRepository { page++; } + page = 1; while (true) { ids = await fetchIds(page, isComment: true); if (ids.isEmpty) { diff --git a/lib/screens/profile/profile_screen.dart b/lib/screens/profile/profile_screen.dart index 2b4a360..1597ab9 100644 --- a/lib/screens/profile/profile_screen.dart +++ b/lib/screens/profile/profile_screen.dart @@ -150,7 +150,11 @@ class _ProfileScreenState extends State ) => TextButton( onPressed: () { - context.read().merge(); + context.read().merge( + onError: (AppException e) { + context.showErrorSnackBar(e.message); + }, + ); }, child: status == Status.inProgress ? const SizedBox( diff --git a/lib/screens/widgets/link_preview/link_view.dart b/lib/screens/widgets/link_preview/link_view.dart index 67e39d3..c76a530 100644 --- a/lib/screens/widgets/link_preview/link_view.dart +++ b/lib/screens/widgets/link_preview/link_view.dart @@ -119,10 +119,14 @@ class LinkView extends StatelessWidget { : CachedNetworkImage( imageUrl: imageUri!, fit: isIcon ? BoxFit.scaleDown : BoxFit.fitWidth, - memCacheHeight: layoutHeight.toInt() * 4, cacheKey: imageUri, - errorWidget: (_, __, ___) => const Center( - child: Text(r'¯\_(ツ)_/¯'), + errorWidget: (_, __, ___) => Center( + child: Text( + r'¯\_(ツ)_/¯', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + ), ), ), ),