diff --git a/lib/locale/l10n/app_en.arb b/lib/locale/l10n/app_en.arb index 1a662ed..b5329f0 100644 --- a/lib/locale/l10n/app_en.arb +++ b/lib/locale/l10n/app_en.arb @@ -2,6 +2,7 @@ "appTitleQuestion": "Quid faciam hodie?", "generalError": "There was an error", + "generalCancelButtonLabel": "Cancel", "welcomeScreenDescription": "Find out what you did all the days and unlock moments you completely forgot!", "welcomeScreenSubtitle": "What did I do today?", @@ -68,5 +69,17 @@ "emptyScreenTitle": "Houston, we have a problem", "emptyScreenSubtitle": "The user hasn't created any memories yet!", "emptyScreenDescription": "To view your timeline you need to create some memories first! :)", - "emptyScreenCreateMemory": "Create a Memory" + "emptyScreenCreateMemory": "Create a Memory", + + + "settingsScreenLoading": "Loading Settings...", + "settingsScreenTitle": "Settings", + "settingsScreenAccountSectionTitle": "Your Account", + "settingsScreenAccountSectionCreationDateLabel": "Created at", + "settingsScreenAccountSectionLogoutLabel": "Log out", + "settingsScreenDangerSectionTitle": "Danger Zone", + "settingsScreenDangerSectionDeleteAccountLabel": "Delete Account", + "settingsScreenDeleteAccountTitle": "Delete Account", + "settingsScreenDeleteAccountDescription": "Are you sure you want to delete your account? This action cannot be undone! All your memories will be deleted as well.", + "settingsScreenDeleteAccountConfirmLabel": "Delete Account now" } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index d6660e5..f274027 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,6 +10,7 @@ import 'package:quid_faciam_hodie/screens/grant_permission_screen.dart'; import 'package:quid_faciam_hodie/screens/login_screen.dart'; import 'package:quid_faciam_hodie/screens/main_screen.dart'; import 'package:quid_faciam_hodie/screens/server_loading_screen.dart'; +import 'package:quid_faciam_hodie/screens/settings_screen.dart'; import 'package:quid_faciam_hodie/screens/timeline_screen.dart'; import 'package:quid_faciam_hodie/screens/welcome_screen.dart'; @@ -64,6 +65,7 @@ class _MyAppState extends State { CalendarScreen.ID: (context) => const CalendarScreen(), ServerLoadingScreen.ID: (context) => const ServerLoadingScreen(), EmptyScreen.ID: (context) => const EmptyScreen(), + SettingsScreen.ID: (context) => const SettingsScreen(), }, initialRoute: ServerLoadingScreen.ID, ), diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 0dc4c38..91ebbc0 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -12,6 +12,7 @@ import 'package:quid_faciam_hodie/constants/values.dart'; import 'package:quid_faciam_hodie/extensions/snackbar.dart'; import 'package:quid_faciam_hodie/managers/file_manager.dart'; import 'package:quid_faciam_hodie/managers/global_values_manager.dart'; +import 'package:quid_faciam_hodie/screens/main_screen/settings_button_overlay.dart'; import 'package:quid_faciam_hodie/utils/auth_required.dart'; import 'package:quid_faciam_hodie/utils/loadable.dart'; import 'package:quid_faciam_hodie/widgets/animate_in_builder.dart'; @@ -317,11 +318,13 @@ class _MainScreenState extends AuthRequiredState with Loadable { child: AspectRatio( aspectRatio: 1 / controller!.value.aspectRatio, child: Stack( + alignment: Alignment.center, fit: StackFit.expand, children: [ controller!.buildPreview(), if (isRecording) RecordingOverlay(controller: controller!), + if (!isRecording) SettingsButtonOverlay(), if (uploadingPhotoAnimation != null) UploadingPhoto( data: uploadingPhotoAnimation!, diff --git a/lib/screens/main_screen/settings_button_overlay.dart b/lib/screens/main_screen/settings_button_overlay.dart new file mode 100644 index 0000000..2248acf --- /dev/null +++ b/lib/screens/main_screen/settings_button_overlay.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; +import 'package:quid_faciam_hodie/constants/spacing.dart'; +import 'package:quid_faciam_hodie/screens/settings_screen.dart'; + +class SettingsButtonOverlay extends StatelessWidget { + const SettingsButtonOverlay({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Positioned( + left: SMALL_SPACE, + top: SMALL_SPACE, + child: IconButton( + icon: Icon(context.platformIcons.settings), + onPressed: () { + Navigator.pushNamed(context, SettingsScreen.ID); + }, + ), + ); + } +} diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart new file mode 100644 index 0000000..898e3eb --- /dev/null +++ b/lib/screens/settings_screen.dart @@ -0,0 +1,164 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; +import 'package:intl/intl.dart'; +import 'package:quid_faciam_hodie/constants/spacing.dart'; +import 'package:quid_faciam_hodie/extensions/snackbar.dart'; +import 'package:quid_faciam_hodie/screens/welcome_screen.dart'; +import 'package:quid_faciam_hodie/utils/auth_required.dart'; +import 'package:quid_faciam_hodie/utils/loadable.dart'; +import 'package:quid_faciam_hodie/utils/theme.dart'; +import 'package:settings_ui/settings_ui.dart'; +import 'package:supabase_flutter/supabase_flutter.dart'; + +final supabase = Supabase.instance.client; + +class SettingsScreen extends StatefulWidget { + static const ID = '/settings'; + + const SettingsScreen({Key? key}) : super(key: key); + + @override + State createState() => _SettingsScreenState(); +} + +class _SettingsScreenState extends AuthRequiredState + with Loadable { + User? user; + + @override + void onAuthenticated(Session session) { + if (session.user != null) { + setState(() { + user = session.user; + }); + } + } + + Future deleteUser() async { + return; + + final localizations = AppLocalizations.of(context)!; + + final response = await supabase + .from('auth.users') + .delete() + .match({'id': user!.id}).execute(); + + if (response.error != null) { + context.showLongErrorSnackBar(message: localizations.generalError); + return; + } + + if (!mounted) { + return; + } + + Navigator.pop(context); + Navigator.pushNamedAndRemoveUntil( + context, + WelcomeScreen.ID, + (route) => false, + ); + } + + @override + Widget build(BuildContext context) { + final localizations = AppLocalizations.of(context)!; + + return PlatformScaffold( + appBar: PlatformAppBar( + title: Text(localizations.settingsScreenTitle), + ), + body: (user == null || isLoading) + ? Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + PlatformCircularProgressIndicator(), + const SizedBox(height: MEDIUM_SPACE), + Text(localizations.settingsScreenLoading), + ], + ), + ) + : SettingsList( + sections: [ + SettingsSection( + title: Text(localizations.settingsScreenAccountSectionTitle), + tiles: [ + SettingsTile( + leading: Icon(context.platformIcons.mail), + title: Text(user!.email!), + ), + SettingsTile( + leading: Text(localizations + .settingsScreenAccountSectionCreationDateLabel), + title: Text( + DateFormat('d. MMMM y, HH:mm:ss') + .format(DateTime.parse(user!.createdAt)), + ), + ), + SettingsTile( + leading: const Icon(Icons.logout_rounded), + title: Text(localizations + .settingsScreenAccountSectionLogoutLabel), + onPressed: (_) async { + await callWithLoading(supabase.auth.signOut); + + if (!mounted) { + return; + } + + Navigator.pushNamedAndRemoveUntil( + context, + WelcomeScreen.ID, + (route) => false, + ); + }, + ) + ], + ), + SettingsSection( + title: Text(localizations.settingsScreenDangerSectionTitle), + tiles: [ + SettingsTile( + leading: Icon(context.platformIcons.delete), + title: Text(localizations + .settingsScreenDangerSectionDeleteAccountLabel), + onPressed: (_) => showPlatformDialog( + context: context, + builder: (platformContext) => PlatformAlertDialog( + title: Text( + localizations.settingsScreenDeleteAccountTitle, + ), + content: Text( + localizations + .settingsScreenDeleteAccountDescription, + style: getBodyTextTextStyle(platformContext), + ), + actions: [ + PlatformDialogAction( + child: Text( + localizations.generalCancelButtonLabel, + ), + onPressed: () => Navigator.pop(context), + ), + PlatformDialogAction( + child: Text( + localizations + .settingsScreenDeleteAccountConfirmLabel, + ), + onPressed: () => callWithLoading(deleteUser), + ) + ], + ), + ), + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/screens/timeline_screen/timeline_page.dart b/lib/screens/timeline_screen/timeline_page.dart index c2acb9c..0846d85 100644 --- a/lib/screens/timeline_screen/timeline_page.dart +++ b/lib/screens/timeline_screen/timeline_page.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; import 'package:provider/provider.dart'; import 'package:quid_faciam_hodie/foreign_types/memory.dart'; import 'package:quid_faciam_hodie/models/timeline.dart'; @@ -67,10 +68,12 @@ class _TimelinePageState extends State { onDoubleTap: () async { timeline.pause(); - await showModalBottomSheet( + await showPlatformModalSheet( context: context, - backgroundColor: Colors.transparent, - isScrollControlled: true, + material: MaterialModalSheetData( + backgroundColor: Colors.transparent, + isScrollControlled: true, + ), builder: (sheetContext) => MemorySheet( memory: timeline.currentMemory, sheetContext: sheetContext, diff --git a/pubspec.lock b/pubspec.lock index 99f241e..c5af600 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -546,6 +546,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.15" + settings_ui: + dependency: "direct main" + description: + name: settings_ui + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 1783dae..90ea384 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,6 +55,7 @@ dependencies: flutter_calendar_widget: ^0.0.2 flutter_platform_widgets: ^2.0.0 lottie: ^1.4.1 + settings_ui: ^2.0.2 dev_dependencies: flutter_test: