forked from Cutlery/immich
		
	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:
		
							parent
							
								
									00f65a53dd
								
							
						
					
					
						commit
						0d0866d5d9
					
				@ -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"
 | 
			
		||||
}
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										44
									
								
								mobile/lib/modules/search/providers/people.provider.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								mobile/lib/modules/search/providers/people.provider.dart
									
									
									
									
									
										Normal 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);
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										56
									
								
								mobile/lib/modules/search/services/person.service.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								mobile/lib/modules/search/services/person.service.dart
									
									
									
									
									
										Normal 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;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										114
									
								
								mobile/lib/modules/search/ui/curated_people_row.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								mobile/lib/modules/search/ui/curated_people_row.dart
									
									
									
									
									
										Normal 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,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -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}'),
 | 
			
		||||
                  );
 | 
			
		||||
          },
 | 
			
		||||
        );
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										82
									
								
								mobile/lib/modules/search/ui/person_name_edit_form.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								mobile/lib/modules/search/ui/person_name_edit_form.dart
									
									
									
									
									
										Normal 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,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								mobile/lib/modules/search/ui/search_row_title.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								mobile/lib/modules/search/ui/search_row_title.dart
									
									
									
									
									
										Normal 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(),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										51
									
								
								mobile/lib/modules/search/views/all_people_page.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								mobile/lib/modules/search/views/all_people_page.dart
									
									
									
									
									
										Normal 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(),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										152
									
								
								mobile/lib/modules/search/views/person_result_page.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								mobile/lib/modules/search/views/person_result_page.dart
									
									
									
									
									
										Normal 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(),
 | 
			
		||||
                          ),
 | 
			
		||||
                        ],
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
          ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -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:
 | 
			
		||||
 | 
			
		||||
@ -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 {
 | 
			
		||||
 | 
			
		||||
@ -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> {
 | 
			
		||||
 | 
			
		||||
@ -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') {
 | 
			
		||||
 | 
			
		||||
@ -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 {
 | 
			
		||||
 | 
			
		||||
@ -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';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
  ),
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
@ -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"
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user