diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml new file mode 100644 index 0000000000..896b740b9b --- /dev/null +++ b/.github/workflows/static_analysis.yml @@ -0,0 +1,31 @@ +name: Static Code Analysis +on: + workflow_dispatch: + pull_request: + push: + branches: [main] + +jobs: + mobile-dart-analyze: + name: Run Dart Code Analysis + + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup Flutter SDK + uses: subosito/flutter-action@v2 + with: + channel: 'stable' + flutter-version: '3.3.10' + + - name: Install dependencies + run: dart pub get + working-directory: ./mobile + + - name: Run dart analyze + run: dart analyze --fatal-infos + working-directory: ./mobile + diff --git a/mobile/integration_test/module_login/login_input_validation_test.dart b/mobile/integration_test/module_login/login_input_validation_test.dart index a70afcbdc3..e8f37acfc2 100644 --- a/mobile/integration_test/module_login/login_input_validation_test.dart +++ b/mobile/integration_test/module_login/login_input_validation_test.dart @@ -2,7 +2,6 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter_test/flutter_test.dart'; import '../test_utils/general_helper.dart'; -import '../test_utils/login_helper.dart'; void main() async { await ImmichTestHelper.initialize(); @@ -13,7 +12,7 @@ void main() async { await helper.loginHelper.acknowledgeNewServerVersion(); await helper.loginHelper.enterCredentials( - email: " demo@immich.app" + email: " demo@immich.app", ); await tester.pump(const Duration(milliseconds: 300)); @@ -21,7 +20,7 @@ void main() async { expect(find.text("login_form_err_leading_whitespace".tr()), findsOneWidget); await helper.loginHelper.enterCredentials( - email: "demo@immich.app " + email: "demo@immich.app ", ); await tester.pump(const Duration(milliseconds: 300)); @@ -34,7 +33,7 @@ void main() async { await helper.loginHelper.acknowledgeNewServerVersion(); await helper.loginHelper.enterCredentials( - email: "demo.immich.app" + email: "demo.immich.app", ); await tester.pump(const Duration(milliseconds: 300)); diff --git a/mobile/integration_test/module_login/login_test.dart b/mobile/integration_test/module_login/login_test.dart index f317b12ca6..c9e6ecc089 100644 --- a/mobile/integration_test/module_login/login_test.dart +++ b/mobile/integration_test/module_login/login_test.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:flutter_test/flutter_test.dart'; import '../test_utils/general_helper.dart'; @@ -12,8 +10,9 @@ void main() async { immichWidgetTest("Test correct credentials", (tester, helper) async { await helper.loginHelper.waitForLoginScreen(); await helper.loginHelper.acknowledgeNewServerVersion(); - await helper.loginHelper - .enterCredentialsOf(LoginCredentials.testInstance); + await helper.loginHelper.enterCredentialsOf( + LoginCredentials.testInstance, + ); await helper.loginHelper.pressLoginButton(); await helper.loginHelper.assertLoginSuccess(); }); @@ -22,16 +21,19 @@ void main() async { await helper.loginHelper.waitForLoginScreen(); await helper.loginHelper.acknowledgeNewServerVersion(); await helper.loginHelper.enterCredentialsOf( - LoginCredentials.testInstanceButWithWrongPassword); + LoginCredentials.testInstanceButWithWrongPassword, + ); await helper.loginHelper.pressLoginButton(); await helper.loginHelper.assertLoginFailed(); }); - immichWidgetTest("Test login with wrong server URL", (tester, helper) async { + immichWidgetTest("Test login with wrong server URL", + (tester, helper) async { await helper.loginHelper.waitForLoginScreen(); await helper.loginHelper.acknowledgeNewServerVersion(); await helper.loginHelper.enterCredentialsOf( - LoginCredentials.wrongInstanceUrl); + LoginCredentials.wrongInstanceUrl, + ); await helper.loginHelper.pressLoginButton(); await helper.loginHelper.assertLoginFailed(); }); diff --git a/mobile/integration_test/test_utils/general_helper.dart b/mobile/integration_test/test_utils/general_helper.dart index 0555bdda92..0ce776ce97 100644 --- a/mobile/integration_test/test_utils/general_helper.dart +++ b/mobile/integration_test/test_utils/general_helper.dart @@ -1,17 +1,14 @@ - import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:hive/hive.dart'; -import 'package:immich_mobile/main.dart'; import 'package:integration_test/integration_test.dart'; +// ignore: depend_on_referenced_packages import 'package:meta/meta.dart'; import 'package:immich_mobile/main.dart' as app; import 'login_helper.dart'; class ImmichTestHelper { - final WidgetTester tester; ImmichTestHelper(this.tester); @@ -43,15 +40,19 @@ class ImmichTestHelper { await tester.pumpAndSettle(); await EasyLocalization.ensureInitialized(); } - } @isTest -void immichWidgetTest(String description, Future Function(WidgetTester, ImmichTestHelper) test) { - - testWidgets(description, (widgetTester) async { - await ImmichTestHelper.loadApp(widgetTester); - await test(widgetTester, ImmichTestHelper(widgetTester)); - }, semanticsEnabled: false); - -} \ No newline at end of file +void immichWidgetTest( + String description, + Future Function(WidgetTester, ImmichTestHelper) test, +) { + testWidgets( + description, + (widgetTester) async { + await ImmichTestHelper.loadApp(widgetTester); + await test(widgetTester, ImmichTestHelper(widgetTester)); + }, + semanticsEnabled: false, + ); +} diff --git a/mobile/integration_test/test_utils/login_helper.dart b/mobile/integration_test/test_utils/login_helper.dart index 244b288b7b..5e6cb547c7 100644 --- a/mobile/integration_test/test_utils/login_helper.dart +++ b/mobile/integration_test/test_utils/login_helper.dart @@ -1,8 +1,6 @@ -import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart'; class ImmichTestLoginHelper { final WidgetTester tester; diff --git a/mobile/lib/modules/album/ui/album_viewer_appbar.dart b/mobile/lib/modules/album/ui/album_viewer_appbar.dart index f49082559a..5c41b08b6e 100644 --- a/mobile/lib/modules/album/ui/album_viewer_appbar.dart +++ b/mobile/lib/modules/album/ui/album_viewer_appbar.dart @@ -96,7 +96,7 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget { if (isSuccess) { Navigator.pop(context); ref.watch(assetSelectionProvider.notifier).disableMultiselection(); - ref.refresh(sharedAlbumDetailProvider(albumId)); + ref.invalidate(sharedAlbumDetailProvider(albumId)); } else { Navigator.pop(context); ImmichToast.show( diff --git a/mobile/lib/modules/album/views/album_viewer_page.dart b/mobile/lib/modules/album/views/album_viewer_page.dart index 65db82eb6b..78b9896362 100644 --- a/mobile/lib/modules/album/views/album_viewer_page.dart +++ b/mobile/lib/modules/album/views/album_viewer_page.dart @@ -62,7 +62,7 @@ class AlbumViewerPage extends HookConsumerWidget { if (addAssetsResult != null && addAssetsResult.successfullyAdded > 0) { - ref.refresh(sharedAlbumDetailProvider(albumId)); + ref.invalidate(sharedAlbumDetailProvider(albumId)); } ImmichLoadingOverlayController.appLoader.hide(); @@ -88,7 +88,7 @@ class AlbumViewerPage extends HookConsumerWidget { .addAdditionalUserToAlbum(sharedUserIds, albumId); if (isSuccess) { - ref.refresh(sharedAlbumDetailProvider(albumId)); + ref.invalidate(sharedAlbumDetailProvider(albumId)); } ImmichLoadingOverlayController.appLoader.hide(); diff --git a/mobile/lib/modules/album/views/library_page.dart b/mobile/lib/modules/album/views/library_page.dart index 7bb91bc815..8b40fbf7bc 100644 --- a/mobile/lib/modules/album/views/library_page.dart +++ b/mobile/lib/modules/album/views/library_page.dart @@ -22,7 +22,7 @@ class LibraryPage extends HookConsumerWidget { [], ); - Widget _buildAppBar() { + Widget buildAppBar() { return const SliverAppBar( centerTitle: true, floating: true, @@ -40,7 +40,7 @@ class LibraryPage extends HookConsumerWidget { ); } - Widget _buildCreateAlbumButton() { + Widget buildCreateAlbumButton() { return GestureDetector( onTap: () { AutoRouter.of(context).push(CreateAlbumRoute(isSharedAlbum: false)); @@ -83,7 +83,7 @@ class LibraryPage extends HookConsumerWidget { return Scaffold( body: CustomScrollView( slivers: [ - _buildAppBar(), + buildAppBar(), SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.all(12.0), @@ -99,7 +99,7 @@ class LibraryPage extends HookConsumerWidget { child: Wrap( spacing: 12, children: [ - _buildCreateAlbumButton(), + buildCreateAlbumButton(), for (var album in albums) AlbumThumbnailCard( album: album, diff --git a/mobile/lib/modules/home/services/asset_cache.service.dart b/mobile/lib/modules/home/services/asset_cache.service.dart index 3eb684f6ea..d7a6af5ccb 100644 --- a/mobile/lib/modules/home/services/asset_cache.service.dart +++ b/mobile/lib/modules/home/services/asset_cache.service.dart @@ -8,7 +8,8 @@ class AssetCacheService extends JsonCache> { AssetCacheService() : super("asset_cache"); static Future>> _computeSerialize( - List assets) async { + List assets, + ) async { return assets.map((e) => e.toJson()).toList(); } diff --git a/mobile/lib/modules/home/ui/asset_grid/asset_grid_data_structure.dart b/mobile/lib/modules/home/ui/asset_grid/asset_grid_data_structure.dart index a2461eb385..9c68cf7cbd 100644 --- a/mobile/lib/modules/home/ui/asset_grid/asset_grid_data_structure.dart +++ b/mobile/lib/modules/home/ui/asset_grid/asset_grid_data_structure.dart @@ -42,8 +42,13 @@ class _AssetGroupsToRenderListComputeParameters { final Map> groups; final int perRow; - _AssetGroupsToRenderListComputeParameters(this.monthFormat, this.dayFormat, - this.dayFormatYear, this.groups, this.perRow); + _AssetGroupsToRenderListComputeParameters( + this.monthFormat, + this.dayFormat, + this.dayFormatYear, + this.groups, + this.perRow, + ); } class RenderList { @@ -52,7 +57,8 @@ class RenderList { RenderList(this.elements); static Future _processAssetGroupData( - _AssetGroupsToRenderListComputeParameters data) async { + _AssetGroupsToRenderListComputeParameters data, + ) async { final monthFormat = DateFormat(data.monthFormat); final dayFormatSameYear = DateFormat(data.dayFormat); final dayFormatOtherYear = DateFormat(data.dayFormatYear); diff --git a/mobile/lib/modules/home/ui/asset_grid/daily_title_text.dart b/mobile/lib/modules/home/ui/asset_grid/daily_title_text.dart index f61d06cac3..7cee410a19 100644 --- a/mobile/lib/modules/home/ui/asset_grid/daily_title_text.dart +++ b/mobile/lib/modules/home/ui/asset_grid/daily_title_text.dart @@ -1,4 +1,3 @@ -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; diff --git a/mobile/lib/modules/login/ui/login_form.dart b/mobile/lib/modules/login/ui/login_form.dart index e98927ef0d..b47e64bd0d 100644 --- a/mobile/lib/modules/login/ui/login_form.dart +++ b/mobile/lib/modules/login/ui/login_form.dart @@ -235,7 +235,7 @@ class ServerEndpointInput extends StatelessWidget { labelText: 'login_form_endpoint_url'.tr(), border: const OutlineInputBorder(), hintText: 'login_form_endpoint_hint'.tr(), - errorMaxLines: 4 + errorMaxLines: 4, ), validator: _validateInput, autovalidateMode: AutovalidateMode.always, diff --git a/mobile/lib/routing/tab_navigation_observer.dart b/mobile/lib/routing/tab_navigation_observer.dart index 25db941848..1085082713 100644 --- a/mobile/lib/routing/tab_navigation_observer.dart +++ b/mobile/lib/routing/tab_navigation_observer.dart @@ -30,8 +30,8 @@ class TabNavigationObserver extends AutoRouterObserver { // Perform tasks on re-visit to SearchRoute if (route.name == 'SearchRoute') { // Refresh Location State - ref.refresh(getCuratedLocationProvider); - ref.refresh(getCuratedObjectProvider); + ref.invalidate(getCuratedLocationProvider); + ref.invalidate(getCuratedObjectProvider); } if (route.name == 'SharingRoute') { diff --git a/mobile/lib/shared/services/immich_logger.service.dart b/mobile/lib/shared/services/immich_logger.service.dart index 4e7d3bf71c..75e7cf8d7e 100644 --- a/mobile/lib/shared/services/immich_logger.service.dart +++ b/mobile/lib/shared/services/immich_logger.service.dart @@ -83,6 +83,7 @@ class ImmichLogger { } // Share file + // ignore: deprecated_member_use await Share.shareFiles( [filePath], subject: "Immich logs $dateTime", diff --git a/mobile/lib/shared/services/share.service.dart b/mobile/lib/shared/services/share.service.dart index 007d76d1d0..c0e61c9d9d 100644 --- a/mobile/lib/shared/services/share.service.dart +++ b/mobile/lib/shared/services/share.service.dart @@ -40,6 +40,7 @@ class ShareService { } }); + // ignore: deprecated_member_use Share.shareFiles( await Future.wait(downloadedFilePaths), sharePositionOrigin: Rect.zero, diff --git a/mobile/lib/utils/image_url_builder.dart b/mobile/lib/utils/image_url_builder.dart index 592c8ebbc5..5fb6408d27 100644 --- a/mobile/lib/utils/image_url_builder.dart +++ b/mobile/lib/utils/image_url_builder.dart @@ -10,8 +10,10 @@ String getThumbnailUrl( return _getThumbnailUrl(asset.id, type: type); } -String getThumbnailCacheKey(final AssetResponseDto asset, - {ThumbnailFormat type = ThumbnailFormat.WEBP}) { +String getThumbnailCacheKey( + final AssetResponseDto asset, { + ThumbnailFormat type = ThumbnailFormat.WEBP, +}) { return _getThumbnailCacheKey(asset.id, type); } diff --git a/mobile/lib/utils/openapi_extensions.dart b/mobile/lib/utils/openapi_extensions.dart index 2959be3d10..96623514dc 100644 --- a/mobile/lib/utils/openapi_extensions.dart +++ b/mobile/lib/utils/openapi_extensions.dart @@ -31,7 +31,9 @@ extension WithETag on AssetApi { final responseBody = await _decodeBodyBytes(response); final etag = response.headers[HttpHeaders.etagHeader]; final data = (await apiClient.deserializeAsync( - responseBody, 'List') as List) + responseBody, + 'List', + ) as List) .cast() .toList(); return Pair(data, etag);