diff --git a/.gitattributes b/.gitattributes index 32ea167bb..48c4dbdb0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,6 +5,8 @@ mobile/openapi/**/*.dart linguist-generated=true mobile/openapi/.openapi-generator/FILES -diff -merge mobile/openapi/.openapi-generator/FILES linguist-generated=true +mobile/lib/**/*.g.dart -diff -merge +mobile/lib/**/*.g.dart linguist-generated=true cli/src/api/open-api/**/*.md -diff -merge cli/src/api/open-api/**/*.md linguist-generated=true diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index 18343c06f..661287252 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -32,3 +32,8 @@ jobs: - name: Run dart analyze run: dart analyze --fatal-infos working-directory: ./mobile + + # Enable after riverpod generator migration is completed + # - name: Run dart custom lint + # run: dart run custom_lint + # working-directory: ./mobile diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml index 2d5071a4f..d026b42fe 100644 --- a/mobile/analysis_options.yaml +++ b/mobile/analysis_options.yaml @@ -36,6 +36,9 @@ analyzer: - openapi/ - openapi/test/ - lib/generated_plugin_registrant.dart + +plugins: + - custom_lint dart_code_metrics: metrics: diff --git a/mobile/assets/i18n/en-US.json b/mobile/assets/i18n/en-US.json index 8233fcd7f..e46b51644 100644 --- a/mobile/assets/i18n/en-US.json +++ b/mobile/assets/i18n/en-US.json @@ -438,5 +438,6 @@ "version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89", "viewer_remove_from_stack": "Remove from Stack", "viewer_stack_use_as_main_asset": "Use as Main Asset", - "viewer_unstack": "Un-Stack" + "viewer_unstack": "Un-Stack", + "scaffold_body_error_occured": "Error occured" } diff --git a/mobile/integration_test/test_utils/general_helper.dart b/mobile/integration_test/test_utils/general_helper.dart index ac0b14ef4..e3b28e1f3 100644 --- a/mobile/integration_test/test_utils/general_helper.dart +++ b/mobile/integration_test/test_utils/general_helper.dart @@ -2,7 +2,9 @@ import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/shared/models/store.dart'; +import 'package:immich_mobile/shared/providers/db.provider.dart'; import 'package:integration_test/integration_test.dart'; import 'package:isar/isar.dart'; // ignore: depend_on_referenced_packages @@ -40,7 +42,12 @@ class ImmichTestHelper { await Store.clear(); await db.writeTxn(() => db.clear()); // Load main Widget - await tester.pumpWidget(app.getMainWidget(db)); + await tester.pumpWidget( + ProviderScope( + overrides: [dbProvider.overrideWithValue(db)], + child: app.getMainWidget(), + ), + ); // Post run tasks await EasyLocalization.ensureInitialized(); } diff --git a/mobile/lib/extensions/asyncvalue_extensions.dart b/mobile/lib/extensions/asyncvalue_extensions.dart new file mode 100644 index 000000000..2c4725de8 --- /dev/null +++ b/mobile/lib/extensions/asyncvalue_extensions.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; +import 'package:immich_mobile/shared/ui/scaffold_error_body.dart'; +import 'package:logging/logging.dart'; + +extension ScaffoldBody on AsyncValue { + static final Logger _scaffoldBodyLog = Logger("ScaffoldBody"); + + Widget scaffoldBodyWhen({ + required Widget Function(T data) onData, + Widget? onError, + }) { + if (isLoading) { + return const Center( + child: ImmichLoadingIndicator(), + ); + } + + if (hasError && !hasValue) { + _scaffoldBodyLog.severe("Error occured in AsyncValue", error, stackTrace); + return onError ?? const ScaffoldErrorBody(); + } + + return onData(requireValue); + } +} diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 224fe3ef4..6d4a812b6 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -43,7 +43,12 @@ void main() async { await initApp(); await migrateDatabaseIfNeeded(db); HttpOverrides.global = HttpSSLCertOverride(); - runApp(getMainWidget(db)); + runApp( + ProviderScope( + overrides: [dbProvider.overrideWithValue(db)], + child: getMainWidget(), + ), + ); } Future initApp() async { @@ -103,16 +108,13 @@ Future loadDb() async { return db; } -Widget getMainWidget(Isar db) { +Widget getMainWidget() { return EasyLocalization( supportedLocales: locales, path: translationsPath, useFallbackTranslations: true, fallbackLocale: locales.first, - child: ProviderScope( - overrides: [dbProvider.overrideWithValue(db)], - child: const ImmichApp(), - ), + child: const ImmichApp(), ); } diff --git a/mobile/lib/modules/search/providers/people.provider.dart b/mobile/lib/modules/search/providers/people.provider.dart index e40ff3fc8..6009ee53a 100644 --- a/mobile/lib/modules/search/providers/people.provider.dart +++ b/mobile/lib/modules/search/providers/people.provider.dart @@ -1,44 +1,51 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart'; +import 'package:immich_mobile/modules/search/models/curated_content.dart'; import 'package:immich_mobile/modules/search/services/person.service.dart'; -import 'package:openapi/api.dart'; +import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; +import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; -final personAssetsProvider = FutureProvider.family - .autoDispose((ref, personId) async { - final PersonService personService = ref.watch(personServiceProvider); +part 'people.provider.g.dart'; +@riverpod +Future> getCuratedPeople( + GetCuratedPeopleRef ref, +) async { + final PersonService personService = ref.read(personServiceProvider); + + final curatedPeople = await personService.getCuratedPeople(); + + return curatedPeople + .map((p) => CuratedContent(id: p.id, label: p.name)) + .toList(); +} + +@riverpod +Future personAssets(PersonAssetsRef ref, String personId) async { + final PersonService personService = ref.read(personServiceProvider); final assets = await personService.getPersonAssets(personId); - if (assets == null) { return RenderList.empty(); } - return RenderList.fromAssets(assets, GroupAssetsBy.auto); -}); - -final getCuratedPeopleProvider = - FutureProvider.autoDispose>((ref) async { - final PersonService personService = ref.watch(personServiceProvider); - - final curatedPeople = await personService.getCuratedPeople(); - - return curatedPeople ?? []; -}); - -class UpdatePersonName { - final String id; - final String name; - - UpdatePersonName(this.id, this.name); + final settings = ref.read(appSettingsServiceProvider); + final groupBy = + GroupAssetsBy.values[settings.getSetting(AppSettingsEnum.groupAssetsBy)]; + return await RenderList.fromAssets(assets, groupBy); } -final updatePersonNameProvider = - StateProvider.family((ref, dto) async { - final PersonService personService = ref.watch(personServiceProvider); +@riverpod +Future updatePersonName( + UpdatePersonNameRef ref, + String personId, + String updatedName, +) async { + final PersonService personService = ref.read(personServiceProvider); + final person = await personService.updateName(personId, updatedName); - final person = await personService.updateName(dto.id, dto.name); - - if (person != null && person.name == dto.name) { + if (person != null && person.name == updatedName) { ref.invalidate(getCuratedPeopleProvider); + return true; } -}); + return false; +} diff --git a/mobile/lib/modules/search/providers/people.provider.g.dart b/mobile/lib/modules/search/providers/people.provider.g.dart new file mode 100644 index 000000000..c13c2c160 Binary files /dev/null and b/mobile/lib/modules/search/providers/people.provider.g.dart differ diff --git a/mobile/lib/modules/search/services/person.service.dart b/mobile/lib/modules/search/services/person.service.dart index 8314ed109..d4cbe0de5 100644 --- a/mobile/lib/modules/search/services/person.service.dart +++ b/mobile/lib/modules/search/services/person.service.dart @@ -1,44 +1,40 @@ -import 'package:flutter/material.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/providers/api.provider.dart'; import 'package:immich_mobile/shared/services/api.service.dart'; +import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; -final personServiceProvider = Provider( - (ref) => PersonService( - ref.watch(apiServiceProvider), - ), -); +part 'person.service.g.dart'; + +@riverpod +PersonService personService(PersonServiceRef ref) => + PersonService(ref.read(apiServiceProvider)); class PersonService { + final Logger _log = Logger("PersonService"); final ApiService _apiService; PersonService(this._apiService); - Future?> getCuratedPeople() async { + Future> getCuratedPeople() async { try { final peopleResponseDto = await _apiService.personApi.getAllPeople(); - return peopleResponseDto?.people; - } catch (e) { - debugPrint("Error [getCuratedPeople] ${e.toString()}"); - return null; + return peopleResponseDto?.people ?? []; + } catch (error, stack) { + _log.severe("Error while fetching curated people", error, stack); + return []; } } Future?> getPersonAssets(String id) async { try { final assets = await _apiService.personApi.getPersonAssets(id); - - if (assets == null) { - return null; - } - - return assets.map((e) => Asset.remote(e)).toList(); - } catch (e) { - debugPrint("Error [getPersonAssets] ${e.toString()}"); - return null; + return assets?.map((e) => Asset.remote(e)).toList(); + } catch (error, stack) { + _log.severe("Error while fetching person assets", error, stack); } + return null; } Future updateName(String id, String name) async { @@ -49,9 +45,9 @@ class PersonService { name: name, ), ); - } catch (e) { - debugPrint("Error [updateName] ${e.toString()}"); - return null; + } catch (error, stack) { + _log.severe("Error while updating person name", error, stack); } + return null; } } diff --git a/mobile/lib/modules/search/services/person.service.g.dart b/mobile/lib/modules/search/services/person.service.g.dart new file mode 100644 index 000000000..e66c6c2aa Binary files /dev/null and b/mobile/lib/modules/search/services/person.service.g.dart differ diff --git a/mobile/lib/modules/search/ui/person_name_edit_form.dart b/mobile/lib/modules/search/ui/person_name_edit_form.dart index 6e50131f9..e32d4a9e0 100644 --- a/mobile/lib/modules/search/ui/person_name_edit_form.dart +++ b/mobile/lib/modules/search/ui/person_name_edit_form.dart @@ -25,6 +25,7 @@ class PersonNameEditForm extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final controller = useTextEditingController(text: personName); + final isError = useState(false); return AlertDialog( title: const Text( @@ -37,18 +38,16 @@ class PersonNameEditForm extends HookConsumerWidget { autofocus: true, decoration: InputDecoration( hintText: 'search_page_person_add_name_dialog_hint'.tr(), + border: const OutlineInputBorder(), + errorText: isError.value ? 'Error occured' : null, ), ), ), actions: [ TextButton( - style: TextButton.styleFrom(), - onPressed: () { - Navigator.of(context, rootNavigator: true) - .pop( - PersonNameEditFormResult(false, ''), - ); - }, + onPressed: () => context.pop( + PersonNameEditFormResult(false, ''), + ), child: Text( "search_page_person_add_name_dialog_cancel", style: TextStyle( @@ -58,17 +57,15 @@ class PersonNameEditForm extends HookConsumerWidget { ).tr(), ), TextButton( - onPressed: () { - ref.read( - updatePersonNameProvider( - UpdatePersonName(personId, controller.text), - ), - ); - - Navigator.of(context, rootNavigator: true) - .pop( - PersonNameEditFormResult(true, controller.text), + onPressed: () async { + isError.value = false; + final result = await ref.read( + updatePersonNameProvider(personId, controller.text).future, ); + isError.value = !result; + if (result) { + context.pop(PersonNameEditFormResult(true, controller.text)); + } }, child: Text( "search_page_person_add_name_dialog_save", diff --git a/mobile/lib/modules/search/views/all_people_page.dart b/mobile/lib/modules/search/views/all_people_page.dart index 3cbedc949..892006293 100644 --- a/mobile/lib/modules/search/views/all_people_page.dart +++ b/mobile/lib/modules/search/views/all_people_page.dart @@ -2,7 +2,6 @@ import 'package:easy_localization/easy_localization.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/modules/search/models/curated_content.dart'; import 'package:immich_mobile/modules/search/providers/people.provider.dart'; import 'package:immich_mobile/modules/search/ui/explore_grid.dart'; import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; @@ -36,14 +35,7 @@ class AllPeoplePage extends HookConsumerWidget { ), data: (people) => ExploreGrid( isPeople: true, - curatedContent: people - .map( - (person) => CuratedContent( - label: person.name, - id: person.id, - ), - ) - .toList(), + curatedContent: people, ), ), ); diff --git a/mobile/lib/modules/search/views/person_result_page.dart b/mobile/lib/modules/search/views/person_result_page.dart index 2e8637bc7..60d199942 100644 --- a/mobile/lib/modules/search/views/person_result_page.dart +++ b/mobile/lib/modules/search/views/person_result_page.dart @@ -2,11 +2,13 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart'; import 'package:immich_mobile/modules/search/providers/people.provider.dart'; import 'package:immich_mobile/modules/search/ui/person_name_edit_form.dart'; import 'package:immich_mobile/shared/models/store.dart' as isar_store; +import 'package:immich_mobile/shared/ui/scaffold_error_body.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; class PersonResultPage extends HookConsumerWidget { @@ -24,12 +26,12 @@ class PersonResultPage extends HookConsumerWidget { final name = useState(personName); showEditNameDialog() { - showDialog( + showDialog( context: context, builder: (BuildContext context) { return PersonNameEditForm( personId: personId, - personName: personName, + personName: name.value, ); }, ).then((result) { @@ -66,35 +68,33 @@ class PersonResultPage extends HookConsumerWidget { } buildTitleBlock() { - if (name.value == "") { - return GestureDetector( - onTap: showEditNameDialog, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'search_page_person_add_name_title', - style: context.textTheme.titleSmall?.copyWith( - color: context.themeData.colorScheme.secondary, - ), - ).tr(), - Text( - 'search_page_person_add_name_subtitle', - style: context.textTheme.labelSmall, - ).tr(), - ], - ), - ); - } - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - name.value, - style: context.textTheme.titleLarge, - ), - ], + return GestureDetector( + onTap: showEditNameDialog, + child: name.value.isEmpty + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'search_page_person_add_name_title', + style: context.textTheme.titleSmall?.copyWith( + color: context.themeData.colorScheme.secondary, + ), + ).tr(), + Text( + 'search_page_person_add_name_subtitle', + style: context.textTheme.labelSmall, + ).tr(), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + name.value, + style: context.textTheme.titleLarge, + ), + ], + ), ); } @@ -112,41 +112,32 @@ class PersonResultPage extends HookConsumerWidget { ), ], ), - body: ref.watch(personAssetsProvider(personId)).when( - loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stackTrace) => Center( - child: Text( - error.toString(), - ), - ), - data: (data) => data.isEmpty - ? const Center( - child: Text('Opps'), - ) - : ImmichAssetGrid( - renderList: data, - topWidget: Padding( - padding: const EdgeInsets.only(left: 8.0, top: 24), - child: Row( - children: [ - CircleAvatar( - radius: 36, - backgroundImage: NetworkImage( - getFaceThumbnailUrl(personId), - headers: { - "Authorization": - "Bearer ${isar_store.Store.get(isar_store.StoreKey.accessToken)}", - }, - ), - ), - Padding( - padding: const EdgeInsets.only(left: 16.0), - child: buildTitleBlock(), - ), - ], + body: ref.watch(personAssetsProvider(personId)).scaffoldBodyWhen( + onData: (renderList) => ImmichAssetGrid( + renderList: renderList, + topWidget: Padding( + padding: const EdgeInsets.only(left: 8.0, top: 24), + child: Row( + children: [ + CircleAvatar( + radius: 36, + backgroundImage: NetworkImage( + getFaceThumbnailUrl(personId), + headers: { + "Authorization": + "Bearer ${isar_store.Store.get(isar_store.StoreKey.accessToken)}", + }, ), ), - ), + Padding( + padding: const EdgeInsets.only(left: 16.0), + child: buildTitleBlock(), + ), + ], + ), + ), + ), + onError: const ScaffoldErrorBody(icon: Icons.person_off_outlined), ), ); } diff --git a/mobile/lib/modules/search/views/search_page.dart b/mobile/lib/modules/search/views/search_page.dart index 562e42c32..cead59e3c 100644 --- a/mobile/lib/modules/search/views/search_page.dart +++ b/mobile/lib/modules/search/views/search_page.dart @@ -77,15 +77,7 @@ class SearchPage extends HookConsumerWidget { loading: () => const Center(child: ImmichLoadingIndicator()), error: (err, stack) => Center(child: Text('Error: $err')), data: (people) => CuratedPeopleRow( - content: people - .map( - (person) => CuratedContent( - id: person.id, - label: person.name, - ), - ) - .take(12) - .toList(), + content: people.take(12).toList(), onTap: (content, index) { context.autoPush( PersonResultRoute( diff --git a/mobile/lib/modules/settings/providers/app_settings.provider.dart b/mobile/lib/modules/settings/providers/app_settings.provider.dart index f5d172e4c..96991451f 100644 --- a/mobile/lib/modules/settings/providers/app_settings.provider.dart +++ b/mobile/lib/modules/settings/providers/app_settings.provider.dart @@ -1,4 +1,8 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; -final appSettingsServiceProvider = Provider((ref) => AppSettingsService()); +part 'app_settings.provider.g.dart'; + +@Riverpod(keepAlive: true) +AppSettingsService appSettingsService(AppSettingsServiceRef ref) => + AppSettingsService(); diff --git a/mobile/lib/modules/settings/providers/app_settings.provider.g.dart b/mobile/lib/modules/settings/providers/app_settings.provider.g.dart new file mode 100644 index 000000000..692dcf7c0 Binary files /dev/null and b/mobile/lib/modules/settings/providers/app_settings.provider.g.dart differ diff --git a/mobile/lib/shared/providers/api.provider.dart b/mobile/lib/shared/providers/api.provider.dart index 24cf864e0..cc73f02b3 100644 --- a/mobile/lib/shared/providers/api.provider.dart +++ b/mobile/lib/shared/providers/api.provider.dart @@ -1,4 +1,7 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/shared/services/api.service.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; -final apiServiceProvider = Provider((ref) => ApiService()); +part 'api.provider.g.dart'; + +@Riverpod(keepAlive: true) +ApiService apiService(ApiServiceRef ref) => ApiService(); diff --git a/mobile/lib/shared/providers/api.provider.g.dart b/mobile/lib/shared/providers/api.provider.g.dart new file mode 100644 index 000000000..4bc7e93d1 Binary files /dev/null and b/mobile/lib/shared/providers/api.provider.g.dart differ diff --git a/mobile/lib/shared/ui/scaffold_error_body.dart b/mobile/lib/shared/ui/scaffold_error_body.dart new file mode 100644 index 000000000..5c29f7c2a --- /dev/null +++ b/mobile/lib/shared/ui/scaffold_error_body.dart @@ -0,0 +1,33 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; + +// Error widget to be used in Scaffold when an AsyncError is received +class ScaffoldErrorBody extends StatelessWidget { + final IconData icon; + + const ScaffoldErrorBody({this.icon = Icons.error_outline, super.key}); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text( + "scaffold_body_error_occured", + style: + TextStyle(fontSize: 14, fontWeight: FontWeight.bold, height: 3), + textAlign: TextAlign.center, + ).tr(), + Center( + child: Icon( + icon, + size: 100, + color: context.themeData.iconTheme.color?.withOpacity(0.5), + ), + ), + ], + ); + } +} diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index a573f6bf7..7cb4188d9 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -17,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.13.0" + analyzer_plugin: + dependency: transitive + description: + name: analyzer_plugin + sha256: c1d5f167683de03d5ab6c3b53fc9aeefc5d59476e7810ba7bbddff50c6f4392d + url: "https://pub.dev" + source: hosted + version: "0.11.2" archive: dependency: transitive description: @@ -201,6 +209,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.7.0" + ci: + dependency: transitive + description: + name: ci + sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" + url: "https://pub.dev" + source: hosted + version: "0.1.0" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 + url: "https://pub.dev" + source: hosted + version: "0.4.0" clock: dependency: transitive description: @@ -281,6 +305,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" + custom_lint: + dependency: "direct dev" + description: + name: custom_lint + sha256: f9a828b696930cf8307f9a3617b2b65c9b370e484dc845d69100cadb77506778 + url: "https://pub.dev" + source: hosted + version: "0.5.6" + custom_lint_builder: + dependency: transitive + description: + name: custom_lint_builder + sha256: c6f656a4d83385fc0656ae60410ed06bb382898c45627bfb8bbaa323aea97883 + url: "https://pub.dev" + source: hosted + version: "0.5.6" + custom_lint_core: + dependency: transitive + description: + name: custom_lint_core + sha256: e20a67737adcf0cf2465e734dd624af535add11f9edd1f2d444909b5b0749650 + url: "https://pub.dev" + source: hosted + version: "0.5.6" dart_style: dependency: transitive description: @@ -455,10 +503,10 @@ packages: dependency: "direct main" description: name: flutter_hooks - sha256: "6a126f703b89499818d73305e4ce1e3de33b4ae1c5512e3b8eab4b986f46774c" + sha256: "7c8db779c2d1010aa7f9ea3fbefe8f86524fcb87b69e8b0af31e1a4b55422dec" url: "https://pub.dev" source: hosted - version: "0.18.6" + version: "0.20.3" flutter_launcher_icons: dependency: "direct dev" description: @@ -540,10 +588,10 @@ packages: dependency: transitive description: name: flutter_riverpod - sha256: b6cb0041c6c11cefb2dcb97ef436eba43c6d41287ac6d8ca93e02a497f53a4f3 + sha256: "305203d1578f6857675f9730568548b03900ce53afd319f4aa9d2fa943334dbe" url: "https://pub.dev" source: hosted - version: "2.3.7" + version: "2.4.5" flutter_test: dependency: "direct dev" description: flutter @@ -578,6 +626,14 @@ packages: url: "https://pub.dev" source: hosted version: "8.2.2" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d + url: "https://pub.dev" + source: hosted + version: "2.4.1" frontend_server_client: dependency: transitive description: @@ -659,10 +715,18 @@ packages: dependency: "direct main" description: name: hooks_riverpod - sha256: "2bb8ae6a729e1334f71f1ef68dd5f0400dca8f01de8cbdcde062584a68017b18" + sha256: "2827136ecc0c2abffc3a58e575db6d5b84d159977fa1edc223c97bf566aa8c73" url: "https://pub.dev" source: hosted - version: "2.3.8" + version: "2.4.5" + hotreloader: + dependency: transitive + description: + name: hotreloader + sha256: "94ee21a60ea2836500799f3af035dc3212b1562027f1e0031c14e087f0231449" + url: "https://pub.dev" + source: hosted + version: "4.1.0" html: dependency: transitive description: @@ -1175,10 +1239,42 @@ packages: dependency: transitive description: name: riverpod - sha256: b0657b5b30c81a3184bdaab353045f0a403ebd60bb381591a8b7ad77dcade793 + sha256: "2e84315036e64c59affaff7443dea51247bc2fe704461a32f26a27986fb63d55" url: "https://pub.dev" source: hosted - version: "2.3.7" + version: "2.4.5" + riverpod_analyzer_utils: + dependency: transitive + description: + name: riverpod_analyzer_utils + sha256: d72d7096964baf288b55619fe48100001fc4564ab7923ed0a7f5c7650e03c0d6 + url: "https://pub.dev" + source: hosted + version: "0.3.4" + riverpod_annotation: + dependency: "direct main" + description: + name: riverpod_annotation + sha256: "9330309e4400f40e39a2a1d1c340e775d0fd23451cf2dd2286e03c7896fd2bd5" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + riverpod_generator: + dependency: "direct dev" + description: + name: riverpod_generator + sha256: "5b36ad2f2b562cffb37212e8d59390b25499bf045b732276e30a207b16a25f61" + url: "https://pub.dev" + source: hosted + version: "2.3.3" + riverpod_lint: + dependency: "direct dev" + description: + name: riverpod_lint + sha256: "70198738c3047ae4f6517ef1a2011a8514a980a52576c7f629a3a08810319a02" + url: "https://pub.dev" + source: hosted + version: "2.1.1" rxdart: dependency: transitive description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index d5c2a8571..88b5cd1cf 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -14,8 +14,9 @@ dependencies: path_provider_ios: photo_manager: ^2.7.2 - flutter_hooks: ^0.18.6 - hooks_riverpod: ^2.2.0 + flutter_hooks: ^0.20.3 + hooks_riverpod: ^2.4.0 + riverpod_annotation: ^2.3.0 cached_network_image: ^3.2.2 flutter_cache_manager: ^3.3.0 intl: ^0.18.0 @@ -86,6 +87,9 @@ dev_dependencies: mockito: ^5.3.2 integration_test: sdk: flutter + custom_lint: ^0.5.6 + riverpod_lint: ^2.1.0 + riverpod_generator: ^2.3.3 flutter: uses-material-design: true