From 1029a9a7a483b8a2fefadb158a5d56cad2714216 Mon Sep 17 00:00:00 2001 From: livinglist Date: Tue, 31 Mar 2026 23:07:05 -0700 Subject: [PATCH 1/7] update --- assets/haptics/heartbeats.ahap | 1 + ios/Podfile.lock | 6 +++++ lib/screens/item/widgets/main_view.dart | 32 +++++++++++++++++++++---- lib/utils/haptic_feedback_util.dart | 15 ++++++++++++ pubspec.lock | 8 +++++++ pubspec.yaml | 2 ++ 6 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 assets/haptics/heartbeats.ahap diff --git a/assets/haptics/heartbeats.ahap b/assets/haptics/heartbeats.ahap new file mode 100644 index 00000000..685c0d4b --- /dev/null +++ b/assets/haptics/heartbeats.ahap @@ -0,0 +1 @@ +{"Version":1,"Metadata":{"CreatedBy":"Haptics Simulator","CreatedAt":"","Description":"Three organic heartbeats over three seconds, made using precisely spaced transient events at varying parameters."},"Pattern":[{"Event":{"EventParameters":[{"ParameterValue":0.8,"ParameterID":"HapticIntensity"},{"ParameterValue":0.2,"ParameterID":"HapticSharpness"}],"Time":0,"EventType":"HapticTransient","EventDuration":0}},{"Event":{"EventParameters":[{"ParameterValue":1,"ParameterID":"HapticIntensity"},{"ParameterValue":0.3,"ParameterID":"HapticSharpness"}],"Time":0.013,"EventType":"HapticTransient","EventDuration":0}},{"Event":{"EventParameters":[{"ParameterValue":0.8,"ParameterID":"HapticIntensity"},{"ParameterValue":0.1,"ParameterID":"HapticSharpness"}],"Time":0.22,"EventType":"HapticTransient","EventDuration":0}},{"Event":{"EventParameters":[{"ParameterValue":0.7,"ParameterID":"HapticIntensity"},{"ParameterValue":0,"ParameterID":"HapticSharpness"}],"Time":0.255,"EventType":"HapticTransient","EventDuration":0}},{"Event":{"EventParameters":[{"ParameterValue":0.8,"ParameterID":"HapticIntensity"},{"ParameterValue":0.2,"ParameterID":"HapticSharpness"}],"Time":1,"EventType":"HapticTransient","EventDuration":0}},{"Event":{"EventParameters":[{"ParameterValue":1,"ParameterID":"HapticIntensity"},{"ParameterValue":0.3,"ParameterID":"HapticSharpness"}],"Time":1.013,"EventType":"HapticTransient","EventDuration":0}},{"Event":{"EventParameters":[{"ParameterValue":0.8,"ParameterID":"HapticIntensity"},{"ParameterValue":0.1,"ParameterID":"HapticSharpness"}],"Time":1.22,"EventType":"HapticTransient","EventDuration":0}},{"Event":{"EventParameters":[{"ParameterValue":0.7,"ParameterID":"HapticIntensity"},{"ParameterValue":0,"ParameterID":"HapticSharpness"}],"Time":1.255,"EventType":"HapticTransient","EventDuration":0}},{"Event":{"EventParameters":[{"ParameterValue":0.8,"ParameterID":"HapticIntensity"},{"ParameterValue":0.2,"ParameterID":"HapticSharpness"}],"Time":2,"EventType":"HapticTransient","EventDuration":0}},{"Event":{"EventParameters":[{"ParameterValue":1,"ParameterID":"HapticIntensity"},{"ParameterValue":0.3,"ParameterID":"HapticSharpness"}],"Time":2.013,"EventType":"HapticTransient","EventDuration":0}},{"Event":{"EventParameters":[{"ParameterValue":0.8,"ParameterID":"HapticIntensity"},{"ParameterValue":0.1,"ParameterID":"HapticSharpness"}],"Time":2.22,"EventType":"HapticTransient","EventDuration":0}},{"Event":{"EventParameters":[{"ParameterValue":0.7,"ParameterID":"HapticIntensity"},{"ParameterValue":0,"ParameterID":"HapticSharpness"}],"Time":2.255,"EventType":"HapticTransient","EventDuration":0}}]} \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 1d9cf491..e3033b05 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,4 +1,6 @@ PODS: + - advanced_haptics (0.0.1): + - Flutter - app_links (7.0.0): - Flutter - connectivity_plus (0.0.1): @@ -95,6 +97,7 @@ PODS: - Flutter DEPENDENCIES: + - advanced_haptics (from `.symlinks/plugins/advanced_haptics/ios`) - app_links (from `.symlinks/plugins/app_links/ios`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) @@ -129,6 +132,8 @@ SPEC REPOS: - SwiftyGif EXTERNAL SOURCES: + advanced_haptics: + :path: ".symlinks/plugins/advanced_haptics/ios" app_links: :path: ".symlinks/plugins/app_links/ios" connectivity_plus: @@ -179,6 +184,7 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/workmanager_apple/ios" SPEC CHECKSUMS: + advanced_haptics: 6bb54f03d66fe7258ab48130a19ce32bf85c212b app_links: 6d01271b3907b0ee7325c5297c75d697c4226c4d connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342 diff --git a/lib/screens/item/widgets/main_view.dart b/lib/screens/item/widgets/main_view.dart index e118657f..c81623a8 100644 --- a/lib/screens/item/widgets/main_view.dart +++ b/lib/screens/item/widgets/main_view.dart @@ -5,6 +5,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_fadein/flutter_fadein.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:hacki/blocs/blocs.dart'; import 'package:hacki/config/constants.dart'; import 'package:hacki/cubits/cubits.dart'; @@ -119,14 +120,35 @@ class MainView extends StatelessWidget { height: MediaQuery.of(context).size.height - MediaQuery.of(context).padding.top, child: Column( + mainAxisSize: MainAxisSize.min, children: [ SizedBoxes.pt100, - Text( - Constants.happyFace, - style: TextStyle( - color: Theme.of(context).hintColor, + if (preferenceState.isEyeCandyEnabled) + GestureDetector( + onLongPressDown: (_) => unawaited( + HapticFeedbackUtil.loadAndPlay(), + ), + onLongPressUp: HapticFeedbackUtil.stop, + child: Center( + child: Padding( + padding: + const EdgeInsets.all(Dimens.pt24), + child: FaIcon( + FontAwesomeIcons.heartPulse, + applyTextScaling: false, + color: Theme.of(context).hintColor, + size: Dimens.pt36, + ), + ), + ), + ) + else + Text( + Constants.happyFace, + style: TextStyle( + color: Theme.of(context).hintColor, + ), ), - ), SizedBoxes.pt36, Text( context.read().currentTips, diff --git a/lib/utils/haptic_feedback_util.dart b/lib/utils/haptic_feedback_util.dart index 7aa6c0f4..38dcae6a 100644 --- a/lib/utils/haptic_feedback_util.dart +++ b/lib/utils/haptic_feedback_util.dart @@ -1,3 +1,6 @@ +import 'dart:io'; + +import 'package:advanced_haptics/advanced_haptics.dart'; import 'package:flutter/services.dart'; abstract class HapticFeedbackUtil { @@ -32,4 +35,16 @@ abstract class HapticFeedbackUtil { HapticFeedback.heavyImpact(); } } + + static Future loadAndPlay() async { + if (Platform.isIOS) { + await AdvancedHaptics.playAhap('assets/haptics/heartbeats.ahap'); + } else { + final List timings = [0, 60, 50, 100, 50]; + final List amplitudes = [0, 255, 0, 150, 0]; + await AdvancedHaptics.playWaveform(timings, amplitudes); + } + } + + static Future stop() async => AdvancedHaptics.stop(); } diff --git a/pubspec.lock b/pubspec.lock index 69da8f52..31cd71a3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.7.2" + advanced_haptics: + dependency: "direct main" + description: + name: advanced_haptics + sha256: "8537e3f27cb87e7775c29ab2ece12e9cd283b29f668e4d0d3a7e6dbdbb1d1aba" + url: "https://pub.dev" + source: hosted + version: "1.0.8" analyzer: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index aa9a6010..92cc377d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,6 +9,7 @@ environment: dependencies: adaptive_theme: ^3.7.2 + advanced_haptics: ^1.0.8 animations: ^2.1.2 app_links: ^7.0.0 badges: ^3.1.2 @@ -102,6 +103,7 @@ flutter: assets: - assets/images/ - assets/videos/ + - assets/haptics/ - assets/remote-config-dev.json fonts: From c5f53f772ecbfe6a354aa60d313d92044ced0846 Mon Sep 17 00:00:00 2001 From: livinglist Date: Tue, 31 Mar 2026 23:25:49 -0700 Subject: [PATCH 2/7] update --- lib/cubits/preference/preference_cubit.dart | 5 +++++ lib/screens/item/widgets/main_view.dart | 3 +-- lib/utils/haptic_feedback_util.dart | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/cubits/preference/preference_cubit.dart b/lib/cubits/preference/preference_cubit.dart index be23f645..ce4067d7 100644 --- a/lib/cubits/preference/preference_cubit.dart +++ b/lib/cubits/preference/preference_cubit.dart @@ -8,6 +8,7 @@ import 'package:hacki/config/locator.dart'; import 'package:hacki/extensions/extensions.dart'; import 'package:hacki/models/models.dart'; import 'package:hacki/repositories/repositories.dart'; +import 'package:hacki/utils/haptic_feedback_util.dart'; part 'preference_state.dart'; @@ -79,6 +80,10 @@ class PreferenceCubit extends Cubit with Loggable { emit(state.copyWithPreference(preference)); + if (preference is EyeCandyPreference && preference.val as bool) { + unawaited(HapticFeedbackUtil.loadAndPlay()); + } + switch (preference.val) { case int(): _preferenceRepository.setInt( diff --git a/lib/screens/item/widgets/main_view.dart b/lib/screens/item/widgets/main_view.dart index c81623a8..fe772054 100644 --- a/lib/screens/item/widgets/main_view.dart +++ b/lib/screens/item/widgets/main_view.dart @@ -125,10 +125,9 @@ class MainView extends StatelessWidget { SizedBoxes.pt100, if (preferenceState.isEyeCandyEnabled) GestureDetector( - onLongPressDown: (_) => unawaited( + onTap: () => unawaited( HapticFeedbackUtil.loadAndPlay(), ), - onLongPressUp: HapticFeedbackUtil.stop, child: Center( child: Padding( padding: diff --git a/lib/utils/haptic_feedback_util.dart b/lib/utils/haptic_feedback_util.dart index 38dcae6a..316f6166 100644 --- a/lib/utils/haptic_feedback_util.dart +++ b/lib/utils/haptic_feedback_util.dart @@ -37,6 +37,7 @@ abstract class HapticFeedbackUtil { } static Future loadAndPlay() async { + await stop(); if (Platform.isIOS) { await AdvancedHaptics.playAhap('assets/haptics/heartbeats.ahap'); } else { @@ -46,5 +47,5 @@ abstract class HapticFeedbackUtil { } } - static Future stop() async => AdvancedHaptics.stop(); + static Future stop() => AdvancedHaptics.stop(); } From 0e20d72475d0b6f086795c822944897e1844420b Mon Sep 17 00:00:00 2001 From: livinglist Date: Tue, 31 Mar 2026 23:34:32 -0700 Subject: [PATCH 3/7] update --- lib/screens/item/widgets/main_view.dart | 3 +++ lib/utils/haptic_feedback_util.dart | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/screens/item/widgets/main_view.dart b/lib/screens/item/widgets/main_view.dart index fe772054..3d08915c 100644 --- a/lib/screens/item/widgets/main_view.dart +++ b/lib/screens/item/widgets/main_view.dart @@ -119,6 +119,8 @@ class MainView extends StatelessWidget { color: Theme.of(context).canvasColor, height: MediaQuery.of(context).size.height - MediaQuery.of(context).padding.top, + padding: const EdgeInsets.symmetric( + horizontal: Dimens.pt48), child: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -156,6 +158,7 @@ class MainView extends StatelessWidget { color: Theme.of(context).hintColor, ), textScaler: TextScaler.noScaling, + textAlign: TextAlign.center, ), ], ), diff --git a/lib/utils/haptic_feedback_util.dart b/lib/utils/haptic_feedback_util.dart index 316f6166..0e947e3f 100644 --- a/lib/utils/haptic_feedback_util.dart +++ b/lib/utils/haptic_feedback_util.dart @@ -47,5 +47,9 @@ abstract class HapticFeedbackUtil { } } - static Future stop() => AdvancedHaptics.stop(); + static Future stop() async { + try { + await AdvancedHaptics.stop(); + } catch (_) {} + } } From c572515a5d9242632497f0ffc274e3d2aae3ed2f Mon Sep 17 00:00:00 2001 From: livinglist Date: Tue, 31 Mar 2026 23:46:24 -0700 Subject: [PATCH 4/7] update --- lib/utils/haptic_feedback_util.dart | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/utils/haptic_feedback_util.dart b/lib/utils/haptic_feedback_util.dart index 0e947e3f..ed77f254 100644 --- a/lib/utils/haptic_feedback_util.dart +++ b/lib/utils/haptic_feedback_util.dart @@ -5,6 +5,7 @@ import 'package:flutter/services.dart'; abstract class HapticFeedbackUtil { static bool enabled = true; + static bool? hasCustomHapticsSupport; static void success() { if (enabled) { @@ -37,13 +38,16 @@ abstract class HapticFeedbackUtil { } static Future loadAndPlay() async { - await stop(); - if (Platform.isIOS) { - await AdvancedHaptics.playAhap('assets/haptics/heartbeats.ahap'); - } else { - final List timings = [0, 60, 50, 100, 50]; - final List amplitudes = [0, 255, 0, 150, 0]; - await AdvancedHaptics.playWaveform(timings, amplitudes); + hasCustomHapticsSupport ??= await AdvancedHaptics.hasCustomHapticsSupport(); + if (hasCustomHapticsSupport ?? false) { + await stop(); + if (Platform.isIOS) { + await AdvancedHaptics.playAhap('assets/haptics/heartbeats.ahap'); + } else { + final List timings = [0, 60, 50, 100, 50]; + final List amplitudes = [0, 255, 0, 150, 0]; + await AdvancedHaptics.playWaveform(timings, amplitudes); + } } } From 2d0f27d0ce8bde572c85812f47bc085f82acb8e4 Mon Sep 17 00:00:00 2001 From: livinglist Date: Tue, 31 Mar 2026 23:54:47 -0700 Subject: [PATCH 5/7] rename --- lib/config/constants.dart | 1 + lib/config/locator.dart | 6 +-- lib/cubits/comments/comments_cubit.dart | 12 ++--- lib/cubits/preference/preference_cubit.dart | 4 +- lib/cubits/search/search_cubit.dart | 20 +++---- lib/cubits/vote/vote_cubit.dart | 20 +++---- lib/extensions/buildable_mixin.dart | 4 +- lib/extensions/item_action_mixin.dart | 10 ++-- lib/extensions/widget_extension.dart | 4 +- lib/main.dart | 8 +-- lib/repositories/hacker_news_repository.dart | 2 +- lib/repositories/post_repository.dart | 4 +- lib/repositories/search_repository.dart | 4 +- lib/screens/home/home_screen.dart | 2 +- lib/screens/home/widgets/pinned_stories.dart | 2 +- lib/screens/item/item_screen.dart | 8 +-- lib/screens/item/widgets/custom_app_bar.dart | 2 +- .../custom_floating_action_button.dart | 4 +- lib/screens/item/widgets/fav_icon_button.dart | 2 +- .../widgets/in_thread_search_icon_button.dart | 4 +- .../item/widgets/item_screen_background.dart | 4 +- .../item/widgets/lazy_fetch_load_button.dart | 2 +- .../item/widgets/link_icon_button.dart | 2 +- lib/screens/item/widgets/login_dialog.dart | 2 +- lib/screens/item/widgets/main_view.dart | 16 +++--- lib/screens/item/widgets/more_popup_menu.dart | 4 +- lib/screens/item/widgets/pin_icon_button.dart | 2 +- lib/screens/item/widgets/poll_view.dart | 2 +- lib/screens/item/widgets/reply_box.dart | 4 +- .../item/widgets/time_machine_dialog.dart | 4 +- lib/screens/logs/logs_screen.dart | 8 +-- lib/screens/profile/profile_screen.dart | 4 +- .../widgets/enter_offline_mode_list_tile.dart | 4 +- .../profile/widgets/favorites_screen.dart | 4 +- lib/screens/profile/widgets/inbox_view.dart | 4 +- .../profile/widgets/offline_list_tile.dart | 4 +- lib/screens/profile/widgets/settings.dart | 52 +++++++++---------- .../profile/widgets/tab_bar_settings.dart | 2 +- .../number_of_comments_filter_chip.dart | 4 +- .../search/widgets/points_filter_chip.dart | 4 +- lib/screens/share/share_screen.dart | 4 +- lib/screens/submit/submit_screen.dart | 2 +- lib/screens/widgets/comment_tile.dart | 6 +-- .../custom_described_feature_overlay.dart | 4 +- .../custom_linkify/custom_linkify.dart | 8 +-- lib/screens/widgets/custom_tab_bar.dart | 4 +- .../widgets/download_progress_reminder.dart | 4 +- lib/screens/widgets/item_text.dart | 8 +-- lib/screens/widgets/items_list_view.dart | 4 +- .../link_preview/image_wrapped_text.dart | 4 +- .../widgets/link_preview/link_view.dart | 4 +- lib/screens/widgets/onboarding_view.dart | 2 +- lib/screens/widgets/stories_list_view.dart | 6 +-- lib/screens/widgets/story_tile.dart | 2 +- lib/services/dialog_proxy.dart | 4 +- lib/services/fetcher.dart | 2 +- .../{color_util.dart => color_utils.dart} | 2 +- lib/utils/date_utils.dart | 0 ...k_util.dart => haptic_feedback_utils.dart} | 2 +- lib/utils/{html_util.dart => html_utils.dart} | 2 +- lib/utils/{link_util.dart => link_utils.dart} | 2 +- ...nkifier_util.dart => linkifier_utils.dart} | 2 +- lib/utils/{log_util.dart => log_utils.dart} | 2 +- .../{theme_util.dart => theme_utils.dart} | 2 +- lib/utils/utils.dart | 14 ++--- 65 files changed, 176 insertions(+), 175 deletions(-) rename lib/utils/{color_util.dart => color_utils.dart} (97%) create mode 100644 lib/utils/date_utils.dart rename lib/utils/{haptic_feedback_util.dart => haptic_feedback_utils.dart} (96%) rename lib/utils/{html_util.dart => html_utils.dart} (98%) rename lib/utils/{link_util.dart => link_utils.dart} (98%) rename lib/utils/{linkifier_util.dart => linkifier_utils.dart} (95%) rename lib/utils/{log_util.dart => log_utils.dart} (98%) rename lib/utils/{theme_util.dart => theme_utils.dart} (98%) diff --git a/lib/config/constants.dart b/lib/config/constants.dart index 57baa42c..641a9684 100644 --- a/lib/config/constants.dart +++ b/lib/config/constants.dart @@ -62,6 +62,7 @@ abstract class Constants { 'Download stories in settings to read stories offline', 'Open any Hacker News links in Hacki via the system share menu', '''Swipe right on a comment and tap the dots icon to search for the poster within the thread or across HN''', + if(DateTime.now()) ].randomlyPicked!; static final String happyFace = [ diff --git a/lib/config/locator.dart b/lib/config/locator.dart index 9a2ee32c..7099c9f6 100644 --- a/lib/config/locator.dart +++ b/lib/config/locator.dart @@ -14,14 +14,14 @@ final GetIt locator = GetIt.instance; /// Set up [GetIt] locator. Future setUpLocator() async { - final File logOutputFile = await LogUtil.initLogFile(); + final File logOutputFile = await LogUtils.initLogFile(); locator ..registerSingleton( Logger( filter: CustomLogFilter(), - printer: LogUtil.logPrinter, - output: LogUtil.logOutput(logOutputFile), + printer: LogUtils.logPrinter, + output: LogUtils.logOutput(logOutputFile), ), ) ..registerSingleton(SembastRepository()) diff --git a/lib/cubits/comments/comments_cubit.dart b/lib/cubits/comments/comments_cubit.dart index 121c311f..ab132846 100644 --- a/lib/cubits/comments/comments_cubit.dart +++ b/lib/cubits/comments/comments_cubit.dart @@ -489,7 +489,7 @@ class CommentsCubit extends Cubit with Loggable, BuildableMixin { } void loadAll(Story story) { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); emit( state.copyWith( onlyShowTargetComment: false, @@ -664,7 +664,7 @@ class CommentsCubit extends Cubit with Loggable, BuildableMixin { } Future loadParentThread() async { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); emit(state.copyWith(fetchParentStatus: CommentsStatus.inProgress)); final Item? parent = await _hackerNewsRepository.fetchItem(id: state.item.parent); @@ -686,7 +686,7 @@ class CommentsCubit extends Cubit with Loggable, BuildableMixin { } Future loadRootThread() async { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); emit(state.copyWith(fetchRootStatus: CommentsStatus.inProgress)); final Story? parent = await _hackerNewsRepository .fetchParentStory(id: state.item.id) @@ -712,7 +712,7 @@ class CommentsCubit extends Cubit with Loggable, BuildableMixin { if (order == null) return; if (state.order == order) return; if (state.status == CommentsStatus.inProgress) return; - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); _streamSubscription?.cancel(); for (final StreamSubscription s in _streamSubscriptions.values) { s.cancel(); @@ -744,7 +744,7 @@ class CommentsCubit extends Cubit with Loggable, BuildableMixin { if (fetchMode == null) return; if (state.fetchMode == fetchMode) return; - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); _streamSubscription?.cancel(); for (final StreamSubscription s in _streamSubscriptions.values) { s.cancel(); @@ -1151,7 +1151,7 @@ comments length is ${state.comments.length} final int newCommentsCount = state.comments.where((Comment c) => c.isNew).length; if (newCommentsCount > 0) { - HapticFeedbackUtil.success(); + HapticFeedbackUtils.success(); navigatorKey.currentContext?.showSnackBar( persist: false, duration: AppDurations.fiveSeconds, diff --git a/lib/cubits/preference/preference_cubit.dart b/lib/cubits/preference/preference_cubit.dart index ce4067d7..05918227 100644 --- a/lib/cubits/preference/preference_cubit.dart +++ b/lib/cubits/preference/preference_cubit.dart @@ -8,7 +8,7 @@ import 'package:hacki/config/locator.dart'; import 'package:hacki/extensions/extensions.dart'; import 'package:hacki/models/models.dart'; import 'package:hacki/repositories/repositories.dart'; -import 'package:hacki/utils/haptic_feedback_util.dart'; +import 'package:hacki/utils/haptic_feedback_utils.dart'; part 'preference_state.dart'; @@ -81,7 +81,7 @@ class PreferenceCubit extends Cubit with Loggable { emit(state.copyWithPreference(preference)); if (preference is EyeCandyPreference && preference.val as bool) { - unawaited(HapticFeedbackUtil.loadAndPlay()); + unawaited(HapticFeedbackUtils.loadAndPlay()); } switch (preference.val) { diff --git a/lib/cubits/search/search_cubit.dart b/lib/cubits/search/search_cubit.dart index 5be47650..424167eb 100644 --- a/lib/cubits/search/search_cubit.dart +++ b/lib/cubits/search/search_cubit.dart @@ -7,7 +7,7 @@ import 'package:hacki/config/locator.dart'; import 'package:hacki/extensions/buildable_mixin.dart'; import 'package:hacki/models/models.dart'; import 'package:hacki/repositories/repositories.dart'; -import 'package:hacki/utils/haptic_feedback_util.dart'; +import 'package:hacki/utils/haptic_feedback_utils.dart'; import 'package:rxdart/rxdart.dart'; part 'search_state.dart'; @@ -72,7 +72,7 @@ class SearchCubit extends Cubit with BuildableMixin { } void addFilter(T filter) { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); if (state.params.contains()) { emit( state.copyWith( @@ -91,7 +91,7 @@ class SearchCubit extends Cubit with BuildableMixin { } void removeFilter() { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); if (state.params.contains() == false) return; emit( @@ -104,7 +104,7 @@ class SearchCubit extends Cubit with BuildableMixin { } void onToggled(TypeTagFilter filter) { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); if (state.params.contains() && state.params.get() == filter) { removeFilter(); @@ -115,7 +115,7 @@ class SearchCubit extends Cubit with BuildableMixin { } void onSortToggled() { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); emit( state.copyWith( params: state.params.copyWith( @@ -128,7 +128,7 @@ class SearchCubit extends Cubit with BuildableMixin { } void onExactMatchToggled() { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); emit( state.copyWith( params: state.params.copyWith( @@ -141,7 +141,7 @@ class SearchCubit extends Cubit with BuildableMixin { } void onDateTimeRangeUpdated(DateTime start, DateTime end) { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); final DateTime updatedStart = start.copyWith( second: 0, millisecond: 0, @@ -168,7 +168,7 @@ class SearchCubit extends Cubit with BuildableMixin { } void onPostedByChanged(String? username) { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); if (username == null) { removeFilter(); } else { @@ -181,7 +181,7 @@ class SearchCubit extends Cubit with BuildableMixin { state.params.filters.whereType().singleOrNull; if (pointsFilter == existingFilter) return; - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); if (pointsFilter == null) { removeFilter(); } else { @@ -194,7 +194,7 @@ class SearchCubit extends Cubit with BuildableMixin { state.params.filters.whereType().singleOrNull; if (filter == existingFilter) return; - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); if (filter == null) { removeFilter(); } else { diff --git a/lib/cubits/vote/vote_cubit.dart b/lib/cubits/vote/vote_cubit.dart index c9161374..31b1c580 100644 --- a/lib/cubits/vote/vote_cubit.dart +++ b/lib/cubits/vote/vote_cubit.dart @@ -6,7 +6,7 @@ import 'package:hacki/blocs/blocs.dart'; import 'package:hacki/config/locator.dart'; import 'package:hacki/models/models.dart'; import 'package:hacki/repositories/repositories.dart'; -import 'package:hacki/utils/haptic_feedback_util.dart'; +import 'package:hacki/utils/haptic_feedback_utils.dart'; part 'vote_state.dart'; @@ -53,19 +53,19 @@ class VoteCubit extends Cubit { Future upvote({bool cancelIfVoted = true}) async { if (!_authBloc.state.isLoggedIn) { - HapticFeedbackUtil.error(); + HapticFeedbackUtils.error(); emit(state.copyWith(status: VoteStatus.failureNotLoggedIn)); return false; } if (state.item.by == _authBloc.state.username) { - HapticFeedbackUtil.error(); + HapticFeedbackUtils.error(); emit(state.copyWith(status: VoteStatus.failureBeHumble)); return false; } if (state.item.dead || state.item.deleted || state.item.by.isEmpty) { - HapticFeedbackUtil.error(); + HapticFeedbackUtils.error(); emit(state.copyWith(status: VoteStatus.failure)); return false; } @@ -77,7 +77,7 @@ class VoteCubit extends Cubit { ); if (success) { - HapticFeedbackUtil.success(); + HapticFeedbackUtils.success(); emit( state.copyWith( vote: Vote.up, @@ -95,7 +95,7 @@ class VoteCubit extends Cubit { return true; } else { - HapticFeedbackUtil.error(); + HapticFeedbackUtils.error(); emit( state.copyWith( @@ -112,7 +112,7 @@ class VoteCubit extends Cubit { id: state.item.id, ); - HapticFeedbackUtil.success(); + HapticFeedbackUtils.success(); emit( state.copyWithVoteRemoved( status: VoteStatus.canceled, @@ -148,7 +148,7 @@ class VoteCubit extends Cubit { vote: false, ); - HapticFeedbackUtil.success(); + HapticFeedbackUtils.success(); emit( state.copyWith( vote: Vote.down, @@ -163,7 +163,7 @@ class VoteCubit extends Cubit { id: state.item.id, ); - HapticFeedbackUtil.success(); + HapticFeedbackUtils.success(); emit( state.copyWithVoteRemoved( status: VoteStatus.canceled, @@ -171,7 +171,7 @@ class VoteCubit extends Cubit { ); } } else { - HapticFeedbackUtil.error(); + HapticFeedbackUtils.error(); emit(state.copyWith(status: VoteStatus.failureKarmaBelowThreshold)); } } diff --git a/lib/extensions/buildable_mixin.dart b/lib/extensions/buildable_mixin.dart index 26ef02b7..52a0894f 100644 --- a/lib/extensions/buildable_mixin.dart +++ b/lib/extensions/buildable_mixin.dart @@ -36,7 +36,7 @@ mixin BuildableMixin { if (comment == null) return null; final List elements = await Isolate.run( - () => LinkifierUtil.linkify( + () => LinkifierUtils.linkify( comment.text, extraLinkifiers: [ if (withHighlightedText != null && withHighlightedText.isNotEmpty) @@ -60,7 +60,7 @@ mixin BuildableMixin { final List elements = await compute>( - LinkifierUtil.linkify, + LinkifierUtils.linkify, story.text, ); diff --git a/lib/extensions/item_action_mixin.dart b/lib/extensions/item_action_mixin.dart index f02231fc..d6d574f9 100644 --- a/lib/extensions/item_action_mixin.dart +++ b/lib/extensions/item_action_mixin.dart @@ -53,7 +53,7 @@ mixin ItemActionMixin on State { Item? parent, VoidCallback? onSearchInThreadTapped, }) { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); if (item.dead || item.deleted) { return; @@ -106,11 +106,11 @@ mixin ItemActionMixin on State { if (isFav) { favCubit.removeFav(item.id); showSnackBar(content: 'Removed from favorites.'); - HapticFeedbackUtil.success(); + HapticFeedbackUtils.success(); } else { favCubit.addFav(item.id); showSnackBar(content: 'Added to favorites.'); - HapticFeedbackUtil.success(); + HapticFeedbackUtils.success(); } } @@ -194,7 +194,7 @@ mixin ItemActionMixin on State { if (mounted && (yesTapped ?? false)) { context.read().add(AuthFlag(item: item)); showSnackBar(content: 'Comment flagged!'); - HapticFeedbackUtil.success(); + HapticFeedbackUtils.success(); } }); } @@ -240,7 +240,7 @@ mixin ItemActionMixin on State { } showSnackBar(content: 'User ${isBlocked ? 'unblocked' : 'blocked'}!'); - HapticFeedbackUtil.success(); + HapticFeedbackUtils.success(); } }); } diff --git a/lib/extensions/widget_extension.dart b/lib/extensions/widget_extension.dart index cb1759e1..f4b4dae6 100644 --- a/lib/extensions/widget_extension.dart +++ b/lib/extensions/widget_extension.dart @@ -46,14 +46,14 @@ extension ContextMenuBuilder on Widget { ) ..addAll([ ContextMenuButtonItem( - onPressed: () => LinkUtil.launch( + onPressed: () => LinkUtils.launch( '''${Constants.wikipediaLink}$selectedText''', context, ), label: 'Wikipedia', ), ContextMenuButtonItem( - onPressed: () => LinkUtil.launch( + onPressed: () => LinkUtils.launch( '''${Constants.wiktionaryLink}$selectedText''', context, ), diff --git a/lib/main.dart b/lib/main.dart index e5e710af..95c1918b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -21,8 +21,8 @@ import 'package:hacki/screens/widgets/widgets.dart'; import 'package:hacki/services/fetcher.dart'; import 'package:hacki/styles/styles.dart'; import 'package:hacki/utils/debug_http_overrides.dart'; -import 'package:hacki/utils/haptic_feedback_util.dart'; -import 'package:hacki/utils/theme_util.dart'; +import 'package:hacki/utils/haptic_feedback_utils.dart'; +import 'package:hacki/utils/theme_utils.dart'; import 'package:hive/hive.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:intl/date_symbol_data_local.dart'; @@ -246,7 +246,7 @@ class HackiApp extends StatelessWidget { listenWhen: (PreferenceState previous, PreferenceState current) => previous.isHapticFeedbackEnabled != current.isHapticFeedbackEnabled, listener: (_, PreferenceState state) { - HapticFeedbackUtil.enabled = state.isHapticFeedbackEnabled; + HapticFeedbackUtils.enabled = state.isHapticFeedbackEnabled; }, buildWhen: (PreferenceState previous, PreferenceState current) => previous.appColor != current.appColor || @@ -286,7 +286,7 @@ class HackiApp extends StatelessWidget { AsyncSnapshot snapshot, ) { final AdaptiveThemeMode? mode = snapshot.data; - ThemeUtil.updateStatusBarSetting( + ThemeUtils.updateStatusBarSetting( SchedulerBinding .instance.platformDispatcher.platformBrightness, mode, diff --git a/lib/repositories/hacker_news_repository.dart b/lib/repositories/hacker_news_repository.dart index 5e557e6f..742d0038 100644 --- a/lib/repositories/hacker_news_repository.dart +++ b/lib/repositories/hacker_news_repository.dart @@ -432,7 +432,7 @@ class HackerNewsRepository with Loggable { final String? text = json.text; if (text == null || text.isEmpty) return json; final String parsedText = await compute( - HtmlUtil.parseHtml, + HtmlUtils.parseHtml, text, ); json.text = parsedText; diff --git a/lib/repositories/post_repository.dart b/lib/repositories/post_repository.dart index 371bc2a9..708f82e1 100644 --- a/lib/repositories/post_repository.dart +++ b/lib/repositories/post_repository.dart @@ -59,7 +59,7 @@ class PostRepository extends PostableRepository { path: 'submitlink', ); final Map? formValues = - HtmlUtil.getHiddenFormValues(formResponse.data); + HtmlUtils.getHiddenFormValues(formResponse.data); if (formValues == null || formValues.isEmpty) { return false; @@ -103,7 +103,7 @@ class PostRepository extends PostableRepository { path: 'edit', ); final Map? formValues = - HtmlUtil.getHiddenFormValues(formResponse.data); + HtmlUtils.getHiddenFormValues(formResponse.data); if (formValues == null || formValues.isEmpty) { return false; diff --git a/lib/repositories/search_repository.dart b/lib/repositories/search_repository.dart index e3199add..add3f655 100644 --- a/lib/repositories/search_repository.dart +++ b/lib/repositories/search_repository.dart @@ -44,7 +44,7 @@ class SearchRepository { if (title.isEmpty) { final String text = hit['comment_text'] as String? ?? ''; final String parsedText = await compute( - HtmlUtil.parseHtml, + HtmlUtils.parseHtml, text, ); final int parentId = hit['parent_id'] as int? ?? 0; @@ -66,7 +66,7 @@ class SearchRepository { } else { final String text = hit['story_text'] as String? ?? ''; final String parsedText = await compute( - HtmlUtil.parseHtml, + HtmlUtils.parseHtml, text, ); final Story story = Story( diff --git a/lib/screens/home/home_screen.dart b/lib/screens/home/home_screen.dart index ba3f42f1..4006f411 100644 --- a/lib/screens/home/home_screen.dart +++ b/lib/screens/home/home_screen.dart @@ -243,7 +243,7 @@ class _HomeScreenState extends State } if (story.url.isNotEmpty && isJobWithLink) { - LinkUtil.launch( + LinkUtils.launch( story.url, context, shouldUseReader: shouldUseReader, diff --git a/lib/screens/home/widgets/pinned_stories.dart b/lib/screens/home/widgets/pinned_stories.dart index c1c6630d..1d611089 100644 --- a/lib/screens/home/widgets/pinned_stories.dart +++ b/lib/screens/home/widgets/pinned_stories.dart @@ -32,7 +32,7 @@ class PinnedStories extends StatelessWidget { children: [ CustomSlidableAction( onPressed: (_) { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); context.read().unpinStory(story); }, backgroundColor: Palette.red, diff --git a/lib/screens/item/item_screen.dart b/lib/screens/item/item_screen.dart index 53edaaf5..7a65a0f3 100644 --- a/lib/screens/item/item_screen.dart +++ b/lib/screens/item/item_screen.dart @@ -247,13 +247,13 @@ class _ItemScreenState extends State ? 'updated' : 'submitted'; final String msg = 'Comment $verb! ${Constants.happyFace}'; - HapticFeedbackUtil.success(); + HapticFeedbackUtils.success(); showSnackBar(content: msg); context.read().onReplySubmittedSuccessfully(); context.read().reset(); } else if (postState.status == Status.failure) { showErrorSnackBar(); - HapticFeedbackUtil.error(); + HapticFeedbackUtils.error(); context.read().reset(); } }, @@ -503,7 +503,7 @@ class _ItemScreenState extends State ), ), onTap: () { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); locator.get().requestReview(); context .read() @@ -518,7 +518,7 @@ class _ItemScreenState extends State Comment comment, Item rootItem, ) { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); showModalBottomSheet( context: context, builder: (BuildContext context) { diff --git a/lib/screens/item/widgets/custom_app_bar.dart b/lib/screens/item/widgets/custom_app_bar.dart index 62e372ab..a4074d59 100644 --- a/lib/screens/item/widgets/custom_app_bar.dart +++ b/lib/screens/item/widgets/custom_app_bar.dart @@ -33,7 +33,7 @@ class CustomAppBar extends AppBar { color: Theme.of(context).colorScheme.onSurface, ), onPressed: () { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); onZoomTap?.call(); }, ), diff --git a/lib/screens/item/widgets/custom_floating_action_button.dart b/lib/screens/item/widgets/custom_floating_action_button.dart index d37f3d65..6252d4c3 100644 --- a/lib/screens/item/widgets/custom_floating_action_button.dart +++ b/lib/screens/item/widgets/custom_floating_action_button.dart @@ -51,7 +51,7 @@ class FloatingSkipButtons extends StatelessWidget { /// default [FloatingActionButton] animation. heroTag: UniqueKey().hashCode, onPressed: () { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); context.read().scrollToPreviousRoot(); }, materialTapTargetSize: MaterialTapTargetSize.padded, @@ -84,7 +84,7 @@ class FloatingSkipButtons extends StatelessWidget { /// Same as above. heroTag: UniqueKey().hashCode, onPressed: () { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); context.read().scrollToNextRoot( onError: () => context.showSnackBar( content: '''No more root level comment below.''', diff --git a/lib/screens/item/widgets/fav_icon_button.dart b/lib/screens/item/widgets/fav_icon_button.dart index 1bea56ef..1896c33e 100644 --- a/lib/screens/item/widgets/fav_icon_button.dart +++ b/lib/screens/item/widgets/fav_icon_button.dart @@ -36,7 +36,7 @@ class FavIconButton extends StatelessWidget { ), ), onPressed: () { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); if (isFav) { context.read().removeFav(storyId); } else { diff --git a/lib/screens/item/widgets/in_thread_search_icon_button.dart b/lib/screens/item/widgets/in_thread_search_icon_button.dart index 249ad113..659abf67 100644 --- a/lib/screens/item/widgets/in_thread_search_icon_button.dart +++ b/lib/screens/item/widgets/in_thread_search_icon_button.dart @@ -11,7 +11,7 @@ import 'package:hacki/models/models.dart'; import 'package:hacki/screens/widgets/widgets.dart'; import 'package:hacki/styles/styles.dart'; import 'package:hacki/utils/debouncer.dart'; -import 'package:hacki/utils/haptic_feedback_util.dart'; +import 'package:hacki/utils/haptic_feedback_utils.dart'; class InThreadSearchIconButton extends StatelessWidget { const InThreadSearchIconButton({super.key}); @@ -248,7 +248,7 @@ class _InThreadSearchViewState extends State<_InThreadSearchView> { selected: false, label: 'clear', onSelected: (_) { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); if (textEditingController.text.isNotEmpty) { textEditingController.clear(); widget.commentsCubit.search( diff --git a/lib/screens/item/widgets/item_screen_background.dart b/lib/screens/item/widgets/item_screen_background.dart index b483589c..f4667ec8 100644 --- a/lib/screens/item/widgets/item_screen_background.dart +++ b/lib/screens/item/widgets/item_screen_background.dart @@ -117,7 +117,7 @@ class _ItemScreenBackgroundState extends State { width: widget.indentLineWidth, child: isEyeCandyEnabled ? AnimatedIndentLine( - color: ColorUtil.getRainbowColor( + color: ColorUtils.getRainbowColor( i, Theme.of(context).canvasColor, ).$1, @@ -127,7 +127,7 @@ class _ItemScreenBackgroundState extends State { : Container( width: widget.indentLineWidth, height: MediaQuery.of(context).size.height, - color: ColorUtil.getRainbowColor( + color: ColorUtils.getRainbowColor( i, Theme.of(context).canvasColor, ).$1.withValues( diff --git a/lib/screens/item/widgets/lazy_fetch_load_button.dart b/lib/screens/item/widgets/lazy_fetch_load_button.dart index 8b940235..002f7535 100644 --- a/lib/screens/item/widgets/lazy_fetch_load_button.dart +++ b/lib/screens/item/widgets/lazy_fetch_load_button.dart @@ -25,7 +25,7 @@ class LazyFetchLoadButton extends StatelessWidget { Expanded( child: TextButton( onPressed: () { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); context.read().loadMore(comment: comment); }, child: Row( diff --git a/lib/screens/item/widgets/link_icon_button.dart b/lib/screens/item/widgets/link_icon_button.dart index f5b229f2..e521dba3 100644 --- a/lib/screens/item/widgets/link_icon_button.dart +++ b/lib/screens/item/widgets/link_icon_button.dart @@ -29,7 +29,7 @@ class LinkIconButton extends StatelessWidget { color: Theme.of(context).colorScheme.onSurface, ), ), - onPressed: () => LinkUtil.launch( + onPressed: () => LinkUtils.launch( '${Constants.hackerNewsItemLinkPrefix}$storyId', context, shouldUseHackiForHnLink: false, diff --git a/lib/screens/item/widgets/login_dialog.dart b/lib/screens/item/widgets/login_dialog.dart index fd26dd6e..7bb6ceca 100644 --- a/lib/screens/item/widgets/login_dialog.dart +++ b/lib/screens/item/widgets/login_dialog.dart @@ -124,7 +124,7 @@ class _LoginDialogState extends State with ItemActionMixin { child: Transform.translate( offset: const Offset(0, 1), child: TapDownWrapper( - onTap: () => LinkUtil.launch( + onTap: () => LinkUtils.launch( Constants.endUserAgreementLink, context, ), diff --git a/lib/screens/item/widgets/main_view.dart b/lib/screens/item/widgets/main_view.dart index 3d08915c..90a81f30 100644 --- a/lib/screens/item/widgets/main_view.dart +++ b/lib/screens/item/widgets/main_view.dart @@ -61,7 +61,7 @@ class MainView extends StatelessWidget { displacement: 200, color: Theme.of(context).colorScheme.primaryContainer, onRefresh: () async { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); if (context.read().state.isOfflineReading == false && @@ -128,7 +128,7 @@ class MainView extends StatelessWidget { if (preferenceState.isEyeCandyEnabled) GestureDetector( onTap: () => unawaited( - HapticFeedbackUtil.loadAndPlay(), + HapticFeedbackUtils.loadAndPlay(), ), child: Center( child: Padding( @@ -258,7 +258,7 @@ class MainView extends StatelessWidget { ); } } else { - HapticFeedbackUtil.error(); + HapticFeedbackUtils.error(); context.showSnackBar( content: SnackBarMessages.notLoggedInNoVoting, action: () { @@ -276,7 +276,7 @@ class MainView extends StatelessWidget { } void onReplyTapped(BuildContext context, Comment cmt) { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); if (cmt.deleted || cmt.dead) { return; } @@ -289,7 +289,7 @@ class MainView extends StatelessWidget { } void onEditTapped(BuildContext context, Comment cmt) { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); if (cmt.deleted || cmt.dead) { return; } @@ -349,7 +349,7 @@ class _ParentItemSection extends StatelessWidget { ), CustomSlidableAction( onPressed: (_) { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); if (item.id != context.read().state.replyingTo?.id) { @@ -423,7 +423,7 @@ class _ParentItemSection extends StatelessWidget { children: [ if (item is Story) InkWell( - onTap: () => LinkUtil.launch( + onTap: () => LinkUtils.launch( item.url, context, shouldUseReader: prefState.isReaderEnabled, @@ -437,7 +437,7 @@ class _ParentItemSection extends StatelessWidget { Clipboard.setData( ClipboardData(text: item.url), ).whenComplete(() { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); if (context.mounted) { context.showSnackBar( content: 'Link copied.', diff --git a/lib/screens/item/widgets/more_popup_menu.dart b/lib/screens/item/widgets/more_popup_menu.dart index 749334c4..beeacedb 100644 --- a/lib/screens/item/widgets/more_popup_menu.dart +++ b/lib/screens/item/widgets/more_popup_menu.dart @@ -131,7 +131,7 @@ class MorePopupMenu extends StatelessWidget { ], ) : SelectableLinkify( - text: HtmlUtil.parseHtml( + text: HtmlUtils.parseHtml( state.user.about, ), style: TextStyle( @@ -144,7 +144,7 @@ class MorePopupMenu extends StatelessWidget { .primary, ), onOpen: (LinkableElement link) => - LinkUtil.launch( + LinkUtils.launch( link.url, context, ), diff --git a/lib/screens/item/widgets/pin_icon_button.dart b/lib/screens/item/widgets/pin_icon_button.dart index edb10feb..9bd069b0 100644 --- a/lib/screens/item/widgets/pin_icon_button.dart +++ b/lib/screens/item/widgets/pin_icon_button.dart @@ -41,7 +41,7 @@ class PinIconButton extends StatelessWidget { ), ), onPressed: () { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); if (pinned) { context.read().unpinStory(story); } else { diff --git a/lib/screens/item/widgets/poll_view.dart b/lib/screens/item/widgets/poll_view.dart index ba7a1701..dbf7d979 100644 --- a/lib/screens/item/widgets/poll_view.dart +++ b/lib/screens/item/widgets/poll_view.dart @@ -97,7 +97,7 @@ class _PollViewState extends State with ItemActionMixin { children: [ IconButton( onPressed: () { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); context.read().upvote(); }, icon: Icon( diff --git a/lib/screens/item/widgets/reply_box.dart b/lib/screens/item/widgets/reply_box.dart index d835e22f..5484299d 100644 --- a/lib/screens/item/widgets/reply_box.dart +++ b/lib/screens/item/widgets/reply_box.dart @@ -328,7 +328,7 @@ class _ReplyBoxState extends State with ItemActionMixin { ), ), onPressed: () { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); setState(() { expanded = false; }); @@ -350,7 +350,7 @@ class _ReplyBoxState extends State with ItemActionMixin { ), onPressed: () => Clipboard.setData( ClipboardData(text: replyingTo.text), - ).then((_) => HapticFeedbackUtil.selection()), + ).then((_) => HapticFeedbackUtils.selection()), ), IconButton( icon: Icon( diff --git a/lib/screens/item/widgets/time_machine_dialog.dart b/lib/screens/item/widgets/time_machine_dialog.dart index acff1f90..042dd3c8 100644 --- a/lib/screens/item/widgets/time_machine_dialog.dart +++ b/lib/screens/item/widgets/time_machine_dialog.dart @@ -8,7 +8,7 @@ import 'package:hacki/models/models.dart'; import 'package:hacki/screens/widgets/comment_tile.dart'; import 'package:hacki/screens/widgets/story_tile.dart'; import 'package:hacki/styles/styles.dart'; -import 'package:hacki/utils/link_util.dart'; +import 'package:hacki/utils/link_utils.dart'; import 'package:responsive_builder/responsive_builder.dart'; class TimeMachineDialog extends StatelessWidget { @@ -85,7 +85,7 @@ class TimeMachineDialog extends StatelessWidget { final String url = rootItem.url.isNotEmpty ? rootItem.url : '''${Constants.hackerNewsItemLinkPrefix}${rootItem.id}'''; - LinkUtil.launch( + LinkUtils.launch( url, context, shouldUseHackiForHnLink: false, diff --git a/lib/screens/logs/logs_screen.dart b/lib/screens/logs/logs_screen.dart index 25ae7025..4811bbc4 100644 --- a/lib/screens/logs/logs_screen.dart +++ b/lib/screens/logs/logs_screen.dart @@ -3,8 +3,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hacki/extensions/context_extension.dart'; import 'package:hacki/styles/styles.dart'; -import 'package:hacki/utils/haptic_feedback_util.dart'; -import 'package:hacki/utils/log_util.dart'; +import 'package:hacki/utils/haptic_feedback_utils.dart'; +import 'package:hacki/utils/log_utils.dart'; class LogsScreen extends StatefulWidget { const LogsScreen({super.key}); @@ -26,7 +26,7 @@ class _LogsScreenState extends State { } Future _fetchLogs() async { - final List logs = await LogUtil.exportLogsAsStrings(); + final List logs = await LogUtils.exportLogsAsStrings(); if (mounted) { setState(() { _logs = logs; @@ -59,7 +59,7 @@ class _LogsScreenState extends State { lhs + rhs, ); Clipboard.setData(ClipboardData(text: data)) - .whenComplete(HapticFeedbackUtil.selection); + .whenComplete(HapticFeedbackUtils.selection); context.showSnackBar(content: 'Logs copied.'); }, icon: Icon( diff --git a/lib/screens/profile/profile_screen.dart b/lib/screens/profile/profile_screen.dart index 5b1e432b..693baa27 100644 --- a/lib/screens/profile/profile_screen.dart +++ b/lib/screens/profile/profile_screen.dart @@ -112,7 +112,7 @@ class _ProfileScreenState extends State .where((Item e) => !e.dead && !e.deleted) .toList(), onRefresh: () { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); context.read().refresh(); }, onLoadMore: () { @@ -184,7 +184,7 @@ class _ProfileScreenState extends State context.read().loadMore(); }, onRefresh: () { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); context.read().refresh(); }, ), diff --git a/lib/screens/profile/widgets/enter_offline_mode_list_tile.dart b/lib/screens/profile/widgets/enter_offline_mode_list_tile.dart index c91c3d02..499f3379 100644 --- a/lib/screens/profile/widgets/enter_offline_mode_list_tile.dart +++ b/lib/screens/profile/widgets/enter_offline_mode_list_tile.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hacki/blocs/blocs.dart'; import 'package:hacki/extensions/extensions.dart'; -import 'package:hacki/utils/haptic_feedback_util.dart'; +import 'package:hacki/utils/haptic_feedback_utils.dart'; class EnterOfflineModeListTile extends StatelessWidget { const EnterOfflineModeListTile({super.key}); @@ -18,7 +18,7 @@ class EnterOfflineModeListTile extends StatelessWidget { activeThumbColor: Theme.of(context).colorScheme.primary, title: const Text('Offline Mode'), onChanged: (bool value) { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); context.read().add( value ? StoriesEnterOfflineMode() : StoriesExitOfflineMode(), ); diff --git a/lib/screens/profile/widgets/favorites_screen.dart b/lib/screens/profile/widgets/favorites_screen.dart index 5d9a1b2f..59ec6dda 100644 --- a/lib/screens/profile/widgets/favorites_screen.dart +++ b/lib/screens/profile/widgets/favorites_screen.dart @@ -151,7 +151,7 @@ class FavoritesScreen extends StatelessWidget { ? favState.favStories : favState.favComments, onRefresh: () { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); context.read().refresh(); }, onLoadMore: () { @@ -167,7 +167,7 @@ class FavoritesScreen extends StatelessWidget { children: [ CustomSlidableAction( onPressed: (_) { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); context.read().removeFav(item.id); }, backgroundColor: Palette.red, diff --git a/lib/screens/profile/widgets/inbox_view.dart b/lib/screens/profile/widgets/inbox_view.dart index 4c32a28a..3cf5576c 100644 --- a/lib/screens/profile/widgets/inbox_view.dart +++ b/lib/screens/profile/widgets/inbox_view.dart @@ -5,7 +5,7 @@ import 'package:hacki/cubits/notification/notification_cubit.dart'; import 'package:hacki/models/models.dart'; import 'package:hacki/screens/widgets/widgets.dart'; import 'package:hacki/styles/styles.dart'; -import 'package:hacki/utils/link_util.dart'; +import 'package:hacki/utils/link_utils.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; class InboxView extends StatelessWidget { @@ -139,7 +139,7 @@ class InboxView extends StatelessWidget { ), maxLines: 4, onOpen: (LinkableElement link) => - LinkUtil.launch(link.url, context), + LinkUtils.launch(link.url, context), ), ], ), diff --git a/lib/screens/profile/widgets/offline_list_tile.dart b/lib/screens/profile/widgets/offline_list_tile.dart index 4e6563c5..f90c4eab 100644 --- a/lib/screens/profile/widgets/offline_list_tile.dart +++ b/lib/screens/profile/widgets/offline_list_tile.dart @@ -7,7 +7,7 @@ import 'package:hacki/models/models.dart'; import 'package:hacki/screens/widgets/widgets.dart'; import 'package:hacki/services/dialog_proxy.dart'; import 'package:hacki/styles/styles.dart'; -import 'package:hacki/utils/haptic_feedback_util.dart'; +import 'package:hacki/utils/haptic_feedback_utils.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; class OfflineListTile extends StatelessWidget { @@ -113,7 +113,7 @@ class OfflineListTile extends StatelessWidget { ListTile( title: Text(count.label), onTap: () { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); context.pop(); final StoriesBloc storiesBloc = diff --git a/lib/screens/profile/widgets/settings.dart b/lib/screens/profile/widgets/settings.dart index 6854ba62..037e67df 100644 --- a/lib/screens/profile/widgets/settings.dart +++ b/lib/screens/profile/widgets/settings.dart @@ -115,7 +115,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { .toList(), onSelected: (FetchMode? fetchMode) { if (fetchMode != null) { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); context.read().update( FetchModePreference( val: fetchMode.index, @@ -149,7 +149,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { .toList(), onSelected: (CommentsOrder? order) { if (order != null) { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); context.read().update( CommentsOrderPreference( val: order.index, @@ -194,7 +194,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { .toList(), onSelected: (DateDisplayFormat? order) { if (order != null) { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); context.read().update( DateFormatPreference( val: order.index, @@ -242,7 +242,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { .toList(), onSelected: (HackerNewsDataSource? source) { if (source != null) { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); context.read().update( HackerNewsDataSourcePreference( val: source.index, @@ -280,7 +280,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { preferenceState.isPreviewImageLeftAligned, index: 0, story: Story.placeholder(), - onTap: () => LinkUtil.launch( + onTap: () => LinkUtils.launch( Constants.guidelineLink, context, ), @@ -335,7 +335,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { onSelectionChanged: preference.dependencies .satisfy(preferenceState.preferences) ? (Set val) { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); context.read().update( preference.copyWith(val: val.single), ); @@ -357,7 +357,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { onChanged: preference.dependencies .satisfy(preferenceState.preferences) ? (bool val) { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); context .read() @@ -386,7 +386,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { initialSelection: preferenceState.storyMarkingMode, onSelected: (StoryMarkingMode? storyMarkingMode) { if (storyMarkingMode != null) { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); context.read().update( StoryMarkingModePreference( val: storyMarkingMode.index, @@ -486,7 +486,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { 'Reset Feature Discovery', ), onTap: () { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); FeatureDiscovery.clearPreferences( context, DiscoverableFeature.values @@ -499,7 +499,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { 'Reset Tips', ), onTap: () { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); context.read().reset(); }, ), @@ -507,7 +507,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { const Divider(), ListTile( title: const Text('Feature Request'), - onTap: () => LinkUtil.launch( + onTap: () => LinkUtils.launch( Constants.githubLink, context, ), @@ -515,7 +515,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { ListTile( title: const Text('Rate Hacki : )'), onTap: () { - LinkUtil.launch( + LinkUtils.launch( Platform.isIOS ? Constants.appStoreLink : Constants.googlePlayLink, @@ -533,7 +533,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { final DevMode updatedDevMode = DevMode(val: !preferenceState.isDevModeEnabled); context.read().update(updatedDevMode); - HapticFeedbackUtil.heavy(); + HapticFeedbackUtils.heavy(); if (updatedDevMode.val) { showSnackBar(content: 'You are a dev now.'); } else { @@ -688,7 +688,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { final Brightness brightness = SchedulerBinding.instance.platformDispatcher.platformBrightness; - ThemeUtil.updateStatusBarSetting(brightness, val); + ThemeUtils.updateStatusBarSetting(brightness, val); } void showColorPicker() { @@ -706,7 +706,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { colors: materialColors, selectedColor: context.read().state.appColor, onMainColorChange: (ColorSwatch? color) { - ColorUtil.levelToRainbowBorderColors.clear(); + ColorUtils.levelToRainbowBorderColors.clear(); context.read().update( AppColorPreference( val: materialColors.indexOf(color ?? Palette.deepOrange), @@ -743,7 +743,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { context.read().restoreDefaultSettings(); - HapticFeedbackUtil.success(); + HapticFeedbackUtils.success(); showSnackBar(content: 'Default settings restored.'); }, child: const Text( @@ -788,7 +788,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { ..deleteCachedMetadata(); locator.get().clear(); - HapticFeedbackUtil.success(); + HapticFeedbackUtils.success(); showSnackBar(content: 'Cache cleared!'); }, child: const Text( @@ -827,7 +827,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { ), children: [ ElevatedButton( - onPressed: () => LinkUtil.launch( + onPressed: () => LinkUtils.launch( Constants.portfolioLink, context, ), @@ -844,7 +844,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { ), ), ElevatedButton( - onPressed: () => LinkUtil.launch( + onPressed: () => LinkUtils.launch( Constants.githubLink, context, ), @@ -861,7 +861,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { ), ), ElevatedButton( - onPressed: () => LinkUtil.launch( + onPressed: () => LinkUtils.launch( Platform.isIOS ? Constants.appStoreLink : Constants.googlePlayLink, @@ -880,7 +880,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { ), ), ElevatedButton( - onPressed: () => LinkUtil.launch( + onPressed: () => LinkUtils.launch( Constants.spotifyLink, context, ), @@ -897,7 +897,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { ), ), ElevatedButton( - onPressed: () => LinkUtil.launch( + onPressed: () => LinkUtils.launch( Constants.privacyPolicyLink, context, ), @@ -981,7 +981,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { final String previousLogPath = '${tempDir.path}/${Constants.previousLogFileName}'; - await LogUtil.exportLogs(); + await LogUtils.exportLogs(); final Email email = Email( body: @@ -997,7 +997,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { /// Open an issue on GitHub. Future onGithubTapped(Rect? rect) async { try { - final File originalFile = await LogUtil.exportLogs(); + final File originalFile = await LogUtils.exportLogs(); final XFile file = XFile(originalFile.path); final ShareResult result = await SharePlus.instance.share( ShareParams( @@ -1008,7 +1008,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { ); if (result.status == ShareResultStatus.success) { - LinkUtil.launchInExternalBrowser(Constants.githubIssueLink); + LinkUtils.launchInExternalBrowser(Constants.githubIssueLink); } } catch (error, stackTrace) { logError(error, stackTrace: stackTrace); @@ -1208,7 +1208,7 @@ class _SettingsState extends State with ItemActionMixin, Loggable { case ExportDestination.clipBoard: try { await Clipboard.setData(ClipboardData(text: allFavoritesStr)) - .whenComplete(HapticFeedbackUtil.selection); + .whenComplete(HapticFeedbackUtils.selection); showSnackBar( content: 'Ids of favorites have been copied to clipboard.', ); diff --git a/lib/screens/profile/widgets/tab_bar_settings.dart b/lib/screens/profile/widgets/tab_bar_settings.dart index 49899386..c34d7028 100644 --- a/lib/screens/profile/widgets/tab_bar_settings.dart +++ b/lib/screens/profile/widgets/tab_bar_settings.dart @@ -39,7 +39,7 @@ class _TabBarSettingsState extends State { scrollDirection: Axis.horizontal, physics: const NeverScrollableScrollPhysics(), onReorder: context.read().update, - onReorderStart: (_) => HapticFeedbackUtil.light(), + onReorderStart: (_) => HapticFeedbackUtils.light(), children: [ for (final StoryType tab in state.tabs) InkWell( diff --git a/lib/screens/search/widgets/number_of_comments_filter_chip.dart b/lib/screens/search/widgets/number_of_comments_filter_chip.dart index 3c45820a..0d01779d 100644 --- a/lib/screens/search/widgets/number_of_comments_filter_chip.dart +++ b/lib/screens/search/widgets/number_of_comments_filter_chip.dart @@ -5,7 +5,7 @@ import 'package:hacki/models/models.dart' show CommentsNumberFilter, NumericCondition; import 'package:hacki/screens/widgets/widgets.dart'; import 'package:hacki/styles/styles.dart'; -import 'package:hacki/utils/haptic_feedback_util.dart'; +import 'package:hacki/utils/haptic_feedback_utils.dart'; class NumberOfCommentsFilterChip extends StatelessWidget { const NumberOfCommentsFilterChip({ @@ -93,7 +93,7 @@ class _NumberOfCommentsDialogState extends State<_NumberOfCommentsDialog> { _selectedCondition, }, onSelectionChanged: (Set val) { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); setState(() { _selectedCondition = val.single; }); diff --git a/lib/screens/search/widgets/points_filter_chip.dart b/lib/screens/search/widgets/points_filter_chip.dart index 271bc74d..bea6fbca 100644 --- a/lib/screens/search/widgets/points_filter_chip.dart +++ b/lib/screens/search/widgets/points_filter_chip.dart @@ -4,7 +4,7 @@ import 'package:go_router/go_router.dart'; import 'package:hacki/models/models.dart' show NumericCondition, PointsFilter; import 'package:hacki/screens/widgets/widgets.dart'; import 'package:hacki/styles/styles.dart'; -import 'package:hacki/utils/haptic_feedback_util.dart'; +import 'package:hacki/utils/haptic_feedback_utils.dart'; class PointsFilterChip extends StatelessWidget { const PointsFilterChip({ @@ -90,7 +90,7 @@ class _PointsDialogState extends State<_PointsDialog> { _selectedCondition, }, onSelectionChanged: (Set val) { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); setState(() { _selectedCondition = val.single; }); diff --git a/lib/screens/share/share_screen.dart b/lib/screens/share/share_screen.dart index 213c4718..9f5b14d4 100644 --- a/lib/screens/share/share_screen.dart +++ b/lib/screens/share/share_screen.dart @@ -11,7 +11,7 @@ import 'package:hacki/extensions/context_extension.dart'; import 'package:hacki/models/models.dart'; import 'package:hacki/screens/widgets/widgets.dart'; import 'package:hacki/styles/styles.dart'; -import 'package:hacki/utils/haptic_feedback_util.dart'; +import 'package:hacki/utils/haptic_feedback_utils.dart'; import 'package:hacki/utils/image_saver.dart'; import 'package:path_provider/path_provider.dart'; import 'package:screenshot/screenshot.dart'; @@ -274,7 +274,7 @@ class _ShareScreenState extends State { ); if (mounted) { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); if (result) { context.showSnackBar(content: 'Image saved.'); } else { diff --git a/lib/screens/submit/submit_screen.dart b/lib/screens/submit/submit_screen.dart index 01ae1fa2..54a3fe71 100644 --- a/lib/screens/submit/submit_screen.dart +++ b/lib/screens/submit/submit_screen.dart @@ -37,7 +37,7 @@ class _SubmitScreenState extends State with ItemActionMixin { listener: (BuildContext context, SubmitState state) { if (state.status == Status.success) { context.pop(); - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); showSnackBar( content: 'Post submitted.', ); diff --git a/lib/screens/widgets/comment_tile.dart b/lib/screens/widgets/comment_tile.dart index fccd7d54..a04827eb 100644 --- a/lib/screens/widgets/comment_tile.dart +++ b/lib/screens/widgets/comment_tile.dart @@ -78,7 +78,7 @@ class CommentTile extends StatelessWidget { BlocklistState blocklistState, ) { final (Color, Color) slidableColors = level > 0 - ? ColorUtil.getRainbowColor( + ? ColorUtils.getRainbowColor( level, Theme.of(context).canvasColor, ) @@ -201,7 +201,7 @@ class CommentTile extends StatelessWidget { splashFactory: NoSplash.splashFactory, onTap: () { if (isCollapsable) { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); _collapse(context); } else { onTap?.call(); @@ -394,7 +394,7 @@ class CommentTile extends StatelessWidget { MediaQuery.of(context).textScaler, onTap: () { if (isCollapsable) { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); _onTextTapped(context); } else { onTap?.call(); diff --git a/lib/screens/widgets/custom_described_feature_overlay.dart b/lib/screens/widgets/custom_described_feature_overlay.dart index 41ef6f88..16c29bac 100644 --- a/lib/screens/widgets/custom_described_feature_overlay.dart +++ b/lib/screens/widgets/custom_described_feature_overlay.dart @@ -46,13 +46,13 @@ class CustomDescribedFeatureOverlay extends StatelessWidget { barrierDismissible: false, contentLocation: contentLocation, onBackgroundTap: () { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); FeatureDiscovery.completeCurrentStep(context); onComplete?.call(); return Future.value(true); }, onComplete: () async { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); onComplete?.call(); return true; }, diff --git a/lib/screens/widgets/custom_linkify/custom_linkify.dart b/lib/screens/widgets/custom_linkify/custom_linkify.dart index 89244311..b92d5dec 100644 --- a/lib/screens/widgets/custom_linkify/custom_linkify.dart +++ b/lib/screens/widgets/custom_linkify/custom_linkify.dart @@ -32,7 +32,7 @@ class Linkify extends StatelessWidget { super.key, this.linkifiers = defaultLinkifiers, this.onOpen, - this.options = LinkifierUtil.linkifyOptions, + this.options = LinkifierUtils.linkifyOptions, // TextSpan this.style, this.linkStyle, @@ -163,7 +163,7 @@ class SelectableLinkify extends StatelessWidget { this.semanticsLabel, this.linkifiers = defaultLinkifiers, this.onOpen, - this.options = LinkifierUtil.linkifyOptions, + this.options = LinkifierUtils.linkifyOptions, // TextSpan this.style, this.linkStyle, @@ -289,7 +289,7 @@ class SelectableLinkify extends StatelessWidget { @override Widget build(BuildContext context) { - final List elements = LinkifierUtil.linkify(text); + final List elements = LinkifierUtils.linkify(text); return SelectableText.rich( buildTextSpan( elements, @@ -389,7 +389,7 @@ TextSpan buildTextSpan( Clipboard.setData( ClipboardData(text: url), ).whenComplete(() { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); navigatorKey.currentContext?.showSnackBar( content: 'Link copied.', ); diff --git a/lib/screens/widgets/custom_tab_bar.dart b/lib/screens/widgets/custom_tab_bar.dart index 9a346cc0..082314dc 100644 --- a/lib/screens/widgets/custom_tab_bar.dart +++ b/lib/screens/widgets/custom_tab_bar.dart @@ -6,7 +6,7 @@ import 'package:hacki/cubits/cubits.dart'; import 'package:hacki/models/models.dart'; import 'package:hacki/screens/widgets/widgets.dart'; import 'package:hacki/styles/styles.dart'; -import 'package:hacki/utils/haptic_feedback_util.dart'; +import 'package:hacki/utils/haptic_feedback_utils.dart'; class CustomTabBar extends StatefulWidget { const CustomTabBar({ @@ -62,7 +62,7 @@ class _CustomTabBarState extends State { bottom: Dimens.pt8, ), onTap: (_) { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); }, tabAlignment: TabAlignment.center, tabs: [ diff --git a/lib/screens/widgets/download_progress_reminder.dart b/lib/screens/widgets/download_progress_reminder.dart index 9fea8751..658541c1 100644 --- a/lib/screens/widgets/download_progress_reminder.dart +++ b/lib/screens/widgets/download_progress_reminder.dart @@ -5,7 +5,7 @@ import 'package:hacki/config/constants.dart'; import 'package:hacki/extensions/extensions.dart'; import 'package:hacki/services/dialog_proxy.dart'; import 'package:hacki/styles/styles.dart'; -import 'package:hacki/utils/haptic_feedback_util.dart'; +import 'package:hacki/utils/haptic_feedback_utils.dart'; class DownloadProgressReminder extends StatefulWidget { const DownloadProgressReminder({this.isDockedAtBottom = false, super.key}); @@ -80,7 +80,7 @@ class _DownloadProgressReminderState extends State visible: isVisible, child: GestureDetector( onTap: () { - HapticFeedbackUtil.selection(); + HapticFeedbackUtils.selection(); DialogProxy.showAbortDownloadDialog(context); }, child: Stack( diff --git a/lib/screens/widgets/item_text.dart b/lib/screens/widgets/item_text.dart index 1398081a..8c0ca36d 100644 --- a/lib/screens/widgets/item_text.dart +++ b/lib/screens/widgets/item_text.dart @@ -53,7 +53,7 @@ class ItemText extends StatelessWidget { primaryColor: Theme.of(context).colorScheme.primaryContainer, style: style, linkStyle: linkStyle, - onOpen: (LinkableElement link) => LinkUtil.launch( + onOpen: (LinkableElement link) => LinkUtils.launch( link.url, context, ), @@ -84,7 +84,7 @@ class ItemText extends StatelessWidget { primaryColor: Theme.of(context).colorScheme.primaryContainer, style: style, linkStyle: linkStyle, - onOpen: (LinkableElement link) => LinkUtil.launch( + onOpen: (LinkableElement link) => LinkUtils.launch( link.url, context, ), @@ -100,7 +100,7 @@ class ItemText extends StatelessWidget { textScaler: textScaler, style: style, linkStyle: linkStyle, - onOpen: (LinkableElement link) => LinkUtil.launch( + onOpen: (LinkableElement link) => LinkUtils.launch( link.url, context, ), @@ -122,7 +122,7 @@ class ItemText extends StatelessWidget { textScaler: textScaler, style: style, linkStyle: linkStyle, - onOpen: (LinkableElement link) => LinkUtil.launch( + onOpen: (LinkableElement link) => LinkUtils.launch( link.url, context, ), diff --git a/lib/screens/widgets/items_list_view.dart b/lib/screens/widgets/items_list_view.dart index 959b0b2b..38cb7a8f 100644 --- a/lib/screens/widgets/items_list_view.dart +++ b/lib/screens/widgets/items_list_view.dart @@ -157,7 +157,7 @@ class ItemsListView extends StatelessWidget { linkStyle: TextStyle( color: Theme.of(context).colorScheme.primary, ), - onOpen: (LinkableElement link) => LinkUtil.launch( + onOpen: (LinkableElement link) => LinkUtils.launch( link.url, context, ), @@ -253,7 +253,7 @@ class ItemsListView extends StatelessWidget { linkStyle: TextStyle( color: Theme.of(context).colorScheme.primary, ), - onOpen: (LinkableElement link) => LinkUtil.launch( + onOpen: (LinkableElement link) => LinkUtils.launch( link.url, context, ), diff --git a/lib/screens/widgets/link_preview/image_wrapped_text.dart b/lib/screens/widgets/link_preview/image_wrapped_text.dart index 5996936f..a10f944f 100644 --- a/lib/screens/widgets/link_preview/image_wrapped_text.dart +++ b/lib/screens/widgets/link_preview/image_wrapped_text.dart @@ -6,7 +6,7 @@ import 'package:hacki/blocs/stories/stories_bloc.dart'; import 'package:hacki/cubits/cubits.dart'; import 'package:hacki/screens/widgets/tap_down_wrapper.dart'; import 'package:hacki/styles/styles.dart'; -import 'package:hacki/utils/link_util.dart'; +import 'package:hacki/utils/link_utils.dart'; class ImageWrapText extends StatelessWidget { const ImageWrapText({ @@ -70,7 +70,7 @@ class ImageWrapText extends StatelessWidget { child: TapDownWrapper( onTap: () { if (url.isNotEmpty) { - LinkUtil.launch( + LinkUtils.launch( url, context, shouldUseHackiForHnLink: false, diff --git a/lib/screens/widgets/link_preview/link_view.dart b/lib/screens/widgets/link_preview/link_view.dart index 2fed3ab4..bc8e438c 100644 --- a/lib/screens/widgets/link_preview/link_view.dart +++ b/lib/screens/widgets/link_preview/link_view.dart @@ -9,7 +9,7 @@ import 'package:hacki/config/constants.dart'; import 'package:hacki/cubits/cubits.dart'; import 'package:hacki/screens/widgets/tap_down_wrapper.dart'; import 'package:hacki/styles/styles.dart'; -import 'package:hacki/utils/link_util.dart'; +import 'package:hacki/utils/link_utils.dart'; import 'package:memoize/function_defs.dart'; import 'package:memoize/memoize.dart'; @@ -107,7 +107,7 @@ class LinkView extends StatelessWidget { child: TapDownWrapper( onTap: () { if (url.isNotEmpty) { - LinkUtil.launch( + LinkUtils.launch( url, context, shouldUseHackiForHnLink: false, diff --git a/lib/screens/widgets/onboarding_view.dart b/lib/screens/widgets/onboarding_view.dart index dff47354..a4351a19 100644 --- a/lib/screens/widgets/onboarding_view.dart +++ b/lib/screens/widgets/onboarding_view.dart @@ -71,7 +71,7 @@ class _OnboardingViewState extends State { right: Dimens.zero, child: ElevatedButton( onPressed: () { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); if (pageController.page! >= 2) { context.pop(); } else { diff --git a/lib/screens/widgets/stories_list_view.dart b/lib/screens/widgets/stories_list_view.dart index 68948c42..77dbbc90 100644 --- a/lib/screens/widgets/stories_list_view.dart +++ b/lib/screens/widgets/stories_list_view.dart @@ -103,7 +103,7 @@ class _StoriesListViewState extends State refreshController: refreshController, items: state.storiesByType[storyType]!, onRefresh: () { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); if (state.statusByType[storyType] != Status.inProgress) { context @@ -176,7 +176,7 @@ class _StoriesListViewState extends State children: [ CustomSlidableAction( onPressed: (_) { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); context.read().pinStory(story); }, backgroundColor: @@ -288,7 +288,7 @@ class _StoriesListViewState extends State } void mark(Story story) { - HapticFeedbackUtil.light(); + HapticFeedbackUtils.light(); final StoriesBloc storiesBloc = context.read(); final HideCubit hideCubit = context.read(); diff --git a/lib/screens/widgets/story_tile.dart b/lib/screens/widgets/story_tile.dart index 94d0bfba..3ef8ddd2 100644 --- a/lib/screens/widgets/story_tile.dart +++ b/lib/screens/widgets/story_tile.dart @@ -161,7 +161,7 @@ class StoryTile extends StatelessWidget { onTap: onTap, onLongPress: () { if (story.url.isNotEmpty) { - LinkUtil.launch( + LinkUtils.launch( story.url, context, shouldUseReader: diff --git a/lib/services/dialog_proxy.dart b/lib/services/dialog_proxy.dart index 52e77c88..fdc58a22 100644 --- a/lib/services/dialog_proxy.dart +++ b/lib/services/dialog_proxy.dart @@ -8,7 +8,7 @@ import 'package:hacki/models/item/item.dart'; import 'package:hacki/screens/item/widgets/time_machine_dialog.dart'; import 'package:hacki/services/services.dart'; import 'package:hacki/styles/styles.dart'; -import 'package:hacki/utils/haptic_feedback_util.dart'; +import 'package:hacki/utils/haptic_feedback_utils.dart'; import 'package:responsive_builder/responsive_builder.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; @@ -78,7 +78,7 @@ abstract final class DialogProxy { } static void showDownloadCompletedDialog([BuildContext? context]) { - HapticFeedbackUtil.success(); + HapticFeedbackUtils.success(); context ??= navigatorKey.currentContext; if (context == null) return; showDialog( diff --git a/lib/services/fetcher.dart b/lib/services/fetcher.dart index 96949dc2..d054782c 100644 --- a/lib/services/fetcher.dart +++ b/lib/services/fetcher.dart @@ -109,7 +109,7 @@ abstract class Fetcher { if (newReply != null) { final Story? story = await hackerNewsRepository.fetchRawParentStory(id: newReply!.id); - final String text = HtmlUtil.parseHtml(newReply!.text); + final String text = HtmlUtils.parseHtml(newReply!.text); if (story != null) { final Map payloadJson = { diff --git a/lib/utils/color_util.dart b/lib/utils/color_utils.dart similarity index 97% rename from lib/utils/color_util.dart rename to lib/utils/color_utils.dart index 0c526576..5a5d1cc3 100644 --- a/lib/utils/color_util.dart +++ b/lib/utils/color_utils.dart @@ -1,7 +1,7 @@ import 'package:flutter/painting.dart'; import 'package:hacki/styles/styles.dart'; -abstract final class ColorUtil { +abstract final class ColorUtils { static final Map levelToRainbowBorderColors = {}; diff --git a/lib/utils/date_utils.dart b/lib/utils/date_utils.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/utils/haptic_feedback_util.dart b/lib/utils/haptic_feedback_utils.dart similarity index 96% rename from lib/utils/haptic_feedback_util.dart rename to lib/utils/haptic_feedback_utils.dart index ed77f254..881b76d1 100644 --- a/lib/utils/haptic_feedback_util.dart +++ b/lib/utils/haptic_feedback_utils.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:advanced_haptics/advanced_haptics.dart'; import 'package:flutter/services.dart'; -abstract class HapticFeedbackUtil { +abstract final class HapticFeedbackUtils { static bool enabled = true; static bool? hasCustomHapticsSupport; diff --git a/lib/utils/html_util.dart b/lib/utils/html_utils.dart similarity index 98% rename from lib/utils/html_util.dart rename to lib/utils/html_utils.dart index f7b9bfd4..657a902b 100644 --- a/lib/utils/html_util.dart +++ b/lib/utils/html_utils.dart @@ -2,7 +2,7 @@ import 'package:html/dom.dart' as dom; import 'package:html/parser.dart' as parser; import 'package:html_unescape/html_unescape.dart'; -abstract class HtmlUtil { +abstract final class HtmlUtils { static String? getTitle(dynamic input) => parser.parse(input).head?.querySelector('title')?.text; diff --git a/lib/utils/link_util.dart b/lib/utils/link_utils.dart similarity index 98% rename from lib/utils/link_util.dart rename to lib/utils/link_utils.dart index 7ef5a808..bee17976 100644 --- a/lib/utils/link_util.dart +++ b/lib/utils/link_utils.dart @@ -13,7 +13,7 @@ import 'package:hacki/repositories/repositories.dart'; import 'package:hacki/screens/screens.dart' show ItemScreenArgs; import 'package:url_launcher/url_launcher.dart'; -abstract class LinkUtil { +abstract final class LinkUtils { static final ChromeSafariBrowser _browser = ChromeSafariBrowser(); static void launchInExternalBrowser( diff --git a/lib/utils/linkifier_util.dart b/lib/utils/linkifier_utils.dart similarity index 95% rename from lib/utils/linkifier_util.dart rename to lib/utils/linkifier_utils.dart index 7c546738..fc008b07 100644 --- a/lib/utils/linkifier_util.dart +++ b/lib/utils/linkifier_utils.dart @@ -1,6 +1,6 @@ import 'package:hacki/screens/widgets/custom_linkify/custom_linkify.dart'; -abstract class LinkifierUtil { +abstract final class LinkifierUtils { static const LinkifyOptions linkifyOptions = LinkifyOptions(humanize: false); static List linkify( diff --git a/lib/utils/log_util.dart b/lib/utils/log_utils.dart similarity index 98% rename from lib/utils/log_util.dart rename to lib/utils/log_utils.dart index 7cf323ad..ae79e4c3 100644 --- a/lib/utils/log_util.dart +++ b/lib/utils/log_utils.dart @@ -7,7 +7,7 @@ import 'package:hacki/utils/logger/simple_log_printer.dart'; import 'package:logger/logger.dart'; import 'package:path_provider/path_provider.dart'; -abstract class LogUtil { +abstract final class LogUtils { static LogPrinter get logPrinter => kDebugMode ? prettyPrinter : SimpleLogPrinter(); diff --git a/lib/utils/theme_util.dart b/lib/utils/theme_utils.dart similarity index 98% rename from lib/utils/theme_util.dart rename to lib/utils/theme_utils.dart index 80dcae99..97a0a82c 100644 --- a/lib/utils/theme_util.dart +++ b/lib/utils/theme_utils.dart @@ -4,7 +4,7 @@ import 'package:adaptive_theme/adaptive_theme.dart'; import 'package:flutter/services.dart'; import 'package:hacki/styles/styles.dart'; -abstract class ThemeUtil { +abstract final class ThemeUtils { static Future updateStatusBarSetting( Brightness brightness, AdaptiveThemeMode? mode, diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index f609302c..9f20117d 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -1,12 +1,12 @@ -export 'color_util.dart'; +export 'color_utils.dart'; export 'debouncer.dart'; export 'debug_http_overrides.dart'; export 'dio_interceptors/interceptors.dart'; -export 'haptic_feedback_util.dart'; -export 'html_util.dart'; +export 'haptic_feedback_utils.dart'; +export 'html_utils.dart'; export 'image_saver.dart'; -export 'link_util.dart'; -export 'linkifier_util.dart'; -export 'log_util.dart'; -export 'theme_util.dart'; +export 'link_utils.dart'; +export 'linkifier_utils.dart'; +export 'log_utils.dart'; +export 'theme_utils.dart'; export 'throttle.dart'; From e8e617a1afa5b3b896a4ffa2af764eadb045aeb2 Mon Sep 17 00:00:00 2001 From: livinglist Date: Wed, 1 Apr 2026 00:27:20 -0700 Subject: [PATCH 6/7] update --- lib/config/constants.dart | 1 - lib/screens/item/widgets/main_view.dart | 25 ++++++++++++++++++++++-- lib/screens/widgets/items_list_view.dart | 3 ++- lib/utils/date_utils.dart | 22 +++++++++++++++++++++ lib/utils/utils.dart | 1 + 5 files changed, 48 insertions(+), 4 deletions(-) diff --git a/lib/config/constants.dart b/lib/config/constants.dart index 641a9684..57baa42c 100644 --- a/lib/config/constants.dart +++ b/lib/config/constants.dart @@ -62,7 +62,6 @@ abstract class Constants { 'Download stories in settings to read stories offline', 'Open any Hacker News links in Hacki via the system share menu', '''Swipe right on a comment and tap the dots icon to search for the poster within the thread or across HN''', - if(DateTime.now()) ].randomlyPicked!; static final String happyFace = [ diff --git a/lib/screens/item/widgets/main_view.dart b/lib/screens/item/widgets/main_view.dart index 90a81f30..467c0d5e 100644 --- a/lib/screens/item/widgets/main_view.dart +++ b/lib/screens/item/widgets/main_view.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' hide DateUtils; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_fadein/flutter_fadein.dart'; @@ -120,7 +120,8 @@ class MainView extends StatelessWidget { height: MediaQuery.of(context).size.height - MediaQuery.of(context).padding.top, padding: const EdgeInsets.symmetric( - horizontal: Dimens.pt48), + horizontal: Dimens.pt48, + ), child: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -143,6 +144,26 @@ class MainView extends StatelessWidget { ), ), ) + else if (DateUtils.isMidnight) + Text( + 'Time for bed', + style: TextStyle( + color: Theme.of(context).hintColor, + fontSize: TextDimens.pt10, + ), + textScaler: TextScaler.noScaling, + textAlign: TextAlign.center, + ) + else if (DateUtils.isTodayAnniversary) + Text( + '''Hacki turns ${DateUtils.yearsSinceFirstCommit} today!''', + style: TextStyle( + color: Theme.of(context).hintColor, + fontSize: TextDimens.pt10, + ), + textScaler: TextScaler.noScaling, + textAlign: TextAlign.center, + ) else Text( Constants.happyFace, diff --git a/lib/screens/widgets/items_list_view.dart b/lib/screens/widgets/items_list_view.dart index 38cb7a8f..91abc300 100644 --- a/lib/screens/widgets/items_list_view.dart +++ b/lib/screens/widgets/items_list_view.dart @@ -253,7 +253,8 @@ class ItemsListView extends StatelessWidget { linkStyle: TextStyle( color: Theme.of(context).colorScheme.primary, ), - onOpen: (LinkableElement link) => LinkUtils.launch( + onOpen: (LinkableElement link) => + LinkUtils.launch( link.url, context, ), diff --git a/lib/utils/date_utils.dart b/lib/utils/date_utils.dart index e69de29b..3282f47b 100644 --- a/lib/utils/date_utils.dart +++ b/lib/utils/date_utils.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart' as date_utils; + +abstract final class DateUtils { + static final DateTime _hackiAnniversary = + DateTime(2021, DateTime.december, 24); + + static final int yearsSinceFirstCommit = () { + final DateTime now = DateTime.now(); + final int year = now.year; + return year - _hackiAnniversary.year; + }(); + + static final bool isTodayAnniversary = () { + return date_utils.DateUtils.isSameDay(DateTime.now(), _hackiAnniversary); + }(); + + static final bool isMidnight = () { + final DateTime now = DateTime.now(); + final int hour = now.hour; + return hour >= 0 && hour < 4; + }(); +} diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 9f20117d..4618f58b 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -1,4 +1,5 @@ export 'color_utils.dart'; +export 'date_utils.dart'; export 'debouncer.dart'; export 'debug_http_overrides.dart'; export 'dio_interceptors/interceptors.dart'; From 8084286c293b5bac66be300e3294429bc82dd786 Mon Sep 17 00:00:00 2001 From: livinglist Date: Wed, 1 Apr 2026 00:28:39 -0700 Subject: [PATCH 7/7] update --- lib/config/constants.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/config/constants.dart b/lib/config/constants.dart index 57baa42c..95e18843 100644 --- a/lib/config/constants.dart +++ b/lib/config/constants.dart @@ -1,6 +1,8 @@ import 'package:hacki/extensions/extensions.dart'; abstract class Constants { + static const String hackiFirstCommitLink = + 'https://github.com/Livinglist/Hacki/tree/fe3e343ed9a9ebb4f608384e0aa64d0f1b19685d'; static const String endUserAgreementLink = 'https://github.com/Livinglist/Hacki/blob/master/assets/eula.md'; static const String privacyPolicyLink =