feat(mobile): Facial recognition (#2507)

* Add API service

* Added service, provider

* merge main

* update pubspec

* styling

* dev: add person search result page

* dev: display person asset on page

* dev: add rename form

* style form

* dev: mechanism to add name to faces

* styling

* fix bad merge

* update api

* test

* revert

* Add header widget

* change name

* show all people page

* fix test

* pr feedback

* Add name to app bar

* feedback

* styling
This commit is contained in:
Alex 2023-06-23 10:44:02 -05:00 committed by GitHub
parent 00f65a53dd
commit 0d0866d5d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 964 additions and 231 deletions

View file

@ -217,6 +217,7 @@
"search_page_selfies": "Selfies",
"search_page_things": "Things",
"search_page_videos": "Videos",
"search_page_people": "People",
"search_page_view_all_button": "View all",
"search_page_your_activity": "Your activity",
"search_result_page_new_search_hint": "New Search",
@ -285,5 +286,6 @@
"version_announcement_overlay_text_1": "Hi friend, there is a new release of",
"version_announcement_overlay_text_2": "please take your time to visit the ",
"version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.",
"version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89"
}
"version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89",
"all_people_page_title": "People"
}

View file

@ -39,7 +39,7 @@ PODS:
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- sqflite (0.0.2):
- sqflite (0.0.3):
- Flutter
- FMDB (>= 2.7.5)
- Toast (4.0.0)
@ -128,21 +128,21 @@ SPEC CHECKSUMS:
flutter_web_auth: c25208760459cec375a3c39f6a8759165ca0fa4d
fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
image_picker_ios: 58b9c4269cb176f89acea5e5d043c9358f2d25f8
image_picker_ios: 4a8aadfbb6dc30ad5141a2ce3832af9214a705b5
integration_test: 13825b8a9334a850581300559b8839134b124670
isar_flutter_libs: b69f437aeab9c521821c3f376198c4371fa21073
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9
path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce
photo_manager: 4f6810b7dfc4feb03b461ac1a70dacf91fba7604
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
video_player_avfoundation: 6d971a232d72e6ee25368378d48a079dea01f1cf
video_player_avfoundation: 81e49bb3d9fb63dccf9fa0f6d877dc3ddbeac126
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
PODFILE CHECKSUM: 599d8aeb73728400c15364e734525722250a5382

View file

@ -28,6 +28,7 @@ class ImmichAssetGrid extends HookConsumerWidget {
final bool showMultiSelectIndicator;
final void Function(ItemPosition start, ItemPosition end)?
visibleItemsListener;
final Widget? topWidget;
const ImmichAssetGrid({
super.key,
@ -44,6 +45,7 @@ class ImmichAssetGrid extends HookConsumerWidget {
this.dynamicLayout,
this.showMultiSelectIndicator = true,
this.visibleItemsListener,
this.topWidget,
});
@override
@ -125,6 +127,7 @@ class ImmichAssetGrid extends HookConsumerWidget {
settings.getSetting(AppSettingsEnum.dynamicLayout),
showMultiSelectIndicator: showMultiSelectIndicator,
visibleItemsListener: visibleItemsListener,
topWidget: topWidget,
),
),
),

View file

@ -206,6 +206,8 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
key: ValueKey(section.offset),
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (section.offset == 0 && widget.topWidget != null)
widget.topWidget!,
if (section.type == RenderAssetGridElementType.monthTitle)
_buildMonthTitle(context, section.date),
if (section.type == RenderAssetGridElementType.groupDividerTitle ||
@ -401,6 +403,7 @@ class ImmichAssetGridView extends StatefulWidget {
final bool showMultiSelectIndicator;
final void Function(ItemPosition start, ItemPosition end)?
visibleItemsListener;
final Widget? topWidget;
const ImmichAssetGridView({
super.key,
@ -416,6 +419,7 @@ class ImmichAssetGridView extends StatefulWidget {
this.dynamicLayout = true,
this.showMultiSelectIndicator = true,
this.visibleItemsListener,
this.topWidget,
});
@override

View file

@ -0,0 +1,44 @@
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/services/person.service.dart';
import 'package:openapi/api.dart';
final personAssetsProvider = FutureProvider.family
.autoDispose<RenderList, String>((ref, personId) async {
final PersonService personService = ref.watch(personServiceProvider);
final assets = await personService.getPersonAssets(personId);
if (assets == null) {
return RenderList.empty();
}
return RenderList.fromAssets(assets, GroupAssetsBy.auto);
});
final getCuratedPeopleProvider =
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 =
StateProvider.family<void, UpdatePersonName>((ref, dto) async {
final PersonService personService = ref.watch(personServiceProvider);
final person = await personService.updateName(dto.id, dto.name);
if (person != null && person.name == dto.name) {
ref.invalidate(getCuratedPeopleProvider);
}
});

View file

@ -0,0 +1,56 @@
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:openapi/api.dart';
final personServiceProvider = Provider(
(ref) => PersonService(
ref.watch(apiServiceProvider),
),
);
class PersonService {
final ApiService _apiService;
PersonService(this._apiService);
Future<List<PersonResponseDto>?> getCuratedPeople() async {
try {
return await _apiService.personApi.getAllPeople();
} catch (e) {
debugPrint("Error [getCuratedPeople] ${e.toString()}");
return null;
}
}
Future<List<Asset>?> 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;
}
}
Future<PersonResponseDto?> updateName(String id, String name) async {
try {
return await _apiService.personApi.updatePerson(
id,
PersonUpdateDto(
name: name,
),
);
} catch (e) {
debugPrint("Error [updateName] ${e.toString()}");
return null;
}
}
}

View file

@ -0,0 +1,114 @@
import 'package:flutter/material.dart';
import 'package:immich_mobile/modules/search/models/curated_content.dart';
import 'package:immich_mobile/modules/search/ui/thumbnail_with_info.dart';
import 'package:immich_mobile/shared/models/store.dart';
import 'package:immich_mobile/utils/image_url_builder.dart';
class CuratedPeopleRow extends StatelessWidget {
final List<CuratedContent> content;
/// Callback with the content and the index when tapped
final Function(CuratedContent, int)? onTap;
final Function(CuratedContent, int)? onNameTap;
const CuratedPeopleRow({
super.key,
required this.content,
this.onTap,
required this.onNameTap,
});
@override
Widget build(BuildContext context) {
const imageSize = 85.0;
// Guard empty [content]
if (content.isEmpty) {
// Return empty thumbnail
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: SizedBox(
width: imageSize,
height: imageSize,
child: ThumbnailWithInfo(
textInfo: '',
onTap: () {},
),
),
),
);
}
return ListView.builder(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.only(
left: 16,
top: 8,
),
itemBuilder: (context, index) {
final person = content[index];
final headers = {
"Authorization": "Bearer ${Store.get(StoreKey.accessToken)}"
};
return Padding(
padding: const EdgeInsets.only(right: 18.0),
child: SizedBox(
width: imageSize,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
GestureDetector(
onTap: () => onTap?.call(person, index),
child: SizedBox(
height: imageSize,
child: Material(
shape: const CircleBorder(side: BorderSide.none),
elevation: 3,
child: CircleAvatar(
maxRadius: imageSize / 2,
backgroundImage: NetworkImage(
getFaceThumbnailUrl(person.id),
headers: headers,
),
),
),
),
),
if (person.label == "")
GestureDetector(
onTap: () => onNameTap?.call(person, index),
child: Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
"Add name",
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryColor,
),
),
),
)
else
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
person.label,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 13.0,
),
),
)
],
),
),
);
},
itemCount: content.length,
);
}
}

View file

@ -4,12 +4,16 @@ import 'package:immich_mobile/modules/search/models/curated_content.dart';
import 'package:immich_mobile/modules/search/ui/thumbnail_with_info.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/models/store.dart';
import 'package:immich_mobile/utils/image_url_builder.dart';
class ExploreGrid extends StatelessWidget {
final List<CuratedContent> curatedContent;
final bool isPeople;
const ExploreGrid({
super.key,
required this.curatedContent,
this.isPeople = false,
});
@override
@ -36,16 +40,25 @@ class ExploreGrid extends StatelessWidget {
),
itemBuilder: (context, index) {
final content = curatedContent[index];
final thumbnailRequestUrl =
'${Store.get(StoreKey.serverEndpoint)}/asset/thumbnail/${content.id}';
final thumbnailRequestUrl = isPeople
? getFaceThumbnailUrl(content.id)
: '${Store.get(StoreKey.serverEndpoint)}/asset/thumbnail/${content.id}';
return ThumbnailWithInfo(
imageUrl: thumbnailRequestUrl,
textInfo: content.label,
borderRadius: 0,
onTap: () {
AutoRouter.of(context).push(
SearchResultRoute(searchTerm: 'm:${content.label}'),
);
isPeople
? AutoRouter.of(context).push(
PersonResultRoute(
personId: content.id,
personName: content.label,
),
)
: AutoRouter.of(context).push(
SearchResultRoute(searchTerm: 'm:${content.label}'),
);
},
);
},

View file

@ -0,0 +1,82 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/search/providers/people.provider.dart';
class PersonNameEditFormResult {
final bool success;
final String updatedName;
PersonNameEditFormResult(this.success, this.updatedName);
}
class PersonNameEditForm extends HookConsumerWidget {
final String personId;
final String personName;
const PersonNameEditForm({
super.key,
required this.personId,
required this.personName,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final controller = useTextEditingController(text: personName);
return AlertDialog(
title: const Text(
"Add a name",
style: TextStyle(fontWeight: FontWeight.bold),
),
content: SingleChildScrollView(
child: TextFormField(
controller: controller,
autofocus: true,
decoration: const InputDecoration(
hintText: 'Name',
),
),
),
actions: [
TextButton(
style: TextButton.styleFrom(),
onPressed: () {
Navigator.of(context, rootNavigator: true)
.pop<PersonNameEditFormResult>(
PersonNameEditFormResult(false, ''),
);
},
child: Text(
"Cancel",
style: TextStyle(
color: Colors.red[300],
fontWeight: FontWeight.bold,
),
),
),
TextButton(
onPressed: () {
ref.read(
updatePersonNameProvider(
UpdatePersonName(personId, controller.text),
),
);
Navigator.of(context, rootNavigator: true)
.pop<PersonNameEditFormResult>(
PersonNameEditFormResult(true, controller.text),
);
},
child: Text(
"Save",
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold,
),
),
),
],
);
}
}

View file

@ -0,0 +1,46 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
class SearchRowTitle extends StatelessWidget {
final Function() onViewAllPressed;
final String title;
final double top;
const SearchRowTitle({
super.key,
required this.onViewAllPressed,
required this.title,
this.top = 12,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(
left: 16.0,
right: 16.0,
top: top,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: Theme.of(context).textTheme.titleSmall,
),
TextButton(
onPressed: onViewAllPressed,
child: Text(
'search_page_view_all_button',
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold,
fontSize: 14.0,
),
).tr(),
),
],
),
);
}
}

View file

@ -0,0 +1,51 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.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';
class AllPeoplePage extends HookConsumerWidget {
const AllPeoplePage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final curatedPeople = ref.watch(getCuratedPeopleProvider);
return Scaffold(
appBar: AppBar(
title: Text(
'all_people_page_title',
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
).tr(),
leading: IconButton(
onPressed: () => AutoRouter.of(context).pop(),
icon: const Icon(Icons.arrow_back_ios_rounded),
),
),
body: curatedPeople.when(
loading: () => const Center(child: ImmichLoadingIndicator()),
error: (err, stack) => Center(
child: Text('Error: $err'),
),
data: (people) => ExploreGrid(
isPeople: true,
curatedContent: people
.map(
(person) => CuratedContent(
label: person.name,
id: person.id,
),
)
.toList(),
),
),
);
}
}

View file

@ -0,0 +1,152 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.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/utils/image_url_builder.dart';
class PersonResultPage extends HookConsumerWidget {
final String personId;
final String personName;
const PersonResultPage({
super.key,
required this.personId,
required this.personName,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final name = useState(personName);
showEditNameDialog() {
showDialog<PersonNameEditFormResult>(
context: context,
builder: (BuildContext context) {
return PersonNameEditForm(
personId: personId,
personName: personName,
);
},
).then((result) {
if (result != null && result.success) {
name.value = result.updatedName;
}
});
}
void buildBottomSheet() {
showModalBottomSheet(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
isScrollControlled: false,
context: context,
useSafeArea: true,
builder: (context) {
return SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.edit_outlined),
title: const Text(
'Edit name',
style: TextStyle(fontWeight: FontWeight.bold),
),
onTap: showEditNameDialog,
)
],
),
);
},
);
}
buildTitleBlock() {
if (name.value == "") {
return GestureDetector(
onTap: showEditNameDialog,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Add a name',
style: Theme.of(context).textTheme.titleSmall?.copyWith(
color: Theme.of(context).colorScheme.secondary,
),
),
Text(
'Find them fast by name with search',
style: Theme.of(context).textTheme.labelSmall,
),
],
),
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name.value,
style: Theme.of(context).textTheme.titleLarge,
),
],
);
}
return Scaffold(
appBar: AppBar(
title: Text(name.value),
leading: IconButton(
onPressed: () => AutoRouter.of(context).pop(),
icon: const Icon(Icons.arrow_back_ios_rounded),
),
actions: [
IconButton(
onPressed: buildBottomSheet,
icon: const Icon(Icons.more_vert_rounded),
),
],
),
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(),
),
],
),
),
),
),
);
}
}

View file

@ -4,13 +4,16 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
import 'package:hooks_riverpod/hooks_riverpod.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/search_page_state.provider.dart';
import 'package:immich_mobile/modules/search/ui/curated_people_row.dart';
import 'package:immich_mobile/modules/search/ui/curated_row.dart';
import 'package:immich_mobile/modules/search/ui/immich_search_bar.dart';
import 'package:immich_mobile/modules/search/ui/person_name_edit_form.dart';
import 'package:immich_mobile/modules/search/ui/search_row_title.dart';
import 'package:immich_mobile/modules/search/ui/search_suggestion_list.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
import 'package:openapi/api.dart';
// ignore: must_be_immutable
class SearchPage extends HookConsumerWidget {
@ -21,10 +24,9 @@ class SearchPage extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final isSearchEnabled = ref.watch(searchPageStateProvider).isSearchEnabled;
AsyncValue<List<CuratedLocationsResponseDto>> curatedLocation =
ref.watch(getCuratedLocationProvider);
AsyncValue<List<CuratedObjectsResponseDto>> curatedObjects =
ref.watch(getCuratedObjectProvider);
final curatedLocation = ref.watch(getCuratedLocationProvider);
final curatedObjects = ref.watch(getCuratedObjectProvider);
final curatedPeople = ref.watch(getCuratedPeopleProvider);
var isDarkTheme = Theme.of(context).brightness == Brightness.dark;
double imageSize = MediaQuery.of(context).size.width / 3;
@ -54,6 +56,50 @@ class SearchPage extends HookConsumerWidget {
);
}
showNameEditModel(
String personId,
String personName,
) {
return showDialog(
context: context,
builder: (BuildContext context) {
return PersonNameEditForm(personId: personId, personName: personName);
},
);
}
buildPeople() {
return SizedBox(
height: MediaQuery.of(context).size.width / 3,
child: curatedPeople.when(
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(),
onTap: (content, index) {
AutoRouter.of(context).push(
PersonResultRoute(
personId: content.id,
personName: content.label,
),
);
},
onNameTap: (person, index) => {
showNameEditModel(person.id, person.label),
},
),
),
);
}
buildPlaces() {
return SizedBox(
height: imageSize,
@ -130,63 +176,25 @@ class SearchPage extends HookConsumerWidget {
children: [
ListView(
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 4.0,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"search_page_places",
style: Theme.of(context).textTheme.titleSmall,
).tr(),
TextButton(
child: Text(
'search_page_view_all_button',
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold,
fontSize: 14.0,
),
).tr(),
onPressed: () => AutoRouter.of(context).push(
const CuratedLocationRoute(),
),
),
],
SearchRowTitle(
title: "search_page_people".tr(),
onViewAllPressed: () => AutoRouter.of(context).push(
const AllPeopleRoute(),
),
),
buildPlaces(),
Padding(
padding: const EdgeInsets.only(
top: 24.0,
bottom: 4.0,
left: 16.0,
right: 16.0,
buildPeople(),
SearchRowTitle(
title: "search_page_places".tr(),
onViewAllPressed: () => AutoRouter.of(context).push(
const CuratedLocationRoute(),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"search_page_things",
style: Theme.of(context).textTheme.titleSmall,
).tr(),
TextButton(
child: Text(
'search_page_view_all_button',
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold,
fontSize: 14.0,
),
).tr(),
onPressed: () => AutoRouter.of(context).push(
const CuratedObjectRoute(),
),
),
],
top: 0,
),
buildPlaces(),
SearchRowTitle(
title: "search_page_things".tr(),
onViewAllPressed: () => AutoRouter.of(context).push(
const CuratedObjectRoute(),
),
),
buildThings(),
@ -200,7 +208,7 @@ class SearchPage extends HookConsumerWidget {
),
ListTile(
leading: Icon(
Icons.favorite_border,
Icons.star_outline,
color: categoryIconColor,
),
title:

View file

@ -25,9 +25,11 @@ import 'package:immich_mobile/modules/login/views/login_page.dart';
import 'package:immich_mobile/modules/onboarding/providers/gallery_permission.provider.dart';
import 'package:immich_mobile/modules/onboarding/views/permission_onboarding_page.dart';
import 'package:immich_mobile/modules/search/views/all_motion_videos_page.dart';
import 'package:immich_mobile/modules/search/views/all_people_page.dart';
import 'package:immich_mobile/modules/search/views/all_videos_page.dart';
import 'package:immich_mobile/modules/search/views/curated_location_page.dart';
import 'package:immich_mobile/modules/search/views/curated_object_page.dart';
import 'package:immich_mobile/modules/search/views/person_result_page.dart';
import 'package:immich_mobile/modules/search/views/recently_added_page.dart';
import 'package:immich_mobile/modules/search/views/search_page.dart';
import 'package:immich_mobile/modules/search/views/search_result_page.dart';
@ -37,8 +39,8 @@ import 'package:immich_mobile/routing/duplicate_guard.dart';
import 'package:immich_mobile/routing/gallery_permission_guard.dart';
import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/models/album.dart';
import 'package:immich_mobile/shared/models/user.dart';
import 'package:immich_mobile/shared/models/logger_message.model.dart';
import 'package:immich_mobile/shared/models/user.dart';
import 'package:immich_mobile/shared/providers/api.provider.dart';
import 'package:immich_mobile/shared/services/api.service.dart';
import 'package:immich_mobile/shared/views/app_log_detail_page.dart';
@ -140,7 +142,15 @@ part 'router.gr.dart';
],
),
AutoRoute(page: PartnerPage, guards: [AuthGuard, DuplicateGuard]),
AutoRoute(page: PartnerDetailPage, guards: [AuthGuard, DuplicateGuard])
AutoRoute(page: PartnerDetailPage, guards: [AuthGuard, DuplicateGuard]),
AutoRoute(
page: PersonResultPage,
guards: [
AuthGuard,
DuplicateGuard,
],
),
AutoRoute(page: AllPeoplePage, guards: [AuthGuard, DuplicateGuard]),
],
)
class AppRouter extends _$AppRouter {

View file

@ -84,6 +84,7 @@ class _$AppRouter extends RootStackRouter {
onVideoEnded: args.onVideoEnded,
onPlaying: args.onPlaying,
onPaused: args.onPaused,
placeholder: args.placeholder,
),
);
},
@ -272,6 +273,23 @@ class _$AppRouter extends RootStackRouter {
),
);
},
PersonResultRoute.name: (routeData) {
final args = routeData.argsAs<PersonResultRouteArgs>();
return MaterialPageX<dynamic>(
routeData: routeData,
child: PersonResultPage(
key: args.key,
personId: args.personId,
personName: args.personName,
),
);
},
AllPeopleRoute.name: (routeData) {
return MaterialPageX<dynamic>(
routeData: routeData,
child: const AllPeoplePage(),
);
},
HomeRoute.name: (routeData) {
return MaterialPageX<dynamic>(
routeData: routeData,
@ -555,6 +573,22 @@ class _$AppRouter extends RootStackRouter {
duplicateGuard,
],
),
RouteConfig(
PersonResultRoute.name,
path: '/person-result-page',
guards: [
authGuard,
duplicateGuard,
],
),
RouteConfig(
AllPeopleRoute.name,
path: '/all-people-page',
guards: [
authGuard,
duplicateGuard,
],
),
];
}
@ -670,9 +704,10 @@ class VideoViewerRoute extends PageRouteInfo<VideoViewerRouteArgs> {
Key? key,
required Asset asset,
required bool isMotionVideo,
required dynamic onVideoEnded,
dynamic onPlaying,
dynamic onPaused,
required void Function() onVideoEnded,
void Function()? onPlaying,
void Function()? onPaused,
Widget? placeholder,
}) : super(
VideoViewerRoute.name,
path: '/video-viewer-page',
@ -683,6 +718,7 @@ class VideoViewerRoute extends PageRouteInfo<VideoViewerRouteArgs> {
onVideoEnded: onVideoEnded,
onPlaying: onPlaying,
onPaused: onPaused,
placeholder: placeholder,
),
);
@ -697,6 +733,7 @@ class VideoViewerRouteArgs {
required this.onVideoEnded,
this.onPlaying,
this.onPaused,
this.placeholder,
});
final Key? key;
@ -705,15 +742,17 @@ class VideoViewerRouteArgs {
final bool isMotionVideo;
final dynamic onVideoEnded;
final void Function() onVideoEnded;
final dynamic onPlaying;
final void Function()? onPlaying;
final dynamic onPaused;
final void Function()? onPaused;
final Widget? placeholder;
@override
String toString() {
return 'VideoViewerRouteArgs{key: $key, asset: $asset, isMotionVideo: $isMotionVideo, onVideoEnded: $onVideoEnded, onPlaying: $onPlaying, onPaused: $onPaused}';
return 'VideoViewerRouteArgs{key: $key, asset: $asset, isMotionVideo: $isMotionVideo, onVideoEnded: $onVideoEnded, onPlaying: $onPlaying, onPaused: $onPaused, placeholder: $placeholder}';
}
}
@ -1191,6 +1230,57 @@ class PartnerDetailRouteArgs {
}
}
/// generated route for
/// [PersonResultPage]
class PersonResultRoute extends PageRouteInfo<PersonResultRouteArgs> {
PersonResultRoute({
Key? key,
required String personId,
required String personName,
}) : super(
PersonResultRoute.name,
path: '/person-result-page',
args: PersonResultRouteArgs(
key: key,
personId: personId,
personName: personName,
),
);
static const String name = 'PersonResultRoute';
}
class PersonResultRouteArgs {
const PersonResultRouteArgs({
this.key,
required this.personId,
required this.personName,
});
final Key? key;
final String personId;
final String personName;
@override
String toString() {
return 'PersonResultRouteArgs{key: $key, personId: $personId, personName: $personName}';
}
}
/// generated route for
/// [AllPeoplePage]
class AllPeopleRoute extends PageRouteInfo<void> {
const AllPeopleRoute()
: super(
AllPeopleRoute.name,
path: '/all-people-page',
);
static const String name = 'AllPeopleRoute';
}
/// generated route for
/// [HomePage]
class HomeRoute extends PageRouteInfo<void> {

View file

@ -1,6 +1,7 @@
import 'package:auto_route/auto_route.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/album/providers/album.provider.dart';
import 'package:immich_mobile/modules/search/providers/people.provider.dart';
import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart';
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
@ -32,6 +33,7 @@ class TabNavigationObserver extends AutoRouterObserver {
// Refresh Location State
ref.invalidate(getCuratedLocationProvider);
ref.invalidate(getCuratedObjectProvider);
ref.invalidate(getCuratedPeopleProvider);
}
if (route.name == 'SharingRoute') {

View file

@ -17,6 +17,7 @@ class ApiService {
late SearchApi searchApi;
late ServerInfoApi serverInfoApi;
late PartnerApi partnerApi;
late PersonApi personApi;
ApiService() {
final endpoint = Store.tryGet(StoreKey.serverEndpoint);
@ -39,6 +40,7 @@ class ApiService {
serverInfoApi = ServerInfoApi(_apiClient);
searchApi = SearchApi(_apiClient);
partnerApi = PartnerApi(_apiClient);
personApi = PersonApi(_apiClient);
}
Future<String> resolveAndSetEndpoint(String serverUrl) async {

View file

@ -59,3 +59,7 @@ String _getThumbnailUrl(
}) {
return '${Store.get(StoreKey.serverEndpoint)}/asset/thumbnail/$id?format=${type.value}';
}
String getFaceThumbnailUrl(final String personId) {
return '${Store.get(StoreKey.serverEndpoint)}/person/$personId/thumbnail';
}

View file

@ -32,6 +32,8 @@ ThemeData immichLightTheme = ThemeData(
primarySwatch: Colors.indigo,
primaryColor: Colors.indigo,
hintColor: Colors.indigo,
focusColor: Colors.indigo,
splashColor: Colors.indigo.withOpacity(0.15),
fontFamily: 'WorkSans',
scaffoldBackgroundColor: immichBackgroundColor,
snackBarTheme: const SnackBarThemeData(
@ -119,6 +121,26 @@ ThemeData immichLightTheme = ThemeData(
),
),
),
dialogTheme: const DialogTheme(
surfaceTintColor: Colors.transparent,
),
inputDecorationTheme: const InputDecorationTheme(
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.indigo,
),
),
labelStyle: TextStyle(
color: Colors.indigo,
),
hintStyle: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.normal,
),
),
textSelectionTheme: const TextSelectionThemeData(
cursorColor: Colors.indigo,
),
);
ThemeData immichDarkTheme = ThemeData(
@ -217,4 +239,24 @@ ThemeData immichDarkTheme = ThemeData(
),
),
),
dialogTheme: const DialogTheme(
surfaceTintColor: Colors.transparent,
),
inputDecorationTheme: InputDecorationTheme(
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: immichDarkThemePrimaryColor,
),
),
labelStyle: TextStyle(
color: immichDarkThemePrimaryColor,
),
hintStyle: const TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.normal,
),
),
textSelectionTheme: TextSelectionThemeData(
cursorColor: immichDarkThemePrimaryColor,
),
);

View file

@ -21,18 +21,18 @@ packages:
dependency: transitive
description:
name: archive
sha256: "80e5141fafcb3361653ce308776cfd7d45e6e9fbb429e14eec571382c0c5fecb"
sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a"
url: "https://pub.dev"
source: hosted
version: "3.3.2"
version: "3.3.7"
args:
dependency: transitive
description:
name: args
sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440"
sha256: c372bb384f273f0c2a8aaaa226dad84dc27c8519a691b888725dec59518ad53a
url: "https://pub.dev"
source: hosted
version: "2.4.0"
version: "2.4.1"
async:
dependency: transitive
description:
@ -77,10 +77,10 @@ packages:
dependency: transitive
description:
name: build
sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777"
sha256: "43865b79fbb78532e4bff7c33087aa43b1d488c4fdef014eaef568af6d8016dc"
url: "https://pub.dev"
source: hosted
version: "2.3.1"
version: "2.4.0"
build_config:
dependency: transitive
description:
@ -93,34 +93,34 @@ packages:
dependency: transitive
description:
name: build_daemon
sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf"
sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
version: "4.0.0"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
sha256: db49b8609ef8c81cca2b310618c3017c00f03a92af44c04d310b907b2d692d95
url: "https://pub.dev"
source: hosted
version: "2.0.10"
version: "2.2.0"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727
sha256: "220ae4553e50d7c21a17c051afc7b183d28a24a420502e842f303f8e4e6edced"
url: "https://pub.dev"
source: hosted
version: "2.3.3"
version: "2.4.4"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292"
sha256: "30859c90e9ddaccc484f56303931f477b1f1ba2bab74aa32ed5d6ce15870f8cf"
url: "https://pub.dev"
source: hosted
version: "7.2.7"
version: "7.2.8"
built_collection:
dependency: transitive
description:
@ -133,10 +133,10 @@ packages:
dependency: transitive
description:
name: built_value
sha256: "169565c8ad06adb760c3645bf71f00bff161b00002cace266cad42c5d22a7725"
sha256: "2f17434bd5d52a26762043d6b43bb53b3acd029b4d9071a329f46d67ef297e6d"
url: "https://pub.dev"
source: hosted
version: "8.4.3"
version: "8.5.0"
cached_network_image:
dependency: "direct main"
description:
@ -165,18 +165,18 @@ packages:
dependency: transitive
description:
name: cancellation_token
sha256: "44891ef71d605bc59ef7974c403630d8e8506fcd897a29c3e38466ef69e5c4eb"
sha256: "7bacc556338b9f84e4db991805fdfa37fa1eda3689b94185bdc7459099455d71"
url: "https://pub.dev"
source: hosted
version: "1.6.1"
version: "2.0.0"
cancellation_token_http:
dependency: "direct main"
description:
name: cancellation_token_http
sha256: e0396730db74d96522cb7162cb390c73b3e30aa24450001a24374cd09f8484ea
sha256: bb91655e2e47d6274b681261ee6a687b7aa9023f49cfc28f42d095b2f86febc3
url: "https://pub.dev"
source: hosted
version: "1.2.0"
version: "1.3.0"
characters:
dependency: transitive
description:
@ -189,18 +189,18 @@ packages:
dependency: transitive
description:
name: checked_yaml
sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311"
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
url: "https://pub.dev"
source: hosted
version: "2.0.2"
version: "2.0.3"
chewie:
dependency: "direct main"
description:
name: chewie
sha256: e9da4898ee4859825404f507969f57113c04ca0060e152b95c9afd73934126ad
sha256: "745e81e84c6d7f3835f89f85bb49771c0a66099e4caf8f8e9e9a372bc66fb2c1"
url: "https://pub.dev"
source: hosted
version: "1.4.0"
version: "1.5.0"
clock:
dependency: transitive
description:
@ -269,10 +269,10 @@ packages:
dependency: transitive
description:
name: dart_style
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
sha256: f4f1f73ab3fd2afcbcca165ee601fe980d966af6a21b5970c6c9376955c528ad
url: "https://pub.dev"
source: hosted
version: "2.2.4"
version: "2.3.1"
dartx:
dependency: transitive
description:
@ -285,10 +285,10 @@ packages:
dependency: "direct main"
description:
name: device_info_plus
sha256: "1d6e5a61674ba3a68fb048a7c7b4ff4bebfed8d7379dbe8f2b718231be9a7c95"
sha256: f52ab3b76b36ede4d135aab80194df8925b553686f0fa12226b4e2d658e45903
url: "https://pub.dev"
source: hosted
version: "8.1.0"
version: "8.2.2"
device_info_plus_platform_interface:
dependency: transitive
description:
@ -309,10 +309,10 @@ packages:
dependency: "direct main"
description:
name: easy_localization
sha256: f30e9b20ed4d1b890171c30241d9b9c43efe21fee55dee7bd68f94daf269ea75
sha256: "30ebf25448ffe169e0bd9bc4b5da94faa8398967a2ad2ca09f438be8b6953645"
url: "https://pub.dev"
source: hosted
version: "3.0.2-dev.2"
version: "3.0.2"
easy_logger:
dependency: transitive
description:
@ -436,18 +436,18 @@ packages:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: "4bef634684b2c7f3468c77c766c831229af829a0cd2d4ee6c1b99558bd14e5d2"
sha256: "950e77c2bbe1692bc0874fc7fb491b96a4dc340457f4ea1641443d0a6c1ea360"
url: "https://pub.dev"
source: hosted
version: "2.0.8"
version: "2.0.15"
flutter_riverpod:
dependency: transitive
description:
name: flutter_riverpod
sha256: "46a27b7a11dc13738054093076f2dc65692ddcd463979b15092accf5681aea20"
sha256: b83ac5827baadefd331ea1d85110f34645827ea234ccabf53a655f41901a9bf4
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "2.3.6"
flutter_test:
dependency: "direct dev"
description: flutter
@ -507,26 +507,26 @@ packages:
dependency: transitive
description:
name: graphs
sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2
sha256: "772db3d53d23361d4ffcf5a9bb091cf3ee9b22f2be52cd107cd7a2683a89ba0e"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "2.3.0"
hooks_riverpod:
dependency: "direct main"
description:
name: hooks_riverpod
sha256: a596bcb1eaf48eae6da1ce8b9e60ec9538ef7d15725e941c3626f29dfcc01d96
sha256: be68cf7653fcab798500f9047ac58c3f109287a1595012412f4a0d654a9bb9c5
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "2.3.6"
html:
dependency: transitive
description:
name: html
sha256: d9793e10dbe0e6c364f4c59bf3e01fb33a9b2a674bc7a1081693dba0614b6269
sha256: "58e3491f7bf0b6a4ea5110c0c688877460d1a6366731155c4a4580e7ded773e8"
url: "https://pub.dev"
source: hosted
version: "0.15.1"
version: "0.15.3"
http:
dependency: "direct main"
description:
@ -563,34 +563,34 @@ packages:
dependency: "direct main"
description:
name: image_picker
sha256: "22207768556b82d55ec70166824350fee32298732d5efa4d6e756f848f51f66a"
sha256: "9978d3510af4e6a902e545ce19229b926e6de6a1828d6134d3aab2e129a4d270"
url: "https://pub.dev"
source: hosted
version: "0.8.6+3"
version: "0.8.7+5"
image_picker_android:
dependency: transitive
description:
name: image_picker_android
sha256: "68d067baf7f6e401b1124ee83dd6967e67847314250fd68012aab34a69beb344"
sha256: "364967c8d581f5d75fc05f6c79fcf1115e3c05db3d3eee1aaca52e0da3f7501c"
url: "https://pub.dev"
source: hosted
version: "0.8.5+7"
version: "0.8.6+15"
image_picker_for_web:
dependency: transitive
description:
name: image_picker_for_web
sha256: "66fc6e3877bbde82c33d122f3588777c3784ac5bd7d1cdd79213ef7aecb85b34"
sha256: "98f50d6b9f294c8ba35e25cc0d13b04bfddd25dbc8d32fa9d566a6572f2c081c"
url: "https://pub.dev"
source: hosted
version: "2.1.11"
version: "2.1.12"
image_picker_ios:
dependency: transitive
description:
name: image_picker_ios
sha256: "39aa70b5f1e5e7c94585b9738632d5fdb764a5655e40cd9e7b95fbd2fc50c519"
sha256: d779210bda268a03b57e923fb1e410f32f5c5e708ad256348bcbf1f44f558fd0
url: "https://pub.dev"
source: hosted
version: "0.8.6+9"
version: "0.8.7+4"
image_picker_platform_interface:
dependency: transitive
description:
@ -656,10 +656,10 @@ packages:
dependency: transitive
description:
name: json_annotation
sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
url: "https://pub.dev"
source: hosted
version: "4.8.0"
version: "4.8.1"
latlong2:
dependency: "direct main"
description:
@ -672,10 +672,10 @@ packages:
dependency: transitive
description:
name: lints
sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593"
sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "2.1.0"
lists:
dependency: transitive
description:
@ -799,26 +799,26 @@ packages:
dependency: "direct main"
description:
name: path_provider
sha256: "04890b994ee89bfa80bf3080bfec40d5a92c5c7a785ebb02c13084a099d2b6f9"
sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2"
url: "https://pub.dev"
source: hosted
version: "2.0.13"
version: "2.0.15"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: "7623b7d4be0f0f7d9a8b5ee6879fc13e4522d4c875ab86801dee4af32b54b83e"
sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86"
url: "https://pub.dev"
source: hosted
version: "2.0.23"
version: "2.0.27"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: eec003594f19fe2456ea965ae36b3fc967bc5005f508890aafe31fa75e41d972
sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
version: "2.2.3"
path_provider_ios:
dependency: "direct main"
description:
@ -831,10 +831,10 @@ packages:
dependency: transitive
description:
name: path_provider_linux
sha256: "525ad5e07622d19447ad740b1ed5070031f7a5437f44355ae915ff56e986429a"
sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1"
url: "https://pub.dev"
source: hosted
version: "2.1.9"
version: "2.1.10"
path_provider_platform_interface:
dependency: transitive
description:
@ -847,10 +847,10 @@ packages:
dependency: transitive
description:
name: path_provider_windows
sha256: "642ddf65fde5404f83267e8459ddb4556316d3ee6d511ed193357e25caa3632d"
sha256: d3f80b32e83ec208ac95253e0cd4d298e104fbc63cb29c5c69edaed43b0c69d6
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.6"
pedantic:
dependency: transitive
description:
@ -871,18 +871,18 @@ packages:
dependency: transitive
description:
name: permission_handler_android
sha256: "8028362b40c4a45298f1cbfccd227c8dd6caf0e27088a69f2ba2ab15464159e2"
sha256: d8cc6a62ded6d0f49c6eac337e080b066ee3bce4d405bd9439a61e1f1927bfe8
url: "https://pub.dev"
source: hosted
version: "10.2.0"
version: "10.2.1"
permission_handler_apple:
dependency: transitive
description:
name: permission_handler_apple
sha256: "9c370ef6a18b1c4b2f7f35944d644a56aa23576f23abee654cf73968de93f163"
sha256: ee96ac32f5a8e6f80756e25b25b9f8e535816c8e6665a96b6d70681f8c4f7e85
url: "https://pub.dev"
source: hosted
version: "9.0.7"
version: "9.0.8"
permission_handler_platform_interface:
dependency: transitive
description:
@ -903,18 +903,18 @@ packages:
dependency: transitive
description:
name: petitparser
sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4"
sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
url: "https://pub.dev"
source: hosted
version: "5.1.0"
version: "5.4.0"
photo_manager:
dependency: "direct main"
description:
name: photo_manager
sha256: "55d50ad1b8f984c57fa7c4bd4980f4760e80d3d9355263cf72624a6ff1bf2b5b"
sha256: bdc4ab1fa9fb064d8ccfea6ab44119f55b220293d7ce2e19eb5a5f998db86c88
url: "https://pub.dev"
source: hosted
version: "2.5.2"
version: "2.6.0"
platform:
dependency: transitive
description:
@ -931,6 +931,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
url: "https://pub.dev"
source: hosted
version: "3.7.3"
polylabel:
dependency: transitive
description:
@ -975,26 +983,26 @@ packages:
dependency: transitive
description:
name: pub_semver
sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17"
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
url: "https://pub.dev"
source: hosted
version: "2.1.3"
version: "2.1.4"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: "75f6614d6dde2dc68948dffbaa4fe5dae32cd700eb9fb763fe11dfb45a3c4d0a"
sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367
url: "https://pub.dev"
source: hosted
version: "1.2.1"
version: "1.2.3"
riverpod:
dependency: transitive
description:
name: riverpod
sha256: "59a48de9c757aa61aa28e9fd625ffb360d43b6b54606f12536622c55be9e8c4b"
sha256: "80e48bebc83010d5e67a11c9514af6b44bbac1ec77b4333c8ea65dbc79e2d8ef"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "2.3.6"
rxdart:
dependency: transitive
description:
@ -1007,98 +1015,98 @@ packages:
dependency: "direct main"
description:
name: scrollable_positioned_list
sha256: ca7fcaa743db712d4f7b1580526f494d0093c77a721a65705ee51fbeac7a2bd3
sha256: "1b54d5f1329a1e263269abc9e2543d90806131aa14fe7c6062a8054d57249287"
url: "https://pub.dev"
source: hosted
version: "0.3.5"
version: "0.3.8"
share_plus:
dependency: "direct main"
description:
name: share_plus
sha256: "8c6892037b1824e2d7e8f59d54b3105932899008642e6372e5079c6939b4b625"
sha256: b1f15232d41e9701ab2f04181f21610c36c83a12ae426b79b4bd011c567934b1
url: "https://pub.dev"
source: hosted
version: "6.3.1"
version: "6.3.4"
share_plus_platform_interface:
dependency: transitive
description:
name: share_plus_platform_interface
sha256: "82ddd4ab9260c295e6e39612d4ff00390b9a7a21f1bb1da771e2f232d80ab8a1"
sha256: "0c6e61471bd71b04a138b8b588fa388e66d8b005e6f2deda63371c5c505a0981"
url: "https://pub.dev"
source: hosted
version: "3.2.0"
version: "3.2.1"
shared_preferences:
dependency: transitive
description:
name: shared_preferences
sha256: ee6257848f822b8481691f20c3e6d2bfee2e9eccb2a3d249907fcfb198c55b41
sha256: "16d3fb6b3692ad244a695c0183fca18cf81fd4b821664394a781de42386bf022"
url: "https://pub.dev"
source: hosted
version: "2.0.18"
version: "2.1.1"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: a51a4f9375097f94df1c6e0a49c0374440d31ab026b59d58a7e7660675879db4
sha256: "6478c6bbbecfe9aced34c483171e90d7c078f5883558b30ec3163cf18402c749"
url: "https://pub.dev"
source: hosted
version: "2.0.16"
version: "2.1.4"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "6b84fdf06b32bb336f972d373cd38b63734f3461ba56ac2ba01b56d052796259"
sha256: e014107bb79d6d3297196f4f2d0db54b5d1f85b8ea8ff63b8e8b391a02700feb
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.2.2"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: d7fb71e6e20cd3dfffcc823a28da3539b392e53ed5fc5c2b90b55fdaa8a7e8fa
sha256: "9d387433ca65717bbf1be88f4d5bb18f10508917a8fa2fb02e0fd0d7479a9afa"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.2.0"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "824bfd02713e37603b2bdade0842e47d56e7db32b1dcdd1cae533fb88e2913fc"
sha256: fb5cf25c0235df2d0640ac1b1174f6466bd311f621574997ac59018a6664548d
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.2.0"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: "6737b757e49ba93de2a233df229d0b6a87728cea1684da828cbc718b65dcf9d7"
sha256: "74083203a8eae241e0de4a0d597dbedab3b8fef5563f33cf3c12d7e93c655ca5"
url: "https://pub.dev"
source: hosted
version: "2.0.5"
version: "2.1.0"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: bd014168e8484837c39ef21065b78f305810ceabc1d4f90be6e3b392ce81b46d
sha256: "5e588e2efef56916a3b229c3bfe81e6a525665a454519ca51dbcc4236a274173"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.2.0"
shelf:
dependency: transitive
description:
name: shelf
sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
url: "https://pub.dev"
source: hosted
version: "1.4.0"
version: "1.4.1"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8
sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
url: "https://pub.dev"
source: hosted
version: "1.0.3"
version: "1.0.4"
sky_engine:
dependency: transitive
description: flutter
@ -1108,26 +1116,26 @@ packages:
dependency: "direct main"
description:
name: socket_io_client
sha256: a9c589d3fe2658506be38ddb36f23348daab73a00ff1645533669d07a5111cfc
sha256: "5d2a0a12c2a4a5f48d14e5b6aef7db78d3b425a9b084071059fa54bd12d2576c"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "2.0.2"
socket_io_common:
dependency: transitive
description:
name: socket_io_common
sha256: "5a218a784df4d1927ae713e17af619caa736cb2ebac287c59e4e24228b22da29"
sha256: "2ab92f8ff3ebbd4b353bf4a98bee45cc157e3255464b2f90f66e09c4472047eb"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
version: "2.0.3"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
sha256: "373f96cf5a8744bc9816c1ff41cf5391bbdbe3d7a96fe98c622b6738a8a7bd33"
url: "https://pub.dev"
source: hosted
version: "1.2.6"
version: "1.3.2"
source_span:
dependency: transitive
description:
@ -1140,18 +1148,18 @@ packages:
dependency: transitive
description:
name: sqflite
sha256: "78324387dc81df14f78df06019175a86a2ee0437624166c382e145d0a7fd9a4f"
sha256: b4d6710e1200e96845747e37338ea8a819a12b51689a3bcf31eff0003b37a0b9
url: "https://pub.dev"
source: hosted
version: "2.2.4+1"
version: "2.2.8+4"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: bfd6973aaeeb93475bc0d875ac9aefddf7965ef22ce09790eb963992ffc5183f
sha256: e77abf6ff961d69dfef41daccbb66b51e9983cdd5cb35bf30733598057401555
url: "https://pub.dev"
source: hosted
version: "2.4.2+2"
version: "2.4.5"
stack_trace:
dependency: transitive
description:
@ -1204,10 +1212,10 @@ packages:
dependency: transitive
description:
name: synchronized
sha256: "33b31b6beb98100bf9add464a36a8dd03eb10c7a8cf15aeec535e9b054aaf04b"
sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
version: "3.1.0"
term_glyph:
dependency: transitive
description:
@ -1252,10 +1260,10 @@ packages:
dependency: transitive
description:
name: typed_data
sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
url: "https://pub.dev"
source: hosted
version: "1.3.1"
version: "1.3.2"
unicode:
dependency: transitive
description:
@ -1276,42 +1284,42 @@ packages:
dependency: "direct main"
description:
name: url_launcher
sha256: "75f2846facd11168d007529d6cd8fcb2b750186bea046af9711f10b907e1587e"
sha256: eb1e00ab44303d50dd487aab67ebc575456c146c6af44422f9c13889984c00f3
url: "https://pub.dev"
source: hosted
version: "6.1.10"
version: "6.1.11"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: "1f4d9ebe86f333c15d318f81dcdc08b01d45da44af74552608455ebdc08d9732"
sha256: "1a5848f598acc5b7d8f7c18b8cb834ab667e59a13edc3c93e9d09cf38cc6bc87"
url: "https://pub.dev"
source: hosted
version: "6.0.24"
version: "6.0.34"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: c9cd648d2f7ab56968e049d4e9116f96a85517f1dd806b96a86ea1018a3a82e5
sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2"
url: "https://pub.dev"
source: hosted
version: "6.1.1"
version: "6.1.4"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
sha256: e29039160ab3730e42f3d811dc2a6d5f2864b90a70fb765ea60144b03307f682
sha256: "207f4ddda99b95b4d4868320a352d374b0b7e05eefad95a4a26f57da413443f5"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
version: "3.0.5"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
sha256: "2dddb3291a57b074dade66b5e07e64401dd2487caefd4e9e2f467138d8c7eb06"
sha256: "91ee3e75ea9dadf38036200c5d3743518f4a5eb77a8d13fda1ee5764373f185e"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
version: "3.0.5"
url_launcher_platform_interface:
dependency: transitive
description:
@ -1324,18 +1332,18 @@ packages:
dependency: transitive
description:
name: url_launcher_web
sha256: "574cfbe2390666003c3a1d129bdc4574aaa6728f0c00a4829a81c316de69dd9b"
sha256: "81fe91b6c4f84f222d186a9d23c73157dc4c8e1c71489c4d08be1ad3b228f1aa"
url: "https://pub.dev"
source: hosted
version: "2.0.15"
version: "2.0.16"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "97c9067950a0d09cbd93e2e3f0383d1403989362b97102fbf446473a48079a4b"
sha256: "254708f17f7c20a9c8c471f67d86d76d4a3f9c1591aad1e15292008aceb82771"
url: "https://pub.dev"
source: hosted
version: "3.0.4"
version: "3.0.6"
uuid:
dependency: transitive
description:
@ -1356,42 +1364,42 @@ packages:
dependency: "direct main"
description:
name: video_player
sha256: "6cec15c21974282994577ffcfb5b42e64a699d38583138ec8dcb3d0a6902a41c"
sha256: de95f0e9405f29b5582573d4166132e71f83b3158aac14e8ee5767a54f4f1fbd
url: "https://pub.dev"
source: hosted
version: "2.5.2"
version: "2.6.1"
video_player_android:
dependency: transitive
description:
name: video_player_android
sha256: "0fc42778d794465f12456ccdade3e729e4339c8a112f9e58d170dc00f17b75f2"
sha256: ae1c7d9a71c236a1bf9e567bd7ed4c90887e389a5f233b2192593f7f7395005c
url: "https://pub.dev"
source: hosted
version: "2.3.11"
version: "2.4.8"
video_player_avfoundation:
dependency: transitive
description:
name: video_player_avfoundation
sha256: "5df5411ff9d316f1dcbfee284e9838aa686e314f2a722b15c02cb7ce40ef9446"
sha256: "4c274e439f349a0ee5cb3c42978393ede173a443b98f50de6ffe6900eaa19216"
url: "https://pub.dev"
source: hosted
version: "2.3.9"
version: "2.4.6"
video_player_platform_interface:
dependency: transitive
description:
name: video_player_platform_interface
sha256: "72ba04ad0eff76123c6d782ac46621cb8be476a89c33c89173fce982b6ec049b"
sha256: a8c4dcae2a7a6e7cc1d7f9808294d968eca1993af34a98e95b9bdfa959bec684
url: "https://pub.dev"
source: hosted
version: "6.0.2"
version: "6.1.0"
video_player_web:
dependency: transitive
description:
name: video_player_web
sha256: d635bb2834f2b14cfd52c7fc9307a95dffbe768d116dd6047a4ecbba203289c8
sha256: "44ce41424d104dfb7cf6982cc6b84af2b007a24d126406025bf40de5d481c74c"
url: "https://pub.dev"
source: hosted
version: "2.0.14"
version: "2.0.16"
vm_service:
dependency: transitive
description:
@ -1444,18 +1452,18 @@ packages:
dependency: transitive
description:
name: watcher
sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0"
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
version: "1.1.0"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
url: "https://pub.dev"
source: hosted
version: "2.3.0"
version: "2.4.0"
webdriver:
dependency: transitive
description:
@ -1468,10 +1476,10 @@ packages:
dependency: transitive
description:
name: win32
sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46
sha256: a6f0236dbda0f63aa9a25ad1ff9a9d8a4eaaa5012da0dc59d21afdb1dc361ca4
url: "https://pub.dev"
source: hosted
version: "3.1.3"
version: "3.1.4"
wkt_parser:
dependency: transitive
description:
@ -1492,10 +1500,10 @@ packages:
dependency: transitive
description:
name: xml
sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5"
sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84"
url: "https://pub.dev"
source: hosted
version: "6.2.2"
version: "6.3.0"
xxh3:
dependency: transitive
description:
@ -1508,10 +1516,10 @@ packages:
dependency: transitive
description:
name: yaml
sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370"
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
version: "3.1.2"
sdks:
dart: ">=3.0.0-0 <4.0.0"
dart: ">=3.0.0 <4.0.0"
flutter: ">=3.3.0"