mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	chore(mobile): Upgrade to Flutter 3.7 (#1416)
This commit is contained in:
		
							
								
								
									
										2
									
								
								.github/workflows/build-mobile.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/build-mobile.yml
									
									
									
									
										vendored
									
									
								
							@@ -39,7 +39,7 @@ jobs:
 | 
			
		||||
        uses: subosito/flutter-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          channel: "stable"
 | 
			
		||||
          flutter-version: "3.3.10"
 | 
			
		||||
          flutter-version: "3.7.3"
 | 
			
		||||
          cache: true
 | 
			
		||||
 | 
			
		||||
      - name: Create the Keystore
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/static_analysis.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/static_analysis.yml
									
									
									
									
										vendored
									
									
								
							@@ -19,7 +19,7 @@ jobs:
 | 
			
		||||
        uses: subosito/flutter-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          channel: 'stable'
 | 
			
		||||
          flutter-version: '3.3.10'
 | 
			
		||||
          flutter-version: '3.7.3'
 | 
			
		||||
 | 
			
		||||
      - name: Install dependencies
 | 
			
		||||
        run: dart pub get
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							@@ -49,7 +49,7 @@ jobs:
 | 
			
		||||
        uses: subosito/flutter-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          channel: 'stable'
 | 
			
		||||
          flutter-version: '3.3.10'
 | 
			
		||||
          flutter-version: '3.7.3'
 | 
			
		||||
      - name: Run tests
 | 
			
		||||
        working-directory: ./mobile
 | 
			
		||||
        run: flutter test
 | 
			
		||||
@@ -78,7 +78,7 @@ jobs:
 | 
			
		||||
        uses: subosito/flutter-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          channel: 'stable'
 | 
			
		||||
          flutter-version: '3.3.10'
 | 
			
		||||
          flutter-version: '3.7.3'
 | 
			
		||||
      - name: Run integration tests
 | 
			
		||||
        uses: reactivecircus/android-emulator-runner@v2.27.0
 | 
			
		||||
        with:
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,11 @@
 | 
			
		||||
  xmlns:tools="http://schemas.android.com/tools">
 | 
			
		||||
  <application android:label="Immich" android:name=".ImmichApp" android:usesCleartextTraffic="true"
 | 
			
		||||
    android:icon="@mipmap/ic_launcher" android:requestLegacyExternalStorage="true">
 | 
			
		||||
 | 
			
		||||
    <meta-data
 | 
			
		||||
      android:name="io.flutter.embedding.android.EnableImpeller"
 | 
			
		||||
      android:value="false" />
 | 
			
		||||
 | 
			
		||||
    <activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop"
 | 
			
		||||
      android:theme="@style/LaunchTheme"
 | 
			
		||||
      android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,8 @@ void main() async {
 | 
			
		||||
  await ImmichTestHelper.initialize();
 | 
			
		||||
 | 
			
		||||
  group("Login input validation test", () {
 | 
			
		||||
    immichWidgetTest("Test leading/trailing whitespace", (tester, helper) async {
 | 
			
		||||
    immichWidgetTest("Test leading/trailing whitespace",
 | 
			
		||||
        (tester, helper) async {
 | 
			
		||||
      await helper.loginHelper.waitForLoginScreen();
 | 
			
		||||
      await helper.loginHelper.acknowledgeNewServerVersion();
 | 
			
		||||
 | 
			
		||||
@@ -17,15 +18,21 @@ void main() async {
 | 
			
		||||
 | 
			
		||||
      await tester.pump(const Duration(milliseconds: 300));
 | 
			
		||||
 | 
			
		||||
      expect(find.text("login_form_err_leading_whitespace".tr()), findsOneWidget);
 | 
			
		||||
      expect(
 | 
			
		||||
        find.text("login_form_err_leading_whitespace".tr()),
 | 
			
		||||
        findsOneWidget,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      await helper.loginHelper.enterCredentials(
 | 
			
		||||
          email: "demo@immich.app ",
 | 
			
		||||
        email: "demo@immich.app ",
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      await tester.pump(const Duration(milliseconds: 300));
 | 
			
		||||
 | 
			
		||||
      expect(find.text("login_form_err_trailing_whitespace".tr()), findsOneWidget);
 | 
			
		||||
      expect(
 | 
			
		||||
        find.text("login_form_err_trailing_whitespace".tr()),
 | 
			
		||||
        findsOneWidget,
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    immichWidgetTest("Test invalid email", (tester, helper) async {
 | 
			
		||||
@@ -33,13 +40,12 @@ void main() async {
 | 
			
		||||
      await helper.loginHelper.acknowledgeNewServerVersion();
 | 
			
		||||
 | 
			
		||||
      await helper.loginHelper.enterCredentials(
 | 
			
		||||
          email: "demo.immich.app",
 | 
			
		||||
        email: "demo.immich.app",
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      await tester.pump(const Duration(milliseconds: 300));
 | 
			
		||||
 | 
			
		||||
      expect(find.text("login_form_err_invalid_email".tr()), findsOneWidget);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
# Uncomment this line to define a global platform for your project
 | 
			
		||||
platform :ios, '11.0'
 | 
			
		||||
# platform :ios, '11.0'
 | 
			
		||||
 | 
			
		||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
 | 
			
		||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
 | 
			
		||||
@@ -34,19 +34,8 @@ target 'Runner' do
 | 
			
		||||
  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
# post_install do |installer|
 | 
			
		||||
#   installer.pods_project.targets.each do |target|
 | 
			
		||||
#     flutter_additional_ios_build_settings(target)
 | 
			
		||||
#   end
 | 
			
		||||
# end
 | 
			
		||||
 | 
			
		||||
post_install do |installer| 
 | 
			
		||||
  installer.pods_project.targets.each do |target| 
 | 
			
		||||
    flutter_additional_ios_build_settings(target) 
 | 
			
		||||
    target.build_configurations.each do |config| 
 | 
			
		||||
      config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64" 
 | 
			
		||||
      config.build_settings['ENABLE_BITCODE'] = 'YES' 
 | 
			
		||||
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0' 
 | 
			
		||||
    end 
 | 
			
		||||
  end 
 | 
			
		||||
end
 | 
			
		||||
post_install do |installer|
 | 
			
		||||
  installer.pods_project.targets.each do |target|
 | 
			
		||||
    flutter_additional_ios_build_settings(target)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -21,16 +21,18 @@ PODS:
 | 
			
		||||
    - Flutter
 | 
			
		||||
  - package_info_plus (0.4.5):
 | 
			
		||||
    - Flutter
 | 
			
		||||
  - path_provider_ios (0.0.1):
 | 
			
		||||
  - path_provider_foundation (0.0.1):
 | 
			
		||||
    - Flutter
 | 
			
		||||
    - FlutterMacOS
 | 
			
		||||
  - photo_manager (2.0.0):
 | 
			
		||||
    - Flutter
 | 
			
		||||
    - FlutterMacOS
 | 
			
		||||
  - SAMKeychain (1.5.3)
 | 
			
		||||
  - share_plus (0.0.1):
 | 
			
		||||
    - Flutter
 | 
			
		||||
  - shared_preferences_ios (0.0.1):
 | 
			
		||||
  - shared_preferences_foundation (0.0.1):
 | 
			
		||||
    - Flutter
 | 
			
		||||
    - FlutterMacOS
 | 
			
		||||
  - sqflite (0.0.2):
 | 
			
		||||
    - Flutter
 | 
			
		||||
    - FMDB (>= 2.7.5)
 | 
			
		||||
@@ -52,10 +54,10 @@ DEPENDENCIES:
 | 
			
		||||
  - integration_test (from `.symlinks/plugins/integration_test/ios`)
 | 
			
		||||
  - isar_flutter_libs (from `.symlinks/plugins/isar_flutter_libs/ios`)
 | 
			
		||||
  - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
 | 
			
		||||
  - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
 | 
			
		||||
  - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
 | 
			
		||||
  - photo_manager (from `.symlinks/plugins/photo_manager/ios`)
 | 
			
		||||
  - share_plus (from `.symlinks/plugins/share_plus/ios`)
 | 
			
		||||
  - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`)
 | 
			
		||||
  - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`)
 | 
			
		||||
  - sqflite (from `.symlinks/plugins/sqflite/ios`)
 | 
			
		||||
  - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
 | 
			
		||||
  - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`)
 | 
			
		||||
@@ -86,14 +88,14 @@ EXTERNAL SOURCES:
 | 
			
		||||
    :path: ".symlinks/plugins/isar_flutter_libs/ios"
 | 
			
		||||
  package_info_plus:
 | 
			
		||||
    :path: ".symlinks/plugins/package_info_plus/ios"
 | 
			
		||||
  path_provider_ios:
 | 
			
		||||
    :path: ".symlinks/plugins/path_provider_ios/ios"
 | 
			
		||||
  path_provider_foundation:
 | 
			
		||||
    :path: ".symlinks/plugins/path_provider_foundation/ios"
 | 
			
		||||
  photo_manager:
 | 
			
		||||
    :path: ".symlinks/plugins/photo_manager/ios"
 | 
			
		||||
  share_plus:
 | 
			
		||||
    :path: ".symlinks/plugins/share_plus/ios"
 | 
			
		||||
  shared_preferences_ios:
 | 
			
		||||
    :path: ".symlinks/plugins/shared_preferences_ios/ios"
 | 
			
		||||
  shared_preferences_foundation:
 | 
			
		||||
    :path: ".symlinks/plugins/shared_preferences_foundation/ios"
 | 
			
		||||
  sqflite:
 | 
			
		||||
    :path: ".symlinks/plugins/sqflite/ios"
 | 
			
		||||
  url_launcher_ios:
 | 
			
		||||
@@ -108,23 +110,23 @@ SPEC CHECKSUMS:
 | 
			
		||||
  flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
 | 
			
		||||
  flutter_udid: 0848809dbed4c055175747ae6a45a8b4f6771e1c
 | 
			
		||||
  flutter_web_auth: c25208760459cec375a3c39f6a8759165ca0fa4d
 | 
			
		||||
  fluttertoast: 16fbe6039d06a763f3533670197d01fc73459037
 | 
			
		||||
  fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0
 | 
			
		||||
  FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
 | 
			
		||||
  image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb
 | 
			
		||||
  integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5
 | 
			
		||||
  isar_flutter_libs: b69f437aeab9c521821c3f376198c4371fa21073
 | 
			
		||||
  package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
 | 
			
		||||
  path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
 | 
			
		||||
  path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852
 | 
			
		||||
  photo_manager: 4f6810b7dfc4feb03b461ac1a70dacf91fba7604
 | 
			
		||||
  SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
 | 
			
		||||
  share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
 | 
			
		||||
  shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad
 | 
			
		||||
  shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca
 | 
			
		||||
  sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
 | 
			
		||||
  Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
 | 
			
		||||
  url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de
 | 
			
		||||
  url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2
 | 
			
		||||
  video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff
 | 
			
		||||
  wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
 | 
			
		||||
 | 
			
		||||
PODFILE CHECKSUM: 05c3056158482c567a3e0cdab1351ceeee238a07
 | 
			
		||||
PODFILE CHECKSUM: c798208781ca5116c4a3d5927d689946791f0189
 | 
			
		||||
 | 
			
		||||
COCOAPODS: 1.11.3
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
	archiveVersion = 1;
 | 
			
		||||
	classes = {
 | 
			
		||||
	};
 | 
			
		||||
	objectVersion = 51;
 | 
			
		||||
	objectVersion = 54;
 | 
			
		||||
	objects = {
 | 
			
		||||
 | 
			
		||||
/* Begin PBXBuildFile section */
 | 
			
		||||
@@ -201,6 +201,7 @@
 | 
			
		||||
/* Begin PBXShellScriptBuildPhase section */
 | 
			
		||||
		3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
 | 
			
		||||
			isa = PBXShellScriptBuildPhase;
 | 
			
		||||
			alwaysOutOfDate = 1;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
			);
 | 
			
		||||
@@ -237,6 +238,7 @@
 | 
			
		||||
		};
 | 
			
		||||
		9740EEB61CF901F6004384FC /* Run Script */ = {
 | 
			
		||||
			isa = PBXShellScriptBuildPhase;
 | 
			
		||||
			alwaysOutOfDate = 1;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
			);
 | 
			
		||||
@@ -341,7 +343,7 @@
 | 
			
		||||
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 | 
			
		||||
				GCC_WARN_UNUSED_FUNCTION = YES;
 | 
			
		||||
				GCC_WARN_UNUSED_VARIABLE = YES;
 | 
			
		||||
				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 | 
			
		||||
				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 | 
			
		||||
				MTL_ENABLE_DEBUG_INFO = NO;
 | 
			
		||||
				NEW_SETTING = "";
 | 
			
		||||
				SDKROOT = iphoneos;
 | 
			
		||||
@@ -425,7 +427,7 @@
 | 
			
		||||
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 | 
			
		||||
				GCC_WARN_UNUSED_FUNCTION = YES;
 | 
			
		||||
				GCC_WARN_UNUSED_VARIABLE = YES;
 | 
			
		||||
				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 | 
			
		||||
				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 | 
			
		||||
				MTL_ENABLE_DEBUG_INFO = YES;
 | 
			
		||||
				NEW_SETTING = "";
 | 
			
		||||
				ONLY_ACTIVE_ARCH = YES;
 | 
			
		||||
@@ -475,7 +477,7 @@
 | 
			
		||||
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 | 
			
		||||
				GCC_WARN_UNUSED_FUNCTION = YES;
 | 
			
		||||
				GCC_WARN_UNUSED_VARIABLE = YES;
 | 
			
		||||
				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 | 
			
		||||
				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 | 
			
		||||
				MTL_ENABLE_DEBUG_INFO = NO;
 | 
			
		||||
				NEW_SETTING = "";
 | 
			
		||||
				SDKROOT = iphoneos;
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -1,97 +1,101 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 | 
			
		||||
<plist version="1.0">
 | 
			
		||||
	<dict>
 | 
			
		||||
		<key>CFBundleDevelopmentRegion</key>
 | 
			
		||||
		<string>$(DEVELOPMENT_LANGUAGE)</string>
 | 
			
		||||
		<key>CFBundleDisplayName</key>
 | 
			
		||||
		<string>Immich</string>
 | 
			
		||||
		<key>CFBundleExecutable</key>
 | 
			
		||||
		<string>$(EXECUTABLE_NAME)</string>
 | 
			
		||||
		<key>CFBundleIdentifier</key>
 | 
			
		||||
		<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
 | 
			
		||||
		<key>CFBundleInfoDictionaryVersion</key>
 | 
			
		||||
		<string>6.0</string>
 | 
			
		||||
		<key>CFBundleName</key>
 | 
			
		||||
		<string>immich_mobile</string>
 | 
			
		||||
		<key>CFBundlePackageType</key>
 | 
			
		||||
		<string>APPL</string>
 | 
			
		||||
		<key>CFBundleShortVersionString</key>
 | 
			
		||||
		<string>1.46.0</string>
 | 
			
		||||
		<key>CFBundleSignature</key>
 | 
			
		||||
		<string>????</string>
 | 
			
		||||
		<key>CFBundleVersion</key>
 | 
			
		||||
		<string>85</string>
 | 
			
		||||
		<key>LSRequiresIPhoneOS</key>
 | 
			
		||||
		<true/>
 | 
			
		||||
		<key>MGLMapboxMetricsEnabledSettingShownInApp</key>
 | 
			
		||||
		<true/>
 | 
			
		||||
		<key>NSAppTransportSecurity</key>
 | 
			
		||||
		<dict>
 | 
			
		||||
			<key>NSAllowsArbitraryLoads</key>
 | 
			
		||||
			<true/>
 | 
			
		||||
		</dict>
 | 
			
		||||
		<key>NSLocationAlwaysUsageDescription</key>
 | 
			
		||||
		<string>Enable location setting to show position of assets on map</string>
 | 
			
		||||
		<key>NSLocationWhenInUseUsageDescription</key>
 | 
			
		||||
		<string>Enable location setting to show position of assets on map</string>
 | 
			
		||||
		<key>NSPhotoLibraryUsageDescription</key>
 | 
			
		||||
		<string>We need to manage backup your photos album</string>
 | 
			
		||||
		<key>NSPhotoLibraryAddUsageDescription</key>
 | 
			
		||||
		<string>We need to manage backup your photos album</string>
 | 
			
		||||
		<key>NSCameraUsageDescription</key>
 | 
			
		||||
		<string>We need to access the camera to let you take beautiful video using this app</string>
 | 
			
		||||
		<key>NSMicrophoneUsageDescription</key>
 | 
			
		||||
		<string>We need to access the microphone to let you take beautiful video using this app</string>
 | 
			
		||||
		<key>UILaunchStoryboardName</key>
 | 
			
		||||
		<string>LaunchScreen</string>
 | 
			
		||||
		<key>UIMainStoryboardFile</key>
 | 
			
		||||
		<string>Main</string>
 | 
			
		||||
		<key>UISupportedInterfaceOrientations</key>
 | 
			
		||||
		<array>
 | 
			
		||||
			<string>UIInterfaceOrientationPortrait</string>
 | 
			
		||||
			<string>UIInterfaceOrientationLandscapeLeft</string>
 | 
			
		||||
			<string>UIInterfaceOrientationLandscapeRight</string>
 | 
			
		||||
		</array>
 | 
			
		||||
		<key>UISupportedInterfaceOrientations~ipad</key>
 | 
			
		||||
		<array>
 | 
			
		||||
			<string>UIInterfaceOrientationPortrait</string>
 | 
			
		||||
			<string>UIInterfaceOrientationPortraitUpsideDown</string>
 | 
			
		||||
			<string>UIInterfaceOrientationLandscapeLeft</string>
 | 
			
		||||
			<string>UIInterfaceOrientationLandscapeRight</string>
 | 
			
		||||
		</array>
 | 
			
		||||
		<key>UIViewControllerBasedStatusBarAppearance</key>
 | 
			
		||||
		<true/>
 | 
			
		||||
		<key>io.flutter.embedded_views_preview</key>
 | 
			
		||||
		<true/>
 | 
			
		||||
		<key>ITSAppUsesNonExemptEncryption</key>
 | 
			
		||||
		<false/>
 | 
			
		||||
		<key>CADisableMinimumFrameDurationOnPhone</key>
 | 
			
		||||
		<true/>
 | 
			
		||||
		<key>LSApplicationQueriesSchemes</key>
 | 
			
		||||
		<array>
 | 
			
		||||
			<string>https</string>
 | 
			
		||||
		</array>
 | 
			
		||||
		<key>CFBundleLocalizations</key>
 | 
			
		||||
		<array>
 | 
			
		||||
			<string>cs</string>
 | 
			
		||||
			<string>da</string>
 | 
			
		||||
			<string>de</string>
 | 
			
		||||
			<string>en</string>
 | 
			
		||||
			<string>es</string>
 | 
			
		||||
			<string>fi</string>
 | 
			
		||||
			<string>fr</string>
 | 
			
		||||
			<string>it</string>
 | 
			
		||||
			<string>ja</string>
 | 
			
		||||
			<string>ko</string>
 | 
			
		||||
			<string>nl</string>
 | 
			
		||||
			<string>pl</string>
 | 
			
		||||
			<string>pt</string>
 | 
			
		||||
			<string>ru</string>
 | 
			
		||||
			<string>sk</string>
 | 
			
		||||
			<string>zh</string>
 | 
			
		||||
		</array>
 | 
			
		||||
		<key>UIStatusBarHidden</key>
 | 
			
		||||
		<false/>
 | 
			
		||||
	</dict>
 | 
			
		||||
</plist>
 | 
			
		||||
  <dict>
 | 
			
		||||
    <key>CFBundleDevelopmentRegion</key>
 | 
			
		||||
    <string>$(DEVELOPMENT_LANGUAGE)</string>
 | 
			
		||||
    <key>CFBundleDisplayName</key>
 | 
			
		||||
    <string>Immich</string>
 | 
			
		||||
    <key>CFBundleExecutable</key>
 | 
			
		||||
    <string>$(EXECUTABLE_NAME)</string>
 | 
			
		||||
    <key>CFBundleIdentifier</key>
 | 
			
		||||
    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
 | 
			
		||||
    <key>CFBundleInfoDictionaryVersion</key>
 | 
			
		||||
    <string>6.0</string>
 | 
			
		||||
    <key>CFBundleName</key>
 | 
			
		||||
    <string>immich_mobile</string>
 | 
			
		||||
    <key>CFBundlePackageType</key>
 | 
			
		||||
    <string>APPL</string>
 | 
			
		||||
    <key>CFBundleShortVersionString</key>
 | 
			
		||||
    <string>1.46.0</string>
 | 
			
		||||
    <key>CFBundleSignature</key>
 | 
			
		||||
    <string>????</string>
 | 
			
		||||
    <key>CFBundleVersion</key>
 | 
			
		||||
    <string>85</string>
 | 
			
		||||
    <key>LSRequiresIPhoneOS</key>
 | 
			
		||||
    <true />
 | 
			
		||||
    <key>MGLMapboxMetricsEnabledSettingShownInApp</key>
 | 
			
		||||
    <true />
 | 
			
		||||
    <key>NSAppTransportSecurity</key>
 | 
			
		||||
    <dict>
 | 
			
		||||
      <key>NSAllowsArbitraryLoads</key>
 | 
			
		||||
      <true />
 | 
			
		||||
    </dict>
 | 
			
		||||
    <key>NSLocationAlwaysUsageDescription</key>
 | 
			
		||||
    <string>Enable location setting to show position of assets on map</string>
 | 
			
		||||
    <key>NSLocationWhenInUseUsageDescription</key>
 | 
			
		||||
    <string>Enable location setting to show position of assets on map</string>
 | 
			
		||||
    <key>NSPhotoLibraryUsageDescription</key>
 | 
			
		||||
    <string>We need to manage backup your photos album</string>
 | 
			
		||||
    <key>NSPhotoLibraryAddUsageDescription</key>
 | 
			
		||||
    <string>We need to manage backup your photos album</string>
 | 
			
		||||
    <key>NSCameraUsageDescription</key>
 | 
			
		||||
    <string>We need to access the camera to let you take beautiful video using this app</string>
 | 
			
		||||
    <key>NSMicrophoneUsageDescription</key>
 | 
			
		||||
    <string>We need to access the microphone to let you take beautiful video using this app</string>
 | 
			
		||||
    <key>UILaunchStoryboardName</key>
 | 
			
		||||
    <string>LaunchScreen</string>
 | 
			
		||||
    <key>UIMainStoryboardFile</key>
 | 
			
		||||
    <string>Main</string>
 | 
			
		||||
    <key>UISupportedInterfaceOrientations</key>
 | 
			
		||||
    <array>
 | 
			
		||||
      <string>UIInterfaceOrientationPortrait</string>
 | 
			
		||||
      <string>UIInterfaceOrientationLandscapeLeft</string>
 | 
			
		||||
      <string>UIInterfaceOrientationLandscapeRight</string>
 | 
			
		||||
    </array>
 | 
			
		||||
    <key>UISupportedInterfaceOrientations~ipad</key>
 | 
			
		||||
    <array>
 | 
			
		||||
      <string>UIInterfaceOrientationPortrait</string>
 | 
			
		||||
      <string>UIInterfaceOrientationPortraitUpsideDown</string>
 | 
			
		||||
      <string>UIInterfaceOrientationLandscapeLeft</string>
 | 
			
		||||
      <string>UIInterfaceOrientationLandscapeRight</string>
 | 
			
		||||
    </array>
 | 
			
		||||
    <key>UIViewControllerBasedStatusBarAppearance</key>
 | 
			
		||||
    <true />
 | 
			
		||||
    <key>io.flutter.embedded_views_preview</key>
 | 
			
		||||
    <true />
 | 
			
		||||
    <key>ITSAppUsesNonExemptEncryption</key>
 | 
			
		||||
    <false />
 | 
			
		||||
    <key>CADisableMinimumFrameDurationOnPhone</key>
 | 
			
		||||
    <true />
 | 
			
		||||
    <key>LSApplicationQueriesSchemes</key>
 | 
			
		||||
    <array>
 | 
			
		||||
      <string>https</string>
 | 
			
		||||
    </array>
 | 
			
		||||
    <key>CFBundleLocalizations</key>
 | 
			
		||||
    <array>
 | 
			
		||||
      <string>cs</string>
 | 
			
		||||
      <string>da</string>
 | 
			
		||||
      <string>de</string>
 | 
			
		||||
      <string>en</string>
 | 
			
		||||
      <string>es</string>
 | 
			
		||||
      <string>fi</string>
 | 
			
		||||
      <string>fr</string>
 | 
			
		||||
      <string>it</string>
 | 
			
		||||
      <string>ja</string>
 | 
			
		||||
      <string>ko</string>
 | 
			
		||||
      <string>nl</string>
 | 
			
		||||
      <string>pl</string>
 | 
			
		||||
      <string>pt</string>
 | 
			
		||||
      <string>ru</string>
 | 
			
		||||
      <string>sk</string>
 | 
			
		||||
      <string>zh</string>
 | 
			
		||||
    </array>
 | 
			
		||||
    <key>UIStatusBarHidden</key>
 | 
			
		||||
    <false />
 | 
			
		||||
    <key>UIApplicationSupportsIndirectInputEvents</key>
 | 
			
		||||
    <true />
 | 
			
		||||
    <key>FLTEnableImpeller</key>
 | 
			
		||||
    <true />
 | 
			
		||||
  </dict>
 | 
			
		||||
</plist>
 | 
			
		||||
@@ -91,7 +91,7 @@ class AddToAlbumBottomSheet extends HookConsumerWidget {
 | 
			
		||||
                    children: [
 | 
			
		||||
                      Text(
 | 
			
		||||
                        'Add to album',
 | 
			
		||||
                        style: Theme.of(context).textTheme.headline2,
 | 
			
		||||
                        style: Theme.of(context).textTheme.displayMedium,
 | 
			
		||||
                      ),
 | 
			
		||||
                      TextButton.icon(
 | 
			
		||||
                        icon: const Icon(Icons.add),
 | 
			
		||||
 
 | 
			
		||||
@@ -24,90 +24,97 @@ class AlbumThumbnailCard extends StatelessWidget {
 | 
			
		||||
    var isDarkMode = Theme.of(context).brightness == Brightness.dark;
 | 
			
		||||
    return LayoutBuilder(
 | 
			
		||||
      builder: (context, constraints) {
 | 
			
		||||
      var cardSize = constraints.maxWidth;
 | 
			
		||||
        var cardSize = constraints.maxWidth;
 | 
			
		||||
 | 
			
		||||
      buildEmptyThumbnail() {
 | 
			
		||||
        return Container(
 | 
			
		||||
          height: cardSize,
 | 
			
		||||
          width: cardSize,
 | 
			
		||||
          decoration: BoxDecoration(
 | 
			
		||||
            color: isDarkMode ? Colors.grey[800] : Colors.grey[200],
 | 
			
		||||
          ),
 | 
			
		||||
          child: Center(
 | 
			
		||||
            child: Icon(
 | 
			
		||||
              Icons.no_photography,
 | 
			
		||||
              size: cardSize * .15,
 | 
			
		||||
        buildEmptyThumbnail() {
 | 
			
		||||
          return Container(
 | 
			
		||||
            height: cardSize,
 | 
			
		||||
            width: cardSize,
 | 
			
		||||
            decoration: BoxDecoration(
 | 
			
		||||
              color: isDarkMode ? Colors.grey[800] : Colors.grey[200],
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
            child: Center(
 | 
			
		||||
              child: Icon(
 | 
			
		||||
                Icons.no_photography,
 | 
			
		||||
                size: cardSize * .15,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      buildAlbumThumbnail() {
 | 
			
		||||
        return CachedNetworkImage(
 | 
			
		||||
          width: cardSize,
 | 
			
		||||
          height: cardSize,
 | 
			
		||||
          fit: BoxFit.cover,
 | 
			
		||||
          fadeInDuration: const Duration(milliseconds: 200),
 | 
			
		||||
          imageUrl: getAlbumThumbnailUrl(
 | 
			
		||||
            album,
 | 
			
		||||
            type: ThumbnailFormat.JPEG,
 | 
			
		||||
          ),
 | 
			
		||||
          httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
 | 
			
		||||
          cacheKey: getAlbumThumbNailCacheKey(album, type: ThumbnailFormat.JPEG),
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
        buildAlbumThumbnail() {
 | 
			
		||||
          return CachedNetworkImage(
 | 
			
		||||
            width: cardSize,
 | 
			
		||||
            height: cardSize,
 | 
			
		||||
            fit: BoxFit.cover,
 | 
			
		||||
            fadeInDuration: const Duration(milliseconds: 200),
 | 
			
		||||
            imageUrl: getAlbumThumbnailUrl(
 | 
			
		||||
              album,
 | 
			
		||||
              type: ThumbnailFormat.JPEG,
 | 
			
		||||
            ),
 | 
			
		||||
            httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
 | 
			
		||||
            cacheKey:
 | 
			
		||||
                getAlbumThumbNailCacheKey(album, type: ThumbnailFormat.JPEG),
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      return GestureDetector(
 | 
			
		||||
        onTap: onTap,
 | 
			
		||||
        child: Padding(
 | 
			
		||||
          padding: const EdgeInsets.only(bottom: 32.0),
 | 
			
		||||
          child: Column(
 | 
			
		||||
            crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
        return GestureDetector(
 | 
			
		||||
          onTap: onTap,
 | 
			
		||||
          child: Flex(
 | 
			
		||||
            direction: Axis.vertical,
 | 
			
		||||
            children: [
 | 
			
		||||
              Expanded(
 | 
			
		||||
                child: ClipRRect(
 | 
			
		||||
                  borderRadius: BorderRadius.circular(8),
 | 
			
		||||
                  child: album.albumThumbnailAssetId == null
 | 
			
		||||
                    ? buildEmptyThumbnail()
 | 
			
		||||
                    : buildAlbumThumbnail(),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              Padding(
 | 
			
		||||
                padding: const EdgeInsets.only(top: 8.0),
 | 
			
		||||
                child: SizedBox(
 | 
			
		||||
                  width: cardSize,
 | 
			
		||||
                  child: Text(
 | 
			
		||||
                    album.name,
 | 
			
		||||
                    style: const TextStyle(
 | 
			
		||||
                      fontWeight: FontWeight.bold,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              Row(
 | 
			
		||||
                mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                children: [
 | 
			
		||||
                  Text(
 | 
			
		||||
                    album.assetCount == 1
 | 
			
		||||
                        ? 'album_thumbnail_card_item'
 | 
			
		||||
                        : 'album_thumbnail_card_items',
 | 
			
		||||
                    style: const TextStyle(
 | 
			
		||||
                      fontSize: 12,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ).tr(args: ['${album.assetCount}']),
 | 
			
		||||
                  if (album.shared)
 | 
			
		||||
                    const Text(
 | 
			
		||||
                      'album_thumbnail_card_shared',
 | 
			
		||||
                      style: TextStyle(
 | 
			
		||||
                        fontSize: 12,
 | 
			
		||||
              Flexible(
 | 
			
		||||
                child: Column(
 | 
			
		||||
                  crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                  children: [
 | 
			
		||||
                    SizedBox(
 | 
			
		||||
                      width: cardSize,
 | 
			
		||||
                      height: cardSize,
 | 
			
		||||
                      child: ClipRRect(
 | 
			
		||||
                        borderRadius: BorderRadius.circular(20),
 | 
			
		||||
                        child: album.albumThumbnailAssetId == null
 | 
			
		||||
                            ? buildEmptyThumbnail()
 | 
			
		||||
                            : buildAlbumThumbnail(),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ).tr()
 | 
			
		||||
                ],
 | 
			
		||||
              )
 | 
			
		||||
                    ),
 | 
			
		||||
                    Padding(
 | 
			
		||||
                      padding: const EdgeInsets.only(top: 8.0),
 | 
			
		||||
                      child: SizedBox(
 | 
			
		||||
                        width: cardSize,
 | 
			
		||||
                        child: Text(
 | 
			
		||||
                          album.name,
 | 
			
		||||
                          style: const TextStyle(
 | 
			
		||||
                            fontWeight: FontWeight.bold,
 | 
			
		||||
                          ),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                    Row(
 | 
			
		||||
                      mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                      children: [
 | 
			
		||||
                        Text(
 | 
			
		||||
                          album.assetCount == 1
 | 
			
		||||
                              ? 'album_thumbnail_card_item'
 | 
			
		||||
                              : 'album_thumbnail_card_items',
 | 
			
		||||
                          style: const TextStyle(
 | 
			
		||||
                            fontSize: 12,
 | 
			
		||||
                          ),
 | 
			
		||||
                        ).tr(args: ['${album.assetCount}']),
 | 
			
		||||
                        if (album.shared)
 | 
			
		||||
                          const Text(
 | 
			
		||||
                            'album_thumbnail_card_shared',
 | 
			
		||||
                            style: TextStyle(
 | 
			
		||||
                              fontSize: 12,
 | 
			
		||||
                            ),
 | 
			
		||||
                          ).tr()
 | 
			
		||||
                      ],
 | 
			
		||||
                    )
 | 
			
		||||
                  ],
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
        );
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ class AlbumTitleTextField extends ConsumerWidget {
 | 
			
		||||
      focusNode: albumTitleTextFieldFocusNode,
 | 
			
		||||
      style: TextStyle(
 | 
			
		||||
        fontSize: 28,
 | 
			
		||||
        color: Colors.grey[700],
 | 
			
		||||
        color: isDarkTheme ? Colors.grey[300] : Colors.grey[700],
 | 
			
		||||
        fontWeight: FontWeight.bold,
 | 
			
		||||
      ),
 | 
			
		||||
      controller: albumTitleController,
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ class CreateAlbumPage extends HookConsumerWidget {
 | 
			
		||||
  final List<Asset>? initialAssets;
 | 
			
		||||
 | 
			
		||||
  const CreateAlbumPage({
 | 
			
		||||
    Key? key, 
 | 
			
		||||
    Key? key,
 | 
			
		||||
    required this.isSharedAlbum,
 | 
			
		||||
    this.initialAssets,
 | 
			
		||||
  }) : super(key: key);
 | 
			
		||||
@@ -84,7 +84,7 @@ class CreateAlbumPage extends HookConsumerWidget {
 | 
			
		||||
            padding: const EdgeInsets.only(top: 200, left: 18),
 | 
			
		||||
            child: Text(
 | 
			
		||||
              'create_shared_album_page_share_add_assets',
 | 
			
		||||
              style: Theme.of(context).textTheme.headline2?.copyWith(
 | 
			
		||||
              style: Theme.of(context).textTheme.displayMedium?.copyWith(
 | 
			
		||||
                    fontSize: 12,
 | 
			
		||||
                    fontWeight: FontWeight.normal,
 | 
			
		||||
                  ),
 | 
			
		||||
@@ -214,7 +214,7 @@ class CreateAlbumPage extends HookConsumerWidget {
 | 
			
		||||
        ),
 | 
			
		||||
        title: Text(
 | 
			
		||||
          'share_create_album',
 | 
			
		||||
          style: Theme.of(context).textTheme.headline2?.copyWith(
 | 
			
		||||
          style: Theme.of(context).textTheme.displayMedium?.copyWith(
 | 
			
		||||
                color: Theme.of(context).primaryColor,
 | 
			
		||||
              ),
 | 
			
		||||
        ).tr(),
 | 
			
		||||
@@ -228,7 +228,9 @@ class CreateAlbumPage extends HookConsumerWidget {
 | 
			
		||||
                'create_shared_album_page_share'.tr(),
 | 
			
		||||
                style: TextStyle(
 | 
			
		||||
                  fontWeight: FontWeight.bold,
 | 
			
		||||
                  color: Theme.of(context).primaryColor,
 | 
			
		||||
                  color: albumTitleController.text.isEmpty
 | 
			
		||||
                      ? Theme.of(context).disabledColor
 | 
			
		||||
                      : Theme.of(context).primaryColor,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ class LibraryPage extends HookConsumerWidget {
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
    final albums = ref.watch(albumProvider);
 | 
			
		||||
    var isDarkMode = Theme.of(context).brightness == Brightness.dark;
 | 
			
		||||
 | 
			
		||||
    useEffect(
 | 
			
		||||
      () {
 | 
			
		||||
@@ -122,9 +123,12 @@ class LibraryPage extends HookConsumerWidget {
 | 
			
		||||
                child: Container(
 | 
			
		||||
                  decoration: BoxDecoration(
 | 
			
		||||
                    border: Border.all(
 | 
			
		||||
                      color: Colors.grey,
 | 
			
		||||
                      color: isDarkMode
 | 
			
		||||
                          ? const Color.fromARGB(255, 53, 53, 53)
 | 
			
		||||
                          : const Color.fromARGB(255, 203, 203, 203),
 | 
			
		||||
                    ),
 | 
			
		||||
                    borderRadius: BorderRadius.circular(8),
 | 
			
		||||
                    color: isDarkMode ? Colors.grey[900] : Colors.grey[50],
 | 
			
		||||
                    borderRadius: BorderRadius.circular(20),
 | 
			
		||||
                  ),
 | 
			
		||||
                  child: Center(
 | 
			
		||||
                    child: Icon(
 | 
			
		||||
@@ -168,25 +172,22 @@ class LibraryPage extends HookConsumerWidget {
 | 
			
		||||
              style: TextStyle(
 | 
			
		||||
                fontWeight: FontWeight.bold,
 | 
			
		||||
                fontSize: 12.0,
 | 
			
		||||
                color: Theme.of(context).brightness == Brightness.dark
 | 
			
		||||
                    ? Colors.white
 | 
			
		||||
                    : Colors.black,
 | 
			
		||||
                color: isDarkMode ? Colors.white : Colors.black,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          style: OutlinedButton.styleFrom(
 | 
			
		||||
            padding: const EdgeInsets.all(12),
 | 
			
		||||
            backgroundColor: isDarkMode ? Colors.grey[900] : Colors.grey[50],
 | 
			
		||||
            side: BorderSide(
 | 
			
		||||
              color: Theme.of(context).brightness == Brightness.dark
 | 
			
		||||
                  ? Colors.grey[600]!
 | 
			
		||||
                  : Colors.grey[300]!,
 | 
			
		||||
              color: isDarkMode ? Colors.grey[800]! : Colors.grey[300]!,
 | 
			
		||||
            ),
 | 
			
		||||
            alignment: Alignment.centerLeft,
 | 
			
		||||
            shape: RoundedRectangleBorder(
 | 
			
		||||
              borderRadius: BorderRadius.circular(6.0),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          icon: Icon(icon, color: Theme.of(context).primaryColor),
 | 
			
		||||
          icon: Icon(
 | 
			
		||||
            icon,
 | 
			
		||||
            color: Theme.of(context).primaryColor,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
@@ -253,7 +254,7 @@ class LibraryPage extends HookConsumerWidget {
 | 
			
		||||
              delegate: SliverChildBuilderDelegate(
 | 
			
		||||
                childCount: sorted.length + 1,
 | 
			
		||||
                (context, index) {
 | 
			
		||||
                  if (index  == 0) {
 | 
			
		||||
                  if (index == 0) {
 | 
			
		||||
                    return buildCreateAlbumButton();
 | 
			
		||||
                  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -77,13 +77,13 @@ class SharingPage extends HookConsumerWidget {
 | 
			
		||||
          child: Card(
 | 
			
		||||
            elevation: 0,
 | 
			
		||||
            shape: RoundedRectangleBorder(
 | 
			
		||||
              borderRadius: BorderRadius.circular(10), // if you need this
 | 
			
		||||
              borderRadius: BorderRadius.circular(20),
 | 
			
		||||
              side: const BorderSide(
 | 
			
		||||
                color: Colors.grey,
 | 
			
		||||
                width: 1,
 | 
			
		||||
                width: 0.5,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            color: Colors.transparent,
 | 
			
		||||
            // color: Colors.transparent,
 | 
			
		||||
            child: Padding(
 | 
			
		||||
              padding: const EdgeInsets.all(18.0),
 | 
			
		||||
              child: Column(
 | 
			
		||||
@@ -92,7 +92,7 @@ class SharingPage extends HookConsumerWidget {
 | 
			
		||||
                  Padding(
 | 
			
		||||
                    padding: const EdgeInsets.only(left: 5.0, bottom: 5),
 | 
			
		||||
                    child: Icon(
 | 
			
		||||
                      Icons.offline_share_outlined,
 | 
			
		||||
                      Icons.insert_photo_rounded,
 | 
			
		||||
                      size: 50,
 | 
			
		||||
                      color: Theme.of(context).primaryColor,
 | 
			
		||||
                    ),
 | 
			
		||||
@@ -101,7 +101,7 @@ class SharingPage extends HookConsumerWidget {
 | 
			
		||||
                    padding: const EdgeInsets.all(8.0),
 | 
			
		||||
                    child: Text(
 | 
			
		||||
                      'sharing_page_empty_list',
 | 
			
		||||
                      style: Theme.of(context).textTheme.headline3,
 | 
			
		||||
                      style: Theme.of(context).textTheme.displaySmall,
 | 
			
		||||
                    ).tr(),
 | 
			
		||||
                  ),
 | 
			
		||||
                  Padding(
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ 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';
 | 
			
		||||
import 'package:immich_mobile/modules/favorite/providers/favorite_provider.dart';
 | 
			
		||||
import 'package:immich_mobile/shared/services/asset.service.dart';
 | 
			
		||||
import 'package:immich_mobile/modules/home/ui/delete_diaglog.dart';
 | 
			
		||||
import 'package:immich_mobile/modules/home/ui/delete_dialog.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/shared/ui/photo_view/photo_view_gallery.dart';
 | 
			
		||||
@@ -213,7 +213,7 @@ class GalleryViewerPage extends HookConsumerWidget {
 | 
			
		||||
 | 
			
		||||
    void handleSwipeUpDown(DragUpdateDetails details) {
 | 
			
		||||
      int sensitivity = 15;
 | 
			
		||||
      int dxThreshhold = 50;
 | 
			
		||||
      int dxThreshold = 50;
 | 
			
		||||
 | 
			
		||||
      if (isZoomed.value) {
 | 
			
		||||
        return;
 | 
			
		||||
@@ -222,7 +222,7 @@ class GalleryViewerPage extends HookConsumerWidget {
 | 
			
		||||
      // Check for delta from initial down point
 | 
			
		||||
      final d = details.localPosition - localPosition;
 | 
			
		||||
      // If the magnitude of the dx swipe is large, we probably didn't mean to go down
 | 
			
		||||
      if (d.dx.abs() > dxThreshhold) {
 | 
			
		||||
      if (d.dx.abs() > dxThreshold) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@@ -247,8 +247,8 @@ class GalleryViewerPage extends HookConsumerWidget {
 | 
			
		||||
            isPlayingMotionVideo: isPlayingMotionVideo.value,
 | 
			
		||||
            asset: assetList[indexOfAsset.value],
 | 
			
		||||
            isFavorite: ref.watch(favoriteProvider).contains(
 | 
			
		||||
              assetList[indexOfAsset.value].id,
 | 
			
		||||
            ),
 | 
			
		||||
                  assetList[indexOfAsset.value].id,
 | 
			
		||||
                ),
 | 
			
		||||
            onMoreInfoPressed: () {
 | 
			
		||||
              showInfo();
 | 
			
		||||
            },
 | 
			
		||||
@@ -314,7 +314,7 @@ class GalleryViewerPage extends HookConsumerWidget {
 | 
			
		||||
                ? (context, event) {
 | 
			
		||||
                    final asset = assetList[indexOfAsset.value];
 | 
			
		||||
                    if (!asset.isLocal) {
 | 
			
		||||
                      // Use the WEBP Thumbnail as a placeholder for the JPEG thumbnail to acheive
 | 
			
		||||
                      // Use the WEBP Thumbnail as a placeholder for the JPEG thumbnail to achieve
 | 
			
		||||
                      // Three-Stage Loading (WEBP -> JPEG -> Original)
 | 
			
		||||
                      final webPThumbnail = CachedNetworkImage(
 | 
			
		||||
                        imageUrl: getThumbnailUrl(
 | 
			
		||||
 
 | 
			
		||||
@@ -81,10 +81,6 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
 | 
			
		||||
          child: GestureDetector(
 | 
			
		||||
            onTap: removeSelection,
 | 
			
		||||
            child: Chip(
 | 
			
		||||
              visualDensity: VisualDensity.compact,
 | 
			
		||||
              shape: RoundedRectangleBorder(
 | 
			
		||||
                borderRadius: BorderRadius.circular(10),
 | 
			
		||||
              ),
 | 
			
		||||
              label: Text(
 | 
			
		||||
                album.name,
 | 
			
		||||
                style: TextStyle(
 | 
			
		||||
@@ -119,10 +115,6 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
 | 
			
		||||
          child: Padding(
 | 
			
		||||
            padding: const EdgeInsets.only(right: 8.0),
 | 
			
		||||
            child: Chip(
 | 
			
		||||
              visualDensity: VisualDensity.compact,
 | 
			
		||||
              shape: RoundedRectangleBorder(
 | 
			
		||||
                borderRadius: BorderRadius.circular(10),
 | 
			
		||||
              ),
 | 
			
		||||
              label: Text(
 | 
			
		||||
                album.name,
 | 
			
		||||
                style: TextStyle(
 | 
			
		||||
 
 | 
			
		||||
@@ -92,11 +92,10 @@ class ImmichAssetGridState extends State<ImmichAssetGrid> {
 | 
			
		||||
    RenderAssetGridRow row,
 | 
			
		||||
    bool scrolling,
 | 
			
		||||
  ) {
 | 
			
		||||
 | 
			
		||||
    return LayoutBuilder(
 | 
			
		||||
      builder: (context, constraints) {
 | 
			
		||||
        final size = constraints.maxWidth / widget.assetsPerRow -
 | 
			
		||||
          widget.margin * (widget.assetsPerRow - 1) / widget.assetsPerRow;
 | 
			
		||||
            widget.margin * (widget.assetsPerRow - 1) / widget.assetsPerRow;
 | 
			
		||||
        return Row(
 | 
			
		||||
          key: Key("asset-row-${row.assets.first.id}"),
 | 
			
		||||
          children: row.assets.mapIndexed((int index, Asset asset) {
 | 
			
		||||
@@ -141,7 +140,7 @@ class ImmichAssetGridState extends State<ImmichAssetGrid> {
 | 
			
		||||
        style: TextStyle(
 | 
			
		||||
          fontSize: 26,
 | 
			
		||||
          fontWeight: FontWeight.bold,
 | 
			
		||||
          color: Theme.of(context).textTheme.headline1?.color,
 | 
			
		||||
          color: Theme.of(context).textTheme.displayLarge?.color,
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ class MonthlyTitleText extends StatelessWidget {
 | 
			
		||||
          style: TextStyle(
 | 
			
		||||
            fontSize: 26,
 | 
			
		||||
            fontWeight: FontWeight.bold,
 | 
			
		||||
            color: Theme.of(context).textTheme.headline1?.color,
 | 
			
		||||
            color: Theme.of(context).textTheme.displayLarge?.color,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,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/album/ui/add_to_album_sliverlist.dart';
 | 
			
		||||
import 'package:immich_mobile/modules/home/ui/delete_diaglog.dart';
 | 
			
		||||
import 'package:immich_mobile/modules/home/ui/delete_dialog.dart';
 | 
			
		||||
import 'package:immich_mobile/shared/ui/drag_sheet.dart';
 | 
			
		||||
import 'package:immich_mobile/shared/models/album.dart';
 | 
			
		||||
 | 
			
		||||
@@ -29,6 +29,8 @@ class ControlBottomAppBar extends ConsumerWidget {
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
    var isDarkMode = Theme.of(context).brightness == Brightness.dark;
 | 
			
		||||
 | 
			
		||||
    Widget renderActionButtons() {
 | 
			
		||||
      return Row(
 | 
			
		||||
        children: [
 | 
			
		||||
@@ -60,7 +62,6 @@ class ControlBottomAppBar extends ConsumerWidget {
 | 
			
		||||
              );
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
 | 
			
		||||
        ],
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
@@ -75,7 +76,9 @@ class ControlBottomAppBar extends ConsumerWidget {
 | 
			
		||||
        ScrollController scrollController,
 | 
			
		||||
      ) {
 | 
			
		||||
        return Card(
 | 
			
		||||
          elevation: 12.0,
 | 
			
		||||
          color: isDarkMode ? Colors.grey[900] : Colors.grey[100],
 | 
			
		||||
          surfaceTintColor: Colors.transparent,
 | 
			
		||||
          elevation: 18.0,
 | 
			
		||||
          shape: const RoundedRectangleBorder(
 | 
			
		||||
            borderRadius: BorderRadius.only(
 | 
			
		||||
              topLeft: Radius.circular(12),
 | 
			
		||||
@@ -83,45 +86,37 @@ class ControlBottomAppBar extends ConsumerWidget {
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          margin: const EdgeInsets.all(0),
 | 
			
		||||
          child: Container(
 | 
			
		||||
            decoration: const BoxDecoration(
 | 
			
		||||
              borderRadius: BorderRadius.only(
 | 
			
		||||
                topLeft: Radius.circular(12),
 | 
			
		||||
                topRight: Radius.circular(12),
 | 
			
		||||
          child: CustomScrollView(
 | 
			
		||||
            controller: scrollController,
 | 
			
		||||
            slivers: [
 | 
			
		||||
              SliverToBoxAdapter(
 | 
			
		||||
                child: Column(
 | 
			
		||||
                  children: <Widget>[
 | 
			
		||||
                    const SizedBox(height: 12),
 | 
			
		||||
                    const CustomDraggingHandle(),
 | 
			
		||||
                    const SizedBox(height: 12),
 | 
			
		||||
                    renderActionButtons(),
 | 
			
		||||
                    const Divider(
 | 
			
		||||
                      indent: 16,
 | 
			
		||||
                      endIndent: 16,
 | 
			
		||||
                      thickness: 1,
 | 
			
		||||
                    ),
 | 
			
		||||
                    AddToAlbumTitleRow(onCreateNewAlbum: onCreateNewAlbum),
 | 
			
		||||
                  ],
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            child: CustomScrollView(
 | 
			
		||||
              controller: scrollController,
 | 
			
		||||
              slivers: [
 | 
			
		||||
                SliverToBoxAdapter(
 | 
			
		||||
                  child: Column(
 | 
			
		||||
                    children: <Widget>[
 | 
			
		||||
                      const SizedBox(height: 12),
 | 
			
		||||
                      const CustomDraggingHandle(),
 | 
			
		||||
                      const SizedBox(height: 12),
 | 
			
		||||
                      renderActionButtons(),
 | 
			
		||||
                      const Divider(
 | 
			
		||||
                        indent: 16,
 | 
			
		||||
                        endIndent: 16,
 | 
			
		||||
                        thickness: 1,
 | 
			
		||||
                      ),
 | 
			
		||||
                      AddToAlbumTitleRow(onCreateNewAlbum: onCreateNewAlbum),
 | 
			
		||||
                    ],
 | 
			
		||||
                  ),
 | 
			
		||||
              SliverPadding(
 | 
			
		||||
                padding: const EdgeInsets.symmetric(horizontal: 16),
 | 
			
		||||
                sliver: AddToAlbumSliverList(
 | 
			
		||||
                  albums: albums,
 | 
			
		||||
                  sharedAlbums: sharedAlbums,
 | 
			
		||||
                  onAddToAlbum: onAddToAlbum,
 | 
			
		||||
                ),
 | 
			
		||||
                SliverPadding(
 | 
			
		||||
                  padding: const EdgeInsets.symmetric(horizontal: 16),
 | 
			
		||||
                  sliver: AddToAlbumSliverList(
 | 
			
		||||
                    albums: albums,
 | 
			
		||||
                    sharedAlbums: sharedAlbums,
 | 
			
		||||
                    onAddToAlbum: onAddToAlbum,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                const SliverToBoxAdapter(
 | 
			
		||||
                  child: SizedBox(height: 200),
 | 
			
		||||
                )
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
              ),
 | 
			
		||||
              const SliverToBoxAdapter(
 | 
			
		||||
                child: SizedBox(height: 200),
 | 
			
		||||
              )
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
      },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
import 'package:auto_route/auto_route.dart';
 | 
			
		||||
import 'package:badges/badges.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
 | 
			
		||||
@@ -29,7 +28,6 @@ class HomePageAppBar extends ConsumerWidget with PreferredSizeWidget {
 | 
			
		||||
    final ServerInfoState serverInfoState = ref.watch(serverInfoProvider);
 | 
			
		||||
 | 
			
		||||
    return AppBar(
 | 
			
		||||
      centerTitle: true,
 | 
			
		||||
      backgroundColor: Theme.of(context).appBarTheme.backgroundColor,
 | 
			
		||||
      shape: const RoundedRectangleBorder(
 | 
			
		||||
        borderRadius: BorderRadius.all(
 | 
			
		||||
@@ -44,10 +42,9 @@ class HomePageAppBar extends ConsumerWidget with PreferredSizeWidget {
 | 
			
		||||
                top: 5,
 | 
			
		||||
                child: IconButton(
 | 
			
		||||
                  splashRadius: 25,
 | 
			
		||||
                  icon: Icon(
 | 
			
		||||
                  icon: const Icon(
 | 
			
		||||
                    Icons.face_outlined,
 | 
			
		||||
                    size: 30,
 | 
			
		||||
                    color: Theme.of(context).primaryColor,
 | 
			
		||||
                  ),
 | 
			
		||||
                  onPressed: () {
 | 
			
		||||
                    Scaffold.of(context).openDrawer();
 | 
			
		||||
@@ -112,16 +109,13 @@ class HomePageAppBar extends ConsumerWidget with PreferredSizeWidget {
 | 
			
		||||
              splashRadius: 25,
 | 
			
		||||
              iconSize: 30,
 | 
			
		||||
              icon: isEnableAutoBackup
 | 
			
		||||
                  ? Icon(
 | 
			
		||||
                  ? const Icon(
 | 
			
		||||
                      Icons.backup_rounded,
 | 
			
		||||
                      color: Theme.of(context).primaryColor,
 | 
			
		||||
                    )
 | 
			
		||||
                  : Badge(
 | 
			
		||||
                      padding: const EdgeInsets.all(4),
 | 
			
		||||
                      elevation: 3,
 | 
			
		||||
                      position: BadgePosition.bottomEnd(bottom: -4, end: -4),
 | 
			
		||||
                      badgeColor: Colors.white,
 | 
			
		||||
                      badgeContent: const Icon(
 | 
			
		||||
                      backgroundColor: Colors.white,
 | 
			
		||||
                      label: const Icon(
 | 
			
		||||
                        Icons.cloud_off_rounded,
 | 
			
		||||
                        size: 8,
 | 
			
		||||
                        color: Colors.indigo,
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ class ProfileDrawer extends HookConsumerWidget {
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
    buildSignoutButton() {
 | 
			
		||||
    buildSignOutButton() {
 | 
			
		||||
      return ListTile(
 | 
			
		||||
        horizontalTitleGap: 0,
 | 
			
		||||
        leading: SizedBox(
 | 
			
		||||
@@ -95,6 +95,9 @@ class ProfileDrawer extends HookConsumerWidget {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return Drawer(
 | 
			
		||||
      shape: const RoundedRectangleBorder(
 | 
			
		||||
        borderRadius: BorderRadius.zero,
 | 
			
		||||
      ),
 | 
			
		||||
      child: Column(
 | 
			
		||||
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
			
		||||
        children: [
 | 
			
		||||
@@ -105,7 +108,7 @@ class ProfileDrawer extends HookConsumerWidget {
 | 
			
		||||
              const ProfileDrawerHeader(),
 | 
			
		||||
              buildSettingButton(),
 | 
			
		||||
              buildAppLogButton(),
 | 
			
		||||
              buildSignoutButton(),
 | 
			
		||||
              buildSignOutButton(),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
          const ServerInfoBox()
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ class ProfileDrawerHeader extends HookConsumerWidget {
 | 
			
		||||
    AuthenticationState authState = ref.watch(authenticationProvider);
 | 
			
		||||
    final uploadProfileImageStatus =
 | 
			
		||||
        ref.watch(uploadProfileImageProvider).status;
 | 
			
		||||
    var dummmy = Random().nextInt(1024);
 | 
			
		||||
    var dummy = Random().nextInt(1024);
 | 
			
		||||
    final isDarkMode = Theme.of(context).brightness == Brightness.dark;
 | 
			
		||||
 | 
			
		||||
    buildUserProfileImage() {
 | 
			
		||||
@@ -39,7 +39,7 @@ class ProfileDrawerHeader extends HookConsumerWidget {
 | 
			
		||||
          return CircleAvatar(
 | 
			
		||||
            radius: 35,
 | 
			
		||||
            backgroundImage: NetworkImage(
 | 
			
		||||
              '$endpoint/user/profile-image/${authState.userId}?d=${dummmy++}',
 | 
			
		||||
              '$endpoint/user/profile-image/${authState.userId}?d=${dummy++}',
 | 
			
		||||
            ),
 | 
			
		||||
            backgroundColor: Colors.transparent,
 | 
			
		||||
          );
 | 
			
		||||
@@ -56,7 +56,7 @@ class ProfileDrawerHeader extends HookConsumerWidget {
 | 
			
		||||
        return CircleAvatar(
 | 
			
		||||
          radius: 35,
 | 
			
		||||
          backgroundImage: NetworkImage(
 | 
			
		||||
            '$endpoint/user/profile-image/${authState.userId}?d=${dummmy++}',
 | 
			
		||||
            '$endpoint/user/profile-image/${authState.userId}?d=${dummy++}',
 | 
			
		||||
          ),
 | 
			
		||||
          backgroundColor: Colors.transparent,
 | 
			
		||||
        );
 | 
			
		||||
 
 | 
			
		||||
@@ -53,6 +53,9 @@ class SearchBar extends HookConsumerWidget with PreferredSizeWidget {
 | 
			
		||||
        },
 | 
			
		||||
        decoration: InputDecoration(
 | 
			
		||||
          hintText: 'search_bar_hint'.tr(),
 | 
			
		||||
          hintStyle: Theme.of(context).textTheme.titleSmall?.copyWith(
 | 
			
		||||
                color: Theme.of(context).colorScheme.onSurface.withOpacity(0.5),
 | 
			
		||||
              ),
 | 
			
		||||
          enabledBorder: const UnderlineInputBorder(
 | 
			
		||||
            borderSide: BorderSide(color: Colors.transparent),
 | 
			
		||||
          ),
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,6 @@ class TabControllerPage extends ConsumerWidget {
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
 | 
			
		||||
    navigationRail(TabsRouter tabsRouter) {
 | 
			
		||||
      return NavigationRail(
 | 
			
		||||
        labelType: NavigationRailLabelType.all,
 | 
			
		||||
@@ -35,32 +34,33 @@ class TabControllerPage extends ConsumerWidget {
 | 
			
		||||
              right: 4,
 | 
			
		||||
              bottom: 4,
 | 
			
		||||
            ),
 | 
			
		||||
            icon: const Icon(Icons.photo_outlined), 
 | 
			
		||||
            icon: const Icon(Icons.photo_outlined),
 | 
			
		||||
            selectedIcon: const Icon(Icons.photo),
 | 
			
		||||
            label: const Text('tab_controller_nav_photos').tr(),
 | 
			
		||||
          ),
 | 
			
		||||
          NavigationRailDestination(
 | 
			
		||||
            padding: const EdgeInsets.all(4),
 | 
			
		||||
            icon: const Icon(Icons.search_rounded), 
 | 
			
		||||
            selectedIcon: const Icon(Icons.search), 
 | 
			
		||||
            icon: const Icon(Icons.search_rounded),
 | 
			
		||||
            selectedIcon: const Icon(Icons.search),
 | 
			
		||||
            label: const Text('tab_controller_nav_search').tr(),
 | 
			
		||||
          ),
 | 
			
		||||
          NavigationRailDestination(
 | 
			
		||||
            padding: const EdgeInsets.all(4),
 | 
			
		||||
            icon: const Icon(Icons.share_rounded), 
 | 
			
		||||
            selectedIcon: const Icon(Icons.share), 
 | 
			
		||||
            icon: const Icon(Icons.share_rounded),
 | 
			
		||||
            selectedIcon: const Icon(Icons.share),
 | 
			
		||||
            label: const Text('tab_controller_nav_sharing').tr(),
 | 
			
		||||
          ),
 | 
			
		||||
          NavigationRailDestination(
 | 
			
		||||
            padding: const EdgeInsets.all(4),
 | 
			
		||||
            icon: const Icon(Icons.photo_album_outlined), 
 | 
			
		||||
            selectedIcon: const Icon(Icons.photo_album), 
 | 
			
		||||
            icon: const Icon(Icons.photo_album_outlined),
 | 
			
		||||
            selectedIcon: const Icon(Icons.photo_album),
 | 
			
		||||
            label: const Text('tab_controller_nav_library').tr(),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ignore: unused_element
 | 
			
		||||
    bottomNavigationBar(TabsRouter tabsRouter) {
 | 
			
		||||
      return BottomNavigationBar(
 | 
			
		||||
        selectedLabelStyle: const TextStyle(
 | 
			
		||||
@@ -101,6 +101,58 @@ class TabControllerPage extends ConsumerWidget {
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    experimentalNavigationBar(TabsRouter tabsRouter) {
 | 
			
		||||
      return NavigationBar(
 | 
			
		||||
        selectedIndex: tabsRouter.activeIndex,
 | 
			
		||||
        onDestinationSelected: (index) {
 | 
			
		||||
          HapticFeedback.selectionClick();
 | 
			
		||||
          tabsRouter.setActiveIndex(index);
 | 
			
		||||
        },
 | 
			
		||||
        destinations: [
 | 
			
		||||
          NavigationDestination(
 | 
			
		||||
            label: 'tab_controller_nav_photos'.tr(),
 | 
			
		||||
            icon: const Icon(
 | 
			
		||||
              Icons.photo_outlined,
 | 
			
		||||
            ),
 | 
			
		||||
            selectedIcon: Icon(
 | 
			
		||||
              Icons.photo,
 | 
			
		||||
              color: Theme.of(context).primaryColor,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          NavigationDestination(
 | 
			
		||||
            label: 'tab_controller_nav_search'.tr(),
 | 
			
		||||
            icon: const Icon(
 | 
			
		||||
              Icons.search_rounded,
 | 
			
		||||
            ),
 | 
			
		||||
            selectedIcon: Icon(
 | 
			
		||||
              Icons.search,
 | 
			
		||||
              color: Theme.of(context).primaryColor,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          NavigationDestination(
 | 
			
		||||
            label: 'tab_controller_nav_sharing'.tr(),
 | 
			
		||||
            icon: const Icon(
 | 
			
		||||
              Icons.group_outlined,
 | 
			
		||||
            ),
 | 
			
		||||
            selectedIcon: Icon(
 | 
			
		||||
              Icons.group,
 | 
			
		||||
              color: Theme.of(context).primaryColor,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          NavigationDestination(
 | 
			
		||||
            label: 'tab_controller_nav_library'.tr(),
 | 
			
		||||
            icon: const Icon(
 | 
			
		||||
              Icons.photo_album_outlined,
 | 
			
		||||
            ),
 | 
			
		||||
            selectedIcon: Icon(
 | 
			
		||||
              Icons.photo_album_rounded,
 | 
			
		||||
              color: Theme.of(context).primaryColor,
 | 
			
		||||
            ),
 | 
			
		||||
          )
 | 
			
		||||
        ],
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final multiselectEnabled = ref.watch(multiselectProvider);
 | 
			
		||||
    return AutoTabsRouter(
 | 
			
		||||
      routes: [
 | 
			
		||||
@@ -116,7 +168,7 @@ class TabControllerPage extends ConsumerWidget {
 | 
			
		||||
            bool atHomeTab = tabsRouter.activeIndex == 0;
 | 
			
		||||
            if (!atHomeTab) {
 | 
			
		||||
              tabsRouter.setActiveIndex(0);
 | 
			
		||||
            } 
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return atHomeTab;
 | 
			
		||||
          },
 | 
			
		||||
@@ -127,7 +179,7 @@ class TabControllerPage extends ConsumerWidget {
 | 
			
		||||
              final Widget body;
 | 
			
		||||
              if (constraints.maxWidth < medium) {
 | 
			
		||||
                // Normal phone width
 | 
			
		||||
                bottom = bottomNavigationBar(tabsRouter);
 | 
			
		||||
                bottom = experimentalNavigationBar(tabsRouter);
 | 
			
		||||
                body = FadeTransition(
 | 
			
		||||
                  opacity: animation,
 | 
			
		||||
                  child: child,
 | 
			
		||||
@@ -146,13 +198,13 @@ class TabControllerPage extends ConsumerWidget {
 | 
			
		||||
                    ),
 | 
			
		||||
                  ],
 | 
			
		||||
                );
 | 
			
		||||
              }              return Scaffold(
 | 
			
		||||
               body: body,
 | 
			
		||||
               bottomNavigationBar: multiselectEnabled
 | 
			
		||||
                  ? null
 | 
			
		||||
                  : bottom,
 | 
			
		||||
            );
 | 
			
		||||
          },),
 | 
			
		||||
              }
 | 
			
		||||
              return Scaffold(
 | 
			
		||||
                body: body,
 | 
			
		||||
                bottomNavigationBar: multiselectEnabled ? null : bottom,
 | 
			
		||||
              );
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,83 @@ final immichThemeProvider = StateProvider<ThemeMode>((ref) {
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
ThemeData base = ThemeData(
 | 
			
		||||
  chipTheme: const ChipThemeData(
 | 
			
		||||
    side: BorderSide.none,
 | 
			
		||||
  ),
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
ThemeData immichLightTheme = ThemeData(
 | 
			
		||||
  useMaterial3: true,
 | 
			
		||||
  brightness: Brightness.light,
 | 
			
		||||
  primarySwatch: Colors.indigo,
 | 
			
		||||
  primaryColor: Colors.indigo,
 | 
			
		||||
  hintColor: Colors.indigo,
 | 
			
		||||
  fontFamily: 'WorkSans',
 | 
			
		||||
  scaffoldBackgroundColor: immichBackgroundColor,
 | 
			
		||||
  snackBarTheme: const SnackBarThemeData(
 | 
			
		||||
    contentTextStyle: TextStyle(fontFamily: 'WorkSans'),
 | 
			
		||||
  ),
 | 
			
		||||
  appBarTheme: AppBarTheme(
 | 
			
		||||
    titleTextStyle: const TextStyle(
 | 
			
		||||
      fontFamily: 'WorkSans',
 | 
			
		||||
      color: Colors.indigo,
 | 
			
		||||
    ),
 | 
			
		||||
    backgroundColor: immichBackgroundColor,
 | 
			
		||||
    foregroundColor: Colors.indigo,
 | 
			
		||||
    elevation: 0,
 | 
			
		||||
    scrolledUnderElevation: 0,
 | 
			
		||||
    centerTitle: true,
 | 
			
		||||
  ),
 | 
			
		||||
  bottomNavigationBarTheme: BottomNavigationBarThemeData(
 | 
			
		||||
    type: BottomNavigationBarType.fixed,
 | 
			
		||||
    backgroundColor: immichBackgroundColor,
 | 
			
		||||
    selectedItemColor: Colors.indigo,
 | 
			
		||||
  ),
 | 
			
		||||
  cardTheme: const CardTheme(
 | 
			
		||||
    surfaceTintColor: Colors.transparent,
 | 
			
		||||
  ),
 | 
			
		||||
  drawerTheme: DrawerThemeData(
 | 
			
		||||
    backgroundColor: immichBackgroundColor,
 | 
			
		||||
  ),
 | 
			
		||||
  textTheme: const TextTheme(
 | 
			
		||||
    displayLarge: TextStyle(
 | 
			
		||||
      fontSize: 26,
 | 
			
		||||
      fontWeight: FontWeight.bold,
 | 
			
		||||
      color: Colors.indigo,
 | 
			
		||||
    ),
 | 
			
		||||
    displayMedium: TextStyle(
 | 
			
		||||
      fontSize: 14,
 | 
			
		||||
      fontWeight: FontWeight.bold,
 | 
			
		||||
      color: Colors.black87,
 | 
			
		||||
    ),
 | 
			
		||||
    displaySmall: TextStyle(
 | 
			
		||||
      fontSize: 12,
 | 
			
		||||
      fontWeight: FontWeight.bold,
 | 
			
		||||
      color: Colors.indigo,
 | 
			
		||||
    ),
 | 
			
		||||
  ),
 | 
			
		||||
  elevatedButtonTheme: ElevatedButtonThemeData(
 | 
			
		||||
    style: ElevatedButton.styleFrom(
 | 
			
		||||
      backgroundColor: Colors.indigo,
 | 
			
		||||
      foregroundColor: Colors.white,
 | 
			
		||||
    ),
 | 
			
		||||
  ),
 | 
			
		||||
  chipTheme: base.chipTheme,
 | 
			
		||||
  popupMenuTheme: const PopupMenuThemeData(
 | 
			
		||||
    shape: RoundedRectangleBorder(
 | 
			
		||||
      borderRadius: BorderRadius.all(Radius.circular(10)),
 | 
			
		||||
    ),
 | 
			
		||||
    surfaceTintColor: Colors.transparent,
 | 
			
		||||
    color: Colors.white,
 | 
			
		||||
  ),
 | 
			
		||||
  navigationBarTheme: NavigationBarThemeData(
 | 
			
		||||
    indicatorColor: Colors.indigo.withOpacity(0.15),
 | 
			
		||||
    backgroundColor: immichBackgroundColor,
 | 
			
		||||
    surfaceTintColor: Colors.transparent,
 | 
			
		||||
  ),
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
ThemeData immichDarkTheme = ThemeData(
 | 
			
		||||
  useMaterial3: true,
 | 
			
		||||
  brightness: Brightness.dark,
 | 
			
		||||
@@ -43,7 +120,8 @@ ThemeData immichDarkTheme = ThemeData(
 | 
			
		||||
    ),
 | 
			
		||||
    backgroundColor: const Color.fromARGB(255, 32, 33, 35),
 | 
			
		||||
    foregroundColor: immichDarkThemePrimaryColor,
 | 
			
		||||
    elevation: 1,
 | 
			
		||||
    elevation: 0,
 | 
			
		||||
    scrolledUnderElevation: 0,
 | 
			
		||||
    centerTitle: true,
 | 
			
		||||
  ),
 | 
			
		||||
  bottomNavigationBarTheme: BottomNavigationBarThemeData(
 | 
			
		||||
@@ -56,17 +134,17 @@ ThemeData immichDarkTheme = ThemeData(
 | 
			
		||||
    scrimColor: Colors.white.withOpacity(0.1),
 | 
			
		||||
  ),
 | 
			
		||||
  textTheme: TextTheme(
 | 
			
		||||
    headline1: const TextStyle(
 | 
			
		||||
    displayLarge: const TextStyle(
 | 
			
		||||
      fontSize: 26,
 | 
			
		||||
      fontWeight: FontWeight.bold,
 | 
			
		||||
      color: Color.fromARGB(255, 255, 255, 255),
 | 
			
		||||
    ),
 | 
			
		||||
    headline2: const TextStyle(
 | 
			
		||||
    displayMedium: const TextStyle(
 | 
			
		||||
      fontSize: 14,
 | 
			
		||||
      fontWeight: FontWeight.bold,
 | 
			
		||||
      color: Color.fromARGB(255, 255, 255, 255),
 | 
			
		||||
    ),
 | 
			
		||||
    headline3: TextStyle(
 | 
			
		||||
    displaySmall: TextStyle(
 | 
			
		||||
      fontSize: 12,
 | 
			
		||||
      fontWeight: FontWeight.bold,
 | 
			
		||||
      color: immichDarkThemePrimaryColor,
 | 
			
		||||
@@ -79,57 +157,19 @@ ThemeData immichDarkTheme = ThemeData(
 | 
			
		||||
      backgroundColor: immichDarkThemePrimaryColor,
 | 
			
		||||
    ),
 | 
			
		||||
  ),
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
ThemeData immichLightTheme = ThemeData(
 | 
			
		||||
  useMaterial3: true,
 | 
			
		||||
  brightness: Brightness.light,
 | 
			
		||||
  primarySwatch: Colors.indigo,
 | 
			
		||||
  hintColor: Colors.indigo,
 | 
			
		||||
  fontFamily: 'WorkSans',
 | 
			
		||||
  scaffoldBackgroundColor: immichBackgroundColor,
 | 
			
		||||
  snackBarTheme: const SnackBarThemeData(
 | 
			
		||||
    contentTextStyle: TextStyle(fontFamily: 'WorkSans'),
 | 
			
		||||
  ),
 | 
			
		||||
  appBarTheme: AppBarTheme(
 | 
			
		||||
    titleTextStyle: const TextStyle(
 | 
			
		||||
      fontFamily: 'WorkSans',
 | 
			
		||||
      color: Colors.indigo,
 | 
			
		||||
  chipTheme: base.chipTheme,
 | 
			
		||||
  popupMenuTheme: const PopupMenuThemeData(
 | 
			
		||||
    shape: RoundedRectangleBorder(
 | 
			
		||||
      borderRadius: BorderRadius.all(Radius.circular(10)),
 | 
			
		||||
    ),
 | 
			
		||||
    backgroundColor: immichBackgroundColor,
 | 
			
		||||
    foregroundColor: Colors.indigo,
 | 
			
		||||
    elevation: 1,
 | 
			
		||||
    centerTitle: true,
 | 
			
		||||
    surfaceTintColor: Colors.transparent,
 | 
			
		||||
  ),
 | 
			
		||||
  bottomNavigationBarTheme: BottomNavigationBarThemeData(
 | 
			
		||||
    type: BottomNavigationBarType.fixed,
 | 
			
		||||
    backgroundColor: immichBackgroundColor,
 | 
			
		||||
    selectedItemColor: Colors.indigo,
 | 
			
		||||
  ),
 | 
			
		||||
  drawerTheme: DrawerThemeData(
 | 
			
		||||
    backgroundColor: immichBackgroundColor,
 | 
			
		||||
  ),
 | 
			
		||||
  textTheme: const TextTheme(
 | 
			
		||||
    headline1: TextStyle(
 | 
			
		||||
      fontSize: 26,
 | 
			
		||||
      fontWeight: FontWeight.bold,
 | 
			
		||||
      color: Colors.indigo,
 | 
			
		||||
    ),
 | 
			
		||||
    headline2: TextStyle(
 | 
			
		||||
      fontSize: 14,
 | 
			
		||||
      fontWeight: FontWeight.bold,
 | 
			
		||||
      color: Colors.black87,
 | 
			
		||||
    ),
 | 
			
		||||
    headline3: TextStyle(
 | 
			
		||||
      fontSize: 12,
 | 
			
		||||
      fontWeight: FontWeight.bold,
 | 
			
		||||
      color: Colors.indigo,
 | 
			
		||||
    ),
 | 
			
		||||
  ),
 | 
			
		||||
  elevatedButtonTheme: ElevatedButtonThemeData(
 | 
			
		||||
    style: ElevatedButton.styleFrom(
 | 
			
		||||
      backgroundColor: Colors.indigo,
 | 
			
		||||
      foregroundColor: Colors.white,
 | 
			
		||||
  navigationBarTheme: NavigationBarThemeData(
 | 
			
		||||
    indicatorColor: immichDarkThemePrimaryColor.withOpacity(0.4),
 | 
			
		||||
    iconTheme: const MaterialStatePropertyAll(
 | 
			
		||||
      IconThemeData(color: Colors.white),
 | 
			
		||||
    ),
 | 
			
		||||
    backgroundColor: Colors.grey[900],
 | 
			
		||||
    surfaceTintColor: Colors.transparent,
 | 
			
		||||
  ),
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user