diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index 9118ad424..c386ecde7 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -81,7 +81,7 @@ jobs: run: mise //mobile:codegen - name: Run analyze - run: mise //mobile:analyze + run: mise //mobile:analyze --full - name: Run format run: mise //mobile:format diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml index 39a48666d..06377279d 100644 --- a/mobile/analysis_options.yaml +++ b/mobile/analysis_options.yaml @@ -104,6 +104,19 @@ analyzer: deprecated_member_use_from_same_package: error dart_code_metrics: + exclude: + unused-code: + - '**/*.g.dart' + - '**/*.freezed.dart' + - '**/*.mapper.dart' + - '**/*.drift.dart' + - '**/*.gr.dart' + - 'lib/data/db/main/database.steps.dart' + # Ideally we wouldn't do this, as this allows for non-column used code, but not complaining + # about the columns is more important + - 'lib/data/db/**/table/**' + - 'lib/widgets/photo_view/**' + - 'test/drift/**/generated/**' rules: - banned-usage: entries: diff --git a/mobile/lib/constants/colors.dart b/mobile/lib/constants/colors.dart index 655d2d9c0..09783cc9a 100644 --- a/mobile/lib/constants/colors.dart +++ b/mobile/lib/constants/colors.dart @@ -5,5 +5,3 @@ enum ImmichColorPreset { indigo, deepPurple, pink, red, orange, yellow, lime, gr const Color immichBrandColorLight = Color(0xFF4150AF); const Color immichBrandColorDark = Color(0xFFACCBFA); const Color whiteOpacity75 = Color.fromRGBO(255, 255, 255, 0.75); -const Color red400 = Color(0xFFEF5350); -const Color grey200 = Color(0xFFEEEEEE); diff --git a/mobile/lib/constants/constants.dart b/mobile/lib/constants/constants.dart index c2a13c3ec..fb425acaf 100644 --- a/mobile/lib/constants/constants.dart +++ b/mobile/lib/constants/constants.dart @@ -7,11 +7,9 @@ const int kLogTruncateLimit = 2000; // Sync const int kSyncEventBatchSize = 5000; -const int kFetchLocalAssetsBatchSize = 40000; // Hash batch limits final int kBatchHashFileLimit = Platform.isIOS ? 32 : 512; -const int kBatchHashSizeLimit = 1024 * 1024 * 1024; // 1GB // Secure storage keys const String kSecuredPinCode = "secured_pin_code"; @@ -51,6 +49,7 @@ const String kImmichLatestRelease = "https://github.com/immich-app/immich/releas const int kPhotoTabIndex = 0; const int kSearchTabIndex = 1; const int kAlbumTabIndex = 2; +// ignore: unused-code const int kLibraryTabIndex = 3; // Workaround for SQLite's variable limit (SQLITE_MAX_VARIABLE_NUMBER = 32766) diff --git a/mobile/lib/constants/filters.dart b/mobile/lib/constants/filters.dart deleted file mode 100644 index e77bcfbf1..000000000 --- a/mobile/lib/constants/filters.dart +++ /dev/null @@ -1,148 +0,0 @@ -import 'package:flutter/material.dart'; - -const List filters = [ - //Original - ColorFilter.matrix([1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0]), - //Vintage - ColorFilter.matrix([0.8, 0.1, 0.1, 0, 20, 0.1, 0.8, 0.1, 0, 20, 0.1, 0.1, 0.8, 0, 20, 0, 0, 0, 1, 0]), - //Mood - ColorFilter.matrix([1.2, 0.1, 0.1, 0, 10, 0.1, 1, 0.1, 0, 10, 0.1, 0.1, 1, 0, 10, 0, 0, 0, 1, 0]), - //Crisp - ColorFilter.matrix([1.2, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]), - //Cool - ColorFilter.matrix([0.9, 0, 0.2, 0, 0, 0, 1, 0.1, 0, 0, 0.1, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]), - //Blush - ColorFilter.matrix([1.1, 0.1, 0.1, 0, 10, 0.1, 1, 0.1, 0, 10, 0.1, 0.1, 1, 0, 5, 0, 0, 0, 1, 0]), - //Sunkissed - ColorFilter.matrix([1.3, 0, 0.1, 0, 15, 0, 1.1, 0.1, 0, 10, 0, 0, 0.9, 0, 5, 0, 0, 0, 1, 0]), - //Fresh - ColorFilter.matrix([1.2, 0, 0, 0, 20, 0, 1.2, 0, 0, 20, 0, 0, 1.1, 0, 20, 0, 0, 0, 1, 0]), - //Classic - ColorFilter.matrix([1.1, 0, -0.1, 0, 10, -0.1, 1.1, 0.1, 0, 5, 0, -0.1, 1.1, 0, 0, 0, 0, 0, 1, 0]), - //Lomo-ish - ColorFilter.matrix([1.5, 0, 0.1, 0, 0, 0, 1.45, 0, 0, 0, 0.1, 0, 1.3, 0, 0, 0, 0, 0, 1, 0]), - //Nashville - ColorFilter.matrix([1.2, 0.15, -0.15, 0, 15, 0.1, 1.1, 0.1, 0, 10, -0.05, 0.2, 1.25, 0, 5, 0, 0, 0, 1, 0]), - //Valencia - ColorFilter.matrix([1.15, 0.1, 0.1, 0, 20, 0.1, 1.1, 0, 0, 10, 0.1, 0.1, 1.2, 0, 5, 0, 0, 0, 1, 0]), - //Clarendon - ColorFilter.matrix([1.2, 0, 0, 0, 10, 0, 1.25, 0, 0, 10, 0, 0, 1.3, 0, 10, 0, 0, 0, 1, 0]), - //Moon - ColorFilter.matrix([0.33, 0.33, 0.33, 0, 0, 0.33, 0.33, 0.33, 0, 0, 0.33, 0.33, 0.33, 0, 0, 0, 0, 0, 1, 0]), - //Willow - ColorFilter.matrix([0.5, 0.5, 0.5, 0, 20, 0.5, 0.5, 0.5, 0, 20, 0.5, 0.5, 0.5, 0, 20, 0, 0, 0, 1, 0]), - //Kodak - ColorFilter.matrix([1.3, 0.1, -0.1, 0, 10, 0, 1.25, 0.1, 0, 10, 0, -0.1, 1.1, 0, 5, 0, 0, 0, 1, 0]), - //Frost - ColorFilter.matrix([0.8, 0.2, 0.1, 0, 0, 0.2, 1.1, 0.1, 0, 0, 0.1, 0.1, 1.2, 0, 10, 0, 0, 0, 1, 0]), - //Night Vision - ColorFilter.matrix([0.1, 0.95, 0.2, 0, 0, 0.1, 1.5, 0.1, 0, 0, 0.2, 0.7, 0, 0, 0, 0, 0, 0, 1, 0]), - //Sunset - ColorFilter.matrix([1.5, 0.2, 0, 0, 0, 0.1, 0.9, 0.1, 0, 0, -0.1, -0.2, 1.3, 0, 0, 0, 0, 0, 1, 0]), - //Noir - ColorFilter.matrix([1.3, -0.3, 0.1, 0, 0, -0.1, 1.2, -0.1, 0, 0, 0.1, -0.2, 1.3, 0, 0, 0, 0, 0, 1, 0]), - //Dreamy - ColorFilter.matrix([1.1, 0.1, 0.1, 0, 0, 0.1, 1.1, 0.1, 0, 0, 0.1, 0.1, 1.1, 0, 15, 0, 0, 0, 1, 0]), - //Sepia - ColorFilter.matrix([0.393, 0.769, 0.189, 0, 0, 0.349, 0.686, 0.168, 0, 0, 0.272, 0.534, 0.131, 0, 0, 0, 0, 0, 1, 0]), - //Radium - ColorFilter.matrix([ - 1.438, - -0.062, - -0.062, - 0, - 0, - -0.122, - 1.378, - -0.122, - 0, - 0, - -0.016, - -0.016, - 1.483, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), - //Aqua - ColorFilter.matrix([ - 0.2126, - 0.7152, - 0.0722, - 0, - 0, - 0.2126, - 0.7152, - 0.0722, - 0, - 0, - 0.7873, - 0.2848, - 0.9278, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), - //Purple Haze - ColorFilter.matrix([1.3, 0, 1.2, 0, 0, 0, 1.1, 0, 0, 0, 0.2, 0, 1.3, 0, 0, 0, 0, 0, 1, 0]), - //Lemonade - ColorFilter.matrix([1.2, 0.1, 0, 0, 0, 0, 1.1, 0.2, 0, 0, 0.1, 0, 0.7, 0, 0, 0, 0, 0, 1, 0]), - //Caramel - ColorFilter.matrix([1.6, 0.2, 0, 0, 0, 0.1, 1.3, 0.1, 0, 0, 0, 0.1, 0.9, 0, 0, 0, 0, 0, 1, 0]), - //Peachy - ColorFilter.matrix([1.3, 0.5, 0, 0, 0, 0.2, 1.1, 0.3, 0, 0, 0.1, 0.1, 1.2, 0, 0, 0, 0, 0, 1, 0]), - //Neon - ColorFilter.matrix([1, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 0]), - //Cold Morning - ColorFilter.matrix([0.9, 0.1, 0.2, 0, 0, 0, 1, 0.1, 0, 0, 0.1, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]), - //Lush - ColorFilter.matrix([0.9, 0.2, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1.1, 0, 0, 0, 0, 0, 1, 0]), - //Urban Neon - ColorFilter.matrix([1.1, 0, 0.3, 0, 0, 0, 0.9, 0.3, 0, 0, 0.3, 0.1, 1.2, 0, 0, 0, 0, 0, 1, 0]), - //Monochrome - ColorFilter.matrix([0.6, 0.2, 0.2, 0, 0, 0.2, 0.6, 0.2, 0, 0, 0.2, 0.2, 0.7, 0, 0, 0, 0, 0, 1, 0]), -]; - -const List filterNames = [ - 'Original', - 'Vintage', - 'Mood', - 'Crisp', - 'Cool', - 'Blush', - 'Sunkissed', - 'Fresh', - 'Classic', - 'Lomo-ish', - 'Nashville', - 'Valencia', - 'Clarendon', - 'Moon', - 'Willow', - 'Kodak', - 'Frost', - 'Night Vision', - 'Sunset', - 'Noir', - 'Dreamy', - 'Sepia', - 'Radium', - 'Aqua', - 'Purple Haze', - 'Lemonade', - 'Caramel', - 'Peachy', - 'Neon', - 'Cold Morning', - 'Lush', - 'Urban Neon', - 'Monochrome', -]; diff --git a/mobile/lib/data/data_controller.dart b/mobile/lib/data/data_controller.dart index 42119a803..bb82d7be2 100644 --- a/mobile/lib/data/data_controller.dart +++ b/mobile/lib/data/data_controller.dart @@ -61,9 +61,12 @@ class DataController { return (logDb, false); } + // ignore: unused-code late final PeopleDatabaseRepository peopleDb = PeopleDatabaseRepository(_db); + // ignore: unused-code late final PersonApiRepository personApi = PersonApiRepository(PeopleApi(_apiClient)); + // ignore: unused-code late final ActivityApiRepository activityApi = ActivityApiRepository(ActivitiesApi(_apiClient)); /// Direct database access for the logic that has not yet been migrated diff --git a/mobile/lib/data/db/logger/database.dart b/mobile/lib/data/db/logger/database.dart index 1c4dcadd0..b2ba3d416 100644 --- a/mobile/lib/data/db/logger/database.dart +++ b/mobile/lib/data/db/logger/database.dart @@ -7,6 +7,7 @@ import 'package:sqlite_async/sqlite_async.dart'; @DriftDatabase(tables: [LogMessageEntity]) class DriftLogger extends $DriftLogger { + // ignore: unused-code DriftLogger.fromExecutor(super.executor); DriftLogger.sqlite(SqliteConnection db) : super(SqliteAsyncDriftConnection(db)); diff --git a/mobile/lib/data/db/main/database.dart b/mobile/lib/data/db/main/database.dart index 48667c56e..6f3b3c249 100644 --- a/mobile/lib/data/db/main/database.dart +++ b/mobile/lib/data/db/main/database.dart @@ -112,6 +112,7 @@ import 'package:sqlite_async/sqlite_async.dart'; class Drift extends $Drift { final SqliteConnectionPool? _updatePool; + @visibleForTesting Drift(super.executor) : _updatePool = null; Drift.sqlite(SqliteConnection db, SqliteConnectionPool updatePool) diff --git a/mobile/lib/data/db/util/asset_mixin.dart b/mobile/lib/data/db/util/asset_mixin.dart index 356c65b8a..c14f67d33 100644 --- a/mobile/lib/data/db/util/asset_mixin.dart +++ b/mobile/lib/data/db/util/asset_mixin.dart @@ -1,3 +1,5 @@ +// ignore_for_file: unused-code + import 'package:drift/drift.dart'; import 'package:immich_mobile/data/db/util/datetime_clamp_type.dart'; import 'package:immich_mobile/data/db/util/defaults_mixin.dart'; diff --git a/mobile/lib/domain/models/asset/asset_metadata.model.dart b/mobile/lib/domain/models/asset/asset_metadata.model.dart index ad326da24..a4acdb0fd 100644 --- a/mobile/lib/domain/models/asset/asset_metadata.model.dart +++ b/mobile/lib/domain/models/asset/asset_metadata.model.dart @@ -1,3 +1,6 @@ +// Types are used for direct JSON serialization +// ignore_for_file: unused-code + import 'package:freezed_annotation/freezed_annotation.dart'; part 'asset_metadata.model.freezed.dart'; diff --git a/mobile/lib/domain/models/asset/base_asset.model.dart b/mobile/lib/domain/models/asset/base_asset.model.dart index ac89fe9a0..2083c0c48 100644 --- a/mobile/lib/domain/models/asset/base_asset.model.dart +++ b/mobile/lib/domain/models/asset/base_asset.model.dart @@ -58,6 +58,7 @@ sealed class BaseAsset { bool get hasRemote => storage == AssetState.remote || storage == AssetState.merged; bool get hasLocal => storage == AssetState.local || storage == AssetState.merged; + // ignore: unused-code bool get isLocalOnly => storage == AssetState.local; bool get isRemoteOnly => storage == AssetState.remote; bool get isMerged => storage == .merged; diff --git a/mobile/lib/domain/models/asset/remote_asset.model.dart b/mobile/lib/domain/models/asset/remote_asset.model.dart index 5707329ba..9b49d48f1 100644 --- a/mobile/lib/domain/models/asset/remote_asset.model.dart +++ b/mobile/lib/domain/models/asset/remote_asset.model.dart @@ -73,6 +73,7 @@ class RemoteAsset extends BaseAsset { bool get isStacked => stackId != null; + // ignore: unused-code bool get isArchived => visibility == .archive; bool get isLocked => visibility == .locked; diff --git a/mobile/lib/domain/models/asset_face.model.dart b/mobile/lib/domain/models/asset_face.model.dart deleted file mode 100644 index c65f159fe..000000000 --- a/mobile/lib/domain/models/asset_face.model.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'asset_face.model.freezed.dart'; - -// Model for an asset face stored in the server -@freezed -abstract class AssetFace with _$AssetFace { - const factory AssetFace({ - required String id, - required String assetId, - String? personId, - required int imageWidth, - required int imageHeight, - required int boundingBoxX1, - required int boundingBoxY1, - required int boundingBoxX2, - required int boundingBoxY2, - required String sourceType, - }) = _AssetFace; -} diff --git a/mobile/lib/domain/models/events.model.dart b/mobile/lib/domain/models/events.model.dart index ef034a0d0..08dfb66bd 100644 --- a/mobile/lib/domain/models/events.model.dart +++ b/mobile/lib/domain/models/events.model.dart @@ -20,16 +20,6 @@ class ViewerShowDetailsEvent extends Event { const ViewerShowDetailsEvent(); } -class ViewerReloadAssetEvent extends Event { - const ViewerReloadAssetEvent(); -} - -class ViewerStackAssetDeletedEvent extends Event { - final int stackIndex; - - const ViewerStackAssetDeletedEvent({required this.stackIndex}); -} - // Multi-Select Events class MultiSelectToggleEvent extends Event { final bool isEnabled; diff --git a/mobile/lib/domain/models/memory.model.dart b/mobile/lib/domain/models/memory.model.dart index 465b2ef6c..055a5437d 100644 --- a/mobile/lib/domain/models/memory.model.dart +++ b/mobile/lib/domain/models/memory.model.dart @@ -26,6 +26,7 @@ abstract class MemoryData with _$MemoryData { return MemoryData(year: map['year'] as int); } + @visibleForTesting String toJson() => json.encode(toMap()); factory MemoryData.fromJson(String source) => MemoryData.fromMap(json.decode(source) as Map); diff --git a/mobile/lib/domain/models/stack.model.dart b/mobile/lib/domain/models/stack.model.dart index a016d6cd3..aa91e250a 100644 --- a/mobile/lib/domain/models/stack.model.dart +++ b/mobile/lib/domain/models/stack.model.dart @@ -3,18 +3,6 @@ import 'package:freezed_annotation/freezed_annotation.dart'; part 'stack.model.freezed.dart'; -// Model for a stack stored in the server -@freezed -abstract class Stack with _$Stack { - const factory Stack({ - required String id, - required DateTime createdAt, - required DateTime updatedAt, - required String ownerId, - required String primaryAssetId, - }) = _Stack; -} - @freezed abstract class StackResponse with _$StackResponse { const factory StackResponse({required String id, required String primaryAssetId, required List assetIds}) = diff --git a/mobile/lib/domain/models/user.model.dart b/mobile/lib/domain/models/user.model.dart index 3b65e4390..469be853b 100644 --- a/mobile/lib/domain/models/user.model.dart +++ b/mobile/lib/domain/models/user.model.dart @@ -101,17 +101,6 @@ abstract class UserDto with _$UserDto { quotaUsageInBytes.hashCode; } -@freezed -abstract class PartnerUserDto with _$PartnerUserDto { - const factory PartnerUserDto({ - required String id, - required String email, - required String name, - required bool inTimeline, - String? profileImagePath, - }) = _PartnerUserDto; -} - class User { final String id; final String name; @@ -152,50 +141,10 @@ class User { int get hashCode => Object.hash(id, name, email, profileChangedAt, hasProfileImage, avatarColor); } -class AuthUser extends User { - final bool isAdmin; - final String? pinCode; - final int? quotaSizeInBytes; - final int quotaUsageInBytes; - - const AuthUser({ - required super.id, - required super.name, - required super.email, - required super.profileChangedAt, - required super.hasProfileImage, - super.avatarColor, - this.isAdmin = false, - this.pinCode, - this.quotaSizeInBytes = 0, - this.quotaUsageInBytes = 0, - }); - - @override - String toString() { - return 'AuthUser(user: ${super.toString()}, isAdmin: $isAdmin, pinCode: $pinCode, quotaSizeInBytes: $quotaSizeInBytes, quotaUsageInBytes: $quotaUsageInBytes)'; - } - - @override - bool operator ==(covariant AuthUser other) { - if (identical(this, other)) { - return true; - } - - return super == other && - other.isAdmin == isAdmin && - other.pinCode == pinCode && - other.quotaSizeInBytes == quotaSizeInBytes && - other.quotaUsageInBytes == quotaUsageInBytes; - } - - @override - int get hashCode => Object.hash(super.hashCode, isAdmin, pinCode, quotaSizeInBytes, quotaUsageInBytes); -} - class Partner extends User { final bool inTimeline; + @visibleForTesting const Partner({ required super.id, required super.name, diff --git a/mobile/lib/domain/services/background_worker.service.dart b/mobile/lib/domain/services/background_worker.service.dart index 560f4aa56..d7e76f6c2 100644 --- a/mobile/lib/domain/services/background_worker.service.dart +++ b/mobile/lib/domain/services/background_worker.service.dart @@ -56,6 +56,7 @@ class BackgroundWorkerFgService { ); } + // ignore: unused-code Future disable() => _foregroundHostApi.disable(); Future wasLaunchedInBackground() => _foregroundHostApi.wasLaunchedInBackground(); diff --git a/mobile/lib/domain/services/memory.service.dart b/mobile/lib/domain/services/memory.service.dart index 311c76d8b..4e085411e 100644 --- a/mobile/lib/domain/services/memory.service.dart +++ b/mobile/lib/domain/services/memory.service.dart @@ -1,14 +1,11 @@ import 'package:immich_mobile/domain/models/memory.model.dart'; import 'package:immich_mobile/infrastructure/repositories/memory.repository.dart'; -import 'package:logging/logging.dart'; /// Accesses Memories; a specialized collection of assets with some novel display mechanism class MemoryService { - final log = Logger("MemoryService"); - final MemoryRepository _repository; - MemoryService(this._repository); + const MemoryService(this._repository); Future> getMemoryLane(String ownerId) { return _repository.getAll(ownerId); diff --git a/mobile/lib/domain/services/setting.service.dart b/mobile/lib/domain/services/setting.service.dart index 1fedf692b..5aae639b0 100644 --- a/mobile/lib/domain/services/setting.service.dart +++ b/mobile/lib/domain/services/setting.service.dart @@ -1,11 +1,6 @@ import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/domain/services/store.service.dart'; -// Singleton instance of SettingsService, to use in places -// where reactivity is not required -// ignore: non_constant_identifier_names -final AppSetting = SettingsService(storeService: StoreService.I); - class SettingsService { final StoreService _storeService; diff --git a/mobile/lib/domain/services/store.service.dart b/mobile/lib/domain/services/store.service.dart index ea830dc32..1ec6bd541 100644 --- a/mobile/lib/domain/services/store.service.dart +++ b/mobile/lib/domain/services/store.service.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:flutter/foundation.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; @@ -95,6 +96,7 @@ class StoreService { } /// Clears all values from the store (cache and DB) + @visibleForTesting Future clear() async { await _storeRepository.deleteAll(); _cache.clear(); diff --git a/mobile/lib/extensions/network_capability_extensions.dart b/mobile/lib/extensions/network_capability_extensions.dart index aeefc11e3..8fad986e2 100644 --- a/mobile/lib/extensions/network_capability_extensions.dart +++ b/mobile/lib/extensions/network_capability_extensions.dart @@ -1,8 +1,11 @@ import 'package:immich_mobile/platform/connectivity_api.g.dart'; extension NetworkCapabilitiesGetters on List { + // ignore: unused-code bool get hasCellular => contains(NetworkCapability.cellular); + // ignore: unused-code bool get hasWifi => contains(NetworkCapability.wifi); + // ignore: unused-code bool get hasVpn => contains(NetworkCapability.vpn); bool get isUnmetered => contains(NetworkCapability.unmetered); } diff --git a/mobile/lib/extensions/string_extensions.dart b/mobile/lib/extensions/string_extensions.dart index 3c318bd2e..1a4bd637d 100644 --- a/mobile/lib/extensions/string_extensions.dart +++ b/mobile/lib/extensions/string_extensions.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - import 'package:diacritic/diacritic.dart' as diacritic; extension StringExtension on String { @@ -32,11 +30,3 @@ extension DurationExtension on String { return int.parse(this); } } - -Map? tryJsonDecode(dynamic json) { - try { - return jsonDecode(json) as Map; - } catch (e) { - return null; - } -} diff --git a/mobile/lib/infrastructure/loaders/image_request.dart b/mobile/lib/infrastructure/loaders/image_request.dart index 6b2be218d..f15fd01b4 100644 --- a/mobile/lib/infrastructure/loaders/image_request.dart +++ b/mobile/lib/infrastructure/loaders/image_request.dart @@ -19,6 +19,7 @@ abstract class ImageRequest { final int requestId = _nextRequestId++; bool _isCancelled = false; + // ignore: unused-code bool get isCancelled => _isCancelled; ImageRequest(); diff --git a/mobile/lib/infrastructure/repositories/network.repository.dart b/mobile/lib/infrastructure/repositories/network.repository.dart index 75f785016..6c39316ed 100644 --- a/mobile/lib/infrastructure/repositories/network.repository.dart +++ b/mobile/lib/infrastructure/repositories/network.repository.dart @@ -50,8 +50,6 @@ class NetworkRepository { } } - const NetworkRepository(); - /// Returns a shared HTTP client that uses native SSL configuration. /// /// On iOS: Uses SharedURLSessionManager's URLSession. diff --git a/mobile/lib/infrastructure/repositories/partner.repository.dart b/mobile/lib/infrastructure/repositories/partner.repository.dart index 29dc4df81..068f00f1b 100644 --- a/mobile/lib/infrastructure/repositories/partner.repository.dart +++ b/mobile/lib/infrastructure/repositories/partner.repository.dart @@ -1,4 +1,5 @@ import 'package:drift/drift.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/data/db/main/database.dart'; import 'package:immich_mobile/data/db/main/table/user/partner.drift.dart'; @@ -12,6 +13,7 @@ class PartnerRepository extends DatabaseAccessor with $PartnerRepositoryM Drift get _db => attachedDatabase; + @visibleForTesting Future get({required String sharedById, required String sharedWithId}) => (_db.select(_db.partnerEntity).join([ innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)), diff --git a/mobile/lib/infrastructure/repositories/store.repository.dart b/mobile/lib/infrastructure/repositories/store.repository.dart index c2b437987..34094ce31 100644 --- a/mobile/lib/infrastructure/repositories/store.repository.dart +++ b/mobile/lib/infrastructure/repositories/store.repository.dart @@ -1,4 +1,5 @@ import 'package:drift/drift.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:immich_mobile/data/db/main/database.dart'; import 'package:immich_mobile/data/db/main/table/app/store.drift.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; @@ -40,6 +41,7 @@ class StoreRepository extends DatabaseAccessor with $StoreRepositoryMixin return true; } + @visibleForTesting Future tryGet(StoreKey key) async { final entity = await _db.managers.storeEntity.filter((entity) => entity.id.equals(key.id)).getSingleOrNull(); if (entity == null) { diff --git a/mobile/lib/mixins/error_logger.mixin.dart b/mobile/lib/mixins/error_logger.mixin.dart deleted file mode 100644 index 482420a97..000000000 --- a/mobile/lib/mixins/error_logger.mixin.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:logging/logging.dart'; - -typedef AsyncFuture = Future>; - -mixin ErrorLoggerMixin { - abstract final Logger logger; - - // ignore: unintended_html_in_doc_comment - /// Returns an AsyncValue if the future is successfully executed - /// Else, logs the error to the overridden logger and returns an AsyncError<> - AsyncFuture guardError( - Future Function() fn, { - required String errorMessage, - Level logLevel = Level.SEVERE, - }) async { - try { - final result = await fn(); - return AsyncData(result); - } catch (error, stackTrace) { - logger.log(logLevel, errorMessage, error, stackTrace); - return AsyncError(error, stackTrace); - } - } - - /// Returns the result of the future if success - /// Else, logs the error and returns the default value - Future logError( - Future Function() fn, { - required T defaultValue, - required String errorMessage, - Level logLevel = Level.SEVERE, - }) async { - try { - return await fn(); - } catch (error, stackTrace) { - logger.log(logLevel, errorMessage, error, stackTrace); - } - return defaultValue; - } -} diff --git a/mobile/lib/models/activities/activity.model.dart b/mobile/lib/models/activities/activity.model.dart index 742327535..6c9bdd8db 100644 --- a/mobile/lib/models/activities/activity.model.dart +++ b/mobile/lib/models/activities/activity.model.dart @@ -18,9 +18,3 @@ abstract class Activity with _$Activity { required UserDto user, }) = _Activity; } - -class ActivityStats { - final int comments; - - const ActivityStats({required this.comments}); -} diff --git a/mobile/lib/models/download/livephotos_medatada.model.dart b/mobile/lib/models/download/livephotos_medatada.model.dart index 40795142b..355164812 100644 --- a/mobile/lib/models/download/livephotos_medatada.model.dart +++ b/mobile/lib/models/download/livephotos_medatada.model.dart @@ -20,6 +20,7 @@ abstract class LivePhotosMetadata with _$LivePhotosMetadata { return LivePhotosMetadata(part: LivePhotosPart.values[map['part'] as int], id: map['id'] as String); } + @visibleForTesting String toJson() => json.encode(toMap()); factory LivePhotosMetadata.fromJson(String source) => diff --git a/mobile/lib/models/map/map_event.model.dart b/mobile/lib/models/map/map_event.model.dart deleted file mode 100644 index a57fcb4c3..000000000 --- a/mobile/lib/models/map/map_event.model.dart +++ /dev/null @@ -1,13 +0,0 @@ -sealed class MapEvent { - const MapEvent(); -} - -class MapAssetsInBoundsUpdated extends MapEvent { - final List assetRemoteIds; - - const MapAssetsInBoundsUpdated(this.assetRemoteIds); -} - -class MapCloseBottomSheet extends MapEvent { - const MapCloseBottomSheet(); -} diff --git a/mobile/lib/models/map/map_marker.model.dart b/mobile/lib/models/map/map_marker.model.dart deleted file mode 100644 index 4093747e8..000000000 --- a/mobile/lib/models/map/map_marker.model.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:maplibre_gl/maplibre_gl.dart'; -import 'package:openapi/api.dart'; - -part 'map_marker.model.freezed.dart'; - -@freezed -abstract class MapMarker with _$MapMarker { - const factory MapMarker({required LatLng latLng, required String assetRemoteId}) = _MapMarker; - - factory MapMarker.fromDto(MapMarkerResponseDto dto) => - MapMarker(latLng: LatLng(dto.lat, dto.lon), assetRemoteId: dto.id); -} diff --git a/mobile/lib/models/search/search_curated_content.model.dart b/mobile/lib/models/search/search_curated_content.model.dart deleted file mode 100644 index 8778c9b40..000000000 --- a/mobile/lib/models/search/search_curated_content.model.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'search_curated_content.model.freezed.dart'; - -/// A wrapper for [CuratedLocationsResponseDto] objects -/// and [CuratedObjectsResponseDto] to be displayed in -/// a view -@freezed -abstract class SearchCuratedContent with _$SearchCuratedContent { - const factory SearchCuratedContent({ - /// The label to show associated with this curated object - required String label, - - /// The id to lookup the asset from the server - required String id, - - /// The subtitle to show below the label - String? subtitle, - }) = _SearchCuratedContent; -} diff --git a/mobile/lib/models/search/search_filter.model.dart b/mobile/lib/models/search/search_filter.model.dart index 4c996da12..dfefce029 100644 --- a/mobile/lib/models/search/search_filter.model.dart +++ b/mobile/lib/models/search/search_filter.model.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/person.model.dart'; @@ -7,105 +5,34 @@ import 'package:immich_mobile/utils/option.dart'; part 'search_filter.model.freezed.dart'; -@Freezed(fromJson: false, toJson: false) +@freezed abstract class SearchLocationFilter with _$SearchLocationFilter { const SearchLocationFilter._(); const factory SearchLocationFilter({String? country, String? state, String? city}) = _SearchLocationFilter; - - Map toMap() { - return {'country': country, 'state': state, 'city': city}; - } - - factory SearchLocationFilter.fromMap(Map map) { - return SearchLocationFilter( - country: map['country'] != null ? map['country'] as String : null, - state: map['state'] != null ? map['state'] as String : null, - city: map['city'] != null ? map['city'] as String : null, - ); - } - - String toJson() => json.encode(toMap()); - - factory SearchLocationFilter.fromJson(String source) => - SearchLocationFilter.fromMap(json.decode(source) as Map); } -@Freezed(fromJson: false, toJson: false) +@freezed abstract class SearchCameraFilter with _$SearchCameraFilter { const SearchCameraFilter._(); const factory SearchCameraFilter({String? make, String? model}) = _SearchCameraFilter; - - Map toMap() { - return {'make': make, 'model': model}; - } - - factory SearchCameraFilter.fromMap(Map map) { - return SearchCameraFilter( - make: map['make'] != null ? map['make'] as String : null, - model: map['model'] != null ? map['model'] as String : null, - ); - } - - String toJson() => json.encode(toMap()); - - factory SearchCameraFilter.fromJson(String source) => - SearchCameraFilter.fromMap(json.decode(source) as Map); } -@Freezed(fromJson: false, toJson: false) +@freezed abstract class SearchDateFilter with _$SearchDateFilter { const SearchDateFilter._(); const factory SearchDateFilter({DateTime? takenBefore, DateTime? takenAfter}) = _SearchDateFilter; - - Map toMap() { - return { - 'takenBefore': takenBefore?.millisecondsSinceEpoch, - 'takenAfter': takenAfter?.millisecondsSinceEpoch, - }; - } - - factory SearchDateFilter.fromMap(Map map) { - return SearchDateFilter( - takenBefore: map['takenBefore'] != null ? DateTime.fromMillisecondsSinceEpoch(map['takenBefore'] as int) : null, - takenAfter: map['takenAfter'] != null ? DateTime.fromMillisecondsSinceEpoch(map['takenAfter'] as int) : null, - ); - } - - String toJson() => json.encode(toMap()); - - factory SearchDateFilter.fromJson(String source) => - SearchDateFilter.fromMap(json.decode(source) as Map); } -@Freezed(fromJson: false, toJson: false) +@freezed abstract class SearchRatingFilter with _$SearchRatingFilter { const SearchRatingFilter._(); /// [rating]: none = no filter; some(null) = filter for unrated; some(1-5) = filter for that rating // TODO(agg23): Switch to enum const factory SearchRatingFilter({@Default(Option.none()) Option rating}) = _SearchRatingFilter; - - Map toMap() { - if (rating.isNone) { - return {'active': false}; - } - return {'active': true, 'value': rating.unwrapOrNull}; - } - - factory SearchRatingFilter.fromMap(Map map) { - if (!(map['active'] as bool? ?? false)) { - return const SearchRatingFilter(); - } - return SearchRatingFilter(rating: Option.some(map['value'] as int?)); - } - - String toJson() => json.encode(toMap()); - - factory SearchRatingFilter.fromJson(String source) => - SearchRatingFilter.fromMap(json.decode(source) as Map); } @freezed diff --git a/mobile/lib/models/upload/share_intent_attachment.model.dart b/mobile/lib/models/upload/share_intent_attachment.model.dart index 2629bf3a0..bcd9923bd 100644 --- a/mobile/lib/models/upload/share_intent_attachment.model.dart +++ b/mobile/lib/models/upload/share_intent_attachment.model.dart @@ -1,4 +1,3 @@ -import 'dart:convert'; import 'dart:io'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -11,7 +10,7 @@ enum ShareIntentAttachmentType { image, video } enum UploadStatus { enqueued, running, complete, failed } -@Freezed(fromJson: false, toJson: false, equal: false) +@Freezed(equal: false) abstract class ShareIntentAttachment with _$ShareIntentAttachment { const ShareIntentAttachment._(); @@ -31,33 +30,11 @@ abstract class ShareIntentAttachment with _$ShareIntentAttachment { bool get isImage => type == ShareIntentAttachmentType.image; + // ignore: unused-code bool get isVideo => type == ShareIntentAttachmentType.video; String get fileSize => formatHumanReadableBytes(fileLength, 2); - Map toMap() { - return { - 'path': path, - 'type': type.index, - 'status': status.index, - 'uploadProgress': uploadProgress, - }; - } - - factory ShareIntentAttachment.fromMap(Map map) { - return ShareIntentAttachment( - path: map['path'] as String, - type: ShareIntentAttachmentType.values[map['type'] as int], - status: UploadStatus.values[map['status'] as int], - uploadProgress: map['uploadProgress'] as double, - ); - } - - String toJson() => json.encode(toMap()); - - factory ShareIntentAttachment.fromJson(String source) => - ShareIntentAttachment.fromMap(json.decode(source) as Map); - // Identity is sourced from the backing file, not from upload progress @override bool operator ==(Object other) { diff --git a/mobile/lib/models/view_intent/view_intent_payload.extension.dart b/mobile/lib/models/view_intent/view_intent_payload.extension.dart index ca66e6a16..cf94b92f9 100644 --- a/mobile/lib/models/view_intent/view_intent_payload.extension.dart +++ b/mobile/lib/models/view_intent/view_intent_payload.extension.dart @@ -11,6 +11,7 @@ extension ViewIntentPayloadX on ViewIntentPayload { return localAssetId ?? 'view_intent_asset'; } + // ignore: unused-code bool get isImage => mimeType.toLowerCase().startsWith('image/'); bool get isVideo => mimeType.toLowerCase().startsWith('video/'); diff --git a/mobile/lib/pages/common/download_panel.dart b/mobile/lib/pages/common/download_task_tile.dart similarity index 64% rename from mobile/lib/pages/common/download_panel.dart rename to mobile/lib/pages/common/download_task_tile.dart index 2cf7d037a..a693a3da8 100644 --- a/mobile/lib/pages/common/download_panel.dart +++ b/mobile/lib/pages/common/download_task_tile.dart @@ -1,52 +1,7 @@ -import 'dart:async'; - import 'package:background_downloader/background_downloader.dart'; import 'package:flutter/material.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/generated/translations.g.dart'; -import 'package:immich_mobile/providers/asset_viewer/download.provider.dart'; - -class DownloadPanel extends ConsumerWidget { - const DownloadPanel({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final showProgress = ref.watch(downloadStateProvider.select((state) => state.showProgress)); - - final tasks = ref.watch(downloadStateProvider.select((state) => state.taskProgress)).entries.toList(); - - void onCancelDownload(String id) { - unawaited(ref.read(downloadStateProvider.notifier).cancelDownload(id)); - } - - return Positioned( - bottom: 140, - left: 16, - child: AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - child: showProgress - ? ConstrainedBox( - constraints: BoxConstraints.loose(Size(context.width - 32, 300)), - child: ListView.builder( - shrinkWrap: true, - itemCount: tasks.length, - itemBuilder: (context, index) { - final task = tasks[index]; - return DownloadTaskTile( - progress: task.value.progress, - fileName: task.value.fileName, - status: task.value.status, - onCancelDownload: () => onCancelDownload(task.key), - ); - }, - ), - ) - : const SizedBox.shrink(key: ValueKey('no_progress')), - ), - ); - } -} class DownloadTaskTile extends StatelessWidget { final double progress; diff --git a/mobile/lib/pages/library/folder/folder.page.dart b/mobile/lib/pages/library/folder/folder.page.dart index 0264ceee1..c915bf72a 100644 --- a/mobile/lib/pages/library/folder/folder.page.dart +++ b/mobile/lib/pages/library/folder/folder.page.dart @@ -90,9 +90,9 @@ class FolderPage extends HookConsumerWidget { body: folderState.when( data: (rootFolder) { if (folder == null) { - return FolderContent(folder: rootFolder, root: rootFolder, sortOrder: sortOrder.value); + return FolderContent(folder: rootFolder, sortOrder: sortOrder.value); } else { - return FolderContent(folder: currentFolder.value, root: rootFolder, sortOrder: sortOrder.value); + return FolderContent(folder: currentFolder.value, sortOrder: sortOrder.value); } }, loading: () => const Center(child: CircularProgressIndicator()), @@ -107,10 +107,9 @@ class FolderPage extends HookConsumerWidget { class FolderContent extends HookConsumerWidget { final RootFolder? folder; - final RootFolder root; final SortOrder sortOrder; - const FolderContent({super.key, this.folder, required this.root, this.sortOrder = SortOrder.asc}); + const FolderContent({super.key, this.folder, this.sortOrder = SortOrder.asc}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -143,7 +142,7 @@ class FolderContent extends HookConsumerWidget { return Column( children: [ - FolderPath(currentFolder: folder!, root: root), + FolderPath(currentFolder: folder!), Expanded( child: folderRenderlist.when( data: (folderAssets) { @@ -224,9 +223,8 @@ class FolderContent extends HookConsumerWidget { class FolderPath extends StatelessWidget { final RootFolder currentFolder; - final RootFolder root; - const FolderPath({super.key, required this.currentFolder, required this.root}); + const FolderPath({super.key, required this.currentFolder}); @override Widget build(BuildContext context) { diff --git a/mobile/lib/presentation/pages/download_info.page.dart b/mobile/lib/presentation/pages/download_info.page.dart index 09a65b458..a02f98501 100644 --- a/mobile/lib/presentation/pages/download_info.page.dart +++ b/mobile/lib/presentation/pages/download_info.page.dart @@ -5,7 +5,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/generated/translations.g.dart'; -import 'package:immich_mobile/pages/common/download_panel.dart'; +import 'package:immich_mobile/pages/common/download_task_tile.dart'; import 'package:immich_mobile/providers/asset_viewer/download.provider.dart'; @RoutePage() diff --git a/mobile/lib/presentation/widgets/album/activity_text_field.dart b/mobile/lib/presentation/widgets/album/activity_text_field.dart index ed63c1cdf..7dd013485 100644 --- a/mobile/lib/presentation/widgets/album/activity_text_field.dart +++ b/mobile/lib/presentation/widgets/album/activity_text_field.dart @@ -8,14 +8,12 @@ import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; class ActivityTextField extends ConsumerStatefulWidget { final bool isEnabled; final bool isBottomSheet; - final String? likeId; final Function(String) onSubmit; final Function()? onKeyboardFocus; const ActivityTextField({ required this.onSubmit, this.isEnabled = true, - this.likeId, this.onKeyboardFocus, this.isBottomSheet = false, super.key, diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart index ad330309a..5deac2886 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:math' as math; import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; @@ -221,52 +220,10 @@ class _AssetViewerState extends ConsumerState { switch (event) { case TimelineReloadEvent(): _onTimelineReloadEvent(); - case ViewerReloadAssetEvent(): - _onViewerReloadEvent(); - case final ViewerStackAssetDeletedEvent event: - unawaited(_onViewerStackAssetDeletedEvent(event)); default: } } - void _onViewerReloadEvent() { - if (_totalAssets <= 1) { - return; - } - - final index = _pageController.page?.round() ?? 0; - final target = index >= _totalAssets - 1 ? index - 1 : index + 1; - unawaited(_pageController.animateToPage(target, duration: Durations.medium1, curve: Curves.easeInOut)); - unawaited(_onAssetChanged(target)); - } - - Future _onViewerStackAssetDeletedEvent(ViewerStackAssetDeletedEvent event) async { - final timelineAsset = ref.read(timelineServiceProvider).getAssetSafe(_currentPage); - if (timelineAsset == null) { - _onViewerReloadEvent(); - return; - } - - final stackProvider = stackChildrenNotifier(timelineAsset); - - ref.invalidate(stackProvider); - final stack = await ref.read(stackProvider.future); - - if (!mounted) { - return; - } - - if (stack.isEmpty) { - _onViewerReloadEvent(); - return; - } - - final targetIndex = math.min(event.stackIndex, stack.length - 1); - ref.read(assetViewerProvider.notifier) - ..setAsset(stack[targetIndex]) - ..setStackIndex(targetIndex); - } - void _onTimelineReloadEvent() { final timelineService = ref.read(timelineServiceProvider); final totalAssets = timelineService.totalAssets; diff --git a/mobile/lib/presentation/widgets/asset_viewer/motion_photo_button.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/motion_photo_button.widget.dart deleted file mode 100644 index 78f234d39..000000000 --- a/mobile/lib/presentation/widgets/asset_viewer/motion_photo_button.widget.dart +++ /dev/null @@ -1,78 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/extensions/platform_extensions.dart'; -import 'package:immich_mobile/generated/translations.g.dart'; -import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart'; -import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart'; - -class MotionPhotoPlayButton extends ConsumerWidget { - const MotionPhotoPlayButton({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final asset = ref.watch(assetViewerProvider.select((state) => state.currentAsset)); - final isPlaying = ref.watch(isPlayingMotionVideoProvider); - final showControls = ref.watch(assetViewerProvider.select((state) => state.showingControls)); - final isShowingDetails = ref.watch(assetViewerProvider.select((state) => state.showingDetails)); - - if (asset == null || !asset.isMotionPhoto || isShowingDetails) { - return const SizedBox.shrink(); - } - - return IgnorePointer( - ignoring: !showControls, - child: AnimatedOpacity( - opacity: showControls ? 1.0 : 0.0, - duration: Durations.short2, - child: SafeArea( - child: Padding( - padding: const EdgeInsets.only(top: 60), - child: Center( - child: _MotionButton( - isPlaying: isPlaying, - onPressed: ref.watch(isPlayingMotionVideoProvider.notifier).toggle, - ), - ), - ), - ), - ), - ); - } -} - -class _MotionButton extends StatelessWidget { - final bool isPlaying; - final VoidCallback onPressed; - - const _MotionButton({required this.isPlaying, required this.onPressed}); - - @override - Widget build(BuildContext context) { - return Material( - color: Colors.grey[900]!.withValues(alpha: 0.4), - borderRadius: const BorderRadius.all(Radius.circular(24)), - child: InkWell( - onTap: onPressed, - borderRadius: const BorderRadius.all(Radius.circular(24)), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - isPlaying ? Icons.motion_photos_pause_outlined : Icons.play_circle_outline_rounded, - color: Colors.white, - size: 16, - ), - const SizedBox(width: 8), - Text( - CurrentPlatform.isAndroid ? context.t.motion : context.t.live, - style: const TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.w500), - ), - ], - ), - ), - ), - ); - } -} diff --git a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart index 851945fe4..4d56ca4c8 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart @@ -22,7 +22,6 @@ class NativeVideoViewer extends ConsumerStatefulWidget { final BaseAsset asset; final String? localFilePath; final bool isCurrent; - final bool showControls; final Widget image; /// Overrides the user's configured loop video setting @@ -34,7 +33,6 @@ class NativeVideoViewer extends ConsumerStatefulWidget { this.localFilePath, required this.image, this.isCurrent = false, - this.showControls = true, this.loopOverride, }); diff --git a/mobile/lib/presentation/widgets/asset_viewer/viewer_kebab_menu.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/viewer_kebab_menu.widget.dart index cbbc85110..0cee4f82f 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/viewer_kebab_menu.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/viewer_kebab_menu.widget.dart @@ -9,7 +9,6 @@ import 'package:immich_mobile/providers/infrastructure/current_album.provider.da import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/routes.provider.dart'; -import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/utils/action_button.utils.dart'; import 'package:immich_ui/immich_ui.dart'; @@ -30,7 +29,6 @@ class ViewerKebabMenu extends ConsumerWidget { final isOwner = asset is RemoteAsset && asset.ownerId == user?.id; final isCasting = ref.watch(castProvider.select((c) => c.isCasting)); final timelineOrigin = ref.watch(timelineServiceProvider).origin; - final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); final isInLockedView = ref.watch(inLockedViewProvider); final currentAlbum = ref.watch(currentRemoteAlbumProvider); final isArchived = asset is RemoteAsset && asset.visibility == AssetVisibility.archive; @@ -40,7 +38,6 @@ class ViewerKebabMenu extends ConsumerWidget { asset: asset, isOwner: isOwner, isArchived: isArchived, - isTrashEnabled: isTrashEnable, isStacked: asset is RemoteAsset && asset.stackId != null, isInLockedView: isInLockedView, currentAlbum: currentAlbum, diff --git a/mobile/lib/presentation/widgets/images/thumbnail.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart index 5bfeb2597..c61c5dcc2 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart @@ -13,8 +13,6 @@ import 'package:logging/logging.dart'; final log = Logger('ThumbnailWidget'); -enum ThumbhashMode { enabled, disabled, only } - class Thumbnail extends StatefulWidget { final ImageProvider? imageProvider; final ImageProvider? thumbhashProvider; diff --git a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart index 953efc144..4e6a62adb 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart @@ -17,7 +17,6 @@ class ThumbnailTile extends ConsumerStatefulWidget { this.asset, { this.size = kThumbnailResolution, this.remoteSize, - this.fit = BoxFit.cover, this.showStorageIndicator = false, this.lockSelection = false, this.heroOffset, @@ -30,7 +29,6 @@ class ThumbnailTile extends ConsumerStatefulWidget { /// Physical size to decode for remote thumbnails. final Size? remoteSize; - final BoxFit fit; final bool showStorageIndicator; final bool lockSelection; final int? heroOffset; diff --git a/mobile/lib/presentation/widgets/map/map_utils.dart b/mobile/lib/presentation/widgets/map/map_utils.dart index 3ed6ceee2..d1b31b1b8 100644 --- a/mobile/lib/presentation/widgets/map/map_utils.dart +++ b/mobile/lib/presentation/widgets/map/map_utils.dart @@ -1,3 +1,4 @@ +// TODO: Dedupe against lib/utils/map_utils.dart import 'dart:async'; import 'package:flutter/material.dart'; @@ -13,17 +14,6 @@ class MapUtils { static const mapZoomToAssetLevel = 12.0; static const defaultSourceId = 'asset-map-markers'; static const defaultHeatMapLayerId = 'asset-heatmap-layer'; - static var markerCompleter = Completer()..complete(); - - static const defaultCircleLayerLayerProperties = CircleLayerProperties( - circleRadius: 10, - circleColor: "rgba(150,86,34,0.7)", - circleBlur: 1.0, - circleOpacity: 0.7, - circleStrokeWidth: 0.1, - circleStrokeColor: "rgba(203,46,19,0.5)", - circleStrokeOpacity: 0.7, - ); static const defaultHeatmapLayerProperties = HeatmapLayerProperties( heatmapColor: [ diff --git a/mobile/lib/presentation/widgets/memory/memory_card.widget.dart b/mobile/lib/presentation/widgets/memory/memory_card.widget.dart index e4b9b5b60..ad043690a 100644 --- a/mobile/lib/presentation/widgets/memory/memory_card.widget.dart +++ b/mobile/lib/presentation/widgets/memory/memory_card.widget.dart @@ -14,14 +14,12 @@ class MemoryCard extends StatelessWidget { final String title; final bool showTitle; final bool isCurrent; - final Function()? onVideoEnded; const MemoryCard({ required this.asset, required this.title, required this.showTitle, this.isCurrent = false, - this.onVideoEnded, super.key, }); @@ -65,7 +63,6 @@ class MemoryCard extends StatelessWidget { key: ValueKey(asset.id), asset: asset, isCurrent: isCurrent, - showControls: false, image: FullImage(asset, size: context.sizeData, fit: BoxFit.contain), ), ), diff --git a/mobile/lib/presentation/widgets/slideshow/slideshow_controller.dart b/mobile/lib/presentation/widgets/slideshow/slideshow_controller.dart index 0044b67fc..9f85ff506 100644 --- a/mobile/lib/presentation/widgets/slideshow/slideshow_controller.dart +++ b/mobile/lib/presentation/widgets/slideshow/slideshow_controller.dart @@ -68,6 +68,7 @@ class SlideshowController extends ChangeNotifier { int get currentIndex => _currentIndex; /// The slide to be displayed next. Null if the slideshow will end after this slide + // ignore: unused-code int? get nextIndex => _nextIndex; /// True when the user paused or the slideshow has completed diff --git a/mobile/lib/presentation/widgets/timeline/constants.dart b/mobile/lib/presentation/widgets/timeline/constants.dart index 86c44d4a7..7d645eedc 100644 --- a/mobile/lib/presentation/widgets/timeline/constants.dart +++ b/mobile/lib/presentation/widgets/timeline/constants.dart @@ -10,4 +10,3 @@ const Duration kTimelineScrubberFadeInDuration = Duration(milliseconds: 300); const Duration kTimelineScrubberFadeOutDuration = Duration(milliseconds: 800); const Size kThumbnailResolution = Size.square(320); -const kThumbnailDiskCacheSize = 1024 << 20; // 1GiB diff --git a/mobile/lib/providers/asset_viewer/is_motion_video_playing.provider.dart b/mobile/lib/providers/asset_viewer/is_motion_video_playing.provider.dart index 08722dc89..a2fb332f0 100644 --- a/mobile/lib/providers/asset_viewer/is_motion_video_playing.provider.dart +++ b/mobile/lib/providers/asset_viewer/is_motion_video_playing.provider.dart @@ -2,13 +2,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; /// Whether to display the video part of a motion photo final isPlayingMotionVideoProvider = StateNotifierProvider((ref) { - return IsPlayingMotionVideo(ref); + return IsPlayingMotionVideo(); }); class IsPlayingMotionVideo extends StateNotifier { - IsPlayingMotionVideo(this.ref) : super(false); - - final Ref ref; + IsPlayingMotionVideo() : super(false); bool get playing => state; diff --git a/mobile/lib/providers/asset_viewer/show_controls.provider.dart b/mobile/lib/providers/asset_viewer/show_controls.provider.dart deleted file mode 100644 index 146894000..000000000 --- a/mobile/lib/providers/asset_viewer/show_controls.provider.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; - -final showControlsProvider = StateNotifierProvider((ref) { - return ShowControls(ref); -}); - -class ShowControls extends StateNotifier { - ShowControls(this.ref) : super(true); - - final Ref ref; - - bool get show => state; - - set show(bool value) { - state = value; - } -} diff --git a/mobile/lib/providers/backup/backup.provider.dart b/mobile/lib/providers/backup/backup.provider.dart index fe8cc8805..c9df035b2 100644 --- a/mobile/lib/providers/backup/backup.provider.dart +++ b/mobile/lib/providers/backup/backup.provider.dart @@ -15,11 +15,6 @@ import 'package:logging/logging.dart'; part 'backup.provider.freezed.dart'; -@freezed -abstract class EnqueueStatus with _$EnqueueStatus { - const factory EnqueueStatus({required int enqueueCount, required int totalCount}) = _EnqueueStatus; -} - @freezed abstract class UploadStatus with _$UploadStatus { const factory UploadStatus({ diff --git a/mobile/lib/providers/gallery_permission.provider.dart b/mobile/lib/providers/gallery_permission.provider.dart index 6be504bd2..30ad23e8f 100644 --- a/mobile/lib/providers/gallery_permission.provider.dart +++ b/mobile/lib/providers/gallery_permission.provider.dart @@ -16,6 +16,7 @@ class GalleryPermissionNotifier extends StateNotifier { final DevicePermissionService _service; + // ignore: unused-code bool get hasPermission => state.hasAccess; Future requestGalleryPermission() async => state = await _service.requestGallery(); diff --git a/mobile/lib/providers/immich_logo_provider.dart b/mobile/lib/providers/immich_logo_provider.dart deleted file mode 100644 index d9e51ecca..000000000 --- a/mobile/lib/providers/immich_logo_provider.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter/services.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; - -final immichLogoProvider = FutureProvider.autoDispose((ref) async { - final json = await rootBundle.loadString('assets/immich-logo.json'); - final j = jsonDecode(json); - return base64Decode(j['content']); -}); diff --git a/mobile/lib/providers/infrastructure/db.provider.dart b/mobile/lib/providers/infrastructure/db.provider.dart index 500b8546d..2880f58c5 100644 --- a/mobile/lib/providers/infrastructure/db.provider.dart +++ b/mobile/lib/providers/infrastructure/db.provider.dart @@ -1,9 +1,11 @@ import 'dart:async'; +import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/data/db/main/database.dart'; import 'package:immich_mobile/data/store.dart'; +@visibleForTesting Drift Function(Ref ref) driftOverride(Drift drift) => (ref) { ref.onDispose(() => unawaited(drift.close())); ref.keepAlive(); diff --git a/mobile/lib/providers/map/map_marker.provider.dart b/mobile/lib/providers/map/map_marker.provider.dart deleted file mode 100644 index ab73f94c1..000000000 --- a/mobile/lib/providers/map/map_marker.provider.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/models/map/map_marker.model.dart'; -import 'package:immich_mobile/providers/map/map_service.provider.dart'; -import 'package:immich_mobile/providers/map/map_state.provider.dart'; - -final mapMarkersProvider = FutureProvider.autoDispose>((ref) async { - final service = ref.read(mapServiceProvider); - final mapState = ref.read(mapStateNotifierProvider); - DateTime? fileCreatedAfter; - bool? isFavorite; - final bool isIncludeArchived = mapState.includeArchived; - final bool isWithPartners = mapState.withPartners; - - if (mapState.relativeTime != 0) { - fileCreatedAfter = DateTime.now().subtract(Duration(days: mapState.relativeTime)); - } - - if (mapState.showFavoriteOnly) { - isFavorite = true; - } - - final markers = await service.getMapMarkers( - isFavorite: isFavorite, - withArchived: isIncludeArchived, - withPartners: isWithPartners, - fileCreatedAfter: fileCreatedAfter, - ); - - return markers.toList(); -}); diff --git a/mobile/lib/providers/map/map_service.provider.dart b/mobile/lib/providers/map/map_service.provider.dart deleted file mode 100644 index a1d47746e..000000000 --- a/mobile/lib/providers/map/map_service.provider.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/providers/api.provider.dart'; -import 'package:immich_mobile/services/map.service.dart'; - -final mapServiceProvider = Provider.autoDispose((ref) => MapService(ref.watch(apiServiceProvider))); diff --git a/mobile/lib/providers/multiselect.provider.dart b/mobile/lib/providers/multiselect.provider.dart deleted file mode 100644 index 22210e88c..000000000 --- a/mobile/lib/providers/multiselect.provider.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; - -final multiselectProvider = StateProvider((ref) { - return false; -}); diff --git a/mobile/lib/providers/search/search_page_state.provider.dart b/mobile/lib/providers/search/search_page_state.provider.dart deleted file mode 100644 index c6a72c922..000000000 --- a/mobile/lib/providers/search/search_page_state.provider.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/models/search/search_curated_content.model.dart'; - -import 'package:immich_mobile/services/search.service.dart'; - -final getPreviewPlacesProvider = FutureProvider.autoDispose>((ref) async { - final SearchService searchService = ref.watch(searchServiceProvider); - - final exploreData = await searchService.getExploreData(); - - if (exploreData == null) { - return []; - } - - final locations = exploreData.firstWhere((data) => data.fieldName == "exifInfo.city").items; - - final curatedContent = locations.map((l) => SearchCuratedContent(label: l.value, id: l.data.id)).toList(); - - return curatedContent; -}); - -final getAllPlacesProvider = FutureProvider.autoDispose>((ref) async { - final SearchService searchService = ref.watch(searchServiceProvider); - - final assetPlaces = await searchService.getAllPlaces(); - - if (assetPlaces == null) { - return []; - } - - final curatedContent = assetPlaces - .map((data) => SearchCuratedContent(label: data.exifInfo.orElse(null)!.city.orElse(null)!, id: data.id)) - .toList(); - - return curatedContent; -}); diff --git a/mobile/lib/providers/secure_storage.provider.dart b/mobile/lib/providers/secure_storage.provider.dart deleted file mode 100644 index 39813d102..000000000 --- a/mobile/lib/providers/secure_storage.provider.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; - -final secureStorageProvider = StateNotifierProvider((ref) { - return SecureStorageProvider(); -}); - -class SecureStorageProvider extends StateNotifier { - SecureStorageProvider() : super(null); -} diff --git a/mobile/lib/providers/sync_status.provider.dart b/mobile/lib/providers/sync_status.provider.dart index 5f9d8c36a..72bef3dc4 100644 --- a/mobile/lib/providers/sync_status.provider.dart +++ b/mobile/lib/providers/sync_status.provider.dart @@ -21,6 +21,7 @@ abstract class SyncStatusState with _$SyncStatusState { bool get isRemoteSyncing => remoteSyncStatus == SyncStatus.syncing; bool get isLocalSyncing => localSyncStatus == SyncStatus.syncing; bool get isHashing => hashJobStatus == SyncStatus.syncing; + // ignore: unused-code bool get isCloudIdSyncing => cloudIdSyncStatus == SyncStatus.syncing; } diff --git a/mobile/lib/providers/timeline/multiselect.provider.dart b/mobile/lib/providers/timeline/multiselect.provider.dart index b14f8527b..c7a7f9f65 100644 --- a/mobile/lib/providers/timeline/multiselect.provider.dart +++ b/mobile/lib/providers/timeline/multiselect.provider.dart @@ -29,10 +29,11 @@ abstract class MultiSelectState with _$MultiSelectState { bool get hasRemote => selectedAssets.any((asset) => asset.storage == AssetState.remote || asset.storage == AssetState.merged); + // ignore: unused-code bool get hasMerged => selectedAssets.any((asset) => asset.storage == AssetState.merged); - + // ignore: unused-code bool get onlyLocal => selectedAssets.any((asset) => asset.storage == AssetState.local); - + // ignore: unused-code bool get onlyRemote => selectedAssets.any((asset) => asset.storage == AssetState.remote); } diff --git a/mobile/lib/repositories/partner_api.repository.dart b/mobile/lib/repositories/partner_api.repository.dart index 240b61f63..f673b15e3 100644 --- a/mobile/lib/repositories/partner_api.repository.dart +++ b/mobile/lib/repositories/partner_api.repository.dart @@ -5,8 +5,6 @@ import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; -enum Direction { sharedWithMe, sharedByMe } - final partnerApiRepositoryProvider = Provider((ref) => PartnerApiRepository(ref.watch(apiServiceProvider).partnersApi)); class PartnerApiRepository extends ApiRepository { diff --git a/mobile/lib/routing/custom_transition_builders.dart b/mobile/lib/routing/custom_transition_builders.dart deleted file mode 100644 index d8412eb7c..000000000 --- a/mobile/lib/routing/custom_transition_builders.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/material.dart'; - -class CustomTransitionsBuilders { - const CustomTransitionsBuilders._(); - - static const ZoomPageTransitionsBuilder zoomPageTransitionsBuilder = ZoomPageTransitionsBuilder(); - - static const RouteTransitionsBuilder zoomedPage = _zoomedPage; - - static Widget _zoomedPage( - BuildContext context, - Animation animation, - Animation secondaryAnimation, - Widget child, - ) { - return zoomPageTransitionsBuilder.buildTransitions( - // Empty PageRoute<> object, only used to pass allowSnapshotting to ZoomPageTransitionsBuilder - PageRouteBuilder( - allowSnapshotting: true, - fullscreenDialog: false, - pageBuilder: (context, animation, secondaryAnimation) => const SizedBox.shrink(), - ), - context, - animation, - secondaryAnimation, - child, - ); - } -} diff --git a/mobile/lib/services/api.service.dart b/mobile/lib/services/api.service.dart index a73c1ed6e..9db9a36ec 100644 --- a/mobile/lib/services/api.service.dart +++ b/mobile/lib/services/api.service.dart @@ -22,17 +22,21 @@ class ApiService { late AssetsApi assetsApi; late SearchApi searchApi; late ServerApi serverInfoApi; + // ignore: unused-code late MapApi mapApi; late PartnersApi partnersApi; late PeopleApi peopleApi; late SharedLinksApi sharedLinksApi; late SyncApi syncApi; + // ignore: unused-code late SystemConfigApi systemConfigApi; late ActivitiesApi activitiesApi; + // ignore: unused-code late DownloadApi downloadApi; late TrashApi trashApi; late StacksApi stacksApi; late ViewsApi viewApi; + // ignore: unused-code late MemoriesApi memoriesApi; late SessionsApi sessionsApi; late TagsApi tagsApi; diff --git a/mobile/lib/services/app_settings.service.dart b/mobile/lib/services/app_settings.service.dart index 28bce32bb..83e0cb3f5 100644 --- a/mobile/lib/services/app_settings.service.dart +++ b/mobile/lib/services/app_settings.service.dart @@ -2,15 +2,14 @@ import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; enum AppSettingsEnum { - advancedTroubleshooting(StoreKey.advancedTroubleshooting, null, false), - manageLocalMediaAndroid(StoreKey.manageLocalMediaAndroid, null, false), - enableHapticFeedback(StoreKey.enableHapticFeedback, null, true), - readonlyModeEnabled(StoreKey.readonlyModeEnabled, "readonlyModeEnabled", false); + advancedTroubleshooting(StoreKey.advancedTroubleshooting, false), + manageLocalMediaAndroid(StoreKey.manageLocalMediaAndroid, false), + enableHapticFeedback(StoreKey.enableHapticFeedback, true), + readonlyModeEnabled(StoreKey.readonlyModeEnabled, false); - const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue); + const AppSettingsEnum(this.storeKey, this.defaultValue); final StoreKey storeKey; - final String? hiveKey; final T defaultValue; } diff --git a/mobile/lib/services/background_upload.service.dart b/mobile/lib/services/background_upload.service.dart index 7e21dbdec..ec4e12a84 100644 --- a/mobile/lib/services/background_upload.service.dart +++ b/mobile/lib/services/background_upload.service.dart @@ -109,7 +109,9 @@ class BackgroundUploadService { final StreamController _taskStatusController = StreamController.broadcast(); final StreamController _taskProgressController = StreamController.broadcast(); + // ignore: unused-code Stream get taskStatusStream => _taskStatusController.stream; + // ignore: unused-code Stream get taskProgressStream => _taskProgressController.stream; bool shouldAbortQueuingTasks = false; diff --git a/mobile/lib/services/immich_logger.service.dart b/mobile/lib/services/immich_logger.service.dart index bfc9bd9b5..67822c69e 100644 --- a/mobile/lib/services/immich_logger.service.dart +++ b/mobile/lib/services/immich_logger.service.dart @@ -15,8 +15,6 @@ import 'package:share_plus/share_plus.dart'; /// Logs can be shared by calling the `shareLogs` method, which will open a share dialog /// and generate a csv file. abstract final class ImmichLogger { - const ImmichLogger(); - static Future shareLogs(BuildContext context) async { final tempDir = await getTemporaryDirectory(); final dateTime = DateTime.now().toIso8601String(); diff --git a/mobile/lib/services/map.service.dart b/mobile/lib/services/map.service.dart deleted file mode 100644 index b439af866..000000000 --- a/mobile/lib/services/map.service.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'dart:async'; - -import 'package:immich_mobile/mixins/error_logger.mixin.dart'; -import 'package:immich_mobile/models/map/map_marker.model.dart'; -import 'package:immich_mobile/services/api.service.dart'; -import 'package:immich_mobile/utils/user_agent.dart'; -import 'package:logging/logging.dart'; -import 'package:maplibre_gl/maplibre_gl.dart'; - -class MapService with ErrorLoggerMixin { - final ApiService _apiService; - @override - final logger = Logger("MapService"); - - MapService(this._apiService) { - unawaited(_setMapUserAgentHeader()); - } - - Future _setMapUserAgentHeader() async { - final userAgent = await getUserAgentString(); - await setHttpHeaders({'User-Agent': userAgent}); - } - - Future> getMapMarkers({ - bool? isFavorite, - bool? withArchived, - bool? withPartners, - DateTime? fileCreatedAfter, - DateTime? fileCreatedBefore, - }) async { - return logError( - () async { - final markers = await _apiService.mapApi.getMapMarkers( - isFavorite: isFavorite, - isArchived: withArchived, - withPartners: withPartners, - fileCreatedAfter: fileCreatedAfter, - fileCreatedBefore: fileCreatedBefore, - ); - - return markers?.map(MapMarker.fromDto) ?? []; - }, - defaultValue: [], - errorMessage: "Failed to get map markers", - ); - } -} diff --git a/mobile/lib/services/search.service.dart b/mobile/lib/services/search.service.dart index e64c529f2..193161015 100644 --- a/mobile/lib/services/search.service.dart +++ b/mobile/lib/services/search.service.dart @@ -39,6 +39,7 @@ class SearchService { } } + // ignore: unused-code Future?> getExploreData() async { try { return await _apiService.searchApi.getExploreData(); @@ -48,6 +49,7 @@ class SearchService { return null; } + // ignore: unused-code Future?> getAllPlaces() async { try { return await _apiService.searchApi.getAssetsByCity(); diff --git a/mobile/lib/utils/action_button.utils.dart b/mobile/lib/utils/action_button.utils.dart index 4abd7f41a..8943bcfc4 100644 --- a/mobile/lib/utils/action_button.utils.dart +++ b/mobile/lib/utils/action_button.utils.dart @@ -33,7 +33,6 @@ class ActionButtonContext { final BaseAsset asset; final bool isOwner; final bool isArchived; - final bool isTrashEnabled; final bool isInLockedView; final bool isStacked; final RemoteAlbum? currentAlbum; @@ -47,7 +46,6 @@ class ActionButtonContext { required this.asset, required this.isOwner, required this.isArchived, - required this.isTrashEnabled, required this.isStacked, required this.isInLockedView, required this.currentAlbum, diff --git a/mobile/lib/utils/async_mutex.dart b/mobile/lib/utils/async_mutex.dart index 6c54c1220..392eb6022 100644 --- a/mobile/lib/utils/async_mutex.dart +++ b/mobile/lib/utils/async_mutex.dart @@ -5,6 +5,7 @@ class AsyncMutex { Future _running = Future.value(null); int _enqueued = 0; + // ignore: unused-code int get enqueued => _enqueued; /// Execute [operation] exclusively, after any currently running operations. diff --git a/mobile/lib/utils/debounce.dart b/mobile/lib/utils/debounce.dart index 0af3a3f2b..2f31bd2e9 100644 --- a/mobile/lib/utils/debounce.dart +++ b/mobile/lib/utils/debounce.dart @@ -1,7 +1,5 @@ import 'dart:async'; -import 'package:flutter_hooks/flutter_hooks.dart'; - /// Used to debounce function calls with the [interval] provided. /// If [maxWaitTime] is provided, the first [run] call as well as the next call since [maxWaitTime] has passed will be immediately executed, even if [interval] is not satisfied. class Debouncer { @@ -49,36 +47,6 @@ class Debouncer { _actionFuture = null; } + // ignore: unused-code bool get isActive => _actionFuture != null || (_timer != null && _timer!.isActive); } - -/// Creates a [Debouncer] that will be disposed automatically. If no [interval] is provided, a -/// default interval of 300ms is used to debounce the function calls -Debouncer useDebouncer({ - Duration interval = const Duration(milliseconds: 300), - Duration? maxWaitTime, - List? keys, -}) => use(_DebouncerHook(interval: interval, maxWaitTime: maxWaitTime, keys: keys)); - -class _DebouncerHook extends Hook { - const _DebouncerHook({required this.interval, this.maxWaitTime, super.keys}); - - final Duration interval; - final Duration? maxWaitTime; - - @override - HookState> createState() => _DebouncerHookState(); -} - -class _DebouncerHookState extends HookState { - late final debouncer = Debouncer(interval: hook.interval, maxWaitTime: hook.maxWaitTime); - - @override - Debouncer build(_) => debouncer; - - @override - void dispose() => debouncer.dispose(); - - @override - String get debugLabel => 'useDebouncer'; -} diff --git a/mobile/lib/utils/diff.dart b/mobile/lib/utils/diff.dart index fae3a5d51..7a1200999 100644 --- a/mobile/lib/utils/diff.dart +++ b/mobile/lib/utils/diff.dart @@ -77,17 +77,3 @@ bool diffSortedListsSync( } return diff; } - -int compareToNullable(T? a, T? b) { - if (a == null && b == null) { - return 0; - } - - if (a == null) { - return 1; - } - if (b == null) { - return -1; - } - return a.compareTo(b); -} diff --git a/mobile/lib/utils/draggable_scroll_controller.dart b/mobile/lib/utils/draggable_scroll_controller.dart deleted file mode 100644 index bab521444..000000000 --- a/mobile/lib/utils/draggable_scroll_controller.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; - -/// Creates a [DraggableScrollableController] that will be disposed automatically. -/// -/// See also: -/// - [DraggableScrollableController] -DraggableScrollableController useDraggableScrollController({List? keys}) { - return use(_DraggableScrollControllerHook(keys: keys)); -} - -class _DraggableScrollControllerHook extends Hook { - const _DraggableScrollControllerHook({super.keys}); - - @override - HookState> createState() => - _DraggableScrollControllerHookState(); -} - -class _DraggableScrollControllerHookState - extends HookState { - late final controller = DraggableScrollableController(); - - @override - DraggableScrollableController build(BuildContext context) => controller; - - @override - void dispose() => controller.dispose(); - - @override - String get debugLabel => 'useDraggableScrollController'; -} diff --git a/mobile/lib/utils/hash.dart b/mobile/lib/utils/hash.dart deleted file mode 100644 index 79bd637f4..000000000 --- a/mobile/lib/utils/hash.dart +++ /dev/null @@ -1,15 +0,0 @@ -/// FNV-1a 64bit hash algorithm optimized for Dart Strings -int fastHash(String string) { - var hash = 0xcbf29ce484222325; - - var i = 0; - while (i < string.length) { - final codeUnit = string.codeUnitAt(i++); - hash ^= codeUnit >> 8; - hash *= 0x100000001b3; - hash ^= codeUnit & 0xFF; - hash *= 0x100000001b3; - } - - return hash; -} diff --git a/mobile/lib/utils/hooks/crop_controller_hook.dart b/mobile/lib/utils/hooks/crop_controller_hook.dart deleted file mode 100644 index cea901bd2..000000000 --- a/mobile/lib/utils/hooks/crop_controller_hook.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'dart:ui'; // Import the dart:ui library for Rect - -import 'package:crop_image/crop_image.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; - -/// A hook that provides a [CropController] instance. -CropController useCropController() { - return useMemoized(() => CropController(defaultCrop: const Rect.fromLTRB(0, 0, 1, 1))); -} diff --git a/mobile/lib/utils/hooks/timer_hook.dart b/mobile/lib/utils/hooks/timer_hook.dart deleted file mode 100644 index 36b78d863..000000000 --- a/mobile/lib/utils/hooks/timer_hook.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:async/async.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; - -RestartableTimer useTimer(Duration duration, void Function() callback) { - return use(_TimerHook(duration: duration, callback: callback)); -} - -class _TimerHook extends Hook { - final Duration duration; - final void Function() callback; - - const _TimerHook({required this.duration, required this.callback}); - @override - HookState> createState() => _TimerHookState(); -} - -class _TimerHookState extends HookState { - late RestartableTimer timer; - @override - void initHook() { - super.initHook(); - timer = RestartableTimer(hook.duration, hook.callback); - } - - @override - RestartableTimer build(BuildContext context) { - return timer; - } - - @override - void dispose() { - timer.cancel(); - super.dispose(); - } -} diff --git a/mobile/lib/utils/map_utils.dart b/mobile/lib/utils/map_utils.dart index 7685e78ef..b9cd3aad2 100644 --- a/mobile/lib/utils/map_utils.dart +++ b/mobile/lib/utils/map_utils.dart @@ -1,3 +1,4 @@ +// TODO: Dedupe against lib/presentation/widgets/map/map_utils.dart import 'dart:async'; import 'package:flutter/material.dart'; @@ -5,48 +6,11 @@ import 'package:geolocator/geolocator.dart'; import 'package:immich_mobile/generated/translations.g.dart'; import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; import 'package:logging/logging.dart'; -import 'package:maplibre_gl/maplibre_gl.dart'; class MapUtils { const MapUtils._(); static final Logger _log = Logger("MapUtils"); - static const defaultSourceId = 'asset-map-markers'; - static const defaultHeatMapLayerId = 'asset-heatmap-layer'; - - static const defaultHeatMapLayerProperties = HeatmapLayerProperties( - heatmapColor: [ - Expressions.interpolate, - ["linear"], - ["heatmap-density"], - 0.0, - "rgba(103,58,183,0.0)", - 0.3, - "rgb(103,58,183)", - 0.5, - "rgb(33,149,243)", - 0.7, - "rgb(76,175,79)", - 0.95, - "rgb(255,235,59)", - 1.0, - "rgb(255,86,34)", - ], - heatmapIntensity: [ - Expressions.interpolate, ["linear"], // - [Expressions.zoom], - 0, 0.5, - 9, 2, - ], - heatmapRadius: [ - Expressions.interpolate, ["linear"], // - [Expressions.zoom], - 0, 4, - 4, 8, - 9, 16, - ], - heatmapOpacity: 0.7, - ); static Future<(Position?, LocationPermission?)> checkPermAndGetLocation({ required BuildContext context, diff --git a/mobile/lib/utils/openapi_patching.dart b/mobile/lib/utils/openapi_patching.dart index 63a1f2460..90ec10d76 100644 --- a/mobile/lib/utils/openapi_patching.dart +++ b/mobile/lib/utils/openapi_patching.dart @@ -45,6 +45,7 @@ final Map> openApiPatches = { 'WorkflowResponseDto': {'logging': false}, }; +// ignore: unused-code void upgradeDto(dynamic value, String targetType) { if (value is! Map) { return; diff --git a/mobile/lib/utils/upload_speed_calculator.dart b/mobile/lib/utils/upload_speed_calculator.dart index cb681e9ce..ce25ff4f6 100644 --- a/mobile/lib/utils/upload_speed_calculator.dart +++ b/mobile/lib/utils/upload_speed_calculator.dart @@ -79,6 +79,7 @@ class UploadSpeedCalculator { } /// Returns the current speed in MB/s, or -1 if not available. + // ignore: unused-code double get speed => _currentSpeed; /// Returns a human-readable string representation of the current speed. @@ -112,6 +113,7 @@ class UploadSpeedCalculator { /// Returns a human-readable string representation of time remaining. /// /// Returns '--:--' if N/A, otherwise HH:MM:SS or MM:SS format. + // ignore: unused-code String get timeRemainingAsString { final remaining = timeRemaining; return switch (remaining.inSeconds) { diff --git a/mobile/lib/utils/user_agent.dart b/mobile/lib/utils/user_agent.dart deleted file mode 100644 index f08793e3a..000000000 --- a/mobile/lib/utils/user_agent.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'dart:io' show Platform; - -import 'package:package_info_plus/package_info_plus.dart'; - -Future getUserAgentString() async { - final packageInfo = await PackageInfo.fromPlatform(); - String platform; - if (Platform.isAndroid) { - platform = 'android'; - } else if (Platform.isIOS) { - platform = 'ios'; - } else { - platform = 'unknown'; - } - return 'immich-$platform/${packageInfo.version}'; -} diff --git a/mobile/lib/widgets/asset_grid/delete_dialog.dart b/mobile/lib/widgets/asset_grid/delete_dialog.dart deleted file mode 100644 index 9b0367a46..000000000 --- a/mobile/lib/widgets/asset_grid/delete_dialog.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/generated/translations.g.dart'; - -class DeleteLocalOnlyDialog extends StatelessWidget { - final void Function(bool onlyMerged) onDeleteLocal; - - const DeleteLocalOnlyDialog({super.key, required this.onDeleteLocal}); - - @override - Widget build(BuildContext context) { - void onDeleteBackedUpOnly() { - context.pop(true); - onDeleteLocal(true); - } - - void onForceDelete() { - context.pop(false); - onDeleteLocal(false); - } - - return AlertDialog( - shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), - title: Text(context.t.delete_dialog_title), - content: Text(context.t.delete_dialog_alert_local_non_backed_up), - actions: [ - SizedBox( - width: double.infinity, - height: 48, - child: FilledButton( - onPressed: () => context.pop(), - style: FilledButton.styleFrom( - backgroundColor: context.colorScheme.surfaceDim, - foregroundColor: context.primaryColor, - ), - child: Text(context.t.cancel, style: const TextStyle(fontWeight: FontWeight.bold)), - ), - ), - const SizedBox(height: 8), - SizedBox( - width: double.infinity, - height: 48, - - child: FilledButton( - onPressed: onDeleteBackedUpOnly, - style: FilledButton.styleFrom( - backgroundColor: context.colorScheme.errorContainer, - foregroundColor: context.colorScheme.onErrorContainer, - ), - child: Text( - context.t.delete_local_dialog_ok_backed_up_only, - style: const TextStyle(fontWeight: FontWeight.bold), - ), - ), - ), - const SizedBox(height: 8), - SizedBox( - width: double.infinity, - height: 48, - child: FilledButton( - onPressed: onForceDelete, - style: FilledButton.styleFrom(backgroundColor: Colors.red[400], foregroundColor: Colors.white), - child: Text(context.t.delete_local_dialog_ok_force, style: const TextStyle(fontWeight: FontWeight.bold)), - ), - ), - ], - ); - } -} diff --git a/mobile/lib/widgets/asset_grid/permanent_delete_dialog.dart b/mobile/lib/widgets/asset_grid/permanent_delete_dialog.dart deleted file mode 100644 index 18265b8d4..000000000 --- a/mobile/lib/widgets/asset_grid/permanent_delete_dialog.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/generated/translations.g.dart'; -import 'package:immich_ui/immich_ui.dart'; - -class PermanentDeleteDialog extends StatelessWidget { - const PermanentDeleteDialog({super.key, required this.count}); - - final int count; - - @override - Widget build(BuildContext context) { - return AlertDialog( - shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), - title: Text(context.t.permanently_delete), - content: ImmichFormattedText(context.t.permanently_delete_assets_prompt(count: count)), - actions: [ - SizedBox( - width: double.infinity, - height: 48, - child: FilledButton( - onPressed: () => context.pop(false), - style: FilledButton.styleFrom( - backgroundColor: context.colorScheme.surfaceDim, - foregroundColor: context.primaryColor, - ), - child: Text(context.t.cancel, style: const TextStyle(fontWeight: FontWeight.bold)), - ), - ), - const SizedBox(height: 8), - SizedBox( - width: double.infinity, - height: 48, - - child: FilledButton( - onPressed: () => context.pop(true), - style: FilledButton.styleFrom( - backgroundColor: context.colorScheme.errorContainer, - foregroundColor: context.colorScheme.onErrorContainer, - ), - child: Text(context.t.delete, style: const TextStyle(fontWeight: FontWeight.bold)), - ), - ), - ], - ); - } -} diff --git a/mobile/lib/widgets/common/delayed_loading_indicator.dart b/mobile/lib/widgets/common/delayed_loading_indicator.dart deleted file mode 100644 index 5fad10530..000000000 --- a/mobile/lib/widgets/common/delayed_loading_indicator.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart'; - -class DelayedLoadingIndicator extends StatelessWidget { - /// The delay to avoid showing the loading indicator - final Duration delay; - - /// Defaults to using the [ImmichLoadingIndicator] - final Widget? child; - - /// An optional fade in duration to animate the loading - final Duration? fadeInDuration; - - const DelayedLoadingIndicator({super.key, this.delay = const Duration(seconds: 3), this.child, this.fadeInDuration}); - - @override - Widget build(BuildContext context) { - return FutureBuilder( - future: Future.delayed(delay), - builder: (context, snapshot) { - late Widget c; - if (snapshot.connectionState == ConnectionState.done) { - c = child ?? const ImmichLoadingIndicator(key: ValueKey('loading')); - } else { - c = Container(key: const ValueKey('hiding')); - } - - return AnimatedSwitcher(duration: fadeInDuration ?? Duration.zero, child: c); - }, - ); - } -} diff --git a/mobile/lib/widgets/common/fade_in_placeholder_image.dart b/mobile/lib/widgets/common/fade_in_placeholder_image.dart deleted file mode 100644 index 2461dbe6b..000000000 --- a/mobile/lib/widgets/common/fade_in_placeholder_image.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:immich_mobile/widgets/common/transparent_image.dart'; - -class FadeInPlaceholderImage extends StatelessWidget { - final Widget placeholder; - final ImageProvider image; - final Duration duration; - final BoxFit fit; - - const FadeInPlaceholderImage({ - super.key, - required this.placeholder, - required this.image, - this.duration = const Duration(milliseconds: 100), - this.fit = BoxFit.cover, - }); - - @override - Widget build(BuildContext context) { - return SizedBox.expand( - child: Stack( - fit: StackFit.expand, - children: [ - placeholder, - FadeInImage(fadeInDuration: duration, image: image, fit: fit, placeholder: MemoryImage(kTransparentImage)), - ], - ), - ); - } -} diff --git a/mobile/lib/widgets/common/immich_loading_indicator.dart b/mobile/lib/widgets/common/immich_loading_indicator.dart index 1fdc5213d..b09753b53 100644 --- a/mobile/lib/widgets/common/immich_loading_indicator.dart +++ b/mobile/lib/widgets/common/immich_loading_indicator.dart @@ -36,10 +36,7 @@ class ImmichLoadingIndicator extends HookWidget { }, child: Padding( padding: const EdgeInsets.all(15), - child: RotationTransition( - turns: logoAnimationController, - child: const ImmichLogo(heroTag: 'logo'), - ), + child: RotationTransition(turns: logoAnimationController, child: const ImmichLogo()), ), ), ); diff --git a/mobile/lib/widgets/common/immich_logo.dart b/mobile/lib/widgets/common/immich_logo.dart index a36927528..4c9eafedf 100644 --- a/mobile/lib/widgets/common/immich_logo.dart +++ b/mobile/lib/widgets/common/immich_logo.dart @@ -2,9 +2,8 @@ import 'package:flutter/material.dart'; class ImmichLogo extends StatelessWidget { final double size; - final dynamic heroTag; - const ImmichLogo({super.key, this.size = 100, this.heroTag}); + const ImmichLogo({super.key, this.size = 100}); @override Widget build(BuildContext context) { diff --git a/mobile/lib/widgets/common/immich_title_text.dart b/mobile/lib/widgets/common/immich_title_text.dart index 3a848a1db..f18275ce2 100644 --- a/mobile/lib/widgets/common/immich_title_text.dart +++ b/mobile/lib/widgets/common/immich_title_text.dart @@ -3,9 +3,8 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; class ImmichTitleText extends StatelessWidget { final double fontSize; - final Color? color; - const ImmichTitleText({super.key, this.fontSize = 48, this.color}); + const ImmichTitleText({super.key, this.fontSize = 48}); @override Widget build(BuildContext context) { diff --git a/mobile/lib/widgets/common/thumbhash_placeholder.dart b/mobile/lib/widgets/common/thumbhash_placeholder.dart deleted file mode 100644 index 8a9c2eb92..000000000 --- a/mobile/lib/widgets/common/thumbhash_placeholder.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:immich_mobile/widgets/asset_grid/thumbnail_placeholder.dart'; -import 'package:immich_mobile/widgets/common/fade_in_placeholder_image.dart'; -import 'package:octo_image/octo_image.dart'; - -OctoPlaceholderBuilder blurHashPlaceholderBuilder(Uint8List? blurhash, {BoxFit? fit}) { - return (context) => blurhash == null - ? const ThumbnailPlaceholder() - : FadeInPlaceholderImage( - placeholder: const ThumbnailPlaceholder(), - image: MemoryImage(blurhash), - fit: fit ?? BoxFit.cover, - ); -} - -OctoErrorBuilder blurHashErrorBuilder( - Uint8List? blurhash, { - BoxFit? fit, - Text? message, - IconData? icon, - Color? iconColor, - double? iconSize, -}) { - return OctoError.placeholderWithErrorIcon( - blurHashPlaceholderBuilder(blurhash, fit: fit), - message: message, - icon: icon, - iconColor: iconColor, - iconSize: iconSize, - ); -} diff --git a/mobile/lib/widgets/common/transparent_image.dart b/mobile/lib/widgets/common/transparent_image.dart deleted file mode 100644 index af338463b..000000000 --- a/mobile/lib/widgets/common/transparent_image.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'dart:typed_data'; - -final Uint8List kTransparentImage = Uint8List.fromList([ - 0x89, - 0x50, - 0x4E, - 0x47, - 0x0D, - 0x0A, - 0x1A, - 0x0A, - 0x00, - 0x00, - 0x00, - 0x0D, - 0x49, - 0x48, - 0x44, - 0x52, - 0x00, - 0x00, - 0x00, - 0x01, - 0x00, - 0x00, - 0x00, - 0x01, - 0x08, - 0x06, - 0x00, - 0x00, - 0x00, - 0x1F, - 0x15, - 0xC4, - 0x89, - 0x00, - 0x00, - 0x00, - 0x0A, - 0x49, - 0x44, - 0x41, - 0x54, - 0x78, - 0x9C, - 0x63, - 0x00, - 0x01, - 0x00, - 0x00, - 0x05, - 0x00, - 0x01, - 0x0D, - 0x0A, - 0x2D, - 0xB4, - 0x00, - 0x00, - 0x00, - 0x00, - 0x49, - 0x45, - 0x4E, - 0x44, - 0xAE, -]); diff --git a/mobile/lib/widgets/common/user_avatar.dart b/mobile/lib/widgets/common/user_avatar.dart deleted file mode 100644 index 911d6a9f1..000000000 --- a/mobile/lib/widgets/common/user_avatar.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:immich_mobile/domain/models/store.model.dart'; -import 'package:immich_mobile/domain/models/user.model.dart'; -import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart'; - -Widget userAvatar(BuildContext context, UserDto u, {double? radius}) { - final url = "${Store.get(StoreKey.serverEndpoint)}/users/${u.id}/profile-image"; - final nameFirstLetter = u.name.isNotEmpty ? u.name[0] : ""; - return CircleAvatar( - radius: radius, - backgroundColor: context.primaryColor.withAlpha(50), - foregroundImage: RemoteImageProvider(url: url), - // silence errors if user has no profile image, use initials as fallback - onForegroundImageError: (exception, stackTrace) {}, - child: Text(nameFirstLetter.toUpperCase()), - ); -} diff --git a/mobile/lib/widgets/forms/change_password_form.dart b/mobile/lib/widgets/forms/change_password_form.dart index d106f68d8..df2a96366 100644 --- a/mobile/lib/widgets/forms/change_password_form.dart +++ b/mobile/lib/widgets/forms/change_password_form.dart @@ -57,7 +57,6 @@ class ChangePasswordForm extends HookConsumerWidget { ), ), ChangePasswordButton( - passwordController: passwordController, onPressed: () async { if (formKey.currentState!.validate()) { final isSuccess = await ref @@ -159,9 +158,8 @@ class ConfirmPasswordInput extends StatelessWidget { } class ChangePasswordButton extends StatelessWidget { - final TextEditingController passwordController; final VoidCallback onPressed; - const ChangePasswordButton({super.key, required this.passwordController, required this.onPressed}); + const ChangePasswordButton({super.key, required this.onPressed}); @override Widget build(BuildContext context) { diff --git a/mobile/lib/widgets/forms/login/login_form.dart b/mobile/lib/widgets/forms/login/login_form.dart index ae5b5285a..317192af8 100644 --- a/mobile/lib/widgets/forms/login/login_form.dart +++ b/mobile/lib/widgets/forms/login/login_form.dart @@ -554,10 +554,7 @@ class LoginForm extends HookConsumerWidget { GestureDetector( onDoubleTap: () => populateTestLoginInfo(), onLongPress: () => populateTestLoginInfo1(), - child: RotationTransition( - turns: logoAnimationController, - child: const ImmichLogo(heroTag: 'logo'), - ), + child: RotationTransition(turns: logoAnimationController, child: const ImmichLogo()), ), const Padding(padding: EdgeInsets.only(top: 8.0, bottom: 16), child: ImmichTitleText()), ], diff --git a/mobile/lib/widgets/map/asset_marker_icon.dart b/mobile/lib/widgets/map/asset_marker_icon.dart index 75881195a..8b1a23ad4 100644 --- a/mobile/lib/widgets/map/asset_marker_icon.dart +++ b/mobile/lib/widgets/map/asset_marker_icon.dart @@ -4,10 +4,9 @@ import 'package:immich_mobile/presentation/widgets/images/remote_image_provider. import 'package:immich_mobile/utils/image_url_builder.dart'; class AssetMarkerIcon extends StatelessWidget { - const AssetMarkerIcon({required this.id, required this.thumbhash, super.key}); + const AssetMarkerIcon({required this.id, super.key}); final String id; - final String thumbhash; @override Widget build(BuildContext context) { diff --git a/mobile/lib/widgets/map/map_thumbnail.dart b/mobile/lib/widgets/map/map_thumbnail.dart index 84c2ac010..d95188983 100644 --- a/mobile/lib/widgets/map/map_thumbnail.dart +++ b/mobile/lib/widgets/map/map_thumbnail.dart @@ -106,7 +106,7 @@ class MapThumbnail extends HookWidget { alignment: Alignment.bottomCenter, child: SizedBox.square( dimension: height / 2.5, - child: AssetMarkerIcon(id: assetMarkerRemoteId!, thumbhash: assetThumbhash!), + child: AssetMarkerIcon(id: assetMarkerRemoteId!), ), ), ], diff --git a/mobile/lib/widgets/search/search_row_title.dart b/mobile/lib/widgets/search/search_row_title.dart deleted file mode 100644 index cf4cc62c1..000000000 --- a/mobile/lib/widgets/search/search_row_title.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/generated/translations.g.dart'; - -class SearchRowTitle extends StatelessWidget { - const SearchRowTitle({super.key, required this.onViewAllPressed, required this.title}); - - final Function() onViewAllPressed; - final String title; - - @override - Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(title, style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), - TextButton( - onPressed: onViewAllPressed, - child: Text( - context.t.search_page_view_all_button, - style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), - ), - ), - ], - ); - } -} diff --git a/mobile/lib/widgets/settings/language_settings.dart b/mobile/lib/widgets/settings/language_settings.dart index 61f208897..ad2ffcebd 100644 --- a/mobile/lib/widgets/settings/language_settings.dart +++ b/mobile/lib/widgets/settings/language_settings.dart @@ -97,7 +97,6 @@ class LanguageSettings extends HookWidget { return _LanguageItem( key: ValueKey(localeValue.toString()), countryName: countryName, - localeValue: localeValue, isSelected: isSelected, onTap: () { selectedLocale.value = localeValue; @@ -225,16 +224,9 @@ class _LanguageApplyButton extends StatelessWidget { } class _LanguageItem extends StatelessWidget { - const _LanguageItem({ - super.key, - required this.countryName, - required this.localeValue, - required this.isSelected, - required this.onTap, - }); + const _LanguageItem({super.key, required this.countryName, required this.isSelected, required this.onTap}); final String countryName; - final Locale localeValue; final bool isSelected; final VoidCallback onTap; diff --git a/mobile/mise.toml b/mobile/mise.toml index 181c66be0..d43e55860 100644 --- a/mobile/mise.toml +++ b/mobile/mise.toml @@ -109,7 +109,15 @@ run = "flutter test" [tasks.analyze] alias = "lint" description = "Analyze Dart code" +usage = ''' +flag "--full" help="Check for unused code" default=#false +''' depends = ["analyze:dart", "analyze:dcm"] +run = ''' +if [ "${usage_full}" = "true" ]; then + mise run analyze:unused +fi +''' [tasks."analyze-fix"] alias = "lint-fix" @@ -200,6 +208,10 @@ description = "Run Dart Code Metrics" hide = true run = "dcm analyze lib --fatal-style --fatal-warnings" +[tasks."analyze:unused"] +description = "Find unused code" +run = "dcm check-unused-code lib test integration_test --concise" + [tasks."analyze-fix:dart"] description = "Auto-fix Dart analysis" hide = true diff --git a/mobile/test/fixtures/album.stub.dart b/mobile/test/fixtures/album.stub.dart deleted file mode 100644 index 5141540a2..000000000 --- a/mobile/test/fixtures/album.stub.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:immich_mobile/domain/models/album/local_album.model.dart'; - -abstract final class LocalAlbumStub { - const LocalAlbumStub._(); - - static final recent = LocalAlbum( - id: "recent-local-id", - name: "Recent", - updatedAt: DateTime(2023), - assetCount: 1000, - backupSelection: BackupSelection.none, - isIosSharedAlbum: false, - ); -} diff --git a/mobile/test/infrastructure/repository.mock.dart b/mobile/test/infrastructure/repository.mock.dart index ce101e3c4..4c40577db 100644 --- a/mobile/test/infrastructure/repository.mock.dart +++ b/mobile/test/infrastructure/repository.mock.dart @@ -1,3 +1,5 @@ +// ignore_for_file: unused-code + import 'package:immich_mobile/data/db/main/database.dart'; import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; diff --git a/mobile/test/medium/service_context.dart b/mobile/test/medium/service_context.dart index dd45e90ae..b77c162ee 100644 --- a/mobile/test/medium/service_context.dart +++ b/mobile/test/medium/service_context.dart @@ -1,7 +1,6 @@ import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/infrastructure/repositories/partner.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/user.repository.dart'; -import 'package:immich_mobile/repositories/partner_api.repository.dart'; import 'package:mocktail/mocktail.dart'; import '../api.mocks.dart'; @@ -12,7 +11,6 @@ void _stubPartnerApi(MockPartnerApiRepository api) { final id = TestUtils.uuid(); final partner = UserDto(id: id, email: '$id@example.com', name: 'name $id', profileChangedAt: TestUtils.now()); - registerFallbackValue(Direction.sharedByMe); when(() => api.create(any())).thenAnswer((_) async => partner); when(() => api.update(any(), inTimeline: any(named: 'inTimeline'))).thenAnswer((_) async => partner); when(() => api.delete(any())).thenAnswer((_) async {}); diff --git a/mobile/test/mock_http_override.dart b/mobile/test/mock_http_override.dart index 877d6f872..0f1c708c2 100644 --- a/mobile/test/mock_http_override.dart +++ b/mobile/test/mock_http_override.dart @@ -1,6 +1,6 @@ import 'dart:io'; +import 'dart:typed_data'; -import 'package:immich_mobile/widgets/common/transparent_image.dart'; import 'package:mocktail/mocktail.dart'; /// Mocks the http client to always return a transparent image for all the requests. Only useful in widget @@ -23,7 +23,7 @@ class MockHttpOverrides extends HttpOverrides { // Response mocks when(() => response.statusCode).thenReturn(HttpStatus.ok); when(() => response.compressionState).thenReturn(HttpClientResponseCompressionState.decompressed); - when(() => response.contentLength).thenAnswer((_) => kTransparentImage.length); + when(() => response.contentLength).thenAnswer((_) => _kTransparentImage.length); when( () => response.listen( captureAny(), @@ -41,7 +41,7 @@ class MockHttpOverrides extends HttpOverrides { final cancelOnError = invocation.namedArguments[#cancelOnError] as bool; return Stream>.fromIterable([ - kTransparentImage.toList(), + _kTransparentImage.toList(), ]).listen(onData, onDone: onDone, onError: onError, cancelOnError: cancelOnError); }); @@ -56,3 +56,70 @@ class _MockHttpClientRequest extends Mock implements HttpClientRequest {} class _MockHttpClientResponse extends Mock implements HttpClientResponse {} class _MockHttpHeaders extends Mock implements HttpHeaders {} + +final Uint8List _kTransparentImage = Uint8List.fromList([ + 0x89, + 0x50, + 0x4E, + 0x47, + 0x0D, + 0x0A, + 0x1A, + 0x0A, + 0x00, + 0x00, + 0x00, + 0x0D, + 0x49, + 0x48, + 0x44, + 0x52, + 0x00, + 0x00, + 0x00, + 0x01, + 0x00, + 0x00, + 0x00, + 0x01, + 0x08, + 0x06, + 0x00, + 0x00, + 0x00, + 0x1F, + 0x15, + 0xC4, + 0x89, + 0x00, + 0x00, + 0x00, + 0x0A, + 0x49, + 0x44, + 0x41, + 0x54, + 0x78, + 0x9C, + 0x63, + 0x00, + 0x01, + 0x00, + 0x00, + 0x05, + 0x00, + 0x01, + 0x0D, + 0x0A, + 0x2D, + 0xB4, + 0x00, + 0x00, + 0x00, + 0x00, + 0x49, + 0x45, + 0x4E, + 0x44, + 0xAE, +]); diff --git a/mobile/test/pages/common/download_panel_test.dart b/mobile/test/pages/common/download_panel_test.dart index f2a6b09d8..ae9c01786 100644 --- a/mobile/test/pages/common/download_panel_test.dart +++ b/mobile/test/pages/common/download_panel_test.dart @@ -1,7 +1,7 @@ import 'package:background_downloader/background_downloader.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:immich_mobile/pages/common/download_panel.dart'; +import 'package:immich_mobile/pages/common/download_task_tile.dart'; import '../../widget_tester_extensions.dart'; diff --git a/mobile/test/providers/view_intent/view_intent_handler_android_test.dart b/mobile/test/providers/view_intent/view_intent_handler_android_test.dart index be7549e20..dafc6b31b 100644 --- a/mobile/test/providers/view_intent/view_intent_handler_android_test.dart +++ b/mobile/test/providers/view_intent/view_intent_handler_android_test.dart @@ -51,8 +51,6 @@ class FakeAssetService extends Fake implements AssetService { class TestViewIntentService extends ViewIntentService { ViewIntentPayload? consumedAttachment; int cleanupStaleTempFilesCalls = 0; - int cleanupManagedTempFileCalls = 0; - final List managedTempPaths = []; TestViewIntentService() : super(MockViewIntentHostApi()); @@ -63,16 +61,6 @@ class TestViewIntentService extends ViewIntentService { Future cleanupStaleTempFiles() async { cleanupStaleTempFilesCalls++; } - - @override - Future cleanupManagedTempFile() async { - cleanupManagedTempFileCalls++; - } - - @override - Future setManagedTempFilePath(String path) async { - managedTempPaths.add(path); - } } class TestAuthNotifier extends AuthNotifier { diff --git a/mobile/test/service.mocks.dart b/mobile/test/service.mocks.dart index e56eb15f1..2b3c20357 100644 --- a/mobile/test/service.mocks.dart +++ b/mobile/test/service.mocks.dart @@ -1,3 +1,5 @@ +// ignore_for_file: unused-code + import 'package:immich_mobile/domain/services/asset.service.dart'; import 'package:immich_mobile/domain/services/background_worker.service.dart'; import 'package:immich_mobile/domain/services/partner.service.dart'; diff --git a/mobile/test/unit/factories/local_album_factory.dart b/mobile/test/unit/factories/local_album_factory.dart index 639237c85..856ea37e0 100644 --- a/mobile/test/unit/factories/local_album_factory.dart +++ b/mobile/test/unit/factories/local_album_factory.dart @@ -3,8 +3,6 @@ import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import '../../utils.dart'; class LocalAlbumFactory { - const LocalAlbumFactory(); - static LocalAlbum create({ String? id, String? name, diff --git a/mobile/test/unit/factories/local_asset_factory.dart b/mobile/test/unit/factories/local_asset_factory.dart index 5b16635d4..ced9d8b53 100644 --- a/mobile/test/unit/factories/local_asset_factory.dart +++ b/mobile/test/unit/factories/local_asset_factory.dart @@ -3,8 +3,6 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import '../../utils.dart'; class LocalAssetFactory { - const LocalAssetFactory(); - static LocalAsset create({String? id, String? name, String? remoteId}) { final assetId = TestUtils.uuid(id); diff --git a/mobile/test/unit/factories/partner_user_factory.dart b/mobile/test/unit/factories/partner_user_factory.dart index 2ee0fa077..2c94cc338 100644 --- a/mobile/test/unit/factories/partner_user_factory.dart +++ b/mobile/test/unit/factories/partner_user_factory.dart @@ -3,8 +3,6 @@ import 'package:immich_mobile/domain/models/user.model.dart'; import '../../utils.dart'; class PartnerFactory { - const PartnerFactory(); - static Partner create({String? id, String? email, String? name, bool? inTimeline}) { final partnerId = TestUtils.uuid(id); return Partner( diff --git a/mobile/test/unit/factories/remote_album_factory.dart b/mobile/test/unit/factories/remote_album_factory.dart index 70976b448..b3040cb6a 100644 --- a/mobile/test/unit/factories/remote_album_factory.dart +++ b/mobile/test/unit/factories/remote_album_factory.dart @@ -3,8 +3,6 @@ import 'package:immich_mobile/domain/models/album/album.model.dart'; import '../../utils.dart'; class RemoteAlbumFactory { - const RemoteAlbumFactory(); - static RemoteAlbum create({ String? id, String? name, diff --git a/mobile/test/unit/factories/remote_asset_factory.dart b/mobile/test/unit/factories/remote_asset_factory.dart index 2936708ab..8c53ddb49 100644 --- a/mobile/test/unit/factories/remote_asset_factory.dart +++ b/mobile/test/unit/factories/remote_asset_factory.dart @@ -3,8 +3,6 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import '../../utils.dart'; class RemoteAssetFactory { - const RemoteAssetFactory(); - static RemoteAsset create({ String? id, String? name, diff --git a/mobile/test/unit/factories/user_factory.dart b/mobile/test/unit/factories/user_factory.dart index d3df9556e..65768cefe 100644 --- a/mobile/test/unit/factories/user_factory.dart +++ b/mobile/test/unit/factories/user_factory.dart @@ -3,8 +3,6 @@ import 'package:immich_mobile/domain/models/user.model.dart'; import '../../utils.dart'; class UserFactory { - const UserFactory(); - static User create({ String? id, String? name, diff --git a/mobile/test/utils.dart b/mobile/test/utils.dart index aa1f82dc2..8822a4e97 100644 --- a/mobile/test/utils.dart +++ b/mobile/test/utils.dart @@ -10,6 +10,7 @@ class TestUtils { static DateTime date([DateTime? date]) => date ?? DateTime.now(); static DateTime now() => DateTime.now(); static DateTime yesterday() => DateTime.now().subtract(const Duration(days: 1)); + // ignore: unused-code static DateTime tomorrow() => DateTime.now().add(const Duration(days: 1)); static T randElement(List list) => list[_random.nextInt(list.length)]; diff --git a/mobile/test/utils_legacy/action_button_utils_test.dart b/mobile/test/utils_legacy/action_button_utils_test.dart index b2316e013..929ac16ed 100644 --- a/mobile/test/utils_legacy/action_button_utils_test.dart +++ b/mobile/test/utils_legacy/action_button_utils_test.dart @@ -86,7 +86,6 @@ void main() { asset: asset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -97,7 +96,6 @@ void main() { expect(context.asset, isA()); expect(context.isOwner, isTrue); expect(context.isArchived, isFalse); - expect(context.isTrashEnabled, isTrue); expect(context.isInLockedView, isFalse); expect(context.currentAlbum, isNull); expect(context.source, ActionSource.timeline); @@ -117,7 +115,6 @@ void main() { asset: mergedAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -133,7 +130,6 @@ void main() { asset: mergedAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: true, currentAlbum: null, advancedTroubleshooting: false, @@ -152,7 +148,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -169,7 +164,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: true, currentAlbum: null, advancedTroubleshooting: false, @@ -186,7 +180,6 @@ void main() { asset: localAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -205,7 +198,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -222,7 +214,6 @@ void main() { asset: remoteAsset, isOwner: false, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -239,7 +230,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: true, currentAlbum: null, advancedTroubleshooting: false, @@ -256,7 +246,6 @@ void main() { asset: localAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -273,7 +262,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: true, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -292,7 +280,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: true, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -309,7 +296,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -326,7 +312,6 @@ void main() { asset: remoteAsset, isOwner: false, isArchived: true, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -345,7 +330,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -362,7 +346,6 @@ void main() { asset: mergedAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -379,7 +362,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: true, currentAlbum: null, advancedTroubleshooting: false, @@ -398,7 +380,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, isStacked: false, currentAlbum: null, @@ -415,7 +396,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: true, currentAlbum: null, isStacked: false, @@ -434,7 +414,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -452,7 +431,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: false, - isTrashEnabled: false, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -472,7 +450,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -491,7 +468,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -510,7 +486,6 @@ void main() { asset: localAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -527,7 +502,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -543,7 +517,6 @@ void main() { asset: mergedAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -562,7 +535,6 @@ void main() { asset: localAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -581,7 +553,6 @@ void main() { asset: mergedAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: album, advancedTroubleshooting: false, @@ -597,7 +568,6 @@ void main() { asset: mergedAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -616,7 +586,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -633,7 +602,6 @@ void main() { asset: remoteAsset, isOwner: false, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -650,7 +618,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: true, currentAlbum: null, advancedTroubleshooting: false, @@ -667,7 +634,6 @@ void main() { asset: localAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -686,7 +652,6 @@ void main() { asset: mergedAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: album, advancedTroubleshooting: false, @@ -704,7 +669,6 @@ void main() { asset: mergedAsset, isOwner: false, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: album, advancedTroubleshooting: false, @@ -722,7 +686,6 @@ void main() { asset: mergedAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: true, currentAlbum: album, advancedTroubleshooting: false, @@ -739,7 +702,6 @@ void main() { asset: mergedAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -757,7 +719,6 @@ void main() { asset: mergedAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: album, advancedTroubleshooting: false, @@ -775,7 +736,6 @@ void main() { asset: mergedAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: album, advancedTroubleshooting: false, @@ -795,7 +755,6 @@ void main() { asset: mergedAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: album, advancedTroubleshooting: false, @@ -812,7 +771,6 @@ void main() { asset: mergedAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: album, advancedTroubleshooting: false, @@ -829,7 +787,6 @@ void main() { asset: mergedAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: album, advancedTroubleshooting: false, @@ -845,7 +802,6 @@ void main() { asset: mergedAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -863,7 +819,6 @@ void main() { asset: mergedAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: true, @@ -879,7 +834,6 @@ void main() { asset: mergedAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -899,7 +853,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -916,7 +869,6 @@ void main() { asset: remoteAsset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -933,7 +885,6 @@ void main() { asset: remoteAsset, isOwner: false, isArchived: true, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -955,7 +906,6 @@ void main() { asset: asset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -974,7 +924,6 @@ void main() { asset: asset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: album, advancedTroubleshooting: false, @@ -988,7 +937,6 @@ void main() { asset: createRemoteAsset(), isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: null, advancedTroubleshooting: false, @@ -1003,7 +951,6 @@ void main() { asset: asset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: album, advancedTroubleshooting: false, @@ -1018,7 +965,6 @@ void main() { asset: asset, isOwner: true, isArchived: false, - isTrashEnabled: true, isInLockedView: false, currentAlbum: album, advancedTroubleshooting: false,