improved performance.

This commit is contained in:
Livinglist
2022-04-25 22:23:44 -07:00
parent 21bd63f591
commit 548ca244c3
16 changed files with 128 additions and 51 deletions

View File

@ -10,6 +10,7 @@ import 'package:hacki/repositories/repositories.dart';
import 'package:responsive_builder/responsive_builder.dart'; import 'package:responsive_builder/responsive_builder.dart';
part 'stories_event.dart'; part 'stories_event.dart';
part 'stories_state.dart'; part 'stories_state.dart';
class StoriesBloc extends Bloc<StoriesEvent, StoriesState> { class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
@ -55,7 +56,7 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
final hasCachedStories = await _cacheRepository.hasCachedStories; final hasCachedStories = await _cacheRepository.hasCachedStories;
final isComplexTile = _preferenceCubit.state.showComplexStoryTile; final isComplexTile = _preferenceCubit.state.showComplexStoryTile;
final pageSize = _getPageSize(isComplexTile: isComplexTile); final pageSize = _getPageSize(isComplexTile: isComplexTile);
emit(state.copyWith( emit(const StoriesState.init().copyWith(
offlineReading: hasCachedStories, offlineReading: hasCachedStories,
currentPageSize: pageSize, currentPageSize: pageSize,
)); ));

View File

@ -18,4 +18,25 @@ abstract class Constants {
static const String featureOpenStoryInWebView = 'open_story_in_web_view'; static const String featureOpenStoryInWebView = 'open_story_in_web_view';
static const String featureLogIn = 'log_in'; static const String featureLogIn = 'log_in';
static const String featurePinToTop = 'pin_to_top'; static const String featurePinToTop = 'pin_to_top';
static const List<String> happyFaces = <String>[
'(๑•̀ㅂ•́)و✧',
'( ͡• ͜ʖ ͡•)',
'( ͡~ ͜ʖ ͡°)',
'٩(˘◡˘)۶',
'(─‿‿─)',
'(¬‿¬)',
];
static const List<String> sadFaces = <String>[
'ಥ_ಥ',
'(╯°□°)╯︵ ┻━┻',
r'¯\_(ツ)_/¯',
'( ͡° ͜ʖ ͡°)',
'(Θ︹Θ)',
'( ˘︹˘ )',
'(ㆆ_ㆆ)',
'ʕ•́ᴥ•̀ʔっ',
'(ㆆ_ㆆ)',
];
} }

View File

@ -45,6 +45,7 @@ class CommentsCubit<T extends Item> extends Cubit<CommentsState> {
emit(state.copyWith( emit(state.copyWith(
comments: targetParents != null ? [...targetParents] : [], comments: targetParents != null ? [...targetParents] : [],
onlyShowTargetComment: true, onlyShowTargetComment: true,
status: CommentsStatus.loaded,
)); ));
return; return;
} }
@ -60,23 +61,15 @@ class CommentsCubit<T extends Item> extends Cubit<CommentsState> {
emit(state.copyWith(item: updatedStory)); emit(state.copyWith(item: updatedStory));
if (state.offlineReading) { if (state.offlineReading) {
_cacheRepository _streamSubscription = _cacheRepository
.getCachedCommentsStream(ids: updatedStory.kids) .getCachedCommentsStream(ids: updatedStory.kids)
.listen(_onCommentFetched) .listen(_onCommentFetched)
.onDone(() { ..onDone(_onDone);
emit(state.copyWith(
status: CommentsStatus.loaded,
));
});
} else { } else {
_streamSubscription = _storiesRepository _streamSubscription = _storiesRepository
.fetchCommentsStream(ids: updatedStory.kids) .fetchCommentsStream(ids: updatedStory.kids)
.listen(_onCommentFetched); .listen(_onCommentFetched)
// ..onDone(() { ..onDone(_onDone);
// emit(state.copyWith(
// status: CommentsStatus.loaded,
// ));
// });
} }
} else { } else {
emit(state.copyWith( emit(state.copyWith(
@ -106,18 +99,15 @@ class CommentsCubit<T extends Item> extends Cubit<CommentsState> {
await _storiesRepository.fetchStoryBy(story.id) ?? story; await _storiesRepository.fetchStoryBy(story.id) ?? story;
if (state.offlineReading) { if (state.offlineReading) {
_cacheRepository _streamSubscription = _cacheRepository
.getCachedCommentsStream(ids: updatedStory.kids) .getCachedCommentsStream(ids: updatedStory.kids)
.listen(_onCommentFetched) .listen(_onCommentFetched)
.onDone(() { ..onDone(_onDone);
emit(state.copyWith(
status: CommentsStatus.loaded,
));
});
} else { } else {
_streamSubscription = _storiesRepository _streamSubscription = _storiesRepository
.fetchCommentsStream(ids: updatedStory.kids) .fetchCommentsStream(ids: updatedStory.kids)
.listen(_onCommentFetched); .listen(_onCommentFetched)
..onDone(_onDone);
} }
emit(state.copyWith( emit(state.copyWith(
@ -141,8 +131,18 @@ class CommentsCubit<T extends Item> extends Cubit<CommentsState> {
} }
void loadMore() { void loadMore() {
emit(state.copyWith(status: CommentsStatus.loading)); if (_streamSubscription != null) {
_streamSubscription?.resume(); emit(state.copyWith(status: CommentsStatus.loading));
_streamSubscription?.resume();
}
}
void _onDone() {
_streamSubscription?.cancel();
_streamSubscription = null;
emit(state.copyWith(
status: CommentsStatus.allLoaded,
));
} }
void _onCommentFetched(Comment? comment) { void _onCommentFetched(Comment? comment) {

View File

@ -4,6 +4,7 @@ enum CommentsStatus {
init, init,
loading, loading,
loaded, loaded,
allLoaded,
failure, failure,
} }

View File

@ -22,6 +22,7 @@ class PinCubit extends Cubit<PinState> {
final StoriesRepository _storiesRepository; final StoriesRepository _storiesRepository;
void init() { void init() {
emit(PinState.init());
_storageRepository.pinnedStoriesIds.then((ids) { _storageRepository.pinnedStoriesIds.then((ids) {
emit(state.copyWith(pinnedStoriesIds: ids)); emit(state.copyWith(pinnedStoriesIds: ids));

View File

@ -1,4 +1,5 @@
export 'date_time_extension.dart'; export 'date_time_extension.dart';
export 'list_extension.dart';
export 'object_extension.dart'; export 'object_extension.dart';
export 'state_extension.dart'; export 'state_extension.dart';
export 'widget_extension.dart'; export 'widget_extension.dart';

View File

@ -0,0 +1,10 @@
import 'dart:math';
extension ListExtension<T> on List<T> {
T? pickRandomly() {
if (isEmpty) return null;
final random = Random(DateTime.now().millisecondsSinceEpoch);
final luckyNumber = random.nextInt(length);
return this[luckyNumber];
}
}

View File

@ -21,6 +21,16 @@ Future main() async {
final savedThemeMode = await AdaptiveTheme.getThemeMode(); final savedThemeMode = await AdaptiveTheme.getThemeMode();
// BlocOverrides.runZoned(
// () {
// runApp(HackiApp(
// savedThemeMode: savedThemeMode,
// ));
// },
// blocObserver: CustomBlocObserver(),
// );
// Uncomment code below for running without logging.
runApp(HackiApp( runApp(HackiApp(
savedThemeMode: savedThemeMode, savedThemeMode: savedThemeMode,
)); ));

View File

@ -1,7 +1,7 @@
export 'auth_repository.dart'; export 'auth_repository.dart';
export 'cache_repository.dart'; export 'cache_repository.dart';
export 'post_repository.dart'; export 'post_repository.dart';
export 'preference_repository.dart';
export 'search_repository.dart'; export 'search_repository.dart';
export 'sembast_repository.dart'; export 'sembast_repository.dart';
export 'storage_repository.dart';
export 'stories_repository.dart'; export 'stories_repository.dart';

View File

@ -90,6 +90,7 @@ class StoriesRepository {
); );
} }
} }
return;
} }
Stream<Item> fetchItemsStream({required List<int> ids}) async* { Stream<Item> fetchItemsStream({required List<int> ids}) async* {

View File

@ -74,9 +74,11 @@ class _HomeScreenState extends State<HomeScreen>
@override @override
void didChangeDependencies() { void didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();
context.read<StoriesBloc>().deviceScreenType = final deviceType = getDeviceType(MediaQuery.of(context).size);
getDeviceType(MediaQuery.of(context).size); if (context.read<StoriesBloc>().deviceScreenType != deviceType) {
context.read<StoriesBloc>().add(StoriesInitialize()); context.read<StoriesBloc>().deviceScreenType = deviceType;
context.read<StoriesBloc>().add(StoriesInitialize());
}
} }
@override @override

View File

@ -129,25 +129,7 @@ class _StoryScreenState extends State<StoryScreen> {
); );
final focusNode = FocusNode(); final focusNode = FocusNode();
final throttle = Throttle(delay: const Duration(seconds: 2)); final throttle = Throttle(delay: const Duration(seconds: 2));
final sadFaces = <String>[ final happyFace = Constants.happyFaces.pickRandomly()!;
'ಥ_ಥ',
'(╯°□°)╯︵ ┻━┻',
r'¯\_(ツ)_/¯',
'( ͡° ͜ʖ ͡°)',
'(Θ︹Θ)',
'( ˘︹˘ )',
'(ㆆ_ㆆ)',
'ʕ•́ᴥ•̀ʔっ',
'(ㆆ_ㆆ)',
];
final happyFaces = <String>[
'(๑•̀ㅂ•́)و✧',
'( ͡• ͜ʖ ͡•)',
'( ͡~ ͜ʖ ͡°)',
'٩(˘◡˘)۶',
'(─‿‿─)',
'(¬‿¬)',
];
@override @override
void initState() { void initState() {
@ -201,7 +183,8 @@ class _StoryScreenState extends State<StoryScreen> {
context.read<EditCubit>().state.replyingTo == null context.read<EditCubit>().state.replyingTo == null
? 'updated' ? 'updated'
: 'submitted'; : 'submitted';
final msg = 'Comment $verb! ${(happyFaces..shuffle()).first}'; final msg =
'Comment $verb! ${Constants.happyFaces.pickRandomly()}';
focusNode.unfocus(); focusNode.unfocus();
HapticFeedback.lightImpact(); HapticFeedback.lightImpact();
showSnackBar(content: msg); showSnackBar(content: msg);
@ -209,8 +192,8 @@ class _StoryScreenState extends State<StoryScreen> {
context.read<PostCubit>().reset(); context.read<PostCubit>().reset();
} else if (postState.status == PostStatus.failure) { } else if (postState.status == PostStatus.failure) {
showSnackBar( showSnackBar(
content: content: 'Something went wrong...'
'Something went wrong...${(sadFaces..shuffle()).first}', '${Constants.sadFaces.pickRandomly()}',
label: 'Okay', label: 'Okay',
action: ScaffoldMessenger.of(context).hideCurrentSnackBar, action: ScaffoldMessenger.of(context).hideCurrentSnackBar,
); );
@ -414,7 +397,7 @@ class _StoryScreenState extends State<StoryScreen> {
), ),
], ],
if (state.comments.isEmpty && if (state.comments.isEmpty &&
state.status == CommentsStatus.loaded) ...[ state.status == CommentsStatus.allLoaded) ...[
const SizedBox( const SizedBox(
height: 240, height: 240,
), ),
@ -461,6 +444,13 @@ class _StoryScreenState extends State<StoryScreen> {
onTimeMachineActivated: onTimeMachineActivated, onTimeMachineActivated: onTimeMachineActivated,
), ),
), ),
if (state.status == CommentsStatus.allLoaded)
SizedBox(
height: 240,
child: Center(
child: Text(happyFace),
),
),
], ],
), ),
); );
@ -955,8 +945,8 @@ class _StoryScreenState extends State<StoryScreen> {
void onLoginTapped() { void onLoginTapped() {
final usernameController = TextEditingController(); final usernameController = TextEditingController();
final passwordController = TextEditingController(); final passwordController = TextEditingController();
final sadFace = (sadFaces..shuffle()).first; final sadFace = Constants.sadFaces.pickRandomly();
final happyFace = (happyFaces..shuffle()).first; final happyFace = Constants.happyFaces.pickRandomly();
showDialog<void>( showDialog<void>(
context: context, context: context,
barrierDismissible: false, barrierDismissible: false,

View File

@ -268,6 +268,10 @@ class CommentTile extends StatelessWidget {
} }
Color _getColor(int level) { Color _getColor(int level) {
while (level >= 10) {
level = level - 10;
}
const r = 255; const r = 255;
var g = level * 40 < 255 ? 152 : (level * 20).clamp(0, 255); var g = level * 40 < 255 ? 152 : (level * 20).clamp(0, 255);
var b = (level * 40).clamp(0, 255); var b = (level * 40).clamp(0, 255);

View File

@ -0,0 +1,34 @@
import 'package:bloc/bloc.dart';
import 'package:hacki/extensions/extensions.dart' show ObjectExtension;
class CustomBlocObserver extends BlocObserver {
@override
void onCreate(BlocBase bloc) {
super.onCreate(bloc);
bloc.log(identifier: 'Bloc Created:');
}
@override
void onEvent(Bloc bloc, Object? event) {
super.onEvent(bloc, event);
event?.log(identifier: 'Bloc Event:');
}
@override
void onChange(BlocBase bloc, Change change) {
super.onChange(bloc, change);
change.log(identifier: 'Bloc Changed:');
}
@override
void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
super.onError(bloc, error, stackTrace);
error.log(identifier: 'Bloc Error:');
}
@override
void onClose(BlocBase bloc) {
super.onClose(bloc);
bloc.log(identifier: 'Bloc Closed:');
}
}

View File

@ -1,2 +1,3 @@
export 'cache_service.dart'; export 'cache_service.dart';
export 'custom_bloc_observer.dart';
export 'firebase_client.dart'; export 'firebase_client.dart';