diff --git a/lib/main.dart b/lib/main.dart index 985e12b80..9c8886fe4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,6 +8,7 @@ import 'package:ente_auth/ente_theme_data.dart'; import 'package:ente_auth/services/authenticator_service.dart'; import 'package:ente_auth/services/billing_service.dart'; import 'package:ente_auth/services/notification_service.dart'; +import 'package:ente_auth/services/preference_service.dart'; import 'package:ente_auth/services/update_service.dart'; import 'package:ente_auth/services/user_remote_flag_service.dart'; import 'package:ente_auth/services/user_service.dart'; @@ -60,6 +61,7 @@ Future _runWithLogs(Function() function, {String prefix = ""}) async { Future _init(bool bool, {String via}) async { InAppPurchaseConnection.enablePendingPurchases(); CryptoUtil.init(); + await PreferenceService.instance.init(); await CodeStore.instance.init(); await Configuration.instance.init(); await Network.instance.init(); diff --git a/lib/services/preference_service.dart b/lib/services/preference_service.dart new file mode 100644 index 000000000..06ab10309 --- /dev/null +++ b/lib/services/preference_service.dart @@ -0,0 +1,27 @@ +import 'package:shared_preferences/shared_preferences.dart'; + +class PreferenceService { + PreferenceService._privateConstructor(); + static final PreferenceService instance = + PreferenceService._privateConstructor(); + + late final SharedPreferences _prefs; + + static const kHasShownCoachMarkKey = "has_shown_coach_mark"; + + Future init() async { + _prefs = await SharedPreferences.getInstance(); + } + + bool hasShownCoachMark() { + if (_prefs.containsKey(kHasShownCoachMarkKey)) { + return _prefs.getBool(kHasShownCoachMarkKey)!; + } else { + return false; + } + } + + Future setHasShownCoachMark(bool value) { + return _prefs.setBool(kHasShownCoachMarkKey, value); + } +} diff --git a/lib/ui/home_page.dart b/lib/ui/home_page.dart index ae0752f83..89437fa04 100644 --- a/lib/ui/home_page.dart +++ b/lib/ui/home_page.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:io'; +import 'dart:ui'; import 'package:ente_auth/core/event_bus.dart'; import 'package:ente_auth/ente_theme_data.dart'; @@ -9,6 +10,7 @@ import 'package:ente_auth/events/codes_updated_event.dart'; import "package:ente_auth/l10n/l10n.dart"; import 'package:ente_auth/models/code.dart'; import 'package:ente_auth/onboarding/view/setup_enter_secret_key_page.dart'; +import 'package:ente_auth/services/preference_service.dart'; import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/store/code_store.dart'; import 'package:ente_auth/ui/code_widget.dart'; @@ -121,7 +123,11 @@ class _HomePageState extends State { appBar: AppBar( title: const Text('ente Authenticator'), ), - floatingActionButton: !_hasLoaded || _codes.isEmpty ? null : _getFab(), + floatingActionButton: !_hasLoaded || + _codes.isEmpty || + !PreferenceService.instance.hasShownCoachMark() + ? null + : _getFab(), ), ); } @@ -131,12 +137,22 @@ class _HomePageState extends State { if (_codes.isEmpty) { return _getEmptyState(); } else { - return ListView.builder( + final list = ListView.builder( itemBuilder: ((context, index) { return CodeWidget(_codes[index]); }), itemCount: _codes.length, ); + if (!PreferenceService.instance.hasShownCoachMark()) { + return Stack( + children: [ + list, + _getCoachMarkWidget(), + ], + ); + } else { + return list; + } } } else { return const EnteLoadingWidget(); @@ -222,6 +238,64 @@ class _HomePageState extends State { ), ); } + + Widget _getCoachMarkWidget() { + return GestureDetector( + onTap: () async { + await PreferenceService.instance.setHasShownCoachMark(true); + setState(() {}); + }, + child: Row( + children: [ + Expanded( + child: Container( + width: double.infinity, + color: Theme.of(context).colorScheme.background.withOpacity(0.1), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 8, sigmaY: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.swipe_left, + size: 42, + ), + const SizedBox( + height: 24, + ), + Text( + "Swipe left to edit or remove codes", + style: Theme.of(context).textTheme.headline6, + ), + const SizedBox( + height: 36, + ), + SizedBox( + width: 160, + child: OutlinedButton( + onPressed: () async { + await PreferenceService.instance + .setHasShownCoachMark(true); + setState(() {}); + }, + child: const Text("OK"), + ), + ) + ], + ), + ], + ), + ), + ), + ), + ], + ), + ); + } } class SpeedDialLabelWidget extends StatelessWidget {