diff --git a/mobile/lib/modules/asset_viewer/ui/advanced_bottom_sheet.dart b/mobile/lib/modules/asset_viewer/ui/advanced_bottom_sheet.dart new file mode 100644 index 0000000000..441492c39b --- /dev/null +++ b/mobile/lib/modules/asset_viewer/ui/advanced_bottom_sheet.dart @@ -0,0 +1,99 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/shared/models/asset.dart'; + +class AdvancedBottomSheet extends HookConsumerWidget { + final Asset assetDetail; + + const AdvancedBottomSheet({Key? key, required this.assetDetail}) + : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + var isDarkMode = Theme.of(context).brightness == Brightness.dark; + + return SingleChildScrollView( + child: Card( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(15), + topRight: Radius.circular(15), + ), + ), + margin: const EdgeInsets.all(0), + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 8.0), + child: LayoutBuilder( + builder: (context, constraints) { + // One column + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SizedBox(height: 32.0), + const Align( + child: Text( + "ADVANCED INFO", + style: TextStyle(fontSize: 12.0), + ), + ), + const SizedBox(height: 32.0), + Container( + decoration: BoxDecoration( + color: isDarkMode ? Colors.grey[900] : Colors.grey[200], + borderRadius: BorderRadius.circular(15.0), + ), + child: Padding( + padding: const EdgeInsets.only( + right: 16.0, + left: 16, + top: 8, + bottom: 16, + ), + child: ListView( + shrinkWrap: true, + children: [ + Align( + alignment: Alignment.centerRight, + child: IconButton( + onPressed: () { + Clipboard.setData( + ClipboardData(text: assetDetail.toString()), + ).then((_) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("Copied to clipboard"), + ), + ); + }); + }, + icon: Icon( + Icons.copy, + size: 16.0, + color: Theme.of(context).primaryColor, + ), + ), + ), + SelectableText( + assetDetail.toString(), + style: const TextStyle( + fontSize: 12.0, + fontWeight: FontWeight.bold, + fontFamily: "Inconsolata", + ), + showCursor: true, + ), + ], + ), + ), + ), + const SizedBox(height: 32.0), + ], + ); + }, + ), + ), + ), + ); + } +} diff --git a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart index f96f1f0624..535ad968f7 100644 --- a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart +++ b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart @@ -8,6 +8,7 @@ import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/album/ui/add_to_album_bottom_sheet.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/image_viewer_page_state.provider.dart'; +import 'package:immich_mobile/modules/asset_viewer/ui/advanced_bottom_sheet.dart'; import 'package:immich_mobile/modules/asset_viewer/ui/exif_bottom_sheet.dart'; import 'package:immich_mobile/modules/asset_viewer/ui/top_control_app_bar.dart'; import 'package:immich_mobile/modules/asset_viewer/views/video_viewer_page.dart'; @@ -189,6 +190,11 @@ class GalleryViewerPage extends HookConsumerWidget { isScrollControlled: true, context: context, builder: (context) { + if (ref + .watch(appSettingsServiceProvider) + .getSetting(AppSettingsEnum.advancedTroubleshooting)) { + return AdvancedBottomSheet(assetDetail: assetDetail!); + } return ExifBottomSheet(assetDetail: assetDetail!); }, ); diff --git a/mobile/lib/modules/settings/services/app_settings.service.dart b/mobile/lib/modules/settings/services/app_settings.service.dart index ce374ef359..dbe2652b63 100644 --- a/mobile/lib/modules/settings/services/app_settings.service.dart +++ b/mobile/lib/modules/settings/services/app_settings.service.dart @@ -43,6 +43,11 @@ enum AppSettingsEnum { "selectedAlbumSortOrder", 0, ), + advancedTroubleshooting( + StoreKey.advancedTroubleshooting, + "advancedTroubleshooting", + false, + ), ; const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue); diff --git a/mobile/lib/modules/settings/ui/advanced_settings/advanced_settings.dart b/mobile/lib/modules/settings/ui/advanced_settings/advanced_settings.dart new file mode 100644 index 0000000000..190f98c820 --- /dev/null +++ b/mobile/lib/modules/settings/ui/advanced_settings/advanced_settings.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; +import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; +import 'package:immich_mobile/modules/settings/ui/settings_switch_list_tile.dart'; + +class AdvancedSettings extends HookConsumerWidget { + const AdvancedSettings({super.key}); + @override + Widget build(BuildContext context, WidgetRef ref) { + final appSettingService = ref.watch(appSettingsServiceProvider); + final isEnabled = + useState(AppSettingsEnum.advancedTroubleshooting.defaultValue); + + useEffect( + () { + isEnabled.value = appSettingService.getSetting( + AppSettingsEnum.advancedTroubleshooting, + ); + return null; + }, + [], + ); + return ExpansionTile( + textColor: Theme.of(context).primaryColor, + title: const Text( + "Advanced", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + children: [ + SettingsSwitchListTile( + enabled: true, + appSettingService: appSettingService, + valueNotifier: isEnabled, + settingsEnum: AppSettingsEnum.advancedTroubleshooting, + title: "Troubleshooting", + subtitle: "Enable additional features for troubleshooting", + ), + ], + ); + } +} diff --git a/mobile/lib/modules/settings/views/settings_page.dart b/mobile/lib/modules/settings/views/settings_page.dart index 6a207f84e9..6e5ddec70c 100644 --- a/mobile/lib/modules/settings/views/settings_page.dart +++ b/mobile/lib/modules/settings/views/settings_page.dart @@ -1,6 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/modules/settings/ui/advanced_settings/advanced_settings.dart'; import 'package:immich_mobile/modules/settings/ui/asset_list_settings/asset_list_settings.dart'; import 'package:immich_mobile/modules/settings/ui/image_viewer_quality_setting/image_viewer_quality_setting.dart'; import 'package:immich_mobile/modules/settings/ui/notification_setting/notification_setting.dart'; @@ -40,7 +41,8 @@ class SettingsPage extends HookConsumerWidget { const ThemeSetting(), const AssetListSettings(), const NotificationSetting(), - //const ExperimentalSettings(), + // const ExperimentalSettings(), + const AdvancedSettings() ], ).toList(), ], diff --git a/mobile/lib/shared/models/asset.dart b/mobile/lib/shared/models/asset.dart index 709accb50d..12fe6a0f52 100644 --- a/mobile/lib/shared/models/asset.dart +++ b/mobile/lib/shared/models/asset.dart @@ -252,6 +252,28 @@ class Asset { static int compareByLocalId(Asset a, Asset b) => a.localId.compareTo(b.localId); + + @override + String toString() { + return """ +{ + "remoteId": "${remoteId ?? "N/A"}", + "localId": "$localId", + "deviceId": "$deviceId", + "ownerId": "$ownerId", + "livePhotoVideoId": "${livePhotoVideoId ?? "N/A"}", + "fileCreatedAt": "$fileCreatedAt", + "fileModifiedAt": "$fileModifiedAt", + "updatedAt": "$updatedAt", + "durationInSeconds": $durationInSeconds, + "type": "$type", + "fileName": "$fileName", + "isFavorite": $isFavorite, + "isLocal": $isLocal, + "width": ${width ?? "N/A"}, + "height": ${height ?? "N/A"} +}"""; + } } enum AssetType { diff --git a/mobile/lib/shared/models/store.dart b/mobile/lib/shared/models/store.dart index 2069d3e584..6c73028498 100644 --- a/mobile/lib/shared/models/store.dart +++ b/mobile/lib/shared/models/store.dart @@ -167,6 +167,7 @@ enum StoreKey { imageCacheSize(111, type: int), albumThumbnailCacheSize(112, type: int), selectedAlbumSortOrder(113, type: int), + advancedTroubleshooting(114, type: bool), ; const StoreKey(