feat(mobile): enabled DCM (#17957)

* enable DCM in CI

* chore: up version

* chore: up version

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
Andreas Tollkötter 2025-06-09 18:09:02 +02:00 committed by GitHub
parent 16f83c0aa9
commit b890440f6b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 271 additions and 312 deletions

View File

@ -58,6 +58,14 @@ jobs:
run: dart pub get
working-directory: ./mobile
- name: Install DCM
run: |
sudo apt-get update
wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg
echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list
sudo apt-get update
sudo apt-get install dcm
- name: Generate translation file
run: make translation
working-directory: ./mobile
@ -100,6 +108,10 @@ jobs:
run: dart run custom_lint
working-directory: ./mobile
- name: Run DCM
run: dcm analyze lib
working-directory: ./mobile
zizmor:
name: zizmor
runs-on: ubuntu-latest

View File

@ -17,6 +17,27 @@ To add a new translation text, enter the key-value pair in the `i18n/en.json` in
make translation
```
## Static Analysis
The following checks of static analysis must pass for a contribution to the mobile app to be valid:
```bash
dart format lib
dart analyze
dart run custom_lint
dcm analyze lib
```
[DCM](https://dcm.dev/) is a vendor tool that needs to be downloaded manually to run locally.
Immich was provided an open source license.
To use it, it is important that you do not have an active free tier license (can be verified with `dcm license`).
If you have write-access to the Immich repository directly, running dcm in your clone should just work.
If you are working on a clone of a fork, you need to connect to the main Immich repository as remote first:
```bash
git remote add immich git@github.com:immich-app/immich.git
```
## Immich-Flutter Directory Structure
Below are the directory inside the `lib` directory:

View File

@ -128,82 +128,169 @@ custom_lint:
- test/**.dart
dart_code_metrics:
extends:
- recommended
rules:
# Common
- arguments-ordering:
last:
- child
- children
- avoid-accessing-other-classes-private-members
- avoid-assigning-to-static-field
- avoid-assignments-as-conditions
- avoid-async-call-in-sync-function
- avoid-collapsible-if
- avoid-collection-equality-checks
- avoid-complex-loop-conditions
- avoid-declaring-call-method
- avoid-extensions-on-records
- avoid-function-type-in-records
- avoid-future-ignore
- avoid-global-state
- avoid-inverted-boolean-checks
- avoid-late-final-reassignment
- avoid-local-functions:
exclude:
- test/**.dart
- avoid-negated-conditions
- avoid-nested-streams-and-futures
- avoid-referencing-subclasses
- avoid-unnecessary-continue
- avoid-unnecessary-nullable-return-type: false
- binary-expression-operand-order
- pattern-fields-ordering
- prefer-abstract-final-static-class
- prefer-commenting-future-delayed
- prefer-early-return
- prefer-first
- prefer-immediate-return
- prefer-last
- prefer-simpler-boolean-expressions
- prefer-switch-expression
- prefer-type-over-var
- use-existing-destructuring
- use-existing-variable
# Flutter
- avoid-border-all
- avoid-complex-arithmetic-expressions
- avoid-expanded-as-spacer
- avoid-if-with-many-branches
- avoid-inherited-widget-in-initstate
- avoid-late-context
- avoid-returning-widgets
- avoid-shrink-wrap-in-lists
- avoid-single-child-column-or-row
- avoid-stateless-widget-initialized-fields
- avoid-wrapping-in-padding
- prefer-align-over-container
- prefer-const-border-radius
- prefer-correct-callback-field-name: false
- prefer-correct-edge-insets-constructor
- prefer-define-hero-tag
- prefer-extracting-callbacks
- prefer-for-loop-in-children
- prefer-match-file-name: false
- prefer-sliver-prefix
- prefer-spacing
- prefer-text-rich
- prefer-transform-over-container
- prefer-using-list-view
- prefer-widget-private-members:
ignore-static: true
- use-closest-build-context
# riverpod
- avoid-calling-notifier-members-inside-build
- avoid-notifier-constructors
- avoid-ref-read-inside-build
- avoid-ref-watch-outside-build
- avoid-unnecessary-consumer-widgets
- dispose-provided-instances
- use-ref-read-synchronously
# All rules from "recommended" preset
# Show potential errors
# - avoid-cascade-after-if-null
# - avoid-collection-methods-with-unrelated-types
# - avoid-duplicate-exports
# - avoid-dynamic
# - avoid-missing-enum-constant-in-map
# - avoid-passing-async-when-sync-expected
# - avoid-throw-in-catch-block
- avoid-unused-parameters
# - avoid-unnecessary-type-assertions
# - avoid-unnecessary-type-casts
# - avoid-unrelated-type-assertions
# - avoid-unrelated-type-casts
# - no-empty-block
# - no-equal-then-else
# - prefer-correct-test-file-name
# - prefer-match-file-name
# - prefer-return-await
# - avoid-self-assignment
# - avoid-self-compare
# - avoid-shadowing
# - prefer-iterable-of
# - no-equal-switch-case
# - no-equal-conditions
# - avoid-equal-expressions
# - avoid-missed-calls
# - avoid-unnecessary-negations
# - avoid-unused-generics
# - function-always-returns-null
# - avoid-throw-objects-without-tostring
# - avoid-unsafe-collection-methods
# - prefer-wildcard-pattern
# - no-equal-switch-expression-cases
# - avoid-future-tostring
# - avoid-unassigned-late-fields
# - avoid-nested-futures
# - avoid-generics-shadowing
# - prefer-parentheses-with-if-null
# - no-equal-nested-conditions
# - avoid-shadowed-extension-methods
# - avoid-unnecessary-conditionals
# - avoid-double-slash-imports
# - avoid-map-keys-contains
# - prefer-correct-json-casts
# - avoid-duplicate-mixins
# - avoid-nullable-interpolation
# - avoid-unused-instances
# - prefer-correct-for-loop-increment
# - prefer-public-exception-classes
# - avoid-uncaught-future-errors
# - always-remove-listener
# - avoid-unnecessary-setstate
# - check-for-equals-in-render-object-setters
# - consistent-update-render-object
# - use-setstate-synchronously
# - avoid-incomplete-copy-with
# - proper-super-calls
# - dispose-fields
# - avoid-empty-setstate
# - avoid-state-constructors
# - avoid-recursive-widget-calls
# - avoid-missing-image-alt
# - avoid-passing-self-as-argument
# - avoid-unnecessary-if
# - avoid-unconditional-break
# - avoid-referencing-discarded-variables
# - avoid-unnecessary-local-late
# - avoid-wildcard-cases-with-enums
# - match-getter-setter-field-names
# - avoid-accessing-collections-by-constant-index
# - prefer-unique-test-names
# - avoid-duplicate-cascades
# - prefer-specific-cases-first
# - avoid-duplicate-switch-case-conditions
# - prefer-explicit-function-type
# - avoid-misused-test-matchers
# - avoid-duplicate-test-assertions
# - prefer-switch-with-enums
# - prefer-any-or-every
# - avoid-duplicate-map-keys
# - avoid-nullable-tostring
# - avoid-undisposed-instances
# - avoid-duplicate-initializers
# - avoid-unassigned-stream-subscriptions
# - avoid-empty-test-groups
# - avoid-not-encodable-in-to-json
# - avoid-contradictory-expressions
# - avoid-excessive-expressions
# - prefer-private-extension-type-field
# - avoid-renaming-representation-getters
# - avoid-empty-spread
# - avoid-unnecessary-gesture-detector
# - avoid-missing-completer-stack-trace
# - avoid-casting-to-extension-type
# - prefer-overriding-parent-equality
# - avoid-missing-controller
# - avoid-unknown-pragma
# - avoid-conditions-with-boolean-literals
# - avoid-multi-assignment
# - avoid-collection-equality-checks
# - avoid-only-rethrow
# - avoid-incorrect-image-opacity
# - avoid-misused-set-literals
# - dispose-class-fields
# - avoid-suspicious-super-overrides
# - avoid-assignments-as-conditions
# - avoid-unused-assignment
# - avoid-unnecessary-overrides
# - avoid-implicitly-nullable-extension-types
# Enable with the next release
# - avoid-late-final-reassignment
# - avoid-duplicate-constant-values
# - function-always-returns-same-value
# - avoid-flexible-outside-flex
# - avoid-unnecessary-patterns
# - use-closest-build-context
# - avoid-commented-out-code
# - avoid-recursive-tostring
# - avoid-enum-values-by-index
# - avoid-constant-assert-conditions
# - avoid-inconsistent-digit-separators
# - pass-existing-future-to-future-builder
# - pass-existing-stream-to-stream-builder
# Code simplification
# - avoid-redundant-async
# - avoid-redundant-else
# - avoid-unnecessary-nullable-return-type
# - avoid-redundant-pragma-inline
# - avoid-nested-records
# - avoid-redundant-positional-field-name
# - avoid-explicit-pattern-field-name
# - prefer-simpler-patterns-null-check
# - avoid-unnecessary-return
# - avoid-duplicate-patterns
# - avoid-keywords-in-wildcard-pattern
# - avoid-unnecessary-futures
# - avoid-unnecessary-reassignment
# - avoid-unnecessary-call
# - avoid-unnecessary-stateful-widgets
# - prefer-dedicated-media-query-methods
# - avoid-unnecessary-overrides-in-state
# - move-variable-closer-to-its-usage
# - avoid-nullable-parameters-with-default-values
# - prefer-null-aware-spread
# - avoid-inferrable-type-arguments
# - avoid-unnecessary-super
# - avoid-unnecessary-collections
# - avoid-unnecessary-extends
# - avoid-unnecessary-enum-arguments
# - prefer-contains
# Enable with the next release
# - prefer-simpler-boolean-expressions
# - prefer-spacing
# - avoid-unnecessary-continue
# - avoid-unnecessary-compare-to
# Style
# - prefer-trailing-comma
# - unnecessary-trailing-comma
# - prefer-declaring-const-constructor
# - prefer-single-widget-per-file
# - prefer-prefixed-global-constants
# - prefer-correct-callback-field-name

1
mobile/dcm_global.yaml Normal file
View File

@ -0,0 +1 @@
version: '>=1.29.0 <1.30.0'

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
List<ColorFilter> filters = [
const List<ColorFilter> filters = [
//Original
const ColorFilter.matrix([
ColorFilter.matrix([
1,
0,
0,
@ -25,7 +25,7 @@ List<ColorFilter> filters = [
0,
]),
//Vintage
const ColorFilter.matrix([
ColorFilter.matrix([
0.8,
0.1,
0.1,
@ -48,7 +48,7 @@ List<ColorFilter> filters = [
0,
]),
//Mood
const ColorFilter.matrix([
ColorFilter.matrix([
1.2,
0.1,
0.1,
@ -71,7 +71,7 @@ List<ColorFilter> filters = [
0,
]),
//Crisp
const ColorFilter.matrix([
ColorFilter.matrix([
1.2,
0,
0,
@ -94,7 +94,7 @@ List<ColorFilter> filters = [
0,
]),
//Cool
const ColorFilter.matrix([
ColorFilter.matrix([
0.9,
0,
0.2,
@ -117,7 +117,7 @@ List<ColorFilter> filters = [
0,
]),
//Blush
const ColorFilter.matrix([
ColorFilter.matrix([
1.1,
0.1,
0.1,
@ -140,7 +140,7 @@ List<ColorFilter> filters = [
0,
]),
//Sunkissed
const ColorFilter.matrix([
ColorFilter.matrix([
1.3,
0,
0.1,
@ -163,7 +163,7 @@ List<ColorFilter> filters = [
0,
]),
//Fresh
const ColorFilter.matrix([
ColorFilter.matrix([
1.2,
0,
0,
@ -186,7 +186,7 @@ List<ColorFilter> filters = [
0,
]),
//Classic
const ColorFilter.matrix([
ColorFilter.matrix([
1.1,
0,
-0.1,
@ -209,7 +209,7 @@ List<ColorFilter> filters = [
0,
]),
//Lomo-ish
const ColorFilter.matrix([
ColorFilter.matrix([
1.5,
0,
0.1,
@ -232,7 +232,7 @@ List<ColorFilter> filters = [
0,
]),
//Nashville
const ColorFilter.matrix([
ColorFilter.matrix([
1.2,
0.15,
-0.15,
@ -255,7 +255,7 @@ List<ColorFilter> filters = [
0,
]),
//Valencia
const ColorFilter.matrix([
ColorFilter.matrix([
1.15,
0.1,
0.1,
@ -278,7 +278,7 @@ List<ColorFilter> filters = [
0,
]),
//Clarendon
const ColorFilter.matrix([
ColorFilter.matrix([
1.2,
0,
0,
@ -301,7 +301,7 @@ List<ColorFilter> filters = [
0,
]),
//Moon
const ColorFilter.matrix([
ColorFilter.matrix([
0.33,
0.33,
0.33,
@ -324,7 +324,7 @@ List<ColorFilter> filters = [
0,
]),
//Willow
const ColorFilter.matrix([
ColorFilter.matrix([
0.5,
0.5,
0.5,
@ -347,7 +347,7 @@ List<ColorFilter> filters = [
0,
]),
//Kodak
const ColorFilter.matrix([
ColorFilter.matrix([
1.3,
0.1,
-0.1,
@ -370,7 +370,7 @@ List<ColorFilter> filters = [
0,
]),
//Frost
const ColorFilter.matrix([
ColorFilter.matrix([
0.8,
0.2,
0.1,
@ -393,7 +393,7 @@ List<ColorFilter> filters = [
0,
]),
//Night Vision
const ColorFilter.matrix([
ColorFilter.matrix([
0.1,
0.95,
0.2,
@ -416,7 +416,7 @@ List<ColorFilter> filters = [
0,
]),
//Sunset
const ColorFilter.matrix([
ColorFilter.matrix([
1.5,
0.2,
0,
@ -439,7 +439,7 @@ List<ColorFilter> filters = [
0,
]),
//Noir
const ColorFilter.matrix([
ColorFilter.matrix([
1.3,
-0.3,
0.1,
@ -462,7 +462,7 @@ List<ColorFilter> filters = [
0,
]),
//Dreamy
const ColorFilter.matrix([
ColorFilter.matrix([
1.1,
0.1,
0.1,
@ -485,7 +485,7 @@ List<ColorFilter> filters = [
0,
]),
//Sepia
const ColorFilter.matrix([
ColorFilter.matrix([
0.393,
0.769,
0.189,
@ -508,7 +508,7 @@ List<ColorFilter> filters = [
0,
]),
//Radium
const ColorFilter.matrix([
ColorFilter.matrix([
1.438,
-0.062,
-0.062,
@ -531,7 +531,7 @@ List<ColorFilter> filters = [
0,
]),
//Aqua
const ColorFilter.matrix([
ColorFilter.matrix([
0.2126,
0.7152,
0.0722,
@ -554,7 +554,7 @@ List<ColorFilter> filters = [
0,
]),
//Purple Haze
const ColorFilter.matrix([
ColorFilter.matrix([
1.3,
0,
1.2,
@ -577,7 +577,7 @@ List<ColorFilter> filters = [
0,
]),
//Lemonade
const ColorFilter.matrix([
ColorFilter.matrix([
1.2,
0.1,
0,
@ -600,7 +600,7 @@ List<ColorFilter> filters = [
0,
]),
//Caramel
const ColorFilter.matrix([
ColorFilter.matrix([
1.6,
0.2,
0,
@ -623,7 +623,7 @@ List<ColorFilter> filters = [
0,
]),
//Peachy
const ColorFilter.matrix([
ColorFilter.matrix([
1.3,
0.5,
0,
@ -646,7 +646,7 @@ List<ColorFilter> filters = [
0,
]),
//Neon
const ColorFilter.matrix([
ColorFilter.matrix([
1,
0,
1,
@ -669,7 +669,7 @@ List<ColorFilter> filters = [
0,
]),
//Cold Morning
const ColorFilter.matrix([
ColorFilter.matrix([
0.9,
0.1,
0.2,
@ -692,7 +692,7 @@ List<ColorFilter> filters = [
0,
]),
//Lush
const ColorFilter.matrix([
ColorFilter.matrix([
0.9,
0.2,
0,
@ -715,7 +715,7 @@ List<ColorFilter> filters = [
0,
]),
//Urban Neon
const ColorFilter.matrix([
ColorFilter.matrix([
1.1,
0,
0.3,
@ -738,7 +738,7 @@ List<ColorFilter> filters = [
0,
]),
//Monochrome
const ColorFilter.matrix([
ColorFilter.matrix([
0.6,
0.2,
0.2,

View File

@ -229,7 +229,6 @@ class ImmichAppState extends ConsumerState<ImmichApp>
}
}
// ignore: prefer-single-widget-per-file
class MainWidget extends StatelessWidget {
const MainWidget({super.key});

View File

@ -1,5 +1,3 @@
import 'dart:typed_data';
import 'package:immich_mobile/entities/album.entity.dart';
class AvailableAlbum {
@ -16,7 +14,6 @@ class AvailableAlbum {
Album? album,
int? assetCount,
DateTime? lastBackup,
Uint8List? thumbnailData,
}) {
return AvailableAlbum(
album: album ?? this.album,

View File

@ -1,5 +1,3 @@
// ignore_for_file: add-copy-with
sealed class MapEvent {
const MapEvent();
}

View File

@ -236,7 +236,7 @@ class GalleryViewerPage extends HookConsumerWidget {
});
});
PhotoViewGalleryPageOptions buildImage(BuildContext context, Asset asset) {
PhotoViewGalleryPageOptions buildImage(Asset asset) {
return PhotoViewGalleryPageOptions(
onDragStart: (_, details, __) {
localPosition.value = details.localPosition;
@ -312,7 +312,7 @@ class GalleryViewerPage extends HookConsumerWidget {
}
if (newAsset.isImage && !isPlayingMotionVideo) {
return buildImage(context, newAsset);
return buildImage(newAsset);
}
return buildVideo(context, newAsset);
}

View File

@ -1,5 +1,3 @@
// ignore_for_file: avoid-local-functions
import 'dart:async';
import 'package:auto_route/auto_route.dart';
@ -144,7 +142,6 @@ class _Feature {
final Future<void> Function(BuildContext, WidgetRef _) onTap;
}
// ignore: prefer-single-widget-per-file
class _DevLogs extends StatelessWidget {
const _DevLogs();
@ -172,7 +169,6 @@ class _DevLogs extends StatelessWidget {
builder: (_, logMessages) {
return ListView.separated(
itemBuilder: (ctx, index) {
// ignore: avoid-unsafe-collection-methods
final logMessage = logMessages.data![index];
return ListTile(
title: Text(

View File

@ -1,5 +1,3 @@
// ignore_for_file: prefer-single-widget-per-file
import 'package:auto_route/auto_route.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';

View File

@ -5,4 +5,4 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'api.provider.g.dart';
@Riverpod(keepAlive: true)
ApiService apiService(Ref ref) => ApiService();
ApiService apiService(Ref _) => ApiService();

View File

@ -6,7 +6,7 @@ part of 'api.provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$apiServiceHash() => r'93a7e3b4d3004741abc3061c4688239c3a72f9c4';
String _$apiServiceHash() => r'187a7de59b064fab1104c23717f18ce0ae3e426c';
/// See also [apiService].
@ProviderFor(apiService)

View File

@ -5,4 +5,4 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'app_settings.provider.g.dart';
@Riverpod(keepAlive: true)
AppSettingsService appSettingsService(Ref ref) => AppSettingsService();
AppSettingsService appSettingsService(Ref _) => AppSettingsService();

View File

@ -7,7 +7,7 @@ part of 'app_settings.provider.dart';
// **************************************************************************
String _$appSettingsServiceHash() =>
r'3736e0d384ec7b1f896938589656dd6eb1552d60';
r'2aa16d76a8df869c39486325efc1d08b2d2c284c';
/// See also [appSettingsService].
@ProviderFor(appSettingsService)

View File

@ -39,6 +39,6 @@ final assetStackStateProvider = StateNotifierProvider.autoDispose
);
@riverpod
int assetStackIndex(Ref ref, Asset asset) {
int assetStackIndex(Ref _) {
return -1;
}

View File

@ -6,155 +6,22 @@ part of 'asset_stack.provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$assetStackIndexHash() => r'38b4b0116e3e4592620b118ae01cf89b77da9cfe';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
String _$assetStackIndexHash() => r'086ddb782e3eb38b80d755666fe35be8fe7322d7';
/// See also [assetStackIndex].
@ProviderFor(assetStackIndex)
const assetStackIndexProvider = AssetStackIndexFamily();
/// See also [assetStackIndex].
class AssetStackIndexFamily extends Family<int> {
/// See also [assetStackIndex].
const AssetStackIndexFamily();
/// See also [assetStackIndex].
AssetStackIndexProvider call(
Asset asset,
) {
return AssetStackIndexProvider(
asset,
);
}
@override
AssetStackIndexProvider getProviderOverride(
covariant AssetStackIndexProvider provider,
) {
return call(
provider.asset,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'assetStackIndexProvider';
}
/// See also [assetStackIndex].
class AssetStackIndexProvider extends AutoDisposeProvider<int> {
/// See also [assetStackIndex].
AssetStackIndexProvider(
Asset asset,
) : this._internal(
(ref) => assetStackIndex(
ref as AssetStackIndexRef,
asset,
),
from: assetStackIndexProvider,
name: r'assetStackIndexProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$assetStackIndexHash,
dependencies: AssetStackIndexFamily._dependencies,
allTransitiveDependencies:
AssetStackIndexFamily._allTransitiveDependencies,
asset: asset,
);
AssetStackIndexProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.asset,
}) : super.internal();
final Asset asset;
@override
Override overrideWith(
int Function(AssetStackIndexRef provider) create,
) {
return ProviderOverride(
origin: this,
override: AssetStackIndexProvider._internal(
(ref) => create(ref as AssetStackIndexRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
asset: asset,
),
);
}
@override
AutoDisposeProviderElement<int> createElement() {
return _AssetStackIndexProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is AssetStackIndexProvider && other.asset == asset;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, asset.hashCode);
return _SystemHash.finish(hash);
}
}
final assetStackIndexProvider = AutoDisposeProvider<int>.internal(
assetStackIndex,
name: r'assetStackIndexProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$assetStackIndexHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin AssetStackIndexRef on AutoDisposeProviderRef<int> {
/// The parameter `asset` of this provider.
Asset get asset;
}
class _AssetStackIndexProviderElement extends AutoDisposeProviderElement<int>
with AssetStackIndexRef {
_AssetStackIndexProviderElement(super.provider);
@override
Asset get asset => (origin as AssetStackIndexProvider).asset;
}
typedef AssetStackIndexRef = AutoDisposeProviderRef<int>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@ -144,7 +144,7 @@ class DownloadStateNotifier extends StateNotifier<DownloadState> {
return await _downloadService.downloadAll(assets);
}
void downloadAsset(Asset asset, BuildContext context) async {
void downloadAsset(Asset asset) async {
await _downloadService.download(asset);
}

View File

@ -7,7 +7,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'immich_logo_provider.g.dart';
@riverpod
Future<Uint8List> immichLogo(Ref ref) async {
Future<Uint8List> immichLogo(Ref _) async {
final json = await rootBundle.loadString('assets/immich-logo.json');
final j = jsonDecode(json);
return base64Decode(j['content']);

View File

@ -6,7 +6,7 @@ part of 'immich_logo_provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$immichLogoHash() => r'6f23d217c44279537b7edee1ca80ebf47f69a4d0';
String _$immichLogoHash() => r'6de7fcca1ef9acef6ab7398eb0c664080747e0ea';
/// See also [immichLogo].
@ProviderFor(immichLogo)

View File

@ -18,7 +18,6 @@ import 'package:immich_mobile/interfaces/backup_album.interface.dart';
import 'package:immich_mobile/models/backup/backup_candidate.model.dart';
import 'package:immich_mobile/models/backup/current_upload_asset.model.dart';
import 'package:immich_mobile/models/backup/error_upload_asset.model.dart';
import 'package:immich_mobile/models/backup/success_upload_asset.model.dart';
import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/providers/db.provider.dart';
@ -489,7 +488,6 @@ class BackgroundService {
_cancellationToken!,
pmProgressHandler: pmProgressHandler,
onSuccess: (result) => _onAssetUploaded(
result: result,
shouldNotify: notifyTotalProgress,
),
onProgress: (bytes, totalBytes) =>
@ -511,7 +509,6 @@ class BackgroundService {
}
void _onAssetUploaded({
required SuccessUploadAsset result,
bool shouldNotify = false,
}) async {
if (!shouldNotify) {

View File

@ -184,10 +184,10 @@ class BackupVerificationService {
// for images: make sure they are pixel-wise identical
// (skip first few KBs containing metadata)
final Uint64List localImage =
_fakeDecodeImg(local, await file.readAsBytes());
_fakeDecodeImg(await file.readAsBytes());
final res = await apiService.assetsApi
.downloadAssetWithHttpInfo(remote.remoteId!);
final Uint64List remoteImage = _fakeDecodeImg(remote, res.bodyBytes);
final Uint64List remoteImage = _fakeDecodeImg(res.bodyBytes);
final eq = const ListEquality().equals(remoteImage, localImage);
return eq;
@ -198,7 +198,7 @@ class BackupVerificationService {
return false;
}
static Uint64List _fakeDecodeImg(Asset asset, Uint8List bytes) {
static Uint64List _fakeDecodeImg(Uint8List bytes) {
const headerLength = 131072; // assume header is at most 128 KB
final start = bytes.length < headerLength * 2
? (bytes.length ~/ (4 * 8)) * 8

View File

@ -1,5 +1,3 @@
// ignore_for_file: avoid-unsafe-collection-methods
import 'dart:convert';
import 'dart:io';

View File

@ -1,5 +1,3 @@
// ignore_for_file: avoid-unsafe-collection-methods
import 'dart:async';
import 'dart:convert';
import 'dart:io';

View File

@ -94,7 +94,7 @@ Future<void> handleFavoriteAssets(
ImmichToast.show(
context: context,
msg: toastMessage,
gravity: ToastGravity.BOTTOM,
gravity: toastGravity,
);
}
}
@ -164,9 +164,8 @@ Future<void> handleSetAssetsVisibility(
WidgetRef ref,
BuildContext context,
AssetVisibilityEnum visibility,
List<Asset> selection, {
ToastGravity toastGravity = ToastGravity.BOTTOM,
}) async {
List<Asset> selection,
) async {
if (selection.isNotEmpty) {
await ref
.watch(assetProvider.notifier)

View File

@ -1,4 +1,3 @@
// ignore_for_file: library_private_types_in_public_api
// Based on https://stackoverflow.com/a/52625182
import 'dart:async';
@ -164,7 +163,6 @@ class _CustomLongPressGestureRecognizer extends LongPressGestureRecognizer {
}
}
// ignore: prefer-single-widget-per-file
class AssetIndexWrapper extends SingleChildRenderObjectWidget {
final int rowIndex;
final int sectionIndex;
@ -177,6 +175,7 @@ class AssetIndexWrapper extends SingleChildRenderObjectWidget {
});
@override
// ignore: library_private_types_in_public_api
_AssetIndexProxy createRenderObject(BuildContext context) {
return _AssetIndexProxy(
index: AssetIndex(rowIndex: rowIndex, sectionIndex: sectionIndex),
@ -186,6 +185,7 @@ class AssetIndexWrapper extends SingleChildRenderObjectWidget {
@override
void updateRenderObject(
BuildContext context,
// ignore: library_private_types_in_public_api
_AssetIndexProxy renderObject,
) {
renderObject.index =

View File

@ -1,5 +1,3 @@
// ignore_for_file: prefer-single-widget-per-file
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';

View File

@ -80,8 +80,7 @@ class DraggableScrollbar extends StatefulWidget {
this.labelTextBuilder,
this.labelConstraints,
}) : assert(child.scrollDirection == Axis.vertical),
scrollThumbBuilder =
_thumbRRectBuilder(scrollThumbKey, alwaysVisibleScrollThumb);
scrollThumbBuilder = _thumbRRectBuilder(alwaysVisibleScrollThumb);
DraggableScrollbar.arrows({
super.key,
@ -97,8 +96,7 @@ class DraggableScrollbar extends StatefulWidget {
this.labelTextBuilder,
this.labelConstraints,
}) : assert(child.scrollDirection == Axis.vertical),
scrollThumbBuilder =
_thumbArrowBuilder(scrollThumbKey, alwaysVisibleScrollThumb);
scrollThumbBuilder = _thumbArrowBuilder(alwaysVisibleScrollThumb);
DraggableScrollbar.semicircle({
super.key,
@ -201,7 +199,6 @@ class DraggableScrollbar extends StatefulWidget {
}
static ScrollThumbBuilder _thumbArrowBuilder(
Key? scrollThumbKey,
bool alwaysVisibleScrollThumb,
) {
return (
@ -239,7 +236,6 @@ class DraggableScrollbar extends StatefulWidget {
}
static ScrollThumbBuilder _thumbRRectBuilder(
Key? scrollThumbKey,
bool alwaysVisibleScrollThumb,
) {
return (

View File

@ -48,7 +48,7 @@ class ThumbnailImage extends ConsumerWidget {
// Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id
final isFromDto = asset.id == noDbId;
Widget buildSelectionIcon(Asset asset) {
Widget buildSelectionIcon() {
if (isSelected) {
return Container(
decoration: BoxDecoration(
@ -233,7 +233,7 @@ class ThumbnailImage extends ConsumerWidget {
padding: const EdgeInsets.all(3.0),
child: Align(
alignment: Alignment.topLeft,
child: buildSelectionIcon(asset),
child: buildSelectionIcon(),
),
),
],

View File

@ -235,7 +235,6 @@ class BottomGalleryBar extends ConsumerWidget {
ref.read(downloadStateProvider.notifier).downloadAsset(
asset,
context,
);
}

View File

@ -96,7 +96,7 @@ class GalleryAppBar extends ConsumerWidget {
}
handleDownloadAsset() {
ref.read(downloadStateProvider.notifier).downloadAsset(asset, context);
ref.read(downloadStateProvider.notifier).downloadAsset(asset);
}
handleLocateAsset() async {

View File

@ -13,7 +13,8 @@ OctoSet blurHashOrPlaceholder(
}) {
return OctoSet(
placeholderBuilder: blurHashPlaceholderBuilder(blurhash, fit: fit),
errorBuilder: blurHashErrorBuilder(blurhash, fit: fit),
errorBuilder:
blurHashErrorBuilder(blurhash, fit: fit, message: errorMessage),
);
}

View File

@ -28,7 +28,6 @@ mixin HitCornersDetector on PhotoViewControllerDelegate {
bool _shouldMoveAxis(
HitCorners hitCorners,
double mainAxisMove,
double crossAxisMove,
) {
if (mainAxisMove == 0) {
return false;
@ -47,17 +46,15 @@ mixin HitCornersDetector on PhotoViewControllerDelegate {
bool _shouldMoveX(Offset move) {
final hitCornersX = _hitCornersX();
final mainAxisMove = move.dx;
final crossAxisMove = move.dy;
return _shouldMoveAxis(hitCornersX, mainAxisMove, crossAxisMove);
return _shouldMoveAxis(hitCornersX, mainAxisMove);
}
bool _shouldMoveY(Offset move) {
final hitCornersY = _hitCornersY();
final mainAxisMove = move.dy;
final crossAxisMove = move.dx;
return _shouldMoveAxis(hitCornersY, mainAxisMove, crossAxisMove);
return _shouldMoveAxis(hitCornersY, mainAxisMove);
}
bool shouldMove(Offset move, Axis mainAxis) {