You've already forked immich
							
							
				mirror of
				https://github.com/immich-app/immich.git
				synced 2025-10-31 00:18:28 +02:00 
			
		
		
		
	feat(mobile): drift people page
This commit is contained in:
		
							
								
								
									
										12
									
								
								mobile/lib/domain/services/person.service.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								mobile/lib/domain/services/person.service.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| import 'package:immich_mobile/domain/models/person.model.dart'; | ||||
| import 'package:immich_mobile/infrastructure/repositories/person.repository.dart'; | ||||
|  | ||||
| class PersonService { | ||||
|   final DriftPersonRepository _repository; | ||||
|  | ||||
|   const PersonService(this._repository); | ||||
|  | ||||
|   Future<List<Person>> getAll(String userId) { | ||||
|     return _repository.getAll(userId); | ||||
|   } | ||||
| } | ||||
| @@ -9,7 +9,7 @@ class DriftPersonRepository extends DriftDatabaseRepository { | ||||
|  | ||||
|   Future<List<Person>> getAll(String userId) { | ||||
|     final query = _db.personEntity.select() | ||||
|       ..where((e) => e.ownerId.equals(userId)); | ||||
|       ..where((row) => row.ownerId.equals(userId) & row.isHidden.equals(false)); | ||||
|  | ||||
|     return query.map((person) { | ||||
|       return person.toDto(); | ||||
|   | ||||
| @@ -1,32 +0,0 @@ | ||||
| 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/extensions/asyncvalue_extensions.dart'; | ||||
| import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart'; | ||||
| import 'package:immich_mobile/providers/search/all_motion_photos.provider.dart'; | ||||
|  | ||||
| @RoutePage() | ||||
| class AllMotionPhotosPage extends HookConsumerWidget { | ||||
|   const AllMotionPhotosPage({super.key}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final motionPhotos = ref.watch(allMotionPhotosProvider); | ||||
|  | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|         title: const Text('search_page_motion_photos').tr(), | ||||
|         leading: IconButton( | ||||
|           onPressed: () => context.maybePop(), | ||||
|           icon: const Icon(Icons.arrow_back_ios_rounded), | ||||
|         ), | ||||
|       ), | ||||
|       body: motionPhotos.widgetWhen( | ||||
|         onData: (assets) => ImmichAssetGrid( | ||||
|           assets: assets, | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -1,38 +0,0 @@ | ||||
| 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/extensions/asyncvalue_extensions.dart'; | ||||
| import 'package:immich_mobile/models/search/search_curated_content.model.dart'; | ||||
| import 'package:immich_mobile/providers/search/people.provider.dart'; | ||||
| import 'package:immich_mobile/widgets/search/explore_grid.dart'; | ||||
|  | ||||
| @RoutePage() | ||||
| class AllPeoplePage extends HookConsumerWidget { | ||||
|   const AllPeoplePage({super.key}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final curatedPeople = ref.watch(getAllPeopleProvider); | ||||
|  | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|         title: const Text( | ||||
|           'people', | ||||
|         ).tr(), | ||||
|         leading: IconButton( | ||||
|           onPressed: () => context.maybePop(), | ||||
|           icon: const Icon(Icons.arrow_back_ios_rounded), | ||||
|         ), | ||||
|       ), | ||||
|       body: curatedPeople.widgetWhen( | ||||
|         onData: (people) => ExploreGrid( | ||||
|           isPeople: true, | ||||
|           curatedContent: people | ||||
|               .map((e) => SearchCuratedContent(label: e.name, id: e.id)) | ||||
|               .toList(), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -1,36 +0,0 @@ | ||||
| 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/extensions/asyncvalue_extensions.dart'; | ||||
| import 'package:immich_mobile/models/search/search_curated_content.model.dart'; | ||||
| import 'package:immich_mobile/providers/search/search_page_state.provider.dart'; | ||||
| import 'package:immich_mobile/widgets/search/explore_grid.dart'; | ||||
|  | ||||
| @RoutePage() | ||||
| class AllPlacesPage extends HookConsumerWidget { | ||||
|   const AllPlacesPage({super.key}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     AsyncValue<List<SearchCuratedContent>> places = | ||||
|         ref.watch(getAllPlacesProvider); | ||||
|  | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|         title: const Text( | ||||
|           'places', | ||||
|         ).tr(), | ||||
|         leading: IconButton( | ||||
|           onPressed: () => context.maybePop(), | ||||
|           icon: const Icon(Icons.arrow_back_ios_rounded), | ||||
|         ), | ||||
|       ), | ||||
|       body: places.widgetWhen( | ||||
|         onData: (data) => ExploreGrid( | ||||
|           curatedContent: data, | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -8,7 +8,6 @@ import 'package:immich_mobile/pages/common/large_leading_tile.dart'; | ||||
| import 'package:immich_mobile/presentation/widgets/images/local_album_thumbnail.widget.dart'; | ||||
| import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; | ||||
| import 'package:immich_mobile/routing/router.dart'; | ||||
| import 'package:immich_mobile/widgets/common/local_album_sliver_app_bar.dart'; | ||||
|  | ||||
| @RoutePage() | ||||
| class DriftLocalAlbumsPage extends StatelessWidget { | ||||
| @@ -19,7 +18,7 @@ class DriftLocalAlbumsPage extends StatelessWidget { | ||||
|     return const Scaffold( | ||||
|       body: CustomScrollView( | ||||
|         slivers: [ | ||||
|           LocalAlbumsSliverAppBar(), | ||||
|           _LocalAlbumsSliverAppBar(), | ||||
|           _AlbumList(), | ||||
|         ], | ||||
|       ), | ||||
| @@ -27,6 +26,28 @@ class DriftLocalAlbumsPage extends StatelessWidget { | ||||
|   } | ||||
| } | ||||
|  | ||||
| class _LocalAlbumsSliverAppBar extends StatelessWidget { | ||||
|   const _LocalAlbumsSliverAppBar(); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return SliverAppBar( | ||||
|       floating: true, | ||||
|       pinned: true, | ||||
|       snap: false, | ||||
|       backgroundColor: context.colorScheme.surfaceContainer, | ||||
|       shape: const RoundedRectangleBorder( | ||||
|         borderRadius: BorderRadius.all(Radius.circular(5)), | ||||
|       ), | ||||
|       automaticallyImplyLeading: true, | ||||
|       centerTitle: true, | ||||
|       title: Text( | ||||
|         "on_this_device".t(context: context), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class _AlbumList extends ConsumerWidget { | ||||
|   const _AlbumList(); | ||||
|  | ||||
|   | ||||
| @@ -17,7 +17,7 @@ class DriftVideoPage extends StatelessWidget { | ||||
|           (ref) { | ||||
|             final user = ref.watch(currentUserProvider); | ||||
|             if (user == null) { | ||||
|               throw Exception('User must be logged in to video'); | ||||
|               throw Exception('User must be logged in to access video'); | ||||
|             } | ||||
|  | ||||
|             final timelineService = | ||||
|   | ||||
| @@ -182,7 +182,7 @@ class _PeopleCollectionCard extends ConsumerWidget { | ||||
|         final size = context.width * widthFactor - 20.0; | ||||
|  | ||||
|         return GestureDetector( | ||||
|           onTap: () => context.pushRoute(const PeopleCollectionRoute()), | ||||
|           onTap: () => context.pushRoute(const DriftPeopleRoute()), | ||||
|           child: Column( | ||||
|             crossAxisAlignment: CrossAxisAlignment.start, | ||||
|             children: [ | ||||
|   | ||||
							
								
								
									
										222
									
								
								mobile/lib/presentation/pages/drift_people.page.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								mobile/lib/presentation/pages/drift_people.page.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,222 @@ | ||||
| import 'package:auto_route/auto_route.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/extensions/build_context_extensions.dart'; | ||||
| import 'package:immich_mobile/extensions/translate_extensions.dart'; | ||||
| import 'package:immich_mobile/providers/infrastructure/person.provider.dart'; | ||||
| import 'package:immich_mobile/providers/user.provider.dart'; | ||||
| import 'package:immich_mobile/routing/router.dart'; | ||||
| import 'package:immich_mobile/services/api.service.dart'; | ||||
| import 'package:immich_mobile/utils/image_url_builder.dart'; | ||||
| import 'package:immich_mobile/widgets/common/search_field.dart'; | ||||
| import 'package:immich_mobile/widgets/search/person_name_edit_form.dart'; | ||||
|  | ||||
| @RoutePage() | ||||
| class DriftPeoplePage extends StatelessWidget { | ||||
|   const DriftPeoplePage({super.key}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     final ValueNotifier<String?> search = ValueNotifier(null); | ||||
|  | ||||
|     return Scaffold( | ||||
|       body: LayoutBuilder( | ||||
|         builder: (context, constraints) { | ||||
|           final isTablet = constraints.maxWidth > 600; | ||||
|           final isPortrait = context.orientation == Orientation.portrait; | ||||
|  | ||||
|           return CustomScrollView( | ||||
|             slivers: [ | ||||
|               _PeopleSliverAppBar(search: search), | ||||
|               _PeopleGrid( | ||||
|                 search: search, | ||||
|                 isTablet: isTablet, | ||||
|                 isPortrait: isPortrait, | ||||
|               ), | ||||
|             ], | ||||
|           ); | ||||
|         }, | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class _PeopleSliverAppBar extends StatelessWidget { | ||||
|   const _PeopleSliverAppBar({required this.search}); | ||||
|  | ||||
|   final ValueNotifier<String?> search; | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     final searchFocusNode = FocusNode(); | ||||
|  | ||||
|     return SliverAppBar( | ||||
|       floating: true, | ||||
|       pinned: true, | ||||
|       snap: false, | ||||
|       backgroundColor: context.colorScheme.surfaceContainer, | ||||
|       shape: const RoundedRectangleBorder( | ||||
|         borderRadius: BorderRadius.all(Radius.circular(5)), | ||||
|       ), | ||||
|       automaticallyImplyLeading: search.value == null, | ||||
|       centerTitle: true, | ||||
|       title: search.value != null | ||||
|           ? SearchField( | ||||
|               focusNode: searchFocusNode, | ||||
|               onTapOutside: (_) => searchFocusNode.unfocus(), | ||||
|               onChanged: (value) => search.value = value, | ||||
|               filled: true, | ||||
|               hintText: 'filter_people'.t(context: context), | ||||
|               autofocus: true, | ||||
|             ) | ||||
|           : Text('people'.t(context: context)), | ||||
|       actions: [ | ||||
|         IconButton( | ||||
|           icon: Icon(search.value != null ? Icons.close : Icons.search), | ||||
|           onPressed: () { | ||||
|             search.value = search.value == null ? '' : null; | ||||
|           }, | ||||
|         ), | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class _PeopleGrid extends ConsumerWidget { | ||||
|   const _PeopleGrid({ | ||||
|     required this.search, | ||||
|     required this.isTablet, | ||||
|     required this.isPortrait, | ||||
|   }); | ||||
|  | ||||
|   final ValueNotifier<String?> search; | ||||
|   final bool isTablet; | ||||
|   final bool isPortrait; | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final user = ref.watch(currentUserProvider); | ||||
|     if (user == null) { | ||||
|       throw Exception('User must be logged in to access people'); | ||||
|     } | ||||
|  | ||||
|     final people = ref.watch(personProvider(user.id)); | ||||
|  | ||||
|     // TODO: migrate to new modal widget and update name in SQLite | ||||
|     showNameEditModel( | ||||
|       String personId, | ||||
|       String personName, | ||||
|     ) { | ||||
|       return showDialog( | ||||
|         context: context, | ||||
|         useRootNavigator: false, | ||||
|         builder: (BuildContext context) { | ||||
|           return PersonNameEditForm(personId: personId, personName: personName); | ||||
|         }, | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     return SliverSafeArea( | ||||
|       sliver: people.when( | ||||
|         loading: () => const SliverToBoxAdapter( | ||||
|           child: Center( | ||||
|             child: Padding( | ||||
|               padding: EdgeInsets.all(20.0), | ||||
|               child: CircularProgressIndicator(), | ||||
|             ), | ||||
|           ), | ||||
|         ), | ||||
|         error: (error, stack) => SliverToBoxAdapter( | ||||
|           child: Center( | ||||
|             child: Padding( | ||||
|               padding: const EdgeInsets.all(20.0), | ||||
|               child: Text( | ||||
|                 'Error loading people: $error, stack: $stack', | ||||
|                 style: TextStyle( | ||||
|                   color: context.colorScheme.error, | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|           ), | ||||
|         ), | ||||
|         data: (people) { | ||||
|           if (search.value != null) { | ||||
|             people = people | ||||
|                 .where( | ||||
|                   (person) => person.name | ||||
|                       .toLowerCase() | ||||
|                       .contains(search.value!.toLowerCase()), | ||||
|                 ) | ||||
|                 .toList(); | ||||
|           } | ||||
|  | ||||
|           return SliverGrid.builder( | ||||
|             gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( | ||||
|               crossAxisCount: isTablet ? 6 : 3, | ||||
|               childAspectRatio: 0.85, | ||||
|               mainAxisSpacing: isPortrait && isTablet ? 36 : 0, | ||||
|             ), | ||||
|             itemCount: people.length, | ||||
|             itemBuilder: (context, index) { | ||||
|               final person = people[index]; | ||||
|  | ||||
|               return Padding( | ||||
|                 padding: const EdgeInsets.symmetric(vertical: 32), | ||||
|                 child: Column( | ||||
|                   children: [ | ||||
|                     GestureDetector( | ||||
|                       onTap: () { | ||||
|                         context.pushRoute( | ||||
|                           // TODO: migrate to drift after face sync | ||||
|                           PersonResultRoute( | ||||
|                             personId: person.id, | ||||
|                             personName: person.name, | ||||
|                           ), | ||||
|                         ); | ||||
|                       }, | ||||
|                       child: Material( | ||||
|                         shape: const CircleBorder(side: BorderSide.none), | ||||
|                         elevation: 3, | ||||
|                         child: CircleAvatar( | ||||
|                           maxRadius: isTablet ? 120 / 2 : 96 / 2, | ||||
|                           // TODO: migrate to face asset id after face sync | ||||
|                           backgroundImage: NetworkImage( | ||||
|                             getFaceThumbnailUrl(person.id), | ||||
|                             headers: ApiService.getRequestHeaders(), | ||||
|                           ), | ||||
|                         ), | ||||
|                       ), | ||||
|                     ), | ||||
|                     const SizedBox(height: 12), | ||||
|                     GestureDetector( | ||||
|                       onTap: () => showNameEditModel(person.id, person.name), | ||||
|                       child: person.name.isEmpty | ||||
|                           ? Text( | ||||
|                               'add_a_name'.t(context: context), | ||||
|                               style: context.textTheme.titleSmall?.copyWith( | ||||
|                                 fontWeight: FontWeight.w500, | ||||
|                                 color: context.colorScheme.primary, | ||||
|                               ), | ||||
|                             ) | ||||
|                           : Padding( | ||||
|                               padding: | ||||
|                                   const EdgeInsets.symmetric(horizontal: 16.0), | ||||
|                               child: Text( | ||||
|                                 person.name, | ||||
|                                 overflow: TextOverflow.ellipsis, | ||||
|                                 style: context.textTheme.titleSmall?.copyWith( | ||||
|                                   fontWeight: FontWeight.w500, | ||||
|                                 ), | ||||
|                               ), | ||||
|                             ), | ||||
|                     ), | ||||
|                   ], | ||||
|                 ), | ||||
|               ); | ||||
|             }, | ||||
|           ); | ||||
|         }, | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -1,7 +1,18 @@ | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/domain/models/person.model.dart'; | ||||
| import 'package:immich_mobile/domain/services/person.service.dart'; | ||||
| import 'package:immich_mobile/infrastructure/repositories/person.repository.dart'; | ||||
| import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; | ||||
|  | ||||
| final driftPersonProvider = Provider<DriftPersonRepository>( | ||||
| final driftPersonRepository = Provider<DriftPersonRepository>( | ||||
|   (ref) => DriftPersonRepository(ref.watch(driftProvider)), | ||||
| ); | ||||
|  | ||||
| final driftPersonServiceProvider = Provider<PersonService>( | ||||
|   (ref) => PersonService(ref.watch(driftPersonRepository)), | ||||
| ); | ||||
|  | ||||
| final personProvider = FutureProvider.family<List<Person>, String>( | ||||
|   (ref, userId) => | ||||
|       PersonService(ref.watch(driftPersonRepository)).getAll(userId), | ||||
| ); | ||||
|   | ||||
| @@ -59,9 +59,6 @@ import 'package:immich_mobile/pages/login/login.page.dart'; | ||||
| import 'package:immich_mobile/pages/onboarding/permission_onboarding.page.dart'; | ||||
| import 'package:immich_mobile/pages/photos/memory.page.dart'; | ||||
| import 'package:immich_mobile/pages/photos/photos.page.dart'; | ||||
| import 'package:immich_mobile/pages/search/all_motion_videos.page.dart'; | ||||
| import 'package:immich_mobile/pages/search/all_people.page.dart'; | ||||
| import 'package:immich_mobile/pages/search/all_places.page.dart'; | ||||
| import 'package:immich_mobile/pages/search/all_videos.page.dart'; | ||||
| import 'package:immich_mobile/pages/search/map/map.page.dart'; | ||||
| import 'package:immich_mobile/pages/search/map/map_location_picker.page.dart'; | ||||
| @@ -87,6 +84,7 @@ import 'package:immich_mobile/presentation/pages/drift_library.page.dart'; | ||||
| import 'package:immich_mobile/presentation/pages/drift_asset_selection_timeline.page.dart'; | ||||
| import 'package:immich_mobile/presentation/pages/drift_create_album.page.dart'; | ||||
| import 'package:immich_mobile/presentation/pages/drift_memory.page.dart'; | ||||
| import 'package:immich_mobile/presentation/pages/drift_people.page.dart'; | ||||
| import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.page.dart'; | ||||
| import 'package:immich_mobile/providers/api.provider.dart'; | ||||
| import 'package:immich_mobile/providers/gallery_permission.provider.dart'; | ||||
| @@ -210,10 +208,6 @@ class AppRouter extends RootStackRouter { | ||||
|       page: BackupControllerRoute.page, | ||||
|       guards: [_authGuard, _duplicateGuard, _backupPermissionGuard], | ||||
|     ), | ||||
|     AutoRoute( | ||||
|       page: AllPlacesRoute.page, | ||||
|       guards: [_authGuard, _duplicateGuard], | ||||
|     ), | ||||
|     AutoRoute( | ||||
|       page: CreateAlbumRoute.page, | ||||
|       guards: [_authGuard, _duplicateGuard], | ||||
| @@ -226,11 +220,6 @@ class AppRouter extends RootStackRouter { | ||||
|       guards: [_authGuard, _duplicateGuard], | ||||
|       transitionsBuilder: TransitionsBuilders.slideLeft, | ||||
|     ), | ||||
|     AutoRoute(page: AllVideosRoute.page, guards: [_authGuard, _duplicateGuard]), | ||||
|     AutoRoute( | ||||
|       page: AllMotionPhotosRoute.page, | ||||
|       guards: [_authGuard, _duplicateGuard], | ||||
|     ), | ||||
|     AutoRoute( | ||||
|       page: RecentlyTakenRoute.page, | ||||
|       guards: [_authGuard, _duplicateGuard], | ||||
| @@ -294,7 +283,6 @@ class AppRouter extends RootStackRouter { | ||||
|       page: PersonResultRoute.page, | ||||
|       guards: [_authGuard, _duplicateGuard], | ||||
|     ), | ||||
|     AutoRoute(page: AllPeopleRoute.page, guards: [_authGuard, _duplicateGuard]), | ||||
|     AutoRoute(page: MemoryRoute.page, guards: [_authGuard, _duplicateGuard]), | ||||
|     AutoRoute(page: MapRoute.page, guards: [_authGuard, _duplicateGuard]), | ||||
|     AutoRoute( | ||||
| @@ -453,7 +441,10 @@ class AppRouter extends RootStackRouter { | ||||
|       page: DriftCreateAlbumRoute.page, | ||||
|       guards: [_authGuard, _duplicateGuard], | ||||
|     ), | ||||
|  | ||||
|     AutoRoute( | ||||
|       page: DriftPeopleRoute.page, | ||||
|       guards: [_authGuard, _duplicateGuard], | ||||
|     ), | ||||
|     // required to handle all deeplinks in deep_link.service.dart | ||||
|     // auto_route_library#1722 | ||||
|     RedirectRoute(path: '*', redirectTo: '/'), | ||||
|   | ||||
| @@ -270,54 +270,6 @@ class AlbumsRoute extends PageRouteInfo<void> { | ||||
|   ); | ||||
| } | ||||
|  | ||||
| /// generated route for | ||||
| /// [AllMotionPhotosPage] | ||||
| class AllMotionPhotosRoute extends PageRouteInfo<void> { | ||||
|   const AllMotionPhotosRoute({List<PageRouteInfo>? children}) | ||||
|       : super(AllMotionPhotosRoute.name, initialChildren: children); | ||||
|  | ||||
|   static const String name = 'AllMotionPhotosRoute'; | ||||
|  | ||||
|   static PageInfo page = PageInfo( | ||||
|     name, | ||||
|     builder: (data) { | ||||
|       return const AllMotionPhotosPage(); | ||||
|     }, | ||||
|   ); | ||||
| } | ||||
|  | ||||
| /// generated route for | ||||
| /// [AllPeoplePage] | ||||
| class AllPeopleRoute extends PageRouteInfo<void> { | ||||
|   const AllPeopleRoute({List<PageRouteInfo>? children}) | ||||
|       : super(AllPeopleRoute.name, initialChildren: children); | ||||
|  | ||||
|   static const String name = 'AllPeopleRoute'; | ||||
|  | ||||
|   static PageInfo page = PageInfo( | ||||
|     name, | ||||
|     builder: (data) { | ||||
|       return const AllPeoplePage(); | ||||
|     }, | ||||
|   ); | ||||
| } | ||||
|  | ||||
| /// generated route for | ||||
| /// [AllPlacesPage] | ||||
| class AllPlacesRoute extends PageRouteInfo<void> { | ||||
|   const AllPlacesRoute({List<PageRouteInfo>? children}) | ||||
|       : super(AllPlacesRoute.name, initialChildren: children); | ||||
|  | ||||
|   static const String name = 'AllPlacesRoute'; | ||||
|  | ||||
|   static PageInfo page = PageInfo( | ||||
|     name, | ||||
|     builder: (data) { | ||||
|       return const AllPlacesPage(); | ||||
|     }, | ||||
|   ); | ||||
| } | ||||
|  | ||||
| /// generated route for | ||||
| /// [AllVideosPage] | ||||
| class AllVideosRoute extends PageRouteInfo<void> { | ||||
| @@ -853,6 +805,22 @@ class DriftPartnerDetailRouteArgs { | ||||
|   } | ||||
| } | ||||
|  | ||||
| /// generated route for | ||||
| /// [DriftPeoplePage] | ||||
| class DriftPeopleRoute extends PageRouteInfo<void> { | ||||
|   const DriftPeopleRoute({List<PageRouteInfo>? children}) | ||||
|       : super(DriftPeopleRoute.name, initialChildren: children); | ||||
|  | ||||
|   static const String name = 'DriftPeopleRoute'; | ||||
|  | ||||
|   static PageInfo page = PageInfo( | ||||
|     name, | ||||
|     builder: (data) { | ||||
|       return const DriftPeoplePage(); | ||||
|     }, | ||||
|   ); | ||||
| } | ||||
|  | ||||
| /// generated route for | ||||
| /// [DriftRecentlyTakenPage] | ||||
| class DriftRecentlyTakenRoute extends PageRouteInfo<void> { | ||||
|   | ||||
| @@ -1,25 +0,0 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:immich_mobile/extensions/build_context_extensions.dart'; | ||||
| import 'package:immich_mobile/extensions/translate_extensions.dart'; | ||||
|  | ||||
| class LocalAlbumsSliverAppBar extends StatelessWidget { | ||||
|   const LocalAlbumsSliverAppBar({super.key}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return SliverAppBar( | ||||
|       floating: true, | ||||
|       pinned: true, | ||||
|       snap: false, | ||||
|       backgroundColor: context.colorScheme.surfaceContainer, | ||||
|       shape: const RoundedRectangleBorder( | ||||
|         borderRadius: BorderRadius.all(Radius.circular(5)), | ||||
|       ), | ||||
|       automaticallyImplyLeading: true, | ||||
|       centerTitle: true, | ||||
|       title: Text( | ||||
|         "on_this_device".t(context: context), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user