From caaa474c2362eb4d1c5e743f18c4f9565fea570c Mon Sep 17 00:00:00 2001 From: xpwmaosldk Date: Wed, 22 Jun 2022 14:23:35 +0900 Subject: [PATCH] Optimize android's Gradle settings and clean up mobile source code (#240) * optimize android side gradle settings * android minsdk back to 21 * remove unused package, update linter and fix lint error --- mobile/.metadata | 39 +++++- mobile/analysis_options.yaml | 1 + mobile/android/app/build.gradle | 1 - .../app/FlutterMultiDexApplication.java | 25 ---- mobile/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 4 +- mobile/lib/main.dart | 11 +- .../asset_viewer/ui/exif_bottom_sheet.dart | 32 +++-- .../asset_viewer/ui/remote_photo_view.dart | 1 - .../asset_viewer/ui/top_control_app_bar.dart | 13 +- .../backup/providers/backup.provider.dart | 89 +++++++++----- .../backup/views/backup_controller_page.dart | 114 +++++++++++------- .../home/ui/disable_multi_select_button.dart | 4 +- .../modules/home/ui/draggable_scrollbar.dart | 61 ++++++---- .../modules/home/ui/immich_sliver_appbar.dart | 30 +++-- .../lib/modules/home/ui/profile_drawer.dart | 60 +++++---- mobile/lib/modules/home/views/home_page.dart | 22 ++-- .../providers/search_page_state.provider.dart | 20 +-- .../lib/modules/search/views/search_page.dart | 1 - .../search/views/search_result_page.dart | 34 ++++-- .../providers/shared_album.provider.dart | 16 ++- .../sharing/ui/album_viewer_appbar.dart | 40 +++--- .../sharing/views/album_viewer_page.dart | 46 ++++--- .../sharing/views/asset_selection_page.dart | 24 ++-- .../views/create_shared_album_page.dart | 33 +++-- .../lib/shared/models/device_info.model.dart | 4 +- .../lib/shared/providers/asset.provider.dart | 24 ++-- .../providers/release_info.provider.dart | 6 +- .../shared/providers/websocket.provider.dart | 21 ++-- .../shared/services/server_info.service.dart | 3 +- .../views/version_announcement_overlay.dart | 28 +++-- mobile/lib/utils/dio_http_interceptor.dart | 1 - mobile/pubspec.lock | 34 ++---- mobile/pubspec.yaml | 13 +- 34 files changed, 524 insertions(+), 333 deletions(-) delete mode 100644 mobile/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java diff --git a/mobile/.metadata b/mobile/.metadata index fd70cabc0..ed0b5185f 100644 --- a/mobile/.metadata +++ b/mobile/.metadata @@ -1,10 +1,45 @@ # This file tracks properties of this Flutter project. # Used by Flutter tool to assess capabilities and perform upgrades etc. # -# This file should be version controlled and should not be manually edited. +# This file should be version controlled. version: - revision: 77d935af4db863f6abd0b9c31c7e6df2a13de57b + revision: cd41fdd495f6944ecd3506c21e94c6567b073278 channel: stable project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 + base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 + - platform: android + create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 + base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 + - platform: ios + create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 + base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 + - platform: linux + create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 + base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 + - platform: macos + create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 + base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 + - platform: web + create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 + base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 + - platform: windows + create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 + base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml index 61b6c4de1..ecf2f65ca 100644 --- a/mobile/analysis_options.yaml +++ b/mobile/analysis_options.yaml @@ -24,6 +24,7 @@ linter: rules: # avoid_print: false # Uncomment to disable the `avoid_print` rule # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + use_build_context_synchronously: false # Additional information about this file can be found at # https://dart.dev/guides/language/analysis-options diff --git a/mobile/android/app/build.gradle b/mobile/android/app/build.gradle index 2a79dedc7..2f7ca021f 100644 --- a/mobile/android/app/build.gradle +++ b/mobile/android/app/build.gradle @@ -81,5 +81,4 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'com.android.support:multidex:1.0.3' } diff --git a/mobile/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java b/mobile/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java deleted file mode 100644 index 752fc185d..000000000 --- a/mobile/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java +++ /dev/null @@ -1,25 +0,0 @@ -// Generated file. -// -// If you wish to remove Flutter's multidex support, delete this entire file. -// -// Modifications to this file should be done in a copy under a different name -// as this file may be regenerated. - -package io.flutter.app; - -import android.app.Application; -import android.content.Context; -import androidx.annotation.CallSuper; -import androidx.multidex.MultiDex; - -/** - * Extension of {@link android.app.Application}, adding multidex support. - */ -public class FlutterMultiDexApplication extends Application { - @Override - @CallSuper - protected void attachBaseContext(Context base) { - super.attachBaseContext(base); - MultiDex.install(this); - } -} diff --git a/mobile/android/build.gradle b/mobile/android/build.gradle index 4256f9173..83ae22004 100644 --- a/mobile/android/build.gradle +++ b/mobile/android/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:7.1.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/mobile/android/gradle/wrapper/gradle-wrapper.properties b/mobile/android/gradle/wrapper/gradle-wrapper.properties index e7baffaa5..b06c5a3f1 100644 --- a/mobile/android/gradle/wrapper/gradle-wrapper.properties +++ b/mobile/android/gradle/wrapper/gradle-wrapper.properties @@ -3,5 +3,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip -distributionSha256Sum=0080de8491f0918e4f529a6db6820fa0b9e818ee2386117f4394f95feb1d5583 \ No newline at end of file +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip +distributionSha256Sum=cd5c2958a107ee7f0722004a12d0f8559b4564c34daad7df06cffd4d12a426d0 \ No newline at end of file diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 0cdc0df67..d256934e3 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -42,10 +42,11 @@ class ImmichApp extends ConsumerStatefulWidget { const ImmichApp({Key? key}) : super(key: key); @override - _ImmichAppState createState() => _ImmichAppState(); + ImmichAppState createState() => ImmichAppState(); } -class _ImmichAppState extends ConsumerState with WidgetsBindingObserver { +class ImmichAppState extends ConsumerState + with WidgetsBindingObserver { @override void didChangeAppLifecycleState(AppLifecycleState state) { switch (state) { @@ -121,7 +122,8 @@ class _ImmichAppState extends ConsumerState with WidgetsBindingObserv brightness: Brightness.light, primarySwatch: Colors.indigo, fontFamily: 'WorkSans', - snackBarTheme: const SnackBarThemeData(contentTextStyle: TextStyle(fontFamily: 'WorkSans')), + snackBarTheme: const SnackBarThemeData( + contentTextStyle: TextStyle(fontFamily: 'WorkSans')), scaffoldBackgroundColor: immichBackgroundColor, appBarTheme: const AppBarTheme( backgroundColor: immichBackgroundColor, @@ -132,7 +134,8 @@ class _ImmichAppState extends ConsumerState with WidgetsBindingObserv ), ), routeInformationParser: _immichRouter.defaultRouteParser(), - routerDelegate: _immichRouter.delegate(navigatorObservers: () => [TabNavigationObserver(ref: ref)]), + routerDelegate: _immichRouter.delegate( + navigatorObservers: () => [TabNavigationObserver(ref: ref)]), ), const ImmichLoadingOverlay(), const VersionAnnouncementOverlay(), diff --git a/mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart b/mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart index c22843b69..e591988bf 100644 --- a/mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart +++ b/mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart @@ -9,12 +9,14 @@ import 'package:latlong2/latlong.dart'; class ExifBottomSheet extends ConsumerWidget { final ImmichAssetWithExif assetDetail; - const ExifBottomSheet({Key? key, required this.assetDetail}) : super(key: key); + const ExifBottomSheet({Key? key, required this.assetDetail}) + : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { _buildMap() { - return (assetDetail.exifInfo!.latitude != null && assetDetail.exifInfo!.longitude != null) + return (assetDetail.exifInfo!.latitude != null && + assetDetail.exifInfo!.longitude != null) ? Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: Container( @@ -25,12 +27,14 @@ class ExifBottomSheet extends ConsumerWidget { ), child: FlutterMap( options: MapOptions( - center: LatLng(assetDetail.exifInfo!.latitude!, assetDetail.exifInfo!.longitude!), + center: LatLng(assetDetail.exifInfo!.latitude!, + assetDetail.exifInfo!.longitude!), zoom: 16.0, ), layers: [ TileLayerOptions( - urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", + urlTemplate: + "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", subdomains: ['a', 'b', 'c'], attributionBuilder: (_) { return const Text( @@ -43,8 +47,10 @@ class ExifBottomSheet extends ConsumerWidget { markers: [ Marker( anchorPos: AnchorPos.align(AnchorAlign.top), - point: LatLng(assetDetail.exifInfo!.latitude!, assetDetail.exifInfo!.longitude!), - builder: (ctx) => const Image(image: AssetImage('assets/location-pin.png')), + point: LatLng(assetDetail.exifInfo!.latitude!, + assetDetail.exifInfo!.longitude!), + builder: (ctx) => const Image( + image: AssetImage('assets/location-pin.png')), ), ], ), @@ -56,10 +62,14 @@ class ExifBottomSheet extends ConsumerWidget { } _buildLocationText() { - return (assetDetail.exifInfo!.city != null && assetDetail.exifInfo!.state != null) + return (assetDetail.exifInfo!.city != null && + assetDetail.exifInfo!.state != null) ? Text( "${assetDetail.exifInfo!.city}, ${assetDetail.exifInfo!.state}", - style: TextStyle(fontSize: 12, color: Colors.grey[200], fontWeight: FontWeight.bold), + style: TextStyle( + fontSize: 12, + color: Colors.grey[200], + fontWeight: FontWeight.bold), ) : Container(); } @@ -131,7 +141,8 @@ class ExifBottomSheet extends ConsumerWidget { padding: const EdgeInsets.only(bottom: 8.0), child: Text( "DETAILS", - style: TextStyle(fontSize: 11, color: Colors.grey[400]), + style: + TextStyle(fontSize: 11, color: Colors.grey[400]), ), ), ListTile( @@ -158,7 +169,8 @@ class ExifBottomSheet extends ConsumerWidget { leading: const Icon(Icons.camera), title: Text( "${assetDetail.exifInfo?.make} ${assetDetail.exifInfo?.model}", - style: const TextStyle(fontWeight: FontWeight.bold), + style: const TextStyle( + fontWeight: FontWeight.bold), ), subtitle: Text( "ƒ/${assetDetail.exifInfo?.fNumber} 1/${(1 / assetDetail.exifInfo!.exposureTime!).toStringAsFixed(0)} ${assetDetail.exifInfo?.focalLength}mm ISO${assetDetail.exifInfo?.iso} "), diff --git a/mobile/lib/modules/asset_viewer/ui/remote_photo_view.dart b/mobile/lib/modules/asset_viewer/ui/remote_photo_view.dart index a9a19b0d5..3a9bce99b 100644 --- a/mobile/lib/modules/asset_viewer/ui/remote_photo_view.dart +++ b/mobile/lib/modules/asset_viewer/ui/remote_photo_view.dart @@ -1,7 +1,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; import 'package:photo_view/photo_view.dart'; enum _RemoteImageStatus { empty, thumbnail, full } diff --git a/mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart b/mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart index 75a08c25b..39e8ecf6a 100644 --- a/mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart +++ b/mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -5,7 +7,10 @@ import 'package:immich_mobile/shared/models/immich_asset.model.dart'; class TopControlAppBar extends ConsumerWidget with PreferredSizeWidget { const TopControlAppBar( - {Key? key, required this.asset, required this.onMoreInfoPressed, required this.onDownloadPressed}) + {Key? key, + required this.asset, + required this.onMoreInfoPressed, + required this.onDownloadPressed}) : super(key: key); final ImmichAsset asset; @@ -42,9 +47,11 @@ class TopControlAppBar extends ConsumerWidget with PreferredSizeWidget { iconSize: iconSize, splashRadius: iconSize, onPressed: () { - print("favorite"); + log("favorite"); }, - icon: asset.isFavorite ? const Icon(Icons.favorite_rounded) : const Icon(Icons.favorite_border_rounded), + icon: asset.isFavorite + ? const Icon(Icons.favorite_rounded) + : const Icon(Icons.favorite_border_rounded), ), IconButton( iconSize: iconSize, diff --git a/mobile/lib/modules/backup/providers/backup.provider.dart b/mobile/lib/modules/backup/providers/backup.provider.dart index 639c2138d..592781d57 100644 --- a/mobile/lib/modules/backup/providers/backup.provider.dart +++ b/mobile/lib/modules/backup/providers/backup.provider.dart @@ -1,16 +1,15 @@ import 'package:cancellation_token_http/http.dart'; -import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/hive_box.dart'; import 'package:immich_mobile/modules/backup/models/available_album.model.dart'; -import 'package:immich_mobile/modules/backup/models/hive_backup_albums.model.dart'; -import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; -import 'package:immich_mobile/shared/services/server_info.service.dart'; import 'package:immich_mobile/modules/backup/models/backup_state.model.dart'; -import 'package:immich_mobile/shared/models/server_info.model.dart'; +import 'package:immich_mobile/modules/backup/models/hive_backup_albums.model.dart'; import 'package:immich_mobile/modules/backup/services/backup.service.dart'; +import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; +import 'package:immich_mobile/shared/models/server_info.model.dart'; +import 'package:immich_mobile/shared/services/server_info.service.dart'; import 'package:photo_manager/photo_manager.dart'; class BackupNotifier extends StateNotifier { @@ -55,7 +54,8 @@ class BackupNotifier extends StateNotifier { removeExcludedAlbumForBackup(album); } - state = state.copyWith(selectedBackupAlbums: {...state.selectedBackupAlbums, album}); + state = state + .copyWith(selectedBackupAlbums: {...state.selectedBackupAlbums, album}); _updateBackupAssetCount(); } @@ -63,7 +63,8 @@ class BackupNotifier extends StateNotifier { if (state.selectedBackupAlbums.contains(album)) { removeAlbumForBackup(album); } - state = state.copyWith(excludedBackupAlbums: {...state.excludedBackupAlbums, album}); + state = state + .copyWith(excludedBackupAlbums: {...state.excludedBackupAlbums, album}); _updateBackupAssetCount(); } @@ -94,16 +95,19 @@ class BackupNotifier extends StateNotifier { Future getBackupAlbumsInfo() async { // Get all albums on the device List availableAlbums = []; - List albums = await PhotoManager.getAssetPathList(hasAll: true, type: RequestType.common); + List albums = await PhotoManager.getAssetPathList( + hasAll: true, type: RequestType.common); for (AssetPathEntity album in albums) { AvailableAlbum availableAlbum = AvailableAlbum(albumEntity: album); - var assetList = await album.getAssetListRange(start: 0, end: album.assetCount); + var assetList = + await album.getAssetListRange(start: 0, end: album.assetCount); if (assetList.isNotEmpty) { var thumbnailAsset = assetList.first; - var thumbnailData = await thumbnailAsset.thumbnailDataWithSize(const ThumbnailSize(512, 512)); + var thumbnailData = await thumbnailAsset + .thumbnailDataWithSize(const ThumbnailSize(512, 512)); availableAlbum = availableAlbum.copyWith(thumbnailData: thumbnailData); } @@ -114,7 +118,8 @@ class BackupNotifier extends StateNotifier { // Put persistent storage info into local state of the app // Get local storage on selected backup album - Box backupAlbumInfoBox = Hive.box(hiveBackupInfoBox); + Box backupAlbumInfoBox = + Hive.box(hiveBackupInfoBox); HiveBackupAlbums? backupAlbumInfo = backupAlbumInfoBox.get( backupInfoKey, defaultValue: HiveBackupAlbums( @@ -133,7 +138,8 @@ class BackupNotifier extends StateNotifier { debugPrint("First time backup setup recent album as default"); // Get album that contains all assets - var list = await PhotoManager.getAssetPathList(hasAll: true, onlyAll: true, type: RequestType.common); + var list = await PhotoManager.getAssetPathList( + hasAll: true, onlyAll: true, type: RequestType.common); AssetPathEntity albumHasAllAssets = list.first; backupAlbumInfoBox.put( @@ -151,12 +157,14 @@ class BackupNotifier extends StateNotifier { try { for (var selectedAlbumId in backupAlbumInfo!.selectedAlbumIds) { var albumAsset = await AssetPathEntity.fromId(selectedAlbumId); - state = state.copyWith(selectedBackupAlbums: {...state.selectedBackupAlbums, albumAsset}); + state = state.copyWith( + selectedBackupAlbums: {...state.selectedBackupAlbums, albumAsset}); } for (var excludedAlbumId in backupAlbumInfo.excludedAlbumsIds) { var albumAsset = await AssetPathEntity.fromId(excludedAlbumId); - state = state.copyWith(excludedBackupAlbums: {...state.excludedBackupAlbums, albumAsset}); + state = state.copyWith( + excludedBackupAlbums: {...state.excludedBackupAlbums, albumAsset}); } } catch (e) { debugPrint("[ERROR] Failed to generate album from id $e"); @@ -173,21 +181,27 @@ class BackupNotifier extends StateNotifier { Set assetsFromExcludedAlbums = {}; for (var album in state.selectedBackupAlbums) { - var assets = await album.getAssetListRange(start: 0, end: album.assetCount); + var assets = + await album.getAssetListRange(start: 0, end: album.assetCount); assetsFromSelectedAlbums.addAll(assets); } for (var album in state.excludedBackupAlbums) { - var assets = await album.getAssetListRange(start: 0, end: album.assetCount); + var assets = + await album.getAssetListRange(start: 0, end: album.assetCount); assetsFromExcludedAlbums.addAll(assets); } - Set allUniqueAssets = assetsFromSelectedAlbums.difference(assetsFromExcludedAlbums); - List allAssetOnDatabase = await _backupService.getDeviceBackupAsset(); + Set allUniqueAssets = + assetsFromSelectedAlbums.difference(assetsFromExcludedAlbums); + List allAssetOnDatabase = + await _backupService.getDeviceBackupAsset(); // Find asset that were backup from selected albums - Set selectedAlbumsBackupAssets = Set.from(allUniqueAssets.map((e) => e.id)); - selectedAlbumsBackupAssets.removeWhere((assetId) => !allAssetOnDatabase.contains(assetId)); + Set selectedAlbumsBackupAssets = + Set.from(allUniqueAssets.map((e) => e.id)); + selectedAlbumsBackupAssets + .removeWhere((assetId) => !allAssetOnDatabase.contains(assetId)); if (allUniqueAssets.isEmpty) { debugPrint("No Asset On Device"); @@ -226,7 +240,8 @@ class BackupNotifier extends StateNotifier { /// Hive database /// void _updatePersistentAlbumsSelection() { - Box backupAlbumInfoBox = Hive.box(hiveBackupInfoBox); + Box backupAlbumInfoBox = + Hive.box(hiveBackupInfoBox); backupAlbumInfoBox.put( backupInfoKey, HiveBackupAlbums( @@ -268,7 +283,8 @@ class BackupNotifier extends StateNotifier { // Perform Backup state = state.copyWith(cancelToken: CancellationToken()); - _backupService.backupAsset(assetsWillBeBackup, state.cancelToken, _onAssetUploaded, _onUploadProgress); + _backupService.backupAsset(assetsWillBeBackup, state.cancelToken, + _onAssetUploaded, _onUploadProgress); } else { PhotoManager.openSetting(); } @@ -276,23 +292,32 @@ class BackupNotifier extends StateNotifier { void cancelBackup() { state.cancelToken.cancel(); - state = state.copyWith(backupProgress: BackUpProgressEnum.idle, progressInPercentage: 0.0); + state = state.copyWith( + backupProgress: BackUpProgressEnum.idle, progressInPercentage: 0.0); } void _onAssetUploaded(String deviceAssetId, String deviceId) { - state = state.copyWith( - selectedAlbumsBackupAssetsIds: {...state.selectedAlbumsBackupAssetsIds, deviceAssetId}, - allAssetOnDatabase: [...state.allAssetOnDatabase, deviceAssetId]); + state = state.copyWith(selectedAlbumsBackupAssetsIds: { + ...state.selectedAlbumsBackupAssetsIds, + deviceAssetId + }, allAssetOnDatabase: [ + ...state.allAssetOnDatabase, + deviceAssetId + ]); - if (state.allUniqueAssets.length - state.selectedAlbumsBackupAssetsIds.length == 0) { - state = state.copyWith(backupProgress: BackUpProgressEnum.done, progressInPercentage: 0.0); + if (state.allUniqueAssets.length - + state.selectedAlbumsBackupAssetsIds.length == + 0) { + state = state.copyWith( + backupProgress: BackUpProgressEnum.done, progressInPercentage: 0.0); } _updateServerInfo(); } void _onUploadProgress(int sent, int total) { - state = state.copyWith(progressInPercentage: (sent.toDouble() / total.toDouble() * 100)); + state = state.copyWith( + progressInPercentage: (sent.toDouble() / total.toDouble() * 100)); } void _updateServerInfo() async { @@ -326,7 +351,8 @@ class BackupNotifier extends StateNotifier { } // Check if this device is enable backup by the user - if ((authState.deviceInfo.deviceId == authState.deviceId) && authState.deviceInfo.isAutoBackup) { + if ((authState.deviceInfo.deviceId == authState.deviceId) && + authState.deviceInfo.isAutoBackup) { // check if backup is alreayd in process - then return if (state.backupProgress == BackUpProgressEnum.inProgress) { debugPrint("[resumeBackup] Backup is already in progress - abort"); @@ -343,6 +369,7 @@ class BackupNotifier extends StateNotifier { } } -final backupProvider = StateNotifierProvider((ref) { +final backupProvider = + StateNotifierProvider((ref) { return BackupNotifier(ref: ref); }); diff --git a/mobile/lib/modules/backup/views/backup_controller_page.dart b/mobile/lib/modules/backup/views/backup_controller_page.dart index 4c8db78a9..eebf65f6b 100644 --- a/mobile/lib/modules/backup/views/backup_controller_page.dart +++ b/mobile/lib/modules/backup/views/backup_controller_page.dart @@ -17,16 +17,21 @@ class BackupControllerPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { BackUpState backupState = ref.watch(backupProvider); - AuthenticationState _authenticationState = ref.watch(authenticationProvider); - bool shouldBackup = - backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length == 0 ? false : true; + AuthenticationState authenticationState = ref.watch(authenticationProvider); + bool shouldBackup = backupState.allUniqueAssets.length - + backupState.selectedAlbumsBackupAssetsIds.length == + 0 + ? false + : true; useEffect(() { if (backupState.backupProgress != BackUpProgressEnum.inProgress) { ref.read(backupProvider.notifier).getBackupInfo(); } - ref.watch(websocketProvider.notifier).stopListenToEvent('on_upload_success'); + ref + .watch(websocketProvider.notifier) + .stopListenToEvent('on_upload_success'); return null; }, []); @@ -48,7 +53,8 @@ class BackupControllerPage extends HookConsumerWidget { Padding( padding: const EdgeInsets.only(top: 8.0), child: LinearPercentIndicator( - padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 0), + padding: + const EdgeInsets.symmetric(horizontal: 0, vertical: 0), barRadius: const Radius.circular(2), lineHeight: 6.0, percent: backupState.serverInfo.diskUsagePercentage / 100.0, @@ -58,7 +64,8 @@ class BackupControllerPage extends HookConsumerWidget { ), Padding( padding: const EdgeInsets.only(top: 12.0), - child: Text('${backupState.serverInfo.diskUse} of ${backupState.serverInfo.diskSize} used'), + child: Text( + '${backupState.serverInfo.diskUse} of ${backupState.serverInfo.diskSize} used'), ), ], ), @@ -67,9 +74,11 @@ class BackupControllerPage extends HookConsumerWidget { } ListTile _buildBackupController() { - var backUpOption = _authenticationState.deviceInfo.isAutoBackup ? "on" : "off"; - var isAutoBackup = _authenticationState.deviceInfo.isAutoBackup; - var backupBtnText = _authenticationState.deviceInfo.isAutoBackup ? "off" : "on"; + var backUpOption = + authenticationState.deviceInfo.isAutoBackup ? "on" : "off"; + var isAutoBackup = authenticationState.deviceInfo.isAutoBackup; + var backupBtnText = + authenticationState.deviceInfo.isAutoBackup ? "off" : "on"; return ListTile( isThreeLine: true, leading: isAutoBackup @@ -104,10 +113,15 @@ class BackupControllerPage extends HookConsumerWidget { ), onPressed: () { isAutoBackup - ? ref.watch(authenticationProvider.notifier).setAutoBackup(false) - : ref.watch(authenticationProvider.notifier).setAutoBackup(true); + ? ref + .watch(authenticationProvider.notifier) + .setAutoBackup(false) + : ref + .watch(authenticationProvider.notifier) + .setAutoBackup(true); }, - child: Text("Turn $backupBtnText Backup", style: const TextStyle(fontWeight: FontWeight.bold)), + child: Text("Turn $backupBtnText Backup", + style: const TextStyle(fontWeight: FontWeight.bold)), ), ) ], @@ -133,7 +147,10 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( text.trim().substring(0, text.length - 2), - style: TextStyle(color: Theme.of(context).primaryColor, fontSize: 12, fontWeight: FontWeight.bold), + style: TextStyle( + color: Theme.of(context).primaryColor, + fontSize: 12, + fontWeight: FontWeight.bold), ), ); } else { @@ -141,7 +158,10 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( "None selected", - style: TextStyle(color: Theme.of(context).primaryColor, fontSize: 12, fontWeight: FontWeight.bold), + style: TextStyle( + color: Theme.of(context).primaryColor, + fontSize: 12, + fontWeight: FontWeight.bold), ), ); } @@ -160,7 +180,10 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( text.trim().substring(0, text.length - 2), - style: TextStyle(color: Colors.red[300], fontSize: 12, fontWeight: FontWeight.bold), + style: TextStyle( + color: Colors.red[300], + fontSize: 12, + fontWeight: FontWeight.bold), ), ); } else { @@ -181,7 +204,8 @@ class BackupControllerPage extends HookConsumerWidget { borderOnForeground: false, child: ListTile( minVerticalPadding: 15, - title: const Text("Backup Albums", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20)), + title: const Text("Backup Albums", + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20)), subtitle: Padding( padding: const EdgeInsets.only(top: 8.0), child: Column( @@ -258,13 +282,16 @@ class BackupControllerPage extends HookConsumerWidget { ), BackupInfoCard( title: "Backup", - subtitle: "Photos and videos from selected albums that are backup", + subtitle: + "Photos and videos from selected albums that are backup", info: "${backupState.selectedAlbumsBackupAssetsIds.length}", ), BackupInfoCard( title: "Remainder", - subtitle: "Photos and videos that has not been backing up from selected albums", - info: "${backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length}", + subtitle: + "Photos and videos that has not been backing up from selected albums", + info: + "${backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length}", ), const Divider(), _buildBackupController(), @@ -289,29 +316,32 @@ class BackupControllerPage extends HookConsumerWidget { Padding( padding: const EdgeInsets.all(8.0), child: Container( - child: backupState.backupProgress == BackUpProgressEnum.inProgress - ? ElevatedButton( - style: ElevatedButton.styleFrom( - primary: Colors.red[300], - onPrimary: Colors.grey[50], - ), - onPressed: () { - ref.read(backupProvider.notifier).cancelBackup(); - }, - child: const Text("Cancel"), - ) - : ElevatedButton( - style: ElevatedButton.styleFrom( - primary: Theme.of(context).primaryColor, - onPrimary: Colors.grey[50], - ), - onPressed: shouldBackup - ? () { - ref.read(backupProvider.notifier).startBackupProcess(); - } - : null, - child: const Text("Start Backup"), - ), + child: + backupState.backupProgress == BackUpProgressEnum.inProgress + ? ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.red[300], + onPrimary: Colors.grey[50], + ), + onPressed: () { + ref.read(backupProvider.notifier).cancelBackup(); + }, + child: const Text("Cancel"), + ) + : ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Theme.of(context).primaryColor, + onPrimary: Colors.grey[50], + ), + onPressed: shouldBackup + ? () { + ref + .read(backupProvider.notifier) + .startBackupProcess(); + } + : null, + child: const Text("Start Backup"), + ), ), ) ], diff --git a/mobile/lib/modules/home/ui/disable_multi_select_button.dart b/mobile/lib/modules/home/ui/disable_multi_select_button.dart index bb1094a59..e99f00b2a 100644 --- a/mobile/lib/modules/home/ui/disable_multi_select_button.dart +++ b/mobile/lib/modules/home/ui/disable_multi_select_button.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart'; class DisableMultiSelectButton extends ConsumerWidget { const DisableMultiSelectButton({ @@ -36,7 +35,8 @@ class DisableMultiSelectButton extends ConsumerWidget { icon: const Icon(Icons.close_rounded), label: Text( selectedItemCount.toString(), - style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 18), + style: const TextStyle( + fontWeight: FontWeight.w600, fontSize: 18), )), ), ), diff --git a/mobile/lib/modules/home/ui/draggable_scrollbar.dart b/mobile/lib/modules/home/ui/draggable_scrollbar.dart index 80b8a69dd..adc4c7472 100644 --- a/mobile/lib/modules/home/ui/draggable_scrollbar.dart +++ b/mobile/lib/modules/home/ui/draggable_scrollbar.dart @@ -81,7 +81,8 @@ class DraggableScrollbar extends StatefulWidget { this.labelTextBuilder, this.labelConstraints, }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbRRectBuilder(scrollThumbKey, alwaysVisibleScrollThumb), + scrollThumbBuilder = + _thumbRRectBuilder(scrollThumbKey, alwaysVisibleScrollThumb), super(key: key); DraggableScrollbar.arrows({ @@ -98,7 +99,8 @@ class DraggableScrollbar extends StatefulWidget { this.labelTextBuilder, this.labelConstraints, }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbArrowBuilder(scrollThumbKey, alwaysVisibleScrollThumb), + scrollThumbBuilder = + _thumbArrowBuilder(scrollThumbKey, alwaysVisibleScrollThumb), super(key: key); DraggableScrollbar.semicircle({ @@ -115,11 +117,12 @@ class DraggableScrollbar extends StatefulWidget { this.labelTextBuilder, this.labelConstraints, }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbSemicircleBuilder(heightScrollThumb * 0.6, scrollThumbKey, alwaysVisibleScrollThumb), + scrollThumbBuilder = _thumbSemicircleBuilder( + heightScrollThumb * 0.6, scrollThumbKey, alwaysVisibleScrollThumb), super(key: key); @override - _DraggableScrollbarState createState() => _DraggableScrollbarState(); + DraggableScrollbarState createState() => DraggableScrollbarState(); static buildScrollThumbAndLabel( {required Widget scrollThumb, @@ -137,9 +140,9 @@ class DraggableScrollbar extends StatefulWidget { children: [ ScrollLabel( animation: labelAnimation, - child: labelText, backgroundColor: backgroundColor, constraints: labelConstraints, + child: labelText, ), scrollThumb, ], @@ -154,7 +157,8 @@ class DraggableScrollbar extends StatefulWidget { ); } - static ScrollThumbBuilder _thumbSemicircleBuilder(double width, Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { + static ScrollThumbBuilder _thumbSemicircleBuilder( + double width, Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { return ( Color backgroundColor, Animation thumbAnimation, @@ -168,9 +172,6 @@ class DraggableScrollbar extends StatefulWidget { foregroundPainter: ArrowCustomPainter(Colors.white), child: Material( elevation: 4.0, - child: Container( - constraints: BoxConstraints.tight(Size(width, height)), - ), color: backgroundColor, borderRadius: BorderRadius.only( topLeft: Radius.circular(height), @@ -178,6 +179,9 @@ class DraggableScrollbar extends StatefulWidget { topRight: const Radius.circular(4.0), bottomRight: const Radius.circular(4.0), ), + child: Container( + constraints: BoxConstraints.tight(Size(width, height)), + ), ), ); @@ -193,7 +197,8 @@ class DraggableScrollbar extends StatefulWidget { }; } - static ScrollThumbBuilder _thumbArrowBuilder(Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { + static ScrollThumbBuilder _thumbArrowBuilder( + Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { return ( Color backgroundColor, Animation thumbAnimation, @@ -203,6 +208,7 @@ class DraggableScrollbar extends StatefulWidget { BoxConstraints? labelConstraints, }) { final scrollThumb = ClipPath( + clipper: ArrowClipper(), child: Container( height: height, width: 20.0, @@ -213,7 +219,6 @@ class DraggableScrollbar extends StatefulWidget { ), ), ), - clipper: ArrowClipper(), ); return buildScrollThumbAndLabel( @@ -228,7 +233,8 @@ class DraggableScrollbar extends StatefulWidget { }; } - static ScrollThumbBuilder _thumbRRectBuilder(Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { + static ScrollThumbBuilder _thumbRRectBuilder( + Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { return ( Color backgroundColor, Animation thumbAnimation, @@ -239,13 +245,13 @@ class DraggableScrollbar extends StatefulWidget { }) { final scrollThumb = Material( elevation: 4.0, + color: backgroundColor, + borderRadius: const BorderRadius.all(Radius.circular(7.0)), child: Container( constraints: BoxConstraints.tight( Size(16.0, height), ), ), - color: backgroundColor, - borderRadius: const BorderRadius.all(Radius.circular(7.0)), ); return buildScrollThumbAndLabel( @@ -267,7 +273,8 @@ class ScrollLabel extends StatelessWidget { final Text child; final BoxConstraints? constraints; - static const BoxConstraints _defaultConstraints = BoxConstraints.tightFor(width: 72.0, height: 28.0); + static const BoxConstraints _defaultConstraints = + BoxConstraints.tightFor(width: 72.0, height: 28.0); const ScrollLabel({ Key? key, @@ -298,7 +305,8 @@ class ScrollLabel extends StatelessWidget { } } -class _DraggableScrollbarState extends State with TickerProviderStateMixin { +class DraggableScrollbarState extends State + with TickerProviderStateMixin { late double _barOffset; late double _viewOffset; late bool _isDragInProcess; @@ -345,7 +353,8 @@ class _DraggableScrollbarState extends State with TickerProv super.dispose(); } - double get barMaxScrollExtent => context.size!.height - widget.heightScrollThumb; + double get barMaxScrollExtent => + context.size!.height - widget.heightScrollThumb; double get barMinScrollExtent => 0; @@ -362,7 +371,8 @@ class _DraggableScrollbarState extends State with TickerProv ); } - return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) { + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { //print("LayoutBuilder constraints=$constraints"); return NotificationListener( @@ -432,7 +442,8 @@ class _DraggableScrollbarState extends State with TickerProv } } - if (notification is ScrollUpdateNotification || notification is OverscrollNotification) { + if (notification is ScrollUpdateNotification || + notification is OverscrollNotification) { if (_thumbAnimationController.status != AnimationStatus.forward) { _thumbAnimationController.forward(); } @@ -486,7 +497,8 @@ class _DraggableScrollbarState extends State with TickerProv _barOffset = barMaxScrollExtent; } - double viewDelta = getScrollViewDelta(details.delta.dy, barMaxScrollExtent, viewMaxScrollExtent); + double viewDelta = getScrollViewDelta( + details.delta.dy, barMaxScrollExtent, viewMaxScrollExtent); _viewOffset = widget.controller.position.pixels + viewDelta; if (_viewOffset < widget.controller.position.minScrollExtent) { @@ -566,7 +578,8 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2); path.lineTo(startPointX + arrowWidth, startPointY); path.lineTo(startPointX + arrowWidth, startPointY + 1.0); - path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2 + 1.0); + path.lineTo( + startPointX + arrowWidth / 2, startPointY - arrowWidth / 2 + 1.0); path.lineTo(startPointX, startPointY + 1.0); path.close(); @@ -575,7 +588,8 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2); path.lineTo(startPointX, startPointY); path.lineTo(startPointX, startPointY - 1.0); - path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2 - 1.0); + path.lineTo( + startPointX + arrowWidth / 2, startPointY + arrowWidth / 2 - 1.0); path.lineTo(startPointX + arrowWidth, startPointY - 1.0); path.close(); @@ -600,7 +614,8 @@ class SlideFadeTransition extends StatelessWidget { Widget build(BuildContext context) { return AnimatedBuilder( animation: animation, - builder: (context, child) => animation.value == 0.0 ? Container() : child!, + builder: (context, child) => + animation.value == 0.0 ? Container() : child!, child: SlideTransition( position: Tween( begin: const Offset(0.3, 0.0), diff --git a/mobile/lib/modules/home/ui/immich_sliver_appbar.dart b/mobile/lib/modules/home/ui/immich_sliver_appbar.dart index 0e5978ea4..689cc33dc 100644 --- a/mobile/lib/modules/home/ui/immich_sliver_appbar.dart +++ b/mobile/lib/modules/home/ui/immich_sliver_appbar.dart @@ -20,16 +20,18 @@ class ImmichSliverAppBar extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final BackUpState _backupState = ref.watch(backupProvider); - bool _isEnableAutoBackup = ref.watch(authenticationProvider).deviceInfo.isAutoBackup; - final ServerInfoState _serverInfoState = ref.watch(serverInfoProvider); + final BackUpState backupState = ref.watch(backupProvider); + bool isEnableAutoBackup = + ref.watch(authenticationProvider).deviceInfo.isAutoBackup; + final ServerInfoState serverInfoState = ref.watch(serverInfoProvider); return SliverAppBar( centerTitle: true, floating: true, pinned: false, snap: false, - shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(5))), leading: Builder( builder: (BuildContext context) { return Stack( @@ -47,7 +49,7 @@ class ImmichSliverAppBar extends ConsumerWidget { }, ), ), - _serverInfoState.isVersionMismatch + serverInfoState.isVersionMismatch ? Positioned( bottom: 12, right: 12, @@ -88,7 +90,7 @@ class ImmichSliverAppBar extends ConsumerWidget { Stack( alignment: AlignmentDirectional.center, children: [ - _backupState.backupProgress == BackUpProgressEnum.inProgress + backupState.backupProgress == BackUpProgressEnum.inProgress ? Positioned( top: 10, right: 12, @@ -97,7 +99,8 @@ class ImmichSliverAppBar extends ConsumerWidget { width: 8, child: CircularProgressIndicator( strokeWidth: 1, - valueColor: AlwaysStoppedAnimation(Theme.of(context).primaryColor), + valueColor: AlwaysStoppedAnimation( + Theme.of(context).primaryColor), ), ), ) @@ -105,7 +108,7 @@ class ImmichSliverAppBar extends ConsumerWidget { IconButton( splashRadius: 25, iconSize: 30, - icon: _isEnableAutoBackup + icon: isEnableAutoBackup ? const Icon(Icons.backup_rounded) : Badge( padding: const EdgeInsets.all(4), @@ -118,20 +121,23 @@ class ImmichSliverAppBar extends ConsumerWidget { ), child: const Icon(Icons.backup_rounded)), onPressed: () async { - var onPop = await AutoRouter.of(context).push(const BackupControllerRoute()); + var onPop = await AutoRouter.of(context) + .push(const BackupControllerRoute()); if (onPop != null && onPop == true) { onPopBack!(); } }, ), - _backupState.backupProgress == BackUpProgressEnum.inProgress + backupState.backupProgress == BackUpProgressEnum.inProgress ? Positioned( bottom: 5, child: Text( - (_backupState.allUniqueAssets.length - _backupState.selectedAlbumsBackupAssetsIds.length) + (backupState.allUniqueAssets.length - + backupState.selectedAlbumsBackupAssetsIds.length) .toString(), - style: const TextStyle(fontSize: 9, fontWeight: FontWeight.bold), + style: const TextStyle( + fontSize: 9, fontWeight: FontWeight.bold), ), ) : Container() diff --git a/mobile/lib/modules/home/ui/profile_drawer.dart b/mobile/lib/modules/home/ui/profile_drawer.dart index 2221a652c..4bfdc8c60 100644 --- a/mobile/lib/modules/home/ui/profile_drawer.dart +++ b/mobile/lib/modules/home/ui/profile_drawer.dart @@ -24,9 +24,10 @@ class ProfileDrawer extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { String endpoint = Hive.box(userInfoBox).get(serverEndpointKey); - AuthenticationState _authState = ref.watch(authenticationProvider); - ServerInfoState _serverInfoState = ref.watch(serverInfoProvider); - final uploadProfileImageStatus = ref.watch(uploadProfileImageProvider).status; + AuthenticationState authState = ref.watch(authenticationProvider); + ServerInfoState serverInfoState = ref.watch(serverInfoProvider); + final uploadProfileImageStatus = + ref.watch(uploadProfileImageProvider).status; final appInfo = useState({}); var dummmy = Random().nextInt(1024); @@ -40,7 +41,7 @@ class ProfileDrawer extends HookConsumerWidget { } _buildUserProfileImage() { - if (_authState.profileImagePath.isEmpty) { + if (authState.profileImagePath.isEmpty) { return const CircleAvatar( radius: 35, backgroundImage: AssetImage('assets/immich-logo-no-outline.png'), @@ -49,10 +50,11 @@ class ProfileDrawer extends HookConsumerWidget { } if (uploadProfileImageStatus == UploadProfileStatus.idle) { - if (_authState.profileImagePath.isNotEmpty) { + if (authState.profileImagePath.isNotEmpty) { return CircleAvatar( radius: 35, - backgroundImage: NetworkImage('$endpoint/user/profile-image/${_authState.userId}?d=${dummmy++}'), + backgroundImage: NetworkImage( + '$endpoint/user/profile-image/${authState.userId}?d=${dummmy++}'), backgroundColor: Colors.transparent, ); } else { @@ -67,7 +69,8 @@ class ProfileDrawer extends HookConsumerWidget { if (uploadProfileImageStatus == UploadProfileStatus.success) { return CircleAvatar( radius: 35, - backgroundImage: NetworkImage('$endpoint/user/profile-image/${_authState.userId}?d=${dummmy++}'), + backgroundImage: NetworkImage( + '$endpoint/user/profile-image/${authState.userId}?d=${dummmy++}'), backgroundColor: Colors.transparent, ); } @@ -88,15 +91,16 @@ class ProfileDrawer extends HookConsumerWidget { } _pickUserProfileImage() async { - final XFile? image = await ImagePicker().pickImage(source: ImageSource.gallery, maxHeight: 1024, maxWidth: 1024); + final XFile? image = await ImagePicker().pickImage( + source: ImageSource.gallery, maxHeight: 1024, maxWidth: 1024); if (image != null) { - var success = await ref.watch(uploadProfileImageProvider.notifier).upload(image); + var success = + await ref.watch(uploadProfileImageProvider.notifier).upload(image); if (success) { - ref - .watch(authenticationProvider.notifier) - .updateUserProfileImagePath(ref.read(uploadProfileImageProvider).profileImagePath); + ref.watch(authenticationProvider.notifier).updateUserProfileImagePath( + ref.read(uploadProfileImageProvider).profileImagePath); } } } @@ -117,7 +121,10 @@ class ProfileDrawer extends HookConsumerWidget { DrawerHeader( decoration: const BoxDecoration( gradient: LinearGradient( - colors: [Color.fromARGB(255, 216, 219, 238), Color.fromARGB(255, 226, 230, 231)], + colors: [ + Color.fromARGB(255, 216, 219, 238), + Color.fromARGB(255, 226, 230, 231) + ], begin: Alignment.centerRight, end: Alignment.centerLeft, ), @@ -155,7 +162,7 @@ class ProfileDrawer extends HookConsumerWidget { ], ), Text( - "${_authState.firstName} ${_authState.lastName}", + "${authState.firstName} ${authState.lastName}", style: TextStyle( color: Theme.of(context).primaryColor, fontWeight: FontWeight.bold, @@ -163,7 +170,7 @@ class ProfileDrawer extends HookConsumerWidget { ), ), Text( - _authState.userEmail, + authState.userEmail, style: TextStyle(color: Colors.grey[800], fontSize: 12), ) ], @@ -177,10 +184,14 @@ class ProfileDrawer extends HookConsumerWidget { ), title: const Text( "Sign Out", - style: TextStyle(color: Colors.black54, fontSize: 14, fontWeight: FontWeight.bold), + style: TextStyle( + color: Colors.black54, + fontSize: 14, + fontWeight: FontWeight.bold), ), onTap: () async { - bool res = await ref.read(authenticationProvider.notifier).logout(); + bool res = + await ref.read(authenticationProvider.notifier).logout(); if (res) { ref.watch(backupProvider.notifier).cancelBackup(); @@ -206,19 +217,22 @@ class ProfileDrawer extends HookConsumerWidget { ), ), child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8), + padding: + const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Padding( padding: const EdgeInsets.all(8.0), child: Text( - _serverInfoState.isVersionMismatch - ? _serverInfoState.versionMismatchErrorMessage + serverInfoState.isVersionMismatch + ? serverInfoState.versionMismatchErrorMessage : "Client and Server are up-to-date", textAlign: TextAlign.center, - style: - TextStyle(fontSize: 11, color: Theme.of(context).primaryColor, fontWeight: FontWeight.w600), + style: TextStyle( + fontSize: 11, + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.w600), ), ), const Divider(), @@ -256,7 +270,7 @@ class ProfileDrawer extends HookConsumerWidget { ), ), Text( - "${_serverInfoState.serverVersion.major}.${_serverInfoState.serverVersion.minor}.${_serverInfoState.serverVersion.patch}", + "${serverInfoState.serverVersion.major}.${serverInfoState.serverVersion.minor}.${serverInfoState.serverVersion.patch}", style: TextStyle( fontSize: 11, color: Colors.grey[500], diff --git a/mobile/lib/modules/home/views/home_page.dart b/mobile/lib/modules/home/views/home_page.dart index 06a312794..4673911c2 100644 --- a/mobile/lib/modules/home/views/home_page.dart +++ b/mobile/lib/modules/home/views/home_page.dart @@ -19,10 +19,11 @@ class HomePage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - ScrollController _scrollController = useScrollController(); + ScrollController scrollController = useScrollController(); var assetGroupByDateTime = ref.watch(assetGroupByDateTimeProvider); - List _imageGridGroup = []; - var isMultiSelectEnable = ref.watch(homePageStateProvider).isMultiSelectEnable; + List imageGridGroup = []; + var isMultiSelectEnable = + ref.watch(homePageStateProvider).isMultiSelectEnable; var homePageState = ref.watch(homePageStateProvider); useEffect(() { @@ -39,7 +40,8 @@ class HomePage extends HookConsumerWidget { _buildSelectedItemCountIndicator() { return isMultiSelectEnable ? DisableMultiSelectButton( - onPressed: ref.watch(homePageStateProvider.notifier).disableMultiSelect, + onPressed: + ref.watch(homePageStateProvider.notifier).disableMultiSelect, selectedItemCount: homePageState.selectedItems.length, ) : Container(); @@ -59,7 +61,7 @@ class HomePage extends HookConsumerWidget { if (lastMonth != null) { if (currentMonth - lastMonth! != 0) { - _imageGridGroup.add( + imageGridGroup.add( MonthlyTitleText( isoDate: dateGroup, ), @@ -67,14 +69,14 @@ class HomePage extends HookConsumerWidget { } } - _imageGridGroup.add( + imageGridGroup.add( DailyTitleText( isoDate: dateGroup, assetGroup: immichAssetList, ), ); - _imageGridGroup.add( + imageGridGroup.add( ImageGrid(assetGroup: immichAssetList), ); @@ -109,12 +111,12 @@ class HomePage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 50.0), child: DraggableScrollbar.semicircle( backgroundColor: Theme.of(context).primaryColor, - controller: _scrollController, + controller: scrollController, heightScrollThumb: 48.0, child: CustomScrollView( - controller: _scrollController, + controller: scrollController, slivers: [ - ..._imageGridGroup, + ...imageGridGroup, ], ), ), diff --git a/mobile/lib/modules/search/providers/search_page_state.provider.dart b/mobile/lib/modules/search/providers/search_page_state.provider.dart index 33f8c9622..6611f1273 100644 --- a/mobile/lib/modules/search/providers/search_page_state.provider.dart +++ b/mobile/lib/modules/search/providers/search_page_state.provider.dart @@ -45,20 +45,23 @@ class SearchPageStateNotifier extends StateNotifier { } void getSuggestedSearchTerms() async { - var userSuggestedSearchTerms = await _searchService.getUserSuggestedSearchTerms(); + var userSuggestedSearchTerms = + await _searchService.getUserSuggestedSearchTerms(); state = state.copyWith(userSuggestedSearchTerms: userSuggestedSearchTerms); } } -final searchPageStateProvider = StateNotifierProvider((ref) { +final searchPageStateProvider = + StateNotifierProvider((ref) { return SearchPageStateNotifier(); }); -final getCuratedLocationProvider = FutureProvider.autoDispose>((ref) async { - final SearchService _searchService = SearchService(); +final getCuratedLocationProvider = + FutureProvider.autoDispose>((ref) async { + final SearchService searchService = SearchService(); - var curatedLocation = await _searchService.getCuratedLocation(); + var curatedLocation = await searchService.getCuratedLocation(); if (curatedLocation != null) { return curatedLocation; } else { @@ -66,10 +69,11 @@ final getCuratedLocationProvider = FutureProvider.autoDispose>((ref) async { - final SearchService _searchService = SearchService(); +final getCuratedObjectProvider = + FutureProvider.autoDispose>((ref) async { + final SearchService searchService = SearchService(); - var curatedObject = await _searchService.getCuratedObjects(); + var curatedObject = await searchService.getCuratedObjects(); if (curatedObject != null) { return curatedObject; } else { diff --git a/mobile/lib/modules/search/views/search_page.dart b/mobile/lib/modules/search/views/search_page.dart index 59866b605..e47074cee 100644 --- a/mobile/lib/modules/search/views/search_page.dart +++ b/mobile/lib/modules/search/views/search_page.dart @@ -1,7 +1,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/hive_box.dart'; diff --git a/mobile/lib/modules/search/views/search_result_page.dart b/mobile/lib/modules/search/views/search_result_page.dart index 38c859542..9efce1a52 100644 --- a/mobile/lib/modules/search/views/search_result_page.dart +++ b/mobile/lib/modules/search/views/search_result_page.dart @@ -12,24 +12,27 @@ import 'package:immich_mobile/modules/search/providers/search_result_page.provid import 'package:immich_mobile/modules/search/ui/search_suggestion_list.dart'; class SearchResultPage extends HookConsumerWidget { - SearchResultPage({Key? key, required this.searchTerm}) : super(key: key); + const SearchResultPage({Key? key, required this.searchTerm}) + : super(key: key); final String searchTerm; - late FocusNode searchFocusNode; @override Widget build(BuildContext context, WidgetRef ref) { - ScrollController _scrollController = useScrollController(); + ScrollController scrollController = useScrollController(); final searchTermController = useTextEditingController(text: ""); final isNewSearch = useState(false); final currentSearchTerm = useState(searchTerm); - List _imageGridGroup = []; + final List imageGridGroup = []; + + late FocusNode searchFocusNode; useEffect(() { searchFocusNode = FocusNode(); - Future.delayed(Duration.zero, () => ref.read(searchResultPageProvider.notifier).search(searchTerm)); + Future.delayed(Duration.zero, + () => ref.read(searchResultPageProvider.notifier).search(searchTerm)); return () => searchFocusNode.dispose(); }, []); @@ -85,7 +88,10 @@ class SearchResultPage extends HookConsumerWidget { children: [ Text( currentSearchTerm.value, - style: TextStyle(color: Theme.of(context).primaryColor, fontSize: 13, fontWeight: FontWeight.bold), + style: TextStyle( + color: Theme.of(context).primaryColor, + fontSize: 13, + fontWeight: FontWeight.bold), maxLines: 1, ), Icon( @@ -124,7 +130,7 @@ class SearchResultPage extends HookConsumerWidget { if (lastMonth != null) { if (currentMonth - lastMonth! != 0) { - _imageGridGroup.add( + imageGridGroup.add( MonthlyTitleText( isoDate: dateGroup, ), @@ -132,14 +138,14 @@ class SearchResultPage extends HookConsumerWidget { } } - _imageGridGroup.add( + imageGridGroup.add( DailyTitleText( isoDate: dateGroup, assetGroup: immichAssetList, ), ); - _imageGridGroup.add( + imageGridGroup.add( ImageGrid(assetGroup: immichAssetList), ); @@ -148,11 +154,11 @@ class SearchResultPage extends HookConsumerWidget { return DraggableScrollbar.semicircle( backgroundColor: Theme.of(context).primaryColor, - controller: _scrollController, + controller: scrollController, heightScrollThumb: 48.0, child: CustomScrollView( - controller: _scrollController, - slivers: [..._imageGridGroup], + controller: scrollController, + slivers: [...imageGridGroup], ), ); } else { @@ -192,7 +198,9 @@ class SearchResultPage extends HookConsumerWidget { child: Stack( children: [ _buildSearchResult(), - isNewSearch.value ? SearchSuggestionList(onSubmitted: _onSearchSubmitted) : Container(), + isNewSearch.value + ? SearchSuggestionList(onSubmitted: _onSearchSubmitted) + : Container(), ], ), ), diff --git a/mobile/lib/modules/sharing/providers/shared_album.provider.dart b/mobile/lib/modules/sharing/providers/shared_album.provider.dart index be9f47581..c65d50dcc 100644 --- a/mobile/lib/modules/sharing/providers/shared_album.provider.dart +++ b/mobile/lib/modules/sharing/providers/shared_album.provider.dart @@ -8,7 +8,8 @@ class SharedAlbumNotifier extends StateNotifier> { final SharedAlbumService _sharedAlbumService = SharedAlbumService(); getAllSharedAlbums() async { - List sharedAlbums = await _sharedAlbumService.getAllSharedAlbum(); + List sharedAlbums = + await _sharedAlbumService.getAllSharedAlbum(); state = sharedAlbums; } @@ -35,7 +36,8 @@ class SharedAlbumNotifier extends StateNotifier> { } } - Future removeAssetFromAlbum(String albumId, List assetIds) async { + Future removeAssetFromAlbum( + String albumId, List assetIds) async { var res = await _sharedAlbumService.removeAssetFromAlbum(albumId, assetIds); if (res) { @@ -46,12 +48,14 @@ class SharedAlbumNotifier extends StateNotifier> { } } -final sharedAlbumProvider = StateNotifierProvider>((ref) { +final sharedAlbumProvider = + StateNotifierProvider>((ref) { return SharedAlbumNotifier(); }); -final sharedAlbumDetailProvider = FutureProvider.autoDispose.family((ref, albumId) async { - final SharedAlbumService _sharedAlbumService = SharedAlbumService(); +final sharedAlbumDetailProvider = FutureProvider.autoDispose + .family((ref, albumId) async { + final SharedAlbumService sharedAlbumService = SharedAlbumService(); - return await _sharedAlbumService.getAlbumDetail(albumId); + return await sharedAlbumService.getAlbumDetail(albumId); }); diff --git a/mobile/lib/modules/sharing/ui/album_viewer_appbar.dart b/mobile/lib/modules/sharing/ui/album_viewer_appbar.dart index e3ec43ba1..d91972091 100644 --- a/mobile/lib/modules/sharing/ui/album_viewer_appbar.dart +++ b/mobile/lib/modules/sharing/ui/album_viewer_appbar.dart @@ -26,18 +26,22 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final isMultiSelectionEnable = ref.watch(assetSelectionProvider).isMultiselectEnable; - final selectedAssetsInAlbum = ref.watch(assetSelectionProvider).selectedAssetsInAlbumViewer; + final isMultiSelectionEnable = + ref.watch(assetSelectionProvider).isMultiselectEnable; + final selectedAssetsInAlbum = + ref.watch(assetSelectionProvider).selectedAssetsInAlbumViewer; final newAlbumTitle = ref.watch(albumViewerProvider).editTitleText; final isEditAlbum = ref.watch(albumViewerProvider).isEditAlbum; void _onDeleteAlbumPressed(String albumId) async { ImmichLoadingOverlayController.appLoader.show(); - bool isSuccess = await ref.watch(sharedAlbumProvider.notifier).deleteAlbum(albumId); + bool isSuccess = + await ref.watch(sharedAlbumProvider.notifier).deleteAlbum(albumId); if (isSuccess) { - AutoRouter.of(context).navigate(const TabControllerRoute(children: [SharingRoute()])); + AutoRouter.of(context) + .navigate(const TabControllerRoute(children: [SharingRoute()])); } else { ImmichToast.show( context: context, @@ -53,10 +57,12 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget { void _onLeaveAlbumPressed(String albumId) async { ImmichLoadingOverlayController.appLoader.show(); - bool isSuccess = await ref.watch(sharedAlbumProvider.notifier).leaveAlbum(albumId); + bool isSuccess = + await ref.watch(sharedAlbumProvider.notifier).leaveAlbum(albumId); if (isSuccess) { - AutoRouter.of(context).navigate(const TabControllerRoute(children: [SharingRoute()])); + AutoRouter.of(context) + .navigate(const TabControllerRoute(children: [SharingRoute()])); } else { Navigator.pop(context); ImmichToast.show( @@ -73,10 +79,11 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget { void _onRemoveFromAlbumPressed(String albumId) async { ImmichLoadingOverlayController.appLoader.show(); - bool isSuccess = await ref.watch(sharedAlbumProvider.notifier).removeAssetFromAlbum( - albumId, - selectedAssetsInAlbum.map((a) => a.id).toList(), - ); + bool isSuccess = + await ref.watch(sharedAlbumProvider.notifier).removeAssetFromAlbum( + albumId, + selectedAssetsInAlbum.map((a) => a.id).toList(), + ); if (isSuccess) { Navigator.pop(context); @@ -153,15 +160,18 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget { _buildLeadingButton() { if (isMultiSelectionEnable) { return IconButton( - onPressed: () => ref.watch(assetSelectionProvider.notifier).disableMultiselection(), + onPressed: () => ref + .watch(assetSelectionProvider.notifier) + .disableMultiselection(), icon: const Icon(Icons.close_rounded), splashRadius: 25, ); } else if (isEditAlbum) { return IconButton( onPressed: () async { - bool isSuccess = - await ref.watch(albumViewerProvider.notifier).changeAlbumTitle(albumId, userId, newAlbumTitle); + bool isSuccess = await ref + .watch(albumViewerProvider.notifier) + .changeAlbumTitle(albumId, userId, newAlbumTitle); if (!isSuccess) { ImmichToast.show( @@ -187,7 +197,9 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget { return AppBar( elevation: 0, leading: _buildLeadingButton(), - title: isMultiSelectionEnable ? Text(selectedAssetsInAlbum.length.toString()) : Container(), + title: isMultiSelectionEnable + ? Text(selectedAssetsInAlbum.length.toString()) + : Container(), centerTitle: false, actions: [ IconButton( diff --git a/mobile/lib/modules/sharing/views/album_viewer_page.dart b/mobile/lib/modules/sharing/views/album_viewer_page.dart index 653f66021..662baf61a 100644 --- a/mobile/lib/modules/sharing/views/album_viewer_page.dart +++ b/mobile/lib/modules/sharing/views/album_viewer_page.dart @@ -28,8 +28,9 @@ class AlbumViewerPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { FocusNode titleFocusNode = useFocusNode(); - ScrollController _scrollController = useScrollController(); - AsyncValue _albumInfo = ref.watch(sharedAlbumDetailProvider(albumId)); + ScrollController scrollController = useScrollController(); + AsyncValue albumInfo = + ref.watch(sharedAlbumDetailProvider(albumId)); final userId = ref.watch(authenticationProvider).userId; @@ -44,16 +45,16 @@ class AlbumViewerPage extends HookConsumerWidget { ref.watch(assetSelectionProvider.notifier).setIsAlbumExist(true); - AssetSelectionPageResult? returnPayload = - await AutoRouter.of(context).push(const AssetSelectionRoute()); + AssetSelectionPageResult? returnPayload = await AutoRouter.of(context) + .push(const AssetSelectionRoute()); if (returnPayload != null) { // Check if there is new assets add if (returnPayload.selectedAdditionalAsset.isNotEmpty) { ImmichLoadingOverlayController.appLoader.show(); - var isSuccess = - await SharedAlbumService().addAdditionalAssetToAlbum(returnPayload.selectedAdditionalAsset, albumId); + var isSuccess = await SharedAlbumService().addAdditionalAssetToAlbum( + returnPayload.selectedAdditionalAsset, albumId); if (isSuccess) { ref.refresh(sharedAlbumDetailProvider(albumId)); @@ -69,13 +70,15 @@ class AlbumViewerPage extends HookConsumerWidget { } void _onAddUsersPressed(SharedAlbum albumInfo) async { - List? sharedUserIds = - await AutoRouter.of(context).push?>(SelectAdditionalUserForSharingRoute(albumInfo: albumInfo)); + List? sharedUserIds = await AutoRouter.of(context) + .push?>( + SelectAdditionalUserForSharingRoute(albumInfo: albumInfo)); if (sharedUserIds != null) { ImmichLoadingOverlayController.appLoader.show(); - var isSuccess = await SharedAlbumService().addAdditionalUserToAlbum(sharedUserIds, albumId); + var isSuccess = await SharedAlbumService() + .addAdditionalUserToAlbum(sharedUserIds, albumId); if (isSuccess) { ref.refresh(sharedAlbumDetailProvider(albumId)); @@ -95,7 +98,9 @@ class AlbumViewerPage extends HookConsumerWidget { ) : Padding( padding: const EdgeInsets.only(left: 8.0), - child: Text(albumInfo.albumName, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold)), + child: Text(albumInfo.albumName, + style: const TextStyle( + fontSize: 24, fontWeight: FontWeight.bold)), ), ); } @@ -103,8 +108,10 @@ class AlbumViewerPage extends HookConsumerWidget { Widget _buildAlbumDateRange(SharedAlbum albumInfo) { if (albumInfo.assets != null && albumInfo.assets!.isNotEmpty) { String startDate = ""; - DateTime parsedStartDate = DateTime.parse(albumInfo.assets!.first.createdAt); - DateTime parsedEndDate = DateTime.parse(albumInfo.assets!.last.createdAt); + DateTime parsedStartDate = + DateTime.parse(albumInfo.assets!.first.createdAt); + DateTime parsedEndDate = + DateTime.parse(albumInfo.assets!.last.createdAt); if (parsedStartDate.year == parsedEndDate.year) { startDate = DateFormat('LLL d').format(parsedStartDate); @@ -118,7 +125,8 @@ class AlbumViewerPage extends HookConsumerWidget { padding: const EdgeInsets.only(left: 16.0, top: 8), child: Text( "$startDate-$endDate", - style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.grey), + style: const TextStyle( + fontSize: 14, fontWeight: FontWeight.bold, color: Colors.grey), ), ); } else { @@ -147,8 +155,9 @@ class AlbumViewerPage extends HookConsumerWidget { child: Padding( padding: const EdgeInsets.all(2.0), child: ClipRRect( - child: Image.asset('assets/immich-logo-no-outline.png'), borderRadius: BorderRadius.circular(50.0), + child: + Image.asset('assets/immich-logo-no-outline.png'), ), ), ), @@ -217,10 +226,10 @@ class AlbumViewerPage extends HookConsumerWidget { }, child: DraggableScrollbar.semicircle( backgroundColor: Theme.of(context).primaryColor, - controller: _scrollController, + controller: scrollController, heightScrollThumb: 48.0, child: CustomScrollView( - controller: _scrollController, + controller: scrollController, slivers: [ _buildHeader(albumInfo), SliverPersistentHeader( @@ -242,8 +251,9 @@ class AlbumViewerPage extends HookConsumerWidget { } return Scaffold( - appBar: AlbumViewerAppbar(albumInfo: _albumInfo, userId: userId, albumId: albumId), - body: _albumInfo.when( + appBar: AlbumViewerAppbar( + albumInfo: albumInfo, userId: userId, albumId: albumId), + body: albumInfo.when( data: (albumInfo) => _buildBody(albumInfo), error: (e, _) => Center(child: Text("Error loading album info $e")), loading: () => const Center( diff --git a/mobile/lib/modules/sharing/views/asset_selection_page.dart b/mobile/lib/modules/sharing/views/asset_selection_page.dart index 5147e71f1..f7fd74d14 100644 --- a/mobile/lib/modules/sharing/views/asset_selection_page.dart +++ b/mobile/lib/modules/sharing/views/asset_selection_page.dart @@ -13,13 +13,15 @@ class AssetSelectionPage extends HookConsumerWidget { const AssetSelectionPage({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { - ScrollController _scrollController = useScrollController(); + ScrollController scrollController = useScrollController(); var assetGroupMonthYear = ref.watch(assetGroupByMonthYearProvider); - final selectedAssets = ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum; - final newAssetsForAlbum = ref.watch(assetSelectionProvider).selectedAdditionalAssetsForAlbum; + final selectedAssets = + ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum; + final newAssetsForAlbum = + ref.watch(assetSelectionProvider).selectedAdditionalAssetsForAlbum; final isAlbumExist = ref.watch(assetSelectionProvider).isAlbumExist; - List _imageGridGroup = []; + List imageGridGroup = []; String _buildAssetCountText() { if (isAlbumExist) { @@ -31,19 +33,20 @@ class AssetSelectionPage extends HookConsumerWidget { Widget _buildBody() { assetGroupMonthYear.forEach((monthYear, assetGroup) { - _imageGridGroup.add(MonthGroupTitle(month: monthYear, assetGroup: assetGroup)); - _imageGridGroup.add(AssetGridByMonth(assetGroup: assetGroup)); + imageGridGroup + .add(MonthGroupTitle(month: monthYear, assetGroup: assetGroup)); + imageGridGroup.add(AssetGridByMonth(assetGroup: assetGroup)); }); return Stack( children: [ DraggableScrollbar.semicircle( backgroundColor: Theme.of(context).primaryColor, - controller: _scrollController, + controller: scrollController, heightScrollThumb: 48.0, child: CustomScrollView( - controller: _scrollController, - slivers: [..._imageGridGroup], + controller: scrollController, + slivers: [...imageGridGroup], ), ), ], @@ -71,7 +74,8 @@ class AssetSelectionPage extends HookConsumerWidget { ), centerTitle: false, actions: [ - (!isAlbumExist && selectedAssets.isNotEmpty) || (isAlbumExist && newAssetsForAlbum.isNotEmpty) + (!isAlbumExist && selectedAssets.isNotEmpty) || + (isAlbumExist && newAssetsForAlbum.isNotEmpty) ? TextButton( onPressed: () { var payload = AssetSelectionPageResult( diff --git a/mobile/lib/modules/sharing/views/create_shared_album_page.dart b/mobile/lib/modules/sharing/views/create_shared_album_page.dart index 1da6312ed..e2440daf3 100644 --- a/mobile/lib/modules/sharing/views/create_shared_album_page.dart +++ b/mobile/lib/modules/sharing/views/create_shared_album_page.dart @@ -15,11 +15,13 @@ class CreateSharedAlbumPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final albumTitleController = useTextEditingController.fromValue(TextEditingValue.empty); + final albumTitleController = + useTextEditingController.fromValue(TextEditingValue.empty); final albumTitleTextFieldFocusNode = useFocusNode(); final isAlbumTitleTextFieldFocus = useState(false); final isAlbumTitleEmpty = useState(true); - final selectedAssets = ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum; + final selectedAssets = + ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum; _showSelectUserPage() { AutoRouter.of(context).push(const SelectUserForSharingRoute()); @@ -38,8 +40,8 @@ class CreateSharedAlbumPage extends HookConsumerWidget { _onSelectPhotosButtonPressed() async { ref.watch(assetSelectionProvider.notifier).setIsAlbumExist(false); - AssetSelectionPageResult? selectedAsset = - await AutoRouter.of(context).push(const AssetSelectionRoute()); + AssetSelectionPageResult? selectedAsset = await AutoRouter.of(context) + .push(const AssetSelectionRoute()); if (selectedAsset == null) { ref.watch(assetSelectionProvider.notifier).removeAll(); @@ -84,16 +86,22 @@ class CreateSharedAlbumPage extends HookConsumerWidget { child: OutlinedButton.icon( style: OutlinedButton.styleFrom( alignment: Alignment.centerLeft, - padding: const EdgeInsets.symmetric(vertical: 22, horizontal: 16), - side: const BorderSide(color: Color.fromARGB(255, 206, 206, 206)), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5))), + padding: + const EdgeInsets.symmetric(vertical: 22, horizontal: 16), + side: const BorderSide( + color: Color.fromARGB(255, 206, 206, 206)), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5))), onPressed: _onSelectPhotosButtonPressed, icon: const Icon(Icons.add_rounded), label: Padding( padding: const EdgeInsets.only(left: 8.0), child: Text( 'Select Photos', - style: TextStyle(fontSize: 16, color: Colors.grey[700], fontWeight: FontWeight.bold), + style: TextStyle( + fontSize: 16, + color: Colors.grey[700], + fontWeight: FontWeight.bold), ), ), ), @@ -141,7 +149,8 @@ class CreateSharedAlbumPage extends HookConsumerWidget { (BuildContext context, int index) { return GestureDetector( onTap: _onBackgroundTapped, - child: SharedAlbumThumbnailImage(asset: selectedAssets.toList()[index]), + child: SharedAlbumThumbnailImage( + asset: selectedAssets.toList()[index]), ); }, childCount: selectedAssets.length, @@ -169,7 +178,9 @@ class CreateSharedAlbumPage extends HookConsumerWidget { ), actions: [ TextButton( - onPressed: albumTitleController.text.isNotEmpty ? _showSelectUserPage : null, + onPressed: albumTitleController.text.isNotEmpty + ? _showSelectUserPage + : null, child: const Text( 'Share', style: TextStyle( @@ -189,13 +200,13 @@ class CreateSharedAlbumPage extends HookConsumerWidget { pinned: true, floating: false, bottom: PreferredSize( + preferredSize: const Size.fromHeight(66.0), child: Column( children: [ _buildTitleInputField(), _buildControlButton(), ], ), - preferredSize: const Size.fromHeight(66.0), ), ), _buildTitle(), diff --git a/mobile/lib/shared/models/device_info.model.dart b/mobile/lib/shared/models/device_info.model.dart index 359bf6e65..0792e9109 100644 --- a/mobile/lib/shared/models/device_info.model.dart +++ b/mobile/lib/shared/models/device_info.model.dart @@ -1,5 +1,4 @@ import 'dart:convert'; -import 'dart:ffi'; class DeviceInfoRemote { final int id; @@ -66,7 +65,8 @@ class DeviceInfoRemote { String toJson() => json.encode(toMap()); - factory DeviceInfoRemote.fromJson(String source) => DeviceInfoRemote.fromMap(json.decode(source)); + factory DeviceInfoRemote.fromJson(String source) => + DeviceInfoRemote.fromMap(json.decode(source)); @override String toString() { diff --git a/mobile/lib/shared/providers/asset.provider.dart b/mobile/lib/shared/providers/asset.provider.dart index 401ef5f54..ab9c09a08 100644 --- a/mobile/lib/shared/providers/asset.provider.dart +++ b/mobile/lib/shared/providers/asset.provider.dart @@ -46,37 +46,45 @@ class AssetNotifier extends StateNotifier> { } } - final List result = await PhotoManager.editor.deleteWithIds(deleteIdList); + // final List result = await PhotoManager.editor.deleteWithIds(deleteIdList); + await PhotoManager.editor.deleteWithIds(deleteIdList); // Delete asset on server - List? deleteAssetResult = await _assetService.deleteAssets(deleteAssets); + List? deleteAssetResult = + await _assetService.deleteAssets(deleteAssets); if (deleteAssetResult == null) { return; } for (var asset in deleteAssetResult) { if (asset.status == 'success') { - state = state.where((immichAsset) => immichAsset.id != asset.id).toList(); + state = + state.where((immichAsset) => immichAsset.id != asset.id).toList(); } } } } -final assetProvider = StateNotifierProvider>((ref) { +final assetProvider = + StateNotifierProvider>((ref) { return AssetNotifier(ref); }); final assetGroupByDateTimeProvider = StateProvider((ref) { var assets = ref.watch(assetProvider); - assets.sortByCompare((e) => DateTime.parse(e.createdAt), (a, b) => b.compareTo(a)); - return assets.groupListsBy((element) => DateFormat('y-MM-dd').format(DateTime.parse(element.createdAt))); + assets.sortByCompare( + (e) => DateTime.parse(e.createdAt), (a, b) => b.compareTo(a)); + return assets.groupListsBy((element) => + DateFormat('y-MM-dd').format(DateTime.parse(element.createdAt))); }); final assetGroupByMonthYearProvider = StateProvider((ref) { var assets = ref.watch(assetProvider); - assets.sortByCompare((e) => DateTime.parse(e.createdAt), (a, b) => b.compareTo(a)); + assets.sortByCompare( + (e) => DateTime.parse(e.createdAt), (a, b) => b.compareTo(a)); - return assets.groupListsBy((element) => DateFormat('MMMM, y').format(DateTime.parse(element.createdAt))); + return assets.groupListsBy((element) => + DateFormat('MMMM, y').format(DateTime.parse(element.createdAt))); }); diff --git a/mobile/lib/shared/providers/release_info.provider.dart b/mobile/lib/shared/providers/release_info.provider.dart index dabe2ab7d..16659960e 100644 --- a/mobile/lib/shared/providers/release_info.provider.dart +++ b/mobile/lib/shared/providers/release_info.provider.dart @@ -34,7 +34,8 @@ class ReleaseInfoNotifier extends StateNotifier { return; } - if (latestTagVersion.isNotEmpty && localReleaseVersion != latestTagVersion) { + if (latestTagVersion.isNotEmpty && + localReleaseVersion != latestTagVersion) { VersionAnnouncementOverlayController.appLoader.show(); return; } @@ -54,4 +55,5 @@ class ReleaseInfoNotifier extends StateNotifier { } } -final releaseInfoProvider = StateNotifierProvider((ref) => ReleaseInfoNotifier()); +final releaseInfoProvider = StateNotifierProvider( + (ref) => ReleaseInfoNotifier()); diff --git a/mobile/lib/shared/providers/websocket.provider.dart b/mobile/lib/shared/providers/websocket.provider.dart index e5bf907f7..e514a8286 100644 --- a/mobile/lib/shared/providers/websocket.provider.dart +++ b/mobile/lib/shared/providers/websocket.provider.dart @@ -3,12 +3,11 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/shared/providers/asset.provider.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; -import 'package:socket_io_client/socket_io_client.dart'; - import 'package:immich_mobile/constants/hive_box.dart'; import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; +import 'package:immich_mobile/shared/models/immich_asset.model.dart'; +import 'package:immich_mobile/shared/providers/asset.provider.dart'; +import 'package:socket_io_client/socket_io_client.dart'; class WebscoketState { final Socket? socket; @@ -30,13 +29,16 @@ class WebscoketState { } @override - String toString() => 'WebscoketState(socket: $socket, isConnected: $isConnected)'; + String toString() => + 'WebscoketState(socket: $socket, isConnected: $isConnected)'; @override bool operator ==(Object other) { if (identical(this, other)) return true; - return other is WebscoketState && other.socket == socket && other.isConnected == isConnected; + return other is WebscoketState && + other.socket == socket && + other.isConnected == isConnected; } @override @@ -44,7 +46,8 @@ class WebscoketState { } class WebsocketNotifier extends StateNotifier { - WebsocketNotifier(this.ref) : super(WebscoketState(socket: null, isConnected: false)) { + WebsocketNotifier(this.ref) + : super(WebscoketState(socket: null, isConnected: false)) { debugPrint("Init websocket instance"); } @@ -59,6 +62,7 @@ class WebsocketNotifier extends StateNotifier { try { debugPrint("[WEBSOCKET] Attempting to connect to ws"); // Configure socket transports must be sepecified + Socket socket = io( endpoint, OptionBuilder() @@ -122,6 +126,7 @@ class WebsocketNotifier extends StateNotifier { } } -final websocketProvider = StateNotifierProvider((ref) { +final websocketProvider = + StateNotifierProvider((ref) { return WebsocketNotifier(ref); }); diff --git a/mobile/lib/shared/services/server_info.service.dart b/mobile/lib/shared/services/server_info.service.dart index 6fcbda4da..bd1b08b6e 100644 --- a/mobile/lib/shared/services/server_info.service.dart +++ b/mobile/lib/shared/services/server_info.service.dart @@ -1,9 +1,8 @@ import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; -import 'package:immich_mobile/shared/models/mapbox_info.model.dart'; +import 'package:immich_mobile/shared/models/server_info.model.dart'; import 'package:immich_mobile/shared/models/server_version.model.dart'; import 'package:immich_mobile/shared/services/network.service.dart'; -import 'package:immich_mobile/shared/models/server_info.model.dart'; class ServerInfoService { final NetworkService _networkService = NetworkService(); diff --git a/mobile/lib/shared/views/version_announcement_overlay.dart b/mobile/lib/shared/views/version_announcement_overlay.dart index 474a13408..ff90a8a23 100644 --- a/mobile/lib/shared/views/version_announcement_overlay.dart +++ b/mobile/lib/shared/views/version_announcement_overlay.dart @@ -12,8 +12,9 @@ class VersionAnnouncementOverlay extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { void goToReleaseNote() async { - final Uri _url = Uri.parse('https://github.com/alextran1502/immich/releases/latest'); - await launchUrl(_url); + final Uri url = + Uri.parse('https://github.com/alextran1502/immich/releases/latest'); + await launchUrl(url); } void onAcknowledgeTapped() { @@ -21,7 +22,8 @@ class VersionAnnouncementOverlay extends HookConsumerWidget { } return ValueListenableBuilder( - valueListenable: VersionAnnouncementOverlayController.appLoader.loaderShowingNotifier, + valueListenable: + VersionAnnouncementOverlayController.appLoader.loaderShowingNotifier, builder: (context, shouldShow, child) { if (shouldShow) { return Scaffold( @@ -51,10 +53,14 @@ class VersionAnnouncementOverlay extends HookConsumerWidget { child: RichText( text: TextSpan( style: const TextStyle( - fontSize: 14, fontFamily: 'WorkSans', color: Colors.black87, height: 1.2), + fontSize: 14, + fontFamily: 'WorkSans', + color: Colors.black87, + height: 1.2), children: [ const TextSpan( - text: 'Hi friend, there is a new release of', + text: + 'Hi friend, there is a new release of', ), const TextSpan( text: ' Immich ', @@ -65,14 +71,16 @@ class VersionAnnouncementOverlay extends HookConsumerWidget { ), ), const TextSpan( - text: "please take your time to visit the ", + text: + "please take your time to visit the ", ), TextSpan( text: "release note", style: const TextStyle( decoration: TextDecoration.underline, ), - recognizer: TapGestureRecognizer()..onTap = goToReleaseNote, + recognizer: TapGestureRecognizer() + ..onTap = goToReleaseNote, ), const TextSpan( text: @@ -91,7 +99,8 @@ class VersionAnnouncementOverlay extends HookConsumerWidget { primary: Colors.indigo, onPrimary: Colors.grey[50], elevation: 2, - padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25), + padding: const EdgeInsets.symmetric( + vertical: 10, horizontal: 25), ), onPressed: onAcknowledgeTapped, child: const Text( @@ -119,7 +128,8 @@ class VersionAnnouncementOverlay extends HookConsumerWidget { } class VersionAnnouncementOverlayController { - static final VersionAnnouncementOverlayController appLoader = VersionAnnouncementOverlayController(); + static final VersionAnnouncementOverlayController appLoader = + VersionAnnouncementOverlayController(); ValueNotifier loaderShowingNotifier = ValueNotifier(false); ValueNotifier loaderTextNotifier = ValueNotifier('error message'); diff --git a/mobile/lib/utils/dio_http_interceptor.dart b/mobile/lib/utils/dio_http_interceptor.dart index be9958ae9..335e7035b 100644 --- a/mobile/lib/utils/dio_http_interceptor.dart +++ b/mobile/lib/utils/dio_http_interceptor.dart @@ -1,5 +1,4 @@ import 'package:dio/dio.dart'; -import 'package:flutter/material.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:immich_mobile/constants/hive_box.dart'; diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index d1b1a5771..90caf6d1d 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -198,7 +198,7 @@ packages: source: hosted version: "4.1.0" collection: - dependency: transitive + dependency: "direct main" description: name: collection url: "https://pub.dartlang.org" @@ -233,7 +233,7 @@ packages: source: hosted version: "0.17.1" cupertino_icons: - dependency: "direct main" + dependency: transitive description: name: cupertino_icons url: "https://pub.dartlang.org" @@ -334,7 +334,7 @@ packages: name: flutter_lints url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "2.0.1" flutter_map: dependency: "direct main" description: @@ -465,12 +465,12 @@ packages: source: hosted version: "3.2.0" http_parser: - dependency: transitive + dependency: "direct main" description: name: http_parser url: "https://pub.dartlang.org" source: hosted - version: "4.0.0" + version: "4.0.1" image: dependency: transitive description: @@ -542,7 +542,7 @@ packages: source: hosted version: "4.5.0" latlong2: - dependency: transitive + dependency: "direct main" description: name: latlong2 url: "https://pub.dartlang.org" @@ -554,7 +554,7 @@ packages: name: lints url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "2.0.0" lists: dependency: transitive description: @@ -668,19 +668,19 @@ packages: source: hosted version: "1.0.5" path: - dependency: transitive + dependency: "direct main" description: name: path url: "https://pub.dartlang.org" source: hosted version: "1.8.1" path_provider: - dependency: transitive + dependency: "direct main" description: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "2.0.10" + version: "2.0.11" path_provider_android: dependency: transitive description: @@ -861,13 +861,6 @@ packages: description: flutter source: sdk version: "0.0.99" - sliver_tools: - dependency: "direct main" - description: - name: sliver_tools - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.6" socket_io_client: dependency: "direct main" description: @@ -1141,13 +1134,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.10" - visibility_detector: - dependency: "direct main" - description: - name: visibility_detector - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.2" wakelock: dependency: transitive description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 51ee30611..c4d3ad9c3 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -10,7 +10,7 @@ environment: dependencies: flutter: sdk: flutter - cupertino_icons: ^1.0.2 + photo_manager: ^2.0.6 flutter_hooks: ^0.18.0 hooks_riverpod: ^2.0.0-dev.0 @@ -23,12 +23,10 @@ dependencies: auto_route: ^4.0.1 exif: ^3.1.1 transparent_image: ^2.0.0 - visibility_detector: ^0.2.2 flutter_launcher_icons: "^0.9.2" fluttertoast: ^8.0.8 video_player: ^2.2.18 chewie: ^1.2.2 - sliver_tools: ^0.2.5 badges: ^2.0.2 photo_view: ^0.14.0 socket_io_client: ^2.0.0-beta.4-nullsafety.0 @@ -43,10 +41,17 @@ dependencies: http: 0.13.4 cancellation_token_http: ^1.1.0 + path: ^1.8.1 + path_provider: ^2.0.11 + latlong2: ^0.8.1 + collection: ^1.16.0 + http_parser: ^4.0.1 + + dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.0 + flutter_lints: ^2.0.1 hive_generator: ^1.1.2 build_runner: ^2.1.7 auto_route_generator: ^4.0.0