mirror of
https://github.com/Livinglist/Hacki.git
synced 2025-08-06 18:24:42 +08:00
improved performance.
This commit is contained in:
@ -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,
|
||||||
));
|
));
|
||||||
|
@ -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'¯\_(ツ)_/¯',
|
||||||
|
'( ͡° ͜ʖ ͡°)',
|
||||||
|
'(Θ︹Θ)',
|
||||||
|
'( ˘︹˘ )',
|
||||||
|
'(ㆆ_ㆆ)',
|
||||||
|
'ʕ•́ᴥ•̀ʔっ',
|
||||||
|
'(ㆆ_ㆆ)',
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
@ -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,9 +131,19 @@ class CommentsCubit<T extends Item> extends Cubit<CommentsState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void loadMore() {
|
void loadMore() {
|
||||||
|
if (_streamSubscription != null) {
|
||||||
emit(state.copyWith(status: CommentsStatus.loading));
|
emit(state.copyWith(status: CommentsStatus.loading));
|
||||||
_streamSubscription?.resume();
|
_streamSubscription?.resume();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onDone() {
|
||||||
|
_streamSubscription?.cancel();
|
||||||
|
_streamSubscription = null;
|
||||||
|
emit(state.copyWith(
|
||||||
|
status: CommentsStatus.allLoaded,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
void _onCommentFetched(Comment? comment) {
|
void _onCommentFetched(Comment? comment) {
|
||||||
if (comment != null) {
|
if (comment != null) {
|
||||||
|
@ -4,6 +4,7 @@ enum CommentsStatus {
|
|||||||
init,
|
init,
|
||||||
loading,
|
loading,
|
||||||
loaded,
|
loaded,
|
||||||
|
allLoaded,
|
||||||
failure,
|
failure,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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));
|
||||||
|
|
||||||
|
@ -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';
|
||||||
|
10
lib/extensions/list_extension.dart
Normal file
10
lib/extensions/list_extension.dart
Normal 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];
|
||||||
|
}
|
||||||
|
}
|
@ -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,
|
||||||
));
|
));
|
||||||
|
@ -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';
|
||||||
|
@ -90,6 +90,7 @@ class StoriesRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<Item> fetchItemsStream({required List<int> ids}) async* {
|
Stream<Item> fetchItemsStream({required List<int> ids}) async* {
|
||||||
|
@ -74,10 +74,12 @@ 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>().deviceScreenType = deviceType;
|
||||||
context.read<StoriesBloc>().add(StoriesInitialize());
|
context.read<StoriesBloc>().add(StoriesInitialize());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
34
lib/services/custom_bloc_observer.dart
Normal file
34
lib/services/custom_bloc_observer.dart
Normal 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:');
|
||||||
|
}
|
||||||
|
}
|
@ -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';
|
||||||
|
Reference in New Issue
Block a user