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 run: dart pub get
working-directory: ./mobile 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 - name: Generate translation file
run: make translation run: make translation
working-directory: ./mobile working-directory: ./mobile
@ -100,6 +108,10 @@ jobs:
run: dart run custom_lint run: dart run custom_lint
working-directory: ./mobile working-directory: ./mobile
- name: Run DCM
run: dcm analyze lib
working-directory: ./mobile
zizmor: zizmor:
name: zizmor name: zizmor
runs-on: ubuntu-latest 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 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 ## Immich-Flutter Directory Structure
Below are the directory inside the `lib` directory: Below are the directory inside the `lib` directory:

View File

@ -128,82 +128,169 @@ custom_lint:
- test/**.dart - test/**.dart
dart_code_metrics: dart_code_metrics:
extends:
- recommended
rules: rules:
# Common # All rules from "recommended" preset
- arguments-ordering: # Show potential errors
last: # - avoid-cascade-after-if-null
- child # - avoid-collection-methods-with-unrelated-types
- children # - avoid-duplicate-exports
- avoid-accessing-other-classes-private-members # - avoid-dynamic
- avoid-assigning-to-static-field # - avoid-missing-enum-constant-in-map
- avoid-assignments-as-conditions # - avoid-passing-async-when-sync-expected
- avoid-async-call-in-sync-function # - avoid-throw-in-catch-block
- avoid-collapsible-if - avoid-unused-parameters
- avoid-collection-equality-checks # - avoid-unnecessary-type-assertions
- avoid-complex-loop-conditions # - avoid-unnecessary-type-casts
- avoid-declaring-call-method # - avoid-unrelated-type-assertions
- avoid-extensions-on-records # - avoid-unrelated-type-casts
- avoid-function-type-in-records # - no-empty-block
- avoid-future-ignore # - no-equal-then-else
- avoid-global-state # - prefer-correct-test-file-name
- avoid-inverted-boolean-checks # - prefer-match-file-name
- avoid-late-final-reassignment # - prefer-return-await
- avoid-local-functions: # - avoid-self-assignment
exclude: # - avoid-self-compare
- test/**.dart # - avoid-shadowing
- avoid-negated-conditions # - prefer-iterable-of
- avoid-nested-streams-and-futures # - no-equal-switch-case
- avoid-referencing-subclasses # - no-equal-conditions
- avoid-unnecessary-continue # - avoid-equal-expressions
- avoid-unnecessary-nullable-return-type: false # - avoid-missed-calls
- binary-expression-operand-order # - avoid-unnecessary-negations
- pattern-fields-ordering # - avoid-unused-generics
- prefer-abstract-final-static-class # - function-always-returns-null
- prefer-commenting-future-delayed # - avoid-throw-objects-without-tostring
- prefer-early-return # - avoid-unsafe-collection-methods
- prefer-first # - prefer-wildcard-pattern
- prefer-immediate-return # - no-equal-switch-expression-cases
- prefer-last # - avoid-future-tostring
- prefer-simpler-boolean-expressions # - avoid-unassigned-late-fields
- prefer-switch-expression # - avoid-nested-futures
- prefer-type-over-var # - avoid-generics-shadowing
- use-existing-destructuring # - prefer-parentheses-with-if-null
- use-existing-variable # - no-equal-nested-conditions
# Flutter # - avoid-shadowed-extension-methods
- avoid-border-all # - avoid-unnecessary-conditionals
- avoid-complex-arithmetic-expressions # - avoid-double-slash-imports
- avoid-expanded-as-spacer # - avoid-map-keys-contains
- avoid-if-with-many-branches # - prefer-correct-json-casts
- avoid-inherited-widget-in-initstate # - avoid-duplicate-mixins
- avoid-late-context # - avoid-nullable-interpolation
- avoid-returning-widgets # - avoid-unused-instances
- avoid-shrink-wrap-in-lists # - prefer-correct-for-loop-increment
- avoid-single-child-column-or-row # - prefer-public-exception-classes
- avoid-stateless-widget-initialized-fields # - avoid-uncaught-future-errors
- avoid-wrapping-in-padding # - always-remove-listener
- prefer-align-over-container # - avoid-unnecessary-setstate
- prefer-const-border-radius # - check-for-equals-in-render-object-setters
- prefer-correct-callback-field-name: false # - consistent-update-render-object
- prefer-correct-edge-insets-constructor # - use-setstate-synchronously
- prefer-define-hero-tag # - avoid-incomplete-copy-with
- prefer-extracting-callbacks # - proper-super-calls
- prefer-for-loop-in-children # - dispose-fields
- prefer-match-file-name: false # - avoid-empty-setstate
- prefer-sliver-prefix # - avoid-state-constructors
- prefer-spacing # - avoid-recursive-widget-calls
- prefer-text-rich # - avoid-missing-image-alt
- prefer-transform-over-container # - avoid-passing-self-as-argument
- prefer-using-list-view # - avoid-unnecessary-if
- prefer-widget-private-members: # - avoid-unconditional-break
ignore-static: true # - avoid-referencing-discarded-variables
- use-closest-build-context # - avoid-unnecessary-local-late
# riverpod # - avoid-wildcard-cases-with-enums
- avoid-calling-notifier-members-inside-build # - match-getter-setter-field-names
- avoid-notifier-constructors # - avoid-accessing-collections-by-constant-index
- avoid-ref-read-inside-build # - prefer-unique-test-names
- avoid-ref-watch-outside-build # - avoid-duplicate-cascades
- avoid-unnecessary-consumer-widgets # - prefer-specific-cases-first
- dispose-provided-instances # - avoid-duplicate-switch-case-conditions
- use-ref-read-synchronously # - 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'; import 'package:flutter/material.dart';
List<ColorFilter> filters = [ const List<ColorFilter> filters = [
//Original //Original
const ColorFilter.matrix([ ColorFilter.matrix([
1, 1,
0, 0,
0, 0,
@ -25,7 +25,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Vintage //Vintage
const ColorFilter.matrix([ ColorFilter.matrix([
0.8, 0.8,
0.1, 0.1,
0.1, 0.1,
@ -48,7 +48,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Mood //Mood
const ColorFilter.matrix([ ColorFilter.matrix([
1.2, 1.2,
0.1, 0.1,
0.1, 0.1,
@ -71,7 +71,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Crisp //Crisp
const ColorFilter.matrix([ ColorFilter.matrix([
1.2, 1.2,
0, 0,
0, 0,
@ -94,7 +94,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Cool //Cool
const ColorFilter.matrix([ ColorFilter.matrix([
0.9, 0.9,
0, 0,
0.2, 0.2,
@ -117,7 +117,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Blush //Blush
const ColorFilter.matrix([ ColorFilter.matrix([
1.1, 1.1,
0.1, 0.1,
0.1, 0.1,
@ -140,7 +140,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Sunkissed //Sunkissed
const ColorFilter.matrix([ ColorFilter.matrix([
1.3, 1.3,
0, 0,
0.1, 0.1,
@ -163,7 +163,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Fresh //Fresh
const ColorFilter.matrix([ ColorFilter.matrix([
1.2, 1.2,
0, 0,
0, 0,
@ -186,7 +186,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Classic //Classic
const ColorFilter.matrix([ ColorFilter.matrix([
1.1, 1.1,
0, 0,
-0.1, -0.1,
@ -209,7 +209,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Lomo-ish //Lomo-ish
const ColorFilter.matrix([ ColorFilter.matrix([
1.5, 1.5,
0, 0,
0.1, 0.1,
@ -232,7 +232,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Nashville //Nashville
const ColorFilter.matrix([ ColorFilter.matrix([
1.2, 1.2,
0.15, 0.15,
-0.15, -0.15,
@ -255,7 +255,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Valencia //Valencia
const ColorFilter.matrix([ ColorFilter.matrix([
1.15, 1.15,
0.1, 0.1,
0.1, 0.1,
@ -278,7 +278,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Clarendon //Clarendon
const ColorFilter.matrix([ ColorFilter.matrix([
1.2, 1.2,
0, 0,
0, 0,
@ -301,7 +301,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Moon //Moon
const ColorFilter.matrix([ ColorFilter.matrix([
0.33, 0.33,
0.33, 0.33,
0.33, 0.33,
@ -324,7 +324,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Willow //Willow
const ColorFilter.matrix([ ColorFilter.matrix([
0.5, 0.5,
0.5, 0.5,
0.5, 0.5,
@ -347,7 +347,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Kodak //Kodak
const ColorFilter.matrix([ ColorFilter.matrix([
1.3, 1.3,
0.1, 0.1,
-0.1, -0.1,
@ -370,7 +370,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Frost //Frost
const ColorFilter.matrix([ ColorFilter.matrix([
0.8, 0.8,
0.2, 0.2,
0.1, 0.1,
@ -393,7 +393,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Night Vision //Night Vision
const ColorFilter.matrix([ ColorFilter.matrix([
0.1, 0.1,
0.95, 0.95,
0.2, 0.2,
@ -416,7 +416,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Sunset //Sunset
const ColorFilter.matrix([ ColorFilter.matrix([
1.5, 1.5,
0.2, 0.2,
0, 0,
@ -439,7 +439,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Noir //Noir
const ColorFilter.matrix([ ColorFilter.matrix([
1.3, 1.3,
-0.3, -0.3,
0.1, 0.1,
@ -462,7 +462,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Dreamy //Dreamy
const ColorFilter.matrix([ ColorFilter.matrix([
1.1, 1.1,
0.1, 0.1,
0.1, 0.1,
@ -485,7 +485,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Sepia //Sepia
const ColorFilter.matrix([ ColorFilter.matrix([
0.393, 0.393,
0.769, 0.769,
0.189, 0.189,
@ -508,7 +508,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Radium //Radium
const ColorFilter.matrix([ ColorFilter.matrix([
1.438, 1.438,
-0.062, -0.062,
-0.062, -0.062,
@ -531,7 +531,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Aqua //Aqua
const ColorFilter.matrix([ ColorFilter.matrix([
0.2126, 0.2126,
0.7152, 0.7152,
0.0722, 0.0722,
@ -554,7 +554,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Purple Haze //Purple Haze
const ColorFilter.matrix([ ColorFilter.matrix([
1.3, 1.3,
0, 0,
1.2, 1.2,
@ -577,7 +577,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Lemonade //Lemonade
const ColorFilter.matrix([ ColorFilter.matrix([
1.2, 1.2,
0.1, 0.1,
0, 0,
@ -600,7 +600,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Caramel //Caramel
const ColorFilter.matrix([ ColorFilter.matrix([
1.6, 1.6,
0.2, 0.2,
0, 0,
@ -623,7 +623,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Peachy //Peachy
const ColorFilter.matrix([ ColorFilter.matrix([
1.3, 1.3,
0.5, 0.5,
0, 0,
@ -646,7 +646,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Neon //Neon
const ColorFilter.matrix([ ColorFilter.matrix([
1, 1,
0, 0,
1, 1,
@ -669,7 +669,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Cold Morning //Cold Morning
const ColorFilter.matrix([ ColorFilter.matrix([
0.9, 0.9,
0.1, 0.1,
0.2, 0.2,
@ -692,7 +692,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Lush //Lush
const ColorFilter.matrix([ ColorFilter.matrix([
0.9, 0.9,
0.2, 0.2,
0, 0,
@ -715,7 +715,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Urban Neon //Urban Neon
const ColorFilter.matrix([ ColorFilter.matrix([
1.1, 1.1,
0, 0,
0.3, 0.3,
@ -738,7 +738,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Monochrome //Monochrome
const ColorFilter.matrix([ ColorFilter.matrix([
0.6, 0.6,
0.2, 0.2,
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 { class MainWidget extends StatelessWidget {
const MainWidget({super.key}); const MainWidget({super.key});

View File

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

View File

@ -1,5 +1,3 @@
// ignore_for_file: add-copy-with
sealed class MapEvent { sealed class MapEvent {
const 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( return PhotoViewGalleryPageOptions(
onDragStart: (_, details, __) { onDragStart: (_, details, __) {
localPosition.value = details.localPosition; localPosition.value = details.localPosition;
@ -312,7 +312,7 @@ class GalleryViewerPage extends HookConsumerWidget {
} }
if (newAsset.isImage && !isPlayingMotionVideo) { if (newAsset.isImage && !isPlayingMotionVideo) {
return buildImage(context, newAsset); return buildImage(newAsset);
} }
return buildVideo(context, newAsset); return buildVideo(context, newAsset);
} }

View File

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

View File

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

View File

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

View File

@ -5,4 +5,4 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'app_settings.provider.g.dart'; part 'app_settings.provider.g.dart';
@Riverpod(keepAlive: true) @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() => String _$appSettingsServiceHash() =>
r'3736e0d384ec7b1f896938589656dd6eb1552d60'; r'2aa16d76a8df869c39486325efc1d08b2d2c284c';
/// See also [appSettingsService]. /// See also [appSettingsService].
@ProviderFor(appSettingsService) @ProviderFor(appSettingsService)

View File

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

View File

@ -6,155 +6,22 @@ part of 'asset_stack.provider.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$assetStackIndexHash() => r'38b4b0116e3e4592620b118ae01cf89b77da9cfe'; String _$assetStackIndexHash() => r'086ddb782e3eb38b80d755666fe35be8fe7322d7';
/// 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));
}
}
/// See also [assetStackIndex]. /// See also [assetStackIndex].
@ProviderFor(assetStackIndex) @ProviderFor(assetStackIndex)
const assetStackIndexProvider = AssetStackIndexFamily(); final assetStackIndexProvider = AutoDisposeProvider<int>.internal(
assetStackIndex,
/// 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', name: r'assetStackIndexProvider',
debugGetCreateSourceHash: debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
const bool.fromEnvironment('dart.vm.product')
? null ? null
: _$assetStackIndexHash, : _$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, dependencies: null,
allTransitiveDependencies: 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);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead') @Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element // ignore: unused_element
mixin AssetStackIndexRef on AutoDisposeProviderRef<int> { typedef AssetStackIndexRef = 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;
}
// ignore_for_file: type=lint // 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 // 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); return await _downloadService.downloadAll(assets);
} }
void downloadAsset(Asset asset, BuildContext context) async { void downloadAsset(Asset asset) async {
await _downloadService.download(asset); await _downloadService.download(asset);
} }

View File

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

View File

@ -6,7 +6,7 @@ part of 'immich_logo_provider.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$immichLogoHash() => r'6f23d217c44279537b7edee1ca80ebf47f69a4d0'; String _$immichLogoHash() => r'6de7fcca1ef9acef6ab7398eb0c664080747e0ea';
/// See also [immichLogo]. /// See also [immichLogo].
@ProviderFor(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/backup_candidate.model.dart';
import 'package:immich_mobile/models/backup/current_upload_asset.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/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/api.provider.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/db.provider.dart';
@ -489,7 +488,6 @@ class BackgroundService {
_cancellationToken!, _cancellationToken!,
pmProgressHandler: pmProgressHandler, pmProgressHandler: pmProgressHandler,
onSuccess: (result) => _onAssetUploaded( onSuccess: (result) => _onAssetUploaded(
result: result,
shouldNotify: notifyTotalProgress, shouldNotify: notifyTotalProgress,
), ),
onProgress: (bytes, totalBytes) => onProgress: (bytes, totalBytes) =>
@ -511,7 +509,6 @@ class BackgroundService {
} }
void _onAssetUploaded({ void _onAssetUploaded({
required SuccessUploadAsset result,
bool shouldNotify = false, bool shouldNotify = false,
}) async { }) async {
if (!shouldNotify) { if (!shouldNotify) {

View File

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

View File

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

View File

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

View File

@ -94,7 +94,7 @@ Future<void> handleFavoriteAssets(
ImmichToast.show( ImmichToast.show(
context: context, context: context,
msg: toastMessage, msg: toastMessage,
gravity: ToastGravity.BOTTOM, gravity: toastGravity,
); );
} }
} }
@ -164,9 +164,8 @@ Future<void> handleSetAssetsVisibility(
WidgetRef ref, WidgetRef ref,
BuildContext context, BuildContext context,
AssetVisibilityEnum visibility, AssetVisibilityEnum visibility,
List<Asset> selection, { List<Asset> selection,
ToastGravity toastGravity = ToastGravity.BOTTOM, ) async {
}) async {
if (selection.isNotEmpty) { if (selection.isNotEmpty) {
await ref await ref
.watch(assetProvider.notifier) .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 // Based on https://stackoverflow.com/a/52625182
import 'dart:async'; import 'dart:async';
@ -164,7 +163,6 @@ class _CustomLongPressGestureRecognizer extends LongPressGestureRecognizer {
} }
} }
// ignore: prefer-single-widget-per-file
class AssetIndexWrapper extends SingleChildRenderObjectWidget { class AssetIndexWrapper extends SingleChildRenderObjectWidget {
final int rowIndex; final int rowIndex;
final int sectionIndex; final int sectionIndex;
@ -177,6 +175,7 @@ class AssetIndexWrapper extends SingleChildRenderObjectWidget {
}); });
@override @override
// ignore: library_private_types_in_public_api
_AssetIndexProxy createRenderObject(BuildContext context) { _AssetIndexProxy createRenderObject(BuildContext context) {
return _AssetIndexProxy( return _AssetIndexProxy(
index: AssetIndex(rowIndex: rowIndex, sectionIndex: sectionIndex), index: AssetIndex(rowIndex: rowIndex, sectionIndex: sectionIndex),
@ -186,6 +185,7 @@ class AssetIndexWrapper extends SingleChildRenderObjectWidget {
@override @override
void updateRenderObject( void updateRenderObject(
BuildContext context, BuildContext context,
// ignore: library_private_types_in_public_api
_AssetIndexProxy renderObject, _AssetIndexProxy renderObject,
) { ) {
renderObject.index = 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:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';

View File

@ -80,8 +80,7 @@ class DraggableScrollbar extends StatefulWidget {
this.labelTextBuilder, this.labelTextBuilder,
this.labelConstraints, this.labelConstraints,
}) : assert(child.scrollDirection == Axis.vertical), }) : assert(child.scrollDirection == Axis.vertical),
scrollThumbBuilder = scrollThumbBuilder = _thumbRRectBuilder(alwaysVisibleScrollThumb);
_thumbRRectBuilder(scrollThumbKey, alwaysVisibleScrollThumb);
DraggableScrollbar.arrows({ DraggableScrollbar.arrows({
super.key, super.key,
@ -97,8 +96,7 @@ class DraggableScrollbar extends StatefulWidget {
this.labelTextBuilder, this.labelTextBuilder,
this.labelConstraints, this.labelConstraints,
}) : assert(child.scrollDirection == Axis.vertical), }) : assert(child.scrollDirection == Axis.vertical),
scrollThumbBuilder = scrollThumbBuilder = _thumbArrowBuilder(alwaysVisibleScrollThumb);
_thumbArrowBuilder(scrollThumbKey, alwaysVisibleScrollThumb);
DraggableScrollbar.semicircle({ DraggableScrollbar.semicircle({
super.key, super.key,
@ -201,7 +199,6 @@ class DraggableScrollbar extends StatefulWidget {
} }
static ScrollThumbBuilder _thumbArrowBuilder( static ScrollThumbBuilder _thumbArrowBuilder(
Key? scrollThumbKey,
bool alwaysVisibleScrollThumb, bool alwaysVisibleScrollThumb,
) { ) {
return ( return (
@ -239,7 +236,6 @@ class DraggableScrollbar extends StatefulWidget {
} }
static ScrollThumbBuilder _thumbRRectBuilder( static ScrollThumbBuilder _thumbRRectBuilder(
Key? scrollThumbKey,
bool alwaysVisibleScrollThumb, bool alwaysVisibleScrollThumb,
) { ) {
return ( 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 // Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id
final isFromDto = asset.id == noDbId; final isFromDto = asset.id == noDbId;
Widget buildSelectionIcon(Asset asset) { Widget buildSelectionIcon() {
if (isSelected) { if (isSelected) {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
@ -233,7 +233,7 @@ class ThumbnailImage extends ConsumerWidget {
padding: const EdgeInsets.all(3.0), padding: const EdgeInsets.all(3.0),
child: Align( child: Align(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: buildSelectionIcon(asset), child: buildSelectionIcon(),
), ),
), ),
], ],

View File

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

View File

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

View File

@ -13,7 +13,8 @@ OctoSet blurHashOrPlaceholder(
}) { }) {
return OctoSet( return OctoSet(
placeholderBuilder: blurHashPlaceholderBuilder(blurhash, fit: fit), 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( bool _shouldMoveAxis(
HitCorners hitCorners, HitCorners hitCorners,
double mainAxisMove, double mainAxisMove,
double crossAxisMove,
) { ) {
if (mainAxisMove == 0) { if (mainAxisMove == 0) {
return false; return false;
@ -47,17 +46,15 @@ mixin HitCornersDetector on PhotoViewControllerDelegate {
bool _shouldMoveX(Offset move) { bool _shouldMoveX(Offset move) {
final hitCornersX = _hitCornersX(); final hitCornersX = _hitCornersX();
final mainAxisMove = move.dx; final mainAxisMove = move.dx;
final crossAxisMove = move.dy;
return _shouldMoveAxis(hitCornersX, mainAxisMove, crossAxisMove); return _shouldMoveAxis(hitCornersX, mainAxisMove);
} }
bool _shouldMoveY(Offset move) { bool _shouldMoveY(Offset move) {
final hitCornersY = _hitCornersY(); final hitCornersY = _hitCornersY();
final mainAxisMove = move.dy; final mainAxisMove = move.dy;
final crossAxisMove = move.dx;
return _shouldMoveAxis(hitCornersY, mainAxisMove, crossAxisMove); return _shouldMoveAxis(hitCornersY, mainAxisMove);
} }
bool shouldMove(Offset move, Axis mainAxis) { bool shouldMove(Offset move, Axis mainAxis) {