diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 244c517..d25e8bd 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,7 +1,13 @@ PODS: + - Alamofire (5.6.2) - camera_avfoundation (0.0.1): - Flutter - Flutter (1.0.0) + - flutter_osm_plugin (0.0.1): + - Alamofire + - Flutter + - Polyline + - Tangram-es - flutter_secure_storage (3.3.1): - Flutter - fluttertoast (0.0.2): @@ -9,10 +15,14 @@ PODS: - Toast - gallery_saver (0.0.1): - Flutter + - location (0.0.1): + - Flutter - path_provider_ios (0.0.1): - Flutter - permission_handler_apple (9.0.4): - Flutter + - Polyline (5.0.2) + - Tangram-es (0.17.1) - Toast (4.0.0) - uni_links (0.0.1): - Flutter @@ -24,9 +34,11 @@ PODS: DEPENDENCIES: - camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`) - Flutter (from `Flutter`) + - flutter_osm_plugin (from `.symlinks/plugins/flutter_osm_plugin/ios`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - gallery_saver (from `.symlinks/plugins/gallery_saver/ios`) + - location (from `.symlinks/plugins/location/ios`) - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - uni_links (from `.symlinks/plugins/uni_links/ios`) @@ -35,6 +47,9 @@ DEPENDENCIES: SPEC REPOS: trunk: + - Alamofire + - Polyline + - Tangram-es - Toast EXTERNAL SOURCES: @@ -42,12 +57,16 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/camera_avfoundation/ios" Flutter: :path: Flutter + flutter_osm_plugin: + :path: ".symlinks/plugins/flutter_osm_plugin/ios" flutter_secure_storage: :path: ".symlinks/plugins/flutter_secure_storage/ios" fluttertoast: :path: ".symlinks/plugins/fluttertoast/ios" gallery_saver: :path: ".symlinks/plugins/gallery_saver/ios" + location: + :path: ".symlinks/plugins/location/ios" path_provider_ios: :path: ".symlinks/plugins/path_provider_ios/ios" permission_handler_apple: @@ -60,18 +79,23 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/video_player_avfoundation/ios" SPEC CHECKSUMS: + Alamofire: d368e1ff8a298e6dde360e35a3e68e6c610e7204 camera_avfoundation: 07c77549ea54ad95d8581be86617c094a46280d9 Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a + flutter_osm_plugin: f06ae1e854af57270c61ae27bdb8a386cfd4afa5 flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec fluttertoast: 16fbe6039d06a763f3533670197d01fc73459037 gallery_saver: 9fc173c9f4fcc48af53b2a9ebea1b643255be542 + location: 3a2eed4dd2fab25e7b7baf2a9efefe82b512d740 path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce + Polyline: fce41d72e1146c41c6d081f7656827226f643dff + Tangram-es: 628b634f7fc09d2217469b9914de00d9de49ff9d Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 uni_links: d97da20c7701486ba192624d99bffaaffcfc298a url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff -PODFILE CHECKSUM: 844e8fbcd0235060f60f771d03577fc655617b90 +PODFILE CHECKSUM: c1e56e861085e01c426a686306e59660903e6fa6 COCOAPODS: 1.11.3 diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 9e55a95..b4b9d37 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -75,5 +75,8 @@ NSLocationUsageDescription Accessing your location allows you to tag your memories + + NSLocationWhenInUseUsageDescription + Accessing your location allows you to tag your memories diff --git a/lib/screens/grant_permission_screen.dart b/lib/screens/grant_permission_screen.dart index d197703..6e57801 100644 --- a/lib/screens/grant_permission_screen.dart +++ b/lib/screens/grant_permission_screen.dart @@ -3,6 +3,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; import 'package:quid_faciam_hodie/constants/spacing.dart'; import 'package:quid_faciam_hodie/screens/login_screen.dart'; +import 'package:quid_faciam_hodie/screens/server_loading_screen.dart'; import 'grant_permission_screen/permissions_required_page.dart'; @@ -24,7 +25,14 @@ class GrantPermissionScreen extends StatelessWidget { child: Center( child: PermissionsRequiredPage( onPermissionsGranted: () { - Navigator.pushReplacementNamed(context, LoginScreen.ID); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const ServerLoadingScreen( + nextScreen: LoginScreen.ID, + ), + ), + ); }, ), ), diff --git a/lib/screens/grant_permission_screen/permissions_required_page.dart b/lib/screens/grant_permission_screen/permissions_required_page.dart index e19137c..531742c 100644 --- a/lib/screens/grant_permission_screen/permissions_required_page.dart +++ b/lib/screens/grant_permission_screen/permissions_required_page.dart @@ -156,7 +156,7 @@ class _PermissionsRequiredPageState extends State { children: [ Text( localizations - .permissionsRequiredPageGrantMicrophonePermission, + .permissionsRequiredPageGrantLocationPermission, ), if (hasGrantedLocationPermission) Icon(context.platformIcons.checkMark), diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index a0988b1..14c302a 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -219,10 +219,12 @@ class _MainScreenState extends AuthRequiredState with Loadable { LocationData? locationData; - if (Platform.isAndroid && (await Permission.location.isGranted)) { + if (await Permission.location.isGranted) { locationData = await Location().getLocation(); - await tagLocationToImage(file, locationData); + if (Platform.isAndroid) { + await tagLocationToImage(file, locationData); + } } try { diff --git a/lib/screens/server_loading_screen.dart b/lib/screens/server_loading_screen.dart index 45264a6..26c0845 100644 --- a/lib/screens/server_loading_screen.dart +++ b/lib/screens/server_loading_screen.dart @@ -48,7 +48,12 @@ class _ServerLoadingScreenState extends State { final memories = context.read(); final session = Supabase.instance.client.auth.session(); - if (session != null) { + if (session == null) { + Navigator.pushReplacementNamed( + context, + WelcomeScreen.ID, + ); + } else { if (!memories.isInitialized) { await memories.initialize(); } @@ -58,7 +63,7 @@ class _ServerLoadingScreenState extends State { } if (widget.nextScreen == null) { - Navigator.pushNamed( + Navigator.pushReplacementNamed( context, MainScreen.ID, ); @@ -75,11 +80,6 @@ class _ServerLoadingScreenState extends State { ); } } - } else { - Navigator.pushReplacementNamed( - context, - WelcomeScreen.ID, - ); } } diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index d01e5d0..0dd695b 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -13,6 +13,7 @@ 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:quid_faciam_hodie/widgets/cupertino_dropdown.dart'; import 'package:settings_ui/settings_ui.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; @@ -81,11 +82,49 @@ class _SettingsScreenState extends AuthRequiredState ); } + Widget getPicker() { + final settings = GlobalValuesManager.settings!; + final resolutionTextMapping = getResolutionTextMapping(context); + final items = ResolutionPreset.values + .map( + (value) => DropdownMenuItem( + value: value, + child: Text(resolutionTextMapping[value]!), + ), + ) + .toList(); + + if (isMaterial(context)) { + return DropdownButtonFormField( + value: settings.resolution, + onChanged: (value) { + if (value == null) { + return; + } + + settings.setResolution(value); + }, + items: items, + ); + } else { + return CupertinoDropdownButton( + itemExtent: 30, + onChanged: (value) { + if (value == null) { + return; + } + + settings.setResolution(value); + }, + value: settings.resolution, + items: items, + ); + } + } + @override Widget build(BuildContext context) { - final settings = GlobalValuesManager.settings!; final localizations = AppLocalizations.of(context)!; - final resolutionTextMapping = getResolutionTextMapping(context); return PlatformScaffold( appBar: PlatformAppBar( @@ -163,23 +202,7 @@ class _SettingsScreenState extends AuthRequiredState localizations .settingsScreenGeneralSectionQualityLabel, ), - title: DropdownButtonFormField( - value: settings.resolution, - onChanged: (value) async { - if (value == null) { - return; - } - - settings.setResolution(value); - }, - items: ResolutionPreset.values - .map((value) => - DropdownMenuItem( - value: value, - child: Text(resolutionTextMapping[value]!), - )) - .toList(), - ), + title: getPicker(), ) ], ), diff --git a/lib/screens/timeline_screen/memory_sheet.dart b/lib/screens/timeline_screen/memory_sheet.dart index da57e1f..e7e6482 100644 --- a/lib/screens/timeline_screen/memory_sheet.dart +++ b/lib/screens/timeline_screen/memory_sheet.dart @@ -135,114 +135,114 @@ class _MemorySheetState extends State with Loadable { background: GestureDetector( onTap: () => Navigator.pop(context), ), - persistentHeader: Container( - padding: const EdgeInsets.all(MEDIUM_SPACE), - decoration: BoxDecoration( - color: backgroundColor, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(LARGE_SPACE), - topRight: Radius.circular(LARGE_SPACE), - ), + persistentHeader: Material( + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(LARGE_SPACE), + topRight: Radius.circular(LARGE_SPACE), ), - child: Column( - children: [ - if (widget.memory.location != null) ...[ - const Padding( - padding: EdgeInsets.all(SMALL_SPACE), - child: SheetIndicator(), + color: backgroundColor, + child: Padding( + padding: const EdgeInsets.all(MEDIUM_SPACE), + child: Column( + children: [ + if (widget.memory.location != null) ...[ + const Padding( + padding: EdgeInsets.all(SMALL_SPACE), + child: SheetIndicator(), + ), + const SizedBox(height: MEDIUM_SPACE), + ], + Text( + localizations.memorySheetTitle, + style: getTitleTextStyle(context), ), const SizedBox(height: MEDIUM_SPACE), - ], - Text( - localizations.memorySheetTitle, - style: getTitleTextStyle(context), - ), - const SizedBox(height: MEDIUM_SPACE), - ListTile( - leading: PlatformWidget( - cupertino: (_, __) => Icon( - CupertinoIcons.down_arrow, - color: getBodyTextColor(context), - ), - material: (_, __) => Icon( - Icons.download, - color: getBodyTextColor(context), - ), - ), - title: Text( - localizations.memorySheetDownloadMemory, - style: getBodyTextTextStyle(context), - ), - enabled: !getIsLoadingSpecificID('download'), - onTap: getIsLoadingSpecificID('download') - ? null - : () => callWithLoading(downloadFile, 'download'), - trailing: getIsLoadingSpecificID('download') - ? buildLoadingIndicator() - : null, - ), - ListTile( - leading: Icon( - widget.memory.isPublic - ? Icons.public_off_rounded - : Icons.public, - color: getBodyTextColor(context), - ), - title: Text( - widget.memory.isPublic - ? localizations.memorySheetUpdateMemoryMakePrivate - : localizations.memorySheetUpdateMemoryMakePublic, - style: getBodyTextTextStyle(context), - ), - enabled: !getIsLoadingSpecificID('public'), - onTap: getIsLoadingSpecificID('public') - ? null - : () => callWithLoading(changeVisibility, 'public'), - trailing: getIsLoadingSpecificID('public') - ? buildLoadingIndicator() - : null, - ), - ListTile( - leading: Icon( - context.platformIcons.delete, - color: getBodyTextColor(context), - ), - title: Text( - localizations.memorySheetDeleteMemory, - style: getBodyTextTextStyle(context), - ), - enabled: !getIsLoadingSpecificID('delete'), - onTap: getIsLoadingSpecificID('delete') - ? null - : () => callWithLoading(deleteFile, 'delete'), - trailing: getIsLoadingSpecificID('delete') - ? buildLoadingIndicator() - : null, - ), - const SizedBox(height: MEDIUM_SPACE), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - context.platformIcons.time, - size: platformThemeData( - context, - material: (data) => data.textTheme.bodyLarge!.fontSize, - cupertino: (data) => data.textTheme.textStyle.fontSize, + ListTile( + leading: PlatformWidget( + cupertino: (_, __) => Icon( + CupertinoIcons.down_arrow, + color: getBodyTextColor(context), + ), + material: (_, __) => Icon( + Icons.download, + color: getBodyTextColor(context), ), ), - const SizedBox(width: TINY_SPACE), - Text( - localizations.memorySheetCreatedAtDataKey( - DateFormat.jms().format( - widget.memory.creationDate, - ), - ), + title: Text( + localizations.memorySheetDownloadMemory, style: getBodyTextTextStyle(context), ), - ], - ), - ], + enabled: !getIsLoadingSpecificID('download'), + onTap: getIsLoadingSpecificID('download') + ? null + : () => callWithLoading(downloadFile, 'download'), + trailing: getIsLoadingSpecificID('download') + ? buildLoadingIndicator() + : null, + ), + ListTile( + leading: Icon( + widget.memory.isPublic + ? Icons.public_off_rounded + : Icons.public, + color: getBodyTextColor(context), + ), + title: Text( + widget.memory.isPublic + ? localizations.memorySheetUpdateMemoryMakePrivate + : localizations.memorySheetUpdateMemoryMakePublic, + style: getBodyTextTextStyle(context), + ), + enabled: !getIsLoadingSpecificID('public'), + onTap: getIsLoadingSpecificID('public') + ? null + : () => callWithLoading(changeVisibility, 'public'), + trailing: getIsLoadingSpecificID('public') + ? buildLoadingIndicator() + : null, + ), + ListTile( + leading: Icon( + context.platformIcons.delete, + color: getBodyTextColor(context), + ), + title: Text( + localizations.memorySheetDeleteMemory, + style: getBodyTextTextStyle(context), + ), + enabled: !getIsLoadingSpecificID('delete'), + onTap: getIsLoadingSpecificID('delete') + ? null + : () => callWithLoading(deleteFile, 'delete'), + trailing: getIsLoadingSpecificID('delete') + ? buildLoadingIndicator() + : null, + ), + const SizedBox(height: MEDIUM_SPACE), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + context.platformIcons.time, + size: platformThemeData( + context, + material: (data) => data.textTheme.bodyLarge!.fontSize, + cupertino: (data) => data.textTheme.textStyle.fontSize, + ), + ), + const SizedBox(width: TINY_SPACE), + Text( + localizations.memorySheetCreatedAtDataKey( + DateFormat.jms().format( + widget.memory.creationDate, + ), + ), + style: getBodyTextTextStyle(context), + ), + ], + ), + ], + ), ), ), expandableContent: widget.memory.location == null diff --git a/lib/widgets/cupertino_dropdown.dart b/lib/widgets/cupertino_dropdown.dart new file mode 100644 index 0000000..810010f --- /dev/null +++ b/lib/widgets/cupertino_dropdown.dart @@ -0,0 +1,116 @@ +// Adopted from https://github.com/Enough-Software/enough_platform_widgets/blob/main/lib/src/cupertino/cupertino_dropdown_button.dart +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:quid_faciam_hodie/constants/spacing.dart'; + +class CupertinoDropdownButton extends StatefulWidget { + final List>? items; + final List Function(BuildContext context)? selectedItemBuilder; + final T? value; + final void Function(T? value)? onChanged; + final double itemExtent; + final Widget? hint; + + const CupertinoDropdownButton({ + Key? key, + required this.items, + this.selectedItemBuilder, + this.value, + this.onChanged, + this.hint, + required this.itemExtent, + }) : super(key: key); + + @override + _CupertinoDropdownButtonState createState() => + _CupertinoDropdownButtonState(); +} + +class _CupertinoDropdownButtonState + extends State> { + int? _selectedIndex; + + @override + Widget build(BuildContext context) { + final itms = widget.items; + if (itms == null || itms.isEmpty) { + return Container(); + } + final builder = widget.selectedItemBuilder; + final children = (builder != null) + ? builder(context) + .map((widget) => Padding( + padding: const EdgeInsets.symmetric(horizontal: MEDIUM_SPACE), + child: FittedBox(child: widget), + )) + .toList() + : itms + .map((itm) => Padding( + padding: const EdgeInsets.symmetric(horizontal: MEDIUM_SPACE), + child: FittedBox(child: itm.child), + )) + .toList(); + final currentValue = widget.value; + + final currentIndex = + max(itms.indexWhere((item) => item.value == currentValue), 0); + final child = currentValue == null + ? widget.hint ?? const Icon(CupertinoIcons.arrow_down) + : children[currentIndex]; + return CupertinoButton( + padding: const EdgeInsets.all(MEDIUM_SPACE), + child: FittedBox(child: child), + onPressed: () async { + final scrollController = (currentValue == null) + ? null + : FixedExtentScrollController( + initialItem: currentIndex, + ); + final result = await showCupertinoModalPopup( + context: context, + builder: (context) => SizedBox( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height * 0.7, + child: SafeArea( + child: Container( + color: CupertinoTheme.of(context).barBackgroundColor, + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + CupertinoButton( + child: const Icon(Icons.clear), + onPressed: () => Navigator.of(context).pop(false)), + CupertinoButton( + child: const Icon(CupertinoIcons.check_mark), + onPressed: () => Navigator.of(context).pop(true)), + ], + ), + Expanded( + child: CupertinoPicker( + itemExtent: widget.itemExtent, + onSelectedItemChanged: (index) => + _selectedIndex = index, + scrollController: scrollController, + children: children, + ), + ), + ], + ), + ), + ), + ), + ); + if (result == true && _selectedIndex != null) { + final callback = widget.onChanged; + if (callback != null) { + callback(widget.items![_selectedIndex!].value); + } + } + }, + ); + } +}