mirror of
https://github.com/Livinglist/Hacki.git
synced 2025-08-16 11:42:14 +08:00
fix flickering image. (#343)
This commit is contained in:
@ -35,7 +35,7 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
|||||||
super(const StoriesState.init()) {
|
super(const StoriesState.init()) {
|
||||||
on<LoadStories>(
|
on<LoadStories>(
|
||||||
onLoadStories,
|
onLoadStories,
|
||||||
transformer: sequential(),
|
transformer: concurrent(),
|
||||||
);
|
);
|
||||||
on<StoriesInitialize>(onInitialize);
|
on<StoriesInitialize>(onInitialize);
|
||||||
on<StoriesRefresh>(onRefresh);
|
on<StoriesRefresh>(onRefresh);
|
||||||
|
@ -169,20 +169,26 @@ class FavCubit extends Cubit<FavState> {
|
|||||||
emit(FavState.init());
|
emit(FavState.init());
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> merge() async {
|
Future<void> merge({required AppExceptionHandler onError}) async {
|
||||||
if (_authBloc.state.isLoggedIn) {
|
if (_authBloc.state.isLoggedIn) {
|
||||||
emit(state.copyWith(mergeStatus: Status.inProgress));
|
emit(state.copyWith(mergeStatus: Status.inProgress));
|
||||||
|
try {
|
||||||
final Iterable<int> ids = await _hackerNewsWebRepository.fetchFavorites(
|
final Iterable<int> ids = await _hackerNewsWebRepository.fetchFavorites(
|
||||||
of: _authBloc.state.username,
|
of: _authBloc.state.username,
|
||||||
);
|
);
|
||||||
final List<int> combinedIds = <int>[...ids, ...state.favIds];
|
final List<int> combinedIds = <int>[...ids, ...state.favIds];
|
||||||
final LinkedHashSet<int> mergedIds = LinkedHashSet<int>.from(combinedIds);
|
final LinkedHashSet<int> mergedIds =
|
||||||
|
LinkedHashSet<int>.from(combinedIds);
|
||||||
await _preferenceRepository.overwriteFav(
|
await _preferenceRepository.overwriteFav(
|
||||||
username: username,
|
username: username,
|
||||||
ids: mergedIds,
|
ids: mergedIds,
|
||||||
);
|
);
|
||||||
emit(state.copyWith(mergeStatus: Status.success));
|
emit(state.copyWith(mergeStatus: Status.success));
|
||||||
refresh();
|
refresh();
|
||||||
|
} on RateLimitedException catch (e) {
|
||||||
|
onError(e);
|
||||||
|
emit(state.copyWith(mergeStatus: Status.failure));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,9 +38,19 @@ extension ContextExtension on BuildContext {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showErrorSnackBar() => showSnackBar(
|
void showErrorSnackBar([String? message]) {
|
||||||
content: Constants.errorMessage,
|
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 {
|
Rect? get rect {
|
||||||
final RenderBox? box = findRenderObject() as RenderBox?;
|
final RenderBox? box = findRenderObject() as RenderBox?;
|
||||||
|
15
lib/models/app_exception.dart
Normal file
15
lib/models/app_exception.dart
Normal file
@ -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...');
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
export 'app_exception.dart';
|
||||||
export 'comments_order.dart';
|
export 'comments_order.dart';
|
||||||
export 'discoverable_feature.dart';
|
export 'discoverable_feature.dart';
|
||||||
export 'export_destination.dart';
|
export 'export_destination.dart';
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/foundation.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/dom.dart' hide Comment;
|
||||||
import 'package:html/parser.dart';
|
import 'package:html/parser.dart';
|
||||||
import 'package:html_unescape/html_unescape.dart';
|
import 'package:html_unescape/html_unescape.dart';
|
||||||
@ -25,6 +26,14 @@ class HackerNewsWebRepository {
|
|||||||
'''$_favoritesBaseUrl$username${isComment ? '&comments=t' : ''}&p=$page''',
|
'''$_favoritesBaseUrl$username${isComment ? '&comments=t' : ''}&p=$page''',
|
||||||
);
|
);
|
||||||
final Response response = await get(url);
|
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<void>.delayed(AppDurations.oneSecond);
|
||||||
|
|
||||||
final Document document = parse(response.body);
|
final Document document = parse(response.body);
|
||||||
final List<Element> elements = document.querySelectorAll(_aThingSelector);
|
final List<Element> elements = document.querySelectorAll(_aThingSelector);
|
||||||
final Iterable<int> parsedIds = elements
|
final Iterable<int> parsedIds = elements
|
||||||
@ -45,6 +54,7 @@ class HackerNewsWebRepository {
|
|||||||
page++;
|
page++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
page = 1;
|
||||||
while (true) {
|
while (true) {
|
||||||
ids = await fetchIds(page, isComment: true);
|
ids = await fetchIds(page, isComment: true);
|
||||||
if (ids.isEmpty) {
|
if (ids.isEmpty) {
|
||||||
|
@ -150,7 +150,11 @@ class _ProfileScreenState extends State<ProfileScreen>
|
|||||||
) =>
|
) =>
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.read<FavCubit>().merge();
|
context.read<FavCubit>().merge(
|
||||||
|
onError: (AppException e) {
|
||||||
|
context.showErrorSnackBar(e.message);
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
child: status == Status.inProgress
|
child: status == Status.inProgress
|
||||||
? const SizedBox(
|
? const SizedBox(
|
||||||
|
@ -119,10 +119,14 @@ class LinkView extends StatelessWidget {
|
|||||||
: CachedNetworkImage(
|
: CachedNetworkImage(
|
||||||
imageUrl: imageUri!,
|
imageUrl: imageUri!,
|
||||||
fit: isIcon ? BoxFit.scaleDown : BoxFit.fitWidth,
|
fit: isIcon ? BoxFit.scaleDown : BoxFit.fitWidth,
|
||||||
memCacheHeight: layoutHeight.toInt() * 4,
|
|
||||||
cacheKey: imageUri,
|
cacheKey: imageUri,
|
||||||
errorWidget: (_, __, ___) => const Center(
|
errorWidget: (_, __, ___) => Center(
|
||||||
child: Text(r'¯\_(ツ)_/¯'),
|
child: Text(
|
||||||
|
r'¯\_(ツ)_/¯',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Reference in New Issue
Block a user