mirror of
https://github.com/immich-app/immich.git
synced 2024-12-29 15:11:58 +00:00
refactor(mobile): riverpod codegen + riverpod lint (#4836)
* build(mobile): add riverpod_lint * refactor(mobile): riverpod_generator for providers * test(mobile): fix integration test helper * refactor: ApiService to riverpod codegen * refactor(mobile): return curatedcontent instead of people dto * refactor: person provider to asyncnotifier * mobile: update service providers to use lambda * mobile: update scaffoldbody default error icon * remove logger mixin --------- Co-authored-by: shalong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
parent
bfab86b70d
commit
983473261b
22 changed files with 337 additions and 175 deletions
2
.gitattributes
vendored
2
.gitattributes
vendored
|
@ -5,6 +5,8 @@ mobile/openapi/**/*.dart linguist-generated=true
|
||||||
mobile/openapi/.openapi-generator/FILES -diff -merge
|
mobile/openapi/.openapi-generator/FILES -diff -merge
|
||||||
mobile/openapi/.openapi-generator/FILES linguist-generated=true
|
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 -diff -merge
|
||||||
cli/src/api/open-api/**/*.md linguist-generated=true
|
cli/src/api/open-api/**/*.md linguist-generated=true
|
||||||
|
|
5
.github/workflows/static_analysis.yml
vendored
5
.github/workflows/static_analysis.yml
vendored
|
@ -32,3 +32,8 @@ jobs:
|
||||||
- name: Run dart analyze
|
- name: Run dart analyze
|
||||||
run: dart analyze --fatal-infos
|
run: dart analyze --fatal-infos
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
|
|
||||||
|
# Enable after riverpod generator migration is completed
|
||||||
|
# - name: Run dart custom lint
|
||||||
|
# run: dart run custom_lint
|
||||||
|
# working-directory: ./mobile
|
||||||
|
|
|
@ -37,6 +37,9 @@ analyzer:
|
||||||
- openapi/test/
|
- openapi/test/
|
||||||
- lib/generated_plugin_registrant.dart
|
- lib/generated_plugin_registrant.dart
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- custom_lint
|
||||||
|
|
||||||
dart_code_metrics:
|
dart_code_metrics:
|
||||||
metrics:
|
metrics:
|
||||||
cyclomatic-complexity: 20
|
cyclomatic-complexity: 20
|
||||||
|
|
|
@ -438,5 +438,6 @@
|
||||||
"version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89",
|
"version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89",
|
||||||
"viewer_remove_from_stack": "Remove from Stack",
|
"viewer_remove_from_stack": "Remove from Stack",
|
||||||
"viewer_stack_use_as_main_asset": "Use as Main Asset",
|
"viewer_stack_use_as_main_asset": "Use as Main Asset",
|
||||||
"viewer_unstack": "Un-Stack"
|
"viewer_unstack": "Un-Stack",
|
||||||
|
"scaffold_body_error_occured": "Error occured"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,9 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter_test/flutter_test.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/models/store.dart';
|
||||||
|
import 'package:immich_mobile/shared/providers/db.provider.dart';
|
||||||
import 'package:integration_test/integration_test.dart';
|
import 'package:integration_test/integration_test.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
// ignore: depend_on_referenced_packages
|
// ignore: depend_on_referenced_packages
|
||||||
|
@ -40,7 +42,12 @@ class ImmichTestHelper {
|
||||||
await Store.clear();
|
await Store.clear();
|
||||||
await db.writeTxn(() => db.clear());
|
await db.writeTxn(() => db.clear());
|
||||||
// Load main Widget
|
// Load main Widget
|
||||||
await tester.pumpWidget(app.getMainWidget(db));
|
await tester.pumpWidget(
|
||||||
|
ProviderScope(
|
||||||
|
overrides: [dbProvider.overrideWithValue(db)],
|
||||||
|
child: app.getMainWidget(),
|
||||||
|
),
|
||||||
|
);
|
||||||
// Post run tasks
|
// Post run tasks
|
||||||
await EasyLocalization.ensureInitialized();
|
await EasyLocalization.ensureInitialized();
|
||||||
}
|
}
|
||||||
|
|
27
mobile/lib/extensions/asyncvalue_extensions.dart
Normal file
27
mobile/lib/extensions/asyncvalue_extensions.dart
Normal file
|
@ -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<T> on AsyncValue<T> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,7 +43,12 @@ void main() async {
|
||||||
await initApp();
|
await initApp();
|
||||||
await migrateDatabaseIfNeeded(db);
|
await migrateDatabaseIfNeeded(db);
|
||||||
HttpOverrides.global = HttpSSLCertOverride();
|
HttpOverrides.global = HttpSSLCertOverride();
|
||||||
runApp(getMainWidget(db));
|
runApp(
|
||||||
|
ProviderScope(
|
||||||
|
overrides: [dbProvider.overrideWithValue(db)],
|
||||||
|
child: getMainWidget(),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initApp() async {
|
Future<void> initApp() async {
|
||||||
|
@ -103,16 +108,13 @@ Future<Isar> loadDb() async {
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget getMainWidget(Isar db) {
|
Widget getMainWidget() {
|
||||||
return EasyLocalization(
|
return EasyLocalization(
|
||||||
supportedLocales: locales,
|
supportedLocales: locales,
|
||||||
path: translationsPath,
|
path: translationsPath,
|
||||||
useFallbackTranslations: true,
|
useFallbackTranslations: true,
|
||||||
fallbackLocale: locales.first,
|
fallbackLocale: locales.first,
|
||||||
child: ProviderScope(
|
|
||||||
overrides: [dbProvider.overrideWithValue(db)],
|
|
||||||
child: const ImmichApp(),
|
child: const ImmichApp(),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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/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: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
|
part 'people.provider.g.dart';
|
||||||
.autoDispose<RenderList, String>((ref, personId) async {
|
|
||||||
final PersonService personService = ref.watch(personServiceProvider);
|
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
Future<List<CuratedContent>> 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<RenderList> personAssets(PersonAssetsRef ref, String personId) async {
|
||||||
|
final PersonService personService = ref.read(personServiceProvider);
|
||||||
final assets = await personService.getPersonAssets(personId);
|
final assets = await personService.getPersonAssets(personId);
|
||||||
|
|
||||||
if (assets == null) {
|
if (assets == null) {
|
||||||
return RenderList.empty();
|
return RenderList.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
return RenderList.fromAssets(assets, GroupAssetsBy.auto);
|
final settings = ref.read(appSettingsServiceProvider);
|
||||||
});
|
final groupBy =
|
||||||
|
GroupAssetsBy.values[settings.getSetting(AppSettingsEnum.groupAssetsBy)];
|
||||||
final getCuratedPeopleProvider =
|
return await RenderList.fromAssets(assets, groupBy);
|
||||||
FutureProvider.autoDispose<List<PersonResponseDto>>((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 updatePersonNameProvider =
|
@riverpod
|
||||||
StateProvider.family<void, UpdatePersonName>((ref, dto) async {
|
Future<bool> updatePersonName(
|
||||||
final PersonService personService = ref.watch(personServiceProvider);
|
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 == updatedName) {
|
||||||
|
|
||||||
if (person != null && person.name == dto.name) {
|
|
||||||
ref.invalidate(getCuratedPeopleProvider);
|
ref.invalidate(getCuratedPeopleProvider);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
BIN
mobile/lib/modules/search/providers/people.provider.g.dart
generated
Normal file
BIN
mobile/lib/modules/search/providers/people.provider.g.dart
generated
Normal file
Binary file not shown.
|
@ -1,45 +1,41 @@
|
||||||
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/models/asset.dart';
|
||||||
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||||
import 'package:immich_mobile/shared/services/api.service.dart';
|
import 'package:immich_mobile/shared/services/api.service.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
final personServiceProvider = Provider(
|
part 'person.service.g.dart';
|
||||||
(ref) => PersonService(
|
|
||||||
ref.watch(apiServiceProvider),
|
@riverpod
|
||||||
),
|
PersonService personService(PersonServiceRef ref) =>
|
||||||
);
|
PersonService(ref.read(apiServiceProvider));
|
||||||
|
|
||||||
class PersonService {
|
class PersonService {
|
||||||
|
final Logger _log = Logger("PersonService");
|
||||||
final ApiService _apiService;
|
final ApiService _apiService;
|
||||||
|
|
||||||
PersonService(this._apiService);
|
PersonService(this._apiService);
|
||||||
|
|
||||||
Future<List<PersonResponseDto>?> getCuratedPeople() async {
|
Future<List<PersonResponseDto>> getCuratedPeople() async {
|
||||||
try {
|
try {
|
||||||
final peopleResponseDto = await _apiService.personApi.getAllPeople();
|
final peopleResponseDto = await _apiService.personApi.getAllPeople();
|
||||||
return peopleResponseDto?.people;
|
return peopleResponseDto?.people ?? [];
|
||||||
} catch (e) {
|
} catch (error, stack) {
|
||||||
debugPrint("Error [getCuratedPeople] ${e.toString()}");
|
_log.severe("Error while fetching curated people", error, stack);
|
||||||
return null;
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Asset>?> getPersonAssets(String id) async {
|
Future<List<Asset>?> getPersonAssets(String id) async {
|
||||||
try {
|
try {
|
||||||
final assets = await _apiService.personApi.getPersonAssets(id);
|
final assets = await _apiService.personApi.getPersonAssets(id);
|
||||||
|
return assets?.map((e) => Asset.remote(e)).toList();
|
||||||
if (assets == null) {
|
} catch (error, stack) {
|
||||||
return null;
|
_log.severe("Error while fetching person assets", error, stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
return assets.map((e) => Asset.remote(e)).toList();
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint("Error [getPersonAssets] ${e.toString()}");
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Future<PersonResponseDto?> updateName(String id, String name) async {
|
Future<PersonResponseDto?> updateName(String id, String name) async {
|
||||||
try {
|
try {
|
||||||
|
@ -49,9 +45,9 @@ class PersonService {
|
||||||
name: name,
|
name: name,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (error, stack) {
|
||||||
debugPrint("Error [updateName] ${e.toString()}");
|
_log.severe("Error while updating person name", error, stack);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
BIN
mobile/lib/modules/search/services/person.service.g.dart
generated
Normal file
BIN
mobile/lib/modules/search/services/person.service.g.dart
generated
Normal file
Binary file not shown.
|
@ -25,6 +25,7 @@ class PersonNameEditForm extends HookConsumerWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final controller = useTextEditingController(text: personName);
|
final controller = useTextEditingController(text: personName);
|
||||||
|
final isError = useState(false);
|
||||||
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text(
|
title: const Text(
|
||||||
|
@ -37,18 +38,16 @@ class PersonNameEditForm extends HookConsumerWidget {
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'search_page_person_add_name_dialog_hint'.tr(),
|
hintText: 'search_page_person_add_name_dialog_hint'.tr(),
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
errorText: isError.value ? 'Error occured' : null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
style: TextButton.styleFrom(),
|
onPressed: () => context.pop(
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context, rootNavigator: true)
|
|
||||||
.pop<PersonNameEditFormResult>(
|
|
||||||
PersonNameEditFormResult(false, ''),
|
PersonNameEditFormResult(false, ''),
|
||||||
);
|
),
|
||||||
},
|
|
||||||
child: Text(
|
child: Text(
|
||||||
"search_page_person_add_name_dialog_cancel",
|
"search_page_person_add_name_dialog_cancel",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
@ -58,17 +57,15 @@ class PersonNameEditForm extends HookConsumerWidget {
|
||||||
).tr(),
|
).tr(),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
ref.read(
|
isError.value = false;
|
||||||
updatePersonNameProvider(
|
final result = await ref.read(
|
||||||
UpdatePersonName(personId, controller.text),
|
updatePersonNameProvider(personId, controller.text).future,
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Navigator.of(context, rootNavigator: true)
|
|
||||||
.pop<PersonNameEditFormResult>(
|
|
||||||
PersonNameEditFormResult(true, controller.text),
|
|
||||||
);
|
);
|
||||||
|
isError.value = !result;
|
||||||
|
if (result) {
|
||||||
|
context.pop(PersonNameEditFormResult(true, controller.text));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
"search_page_person_add_name_dialog_save",
|
"search_page_person_add_name_dialog_save",
|
||||||
|
|
|
@ -2,7 +2,6 @@ import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.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/providers/people.provider.dart';
|
||||||
import 'package:immich_mobile/modules/search/ui/explore_grid.dart';
|
import 'package:immich_mobile/modules/search/ui/explore_grid.dart';
|
||||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||||
|
@ -36,14 +35,7 @@ class AllPeoplePage extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
data: (people) => ExploreGrid(
|
data: (people) => ExploreGrid(
|
||||||
isPeople: true,
|
isPeople: true,
|
||||||
curatedContent: people
|
curatedContent: people,
|
||||||
.map(
|
|
||||||
(person) => CuratedContent(
|
|
||||||
label: person.name,
|
|
||||||
id: person.id,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,11 +2,13 @@ import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.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/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.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/providers/people.provider.dart';
|
||||||
import 'package:immich_mobile/modules/search/ui/person_name_edit_form.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/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';
|
import 'package:immich_mobile/utils/image_url_builder.dart';
|
||||||
|
|
||||||
class PersonResultPage extends HookConsumerWidget {
|
class PersonResultPage extends HookConsumerWidget {
|
||||||
|
@ -24,12 +26,12 @@ class PersonResultPage extends HookConsumerWidget {
|
||||||
final name = useState(personName);
|
final name = useState(personName);
|
||||||
|
|
||||||
showEditNameDialog() {
|
showEditNameDialog() {
|
||||||
showDialog<PersonNameEditFormResult>(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return PersonNameEditForm(
|
return PersonNameEditForm(
|
||||||
personId: personId,
|
personId: personId,
|
||||||
personName: personName,
|
personName: name.value,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
).then((result) {
|
).then((result) {
|
||||||
|
@ -66,10 +68,10 @@ class PersonResultPage extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTitleBlock() {
|
buildTitleBlock() {
|
||||||
if (name.value == "") {
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: showEditNameDialog,
|
onTap: showEditNameDialog,
|
||||||
child: Column(
|
child: name.value.isEmpty
|
||||||
|
? Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
|
@ -83,11 +85,8 @@ class PersonResultPage extends HookConsumerWidget {
|
||||||
style: context.textTheme.labelSmall,
|
style: context.textTheme.labelSmall,
|
||||||
).tr(),
|
).tr(),
|
||||||
],
|
],
|
||||||
),
|
)
|
||||||
);
|
: Column(
|
||||||
}
|
|
||||||
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
|
@ -95,6 +94,7 @@ class PersonResultPage extends HookConsumerWidget {
|
||||||
style: context.textTheme.titleLarge,
|
style: context.textTheme.titleLarge,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,19 +112,9 @@ class PersonResultPage extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: ref.watch(personAssetsProvider(personId)).when(
|
body: ref.watch(personAssetsProvider(personId)).scaffoldBodyWhen(
|
||||||
loading: () => const Center(child: CircularProgressIndicator()),
|
onData: (renderList) => ImmichAssetGrid(
|
||||||
error: (error, stackTrace) => Center(
|
renderList: renderList,
|
||||||
child: Text(
|
|
||||||
error.toString(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
data: (data) => data.isEmpty
|
|
||||||
? const Center(
|
|
||||||
child: Text('Opps'),
|
|
||||||
)
|
|
||||||
: ImmichAssetGrid(
|
|
||||||
renderList: data,
|
|
||||||
topWidget: Padding(
|
topWidget: Padding(
|
||||||
padding: const EdgeInsets.only(left: 8.0, top: 24),
|
padding: const EdgeInsets.only(left: 8.0, top: 24),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
@ -147,6 +137,7 @@ class PersonResultPage extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
onError: const ScaffoldErrorBody(icon: Icons.person_off_outlined),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,15 +77,7 @@ class SearchPage extends HookConsumerWidget {
|
||||||
loading: () => const Center(child: ImmichLoadingIndicator()),
|
loading: () => const Center(child: ImmichLoadingIndicator()),
|
||||||
error: (err, stack) => Center(child: Text('Error: $err')),
|
error: (err, stack) => Center(child: Text('Error: $err')),
|
||||||
data: (people) => CuratedPeopleRow(
|
data: (people) => CuratedPeopleRow(
|
||||||
content: people
|
content: people.take(12).toList(),
|
||||||
.map(
|
|
||||||
(person) => CuratedContent(
|
|
||||||
id: person.id,
|
|
||||||
label: person.name,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.take(12)
|
|
||||||
.toList(),
|
|
||||||
onTap: (content, index) {
|
onTap: (content, index) {
|
||||||
context.autoPush(
|
context.autoPush(
|
||||||
PersonResultRoute(
|
PersonResultRoute(
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:immich_mobile/modules/settings/services/app_settings.service.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();
|
||||||
|
|
BIN
mobile/lib/modules/settings/providers/app_settings.provider.g.dart
generated
Normal file
BIN
mobile/lib/modules/settings/providers/app_settings.provider.g.dart
generated
Normal file
Binary file not shown.
|
@ -1,4 +1,7 @@
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:immich_mobile/shared/services/api.service.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();
|
||||||
|
|
BIN
mobile/lib/shared/providers/api.provider.g.dart
generated
Normal file
BIN
mobile/lib/shared/providers/api.provider.g.dart
generated
Normal file
Binary file not shown.
33
mobile/lib/shared/ui/scaffold_error_body.dart
Normal file
33
mobile/lib/shared/ui/scaffold_error_body.dart
Normal file
|
@ -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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.13.0"
|
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:
|
archive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -201,6 +209,22 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.0"
|
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:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -281,6 +305,30 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
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:
|
dart_style:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -455,10 +503,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_hooks
|
name: flutter_hooks
|
||||||
sha256: "6a126f703b89499818d73305e4ce1e3de33b4ae1c5512e3b8eab4b986f46774c"
|
sha256: "7c8db779c2d1010aa7f9ea3fbefe8f86524fcb87b69e8b0af31e1a4b55422dec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.18.6"
|
version: "0.20.3"
|
||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
@ -540,10 +588,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_riverpod
|
name: flutter_riverpod
|
||||||
sha256: b6cb0041c6c11cefb2dcb97ef436eba43c6d41287ac6d8ca93e02a497f53a4f3
|
sha256: "305203d1578f6857675f9730568548b03900ce53afd319f4aa9d2fa943334dbe"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.7"
|
version: "2.4.5"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -578,6 +626,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.2.2"
|
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:
|
frontend_server_client:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -659,10 +715,18 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: hooks_riverpod
|
name: hooks_riverpod
|
||||||
sha256: "2bb8ae6a729e1334f71f1ef68dd5f0400dca8f01de8cbdcde062584a68017b18"
|
sha256: "2827136ecc0c2abffc3a58e575db6d5b84d159977fa1edc223c97bf566aa8c73"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
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:
|
html:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1175,10 +1239,42 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: riverpod
|
name: riverpod
|
||||||
sha256: b0657b5b30c81a3184bdaab353045f0a403ebd60bb381591a8b7ad77dcade793
|
sha256: "2e84315036e64c59affaff7443dea51247bc2fe704461a32f26a27986fb63d55"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
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:
|
rxdart:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -14,8 +14,9 @@ dependencies:
|
||||||
|
|
||||||
path_provider_ios:
|
path_provider_ios:
|
||||||
photo_manager: ^2.7.2
|
photo_manager: ^2.7.2
|
||||||
flutter_hooks: ^0.18.6
|
flutter_hooks: ^0.20.3
|
||||||
hooks_riverpod: ^2.2.0
|
hooks_riverpod: ^2.4.0
|
||||||
|
riverpod_annotation: ^2.3.0
|
||||||
cached_network_image: ^3.2.2
|
cached_network_image: ^3.2.2
|
||||||
flutter_cache_manager: ^3.3.0
|
flutter_cache_manager: ^3.3.0
|
||||||
intl: ^0.18.0
|
intl: ^0.18.0
|
||||||
|
@ -86,6 +87,9 @@ dev_dependencies:
|
||||||
mockito: ^5.3.2
|
mockito: ^5.3.2
|
||||||
integration_test:
|
integration_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
custom_lint: ^0.5.6
|
||||||
|
riverpod_lint: ^2.1.0
|
||||||
|
riverpod_generator: ^2.3.3
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|
Loading…
Reference in a new issue