diff --git a/lib/config/constants.dart b/lib/config/constants.dart index e7594c70..054fac99 100644 --- a/lib/config/constants.dart +++ b/lib/config/constants.dart @@ -27,6 +27,8 @@ abstract class Constants { static const String hackerNewsItemLinkPrefix = 'https://news.ycombinator.com/item?id='; static const String supportEmail = 'georgefung98@gmail.com'; + static const String updatifyProjectId = + '6bd53821-c2c7-426e-befa-a8d6a6707d1d'; static const String _imagePath = 'assets/images'; static const String _videoPath = 'assets/videos'; diff --git a/lib/config/updatify.dart b/lib/config/updatify.dart new file mode 100644 index 00000000..5d75243e --- /dev/null +++ b/lib/config/updatify.dart @@ -0,0 +1,46 @@ +import 'dart:async'; + +import 'package:hacki/config/constants.dart'; +import 'package:hacki/styles/styles.dart'; +import 'package:material_ui/material_ui.dart'; +import 'package:updatify_flutter/updatify_flutter.dart'; + +// Just a styles config to match overall app UI +UpdatifyPostDecoration updatifyPostDecoration(BuildContext context) { + final ThemeData theme = Theme.of(context); + final ColorScheme colors = theme.colorScheme; + + return UpdatifyPostDecoration( + titleStyle: theme.textTheme.titleLarge, + headerStyle: theme.textTheme.bodySmall?.copyWith( + color: colors.onSurfaceVariant, + ), + bodyStyle: theme.textTheme.bodyMedium, + codeStyle: const TextStyle(fontFamily: 'UbuntuMono'), + chipColors: { + for (final PostType type in PostType.values) type: colors.primary, + }, + blockquoteDecoration: BoxDecoration( + border: Border( + left: BorderSide(color: colors.primary, width: Dimens.pt3), + ), + ), + horizontalRuleColor: theme.dividerColor, + ); +} + +void showWhatsNewIfNeeded(BuildContext context) { + final ThemeData theme = Theme.of(context); + + unawaited( + maybeShowUpdatifyPopup( + context, + projectId: Constants.updatifyProjectId, + popupType: UpdatifyPopupType.bottomSheet, + titleStyle: theme.textTheme.titleMedium, + backgroundColor: theme.canvasColor, + closeButtonColor: theme.colorScheme.onSurface, + itemDecoration: updatifyPostDecoration(context), + ), + ); +} diff --git a/lib/screens/home/home_screen.dart b/lib/screens/home/home_screen.dart index 90caf319..f7db3663 100644 --- a/lib/screens/home/home_screen.dart +++ b/lib/screens/home/home_screen.dart @@ -9,6 +9,7 @@ import 'package:hacki/blocs/blocs.dart'; import 'package:hacki/config/constants.dart'; import 'package:hacki/config/locator.dart'; import 'package:hacki/config/paths.dart'; +import 'package:hacki/config/updatify.dart'; import 'package:hacki/config/router.dart'; import 'package:hacki/cubits/cubits.dart'; import 'package:hacki/extensions/extensions.dart'; @@ -83,6 +84,7 @@ class _HomeScreenState extends State SchedulerBinding.instance ..addPostFrameCallback((_) => unawaited(applyIosLaunchDeepLink())) ..addPostFrameCallback((_) => showFeatureDiscoveryDialog()) + ..addPostFrameCallback((_) => showWhatsNewIfNeeded(context)) ..addPostFrameCallback((_) { final ModalRoute? route = ModalRoute.of(context); diff --git a/lib/screens/settings/settings_screen.dart b/lib/screens/settings/settings_screen.dart index 01b8ec69..48f8162b 100644 --- a/lib/screens/settings/settings_screen.dart +++ b/lib/screens/settings/settings_screen.dart @@ -92,6 +92,7 @@ class _SettingsViewState extends State } }, ), + const WhatsNewListTile(), const EnterOfflineModeListTile(), const OfflineListTile(), const SizedBox(height: Dimens.pt8), diff --git a/lib/screens/settings/widgets/whats_new_list_tile.dart b/lib/screens/settings/widgets/whats_new_list_tile.dart new file mode 100644 index 00000000..ec1d901b --- /dev/null +++ b/lib/screens/settings/widgets/whats_new_list_tile.dart @@ -0,0 +1,47 @@ +import 'package:hacki/config/constants.dart'; +import 'package:hacki/config/updatify.dart'; +import 'package:hacki/styles/styles.dart'; +import 'package:hacki/utils/haptic_feedback_utils.dart'; +import 'package:material_ui/material_ui.dart'; +import 'package:updatify_flutter/updatify_flutter.dart'; + +class WhatsNewListTile extends StatelessWidget { + const WhatsNewListTile({super.key}); + + static const Duration _checkInterval = Duration(hours: 1); + + @override + Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + + return UpdatifyTrigger( + projectId: Constants.updatifyProjectId, + popupType: UpdatifyPopupType.bottomSheet, + checkInterval: _checkInterval, + titleStyle: theme.textTheme.titleMedium, + backgroundColor: theme.canvasColor, + closeButtonColor: theme.colorScheme.onSurface, + itemDecoration: updatifyPostDecoration(context), + builder: + ( + BuildContext context, + bool hasNewUpdates, + VoidCallback openUpdates, + ) => ListTile( + title: const Text("What's New"), + subtitle: const Text('Latest changes in Hacki.'), + trailing: hasNewUpdates + ? Icon( + Icons.circle, + size: Dimens.pt10, + color: theme.colorScheme.primary, + ) + : null, + onTap: () { + HapticFeedbackUtils.light(); + openUpdates(); + }, + ), + ); + } +} diff --git a/lib/screens/settings/widgets/widgets.dart b/lib/screens/settings/widgets/widgets.dart index 5e354b2f..971530b6 100644 --- a/lib/screens/settings/widgets/widgets.dart +++ b/lib/screens/settings/widgets/widgets.dart @@ -2,3 +2,4 @@ export 'enter_offline_mode_list_tile.dart'; export 'offline_list_tile.dart'; export 'tab_bar_settings.dart'; export 'text_scale_factor_settings.dart'; +export 'whats_new_list_tile.dart'; diff --git a/pubspec.lock b/pubspec.lock index 2e52858f..be20abc5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -941,6 +941,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + markdown: + dependency: transitive + description: + name: markdown + sha256: ee85086ad7698b42522c6ad42fe195f1b9898e4d974a1af4576c1a3a176cada9 + url: "https://pub.dev" + source: hosted + version: "7.3.1" + markdown_rich_text: + dependency: transitive + description: + name: markdown_rich_text + sha256: "1af7e880480af8f039efa87bda07051c4b1c79287abf64efdb392bb5c0ea78d7" + url: "https://pub.dev" + source: hosted + version: "0.1.4" matcher: dependency: transitive description: @@ -1586,6 +1602,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + updatify_flutter: + dependency: "direct main" + description: + name: updatify_flutter + sha256: c5bab258881c9c029c5b4331273becae18694d3c690c0c563dd7262b0bea1ffb + url: "https://pub.dev" + source: hosted + version: "1.6.0" url_launcher: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index cdc64318..58738ec0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -86,6 +86,7 @@ dependencies: synced_shared_preferences: path: components/synced_shared_preferences universal_platform: ^1.1.0 + updatify_flutter: ^1.6.0 url_launcher: ^6.3.2 video_player: ^2.14.0 visibility_detector: ^0.4.0+2