mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:17:11 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			131 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			131 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
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/build_context_extensions.dart';
 | 
						|
import 'package:immich_mobile/providers/infrastructure/people.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/utils/people.utils.dart';
 | 
						|
import 'package:immich_mobile/widgets/common/search_field.dart';
 | 
						|
 | 
						|
@RoutePage()
 | 
						|
class DriftPeopleCollectionPage extends ConsumerStatefulWidget {
 | 
						|
  const DriftPeopleCollectionPage({super.key});
 | 
						|
 | 
						|
  @override
 | 
						|
  ConsumerState<DriftPeopleCollectionPage> createState() => _DriftPeopleCollectionPageState();
 | 
						|
}
 | 
						|
 | 
						|
class _DriftPeopleCollectionPageState extends ConsumerState<DriftPeopleCollectionPage> {
 | 
						|
  final FocusNode _formFocus = FocusNode();
 | 
						|
  String? _search;
 | 
						|
 | 
						|
  @override
 | 
						|
  void dispose() {
 | 
						|
    _formFocus.dispose();
 | 
						|
    super.dispose();
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context) {
 | 
						|
    final people = ref.watch(driftGetAllPeopleProvider);
 | 
						|
    final headers = ApiService.getRequestHeaders();
 | 
						|
 | 
						|
    return LayoutBuilder(
 | 
						|
      builder: (context, constraints) {
 | 
						|
        final isTablet = constraints.maxWidth > 600;
 | 
						|
        final isPortrait = context.orientation == Orientation.portrait;
 | 
						|
 | 
						|
        return Scaffold(
 | 
						|
          appBar: AppBar(
 | 
						|
            automaticallyImplyLeading: _search == null,
 | 
						|
            title: _search != null
 | 
						|
                ? SearchField(
 | 
						|
                    focusNode: _formFocus,
 | 
						|
                    onTapOutside: (_) => _formFocus.unfocus(),
 | 
						|
                    onChanged: (value) => setState(() => _search = value),
 | 
						|
                    filled: true,
 | 
						|
                    hintText: 'filter_people'.tr(),
 | 
						|
                    autofocus: true,
 | 
						|
                  )
 | 
						|
                : Text('people'.tr()),
 | 
						|
            actions: [
 | 
						|
              IconButton(
 | 
						|
                icon: Icon(_search != null ? Icons.close : Icons.search),
 | 
						|
                onPressed: () {
 | 
						|
                  setState(() => _search = _search == null ? '' : null);
 | 
						|
                },
 | 
						|
              ),
 | 
						|
            ],
 | 
						|
          ),
 | 
						|
          body: SafeArea(
 | 
						|
            child: people.when(
 | 
						|
              data: (people) {
 | 
						|
                if (_search != null) {
 | 
						|
                  people = people.where((person) {
 | 
						|
                    return person.name.toLowerCase().contains(_search!.toLowerCase());
 | 
						|
                  }).toList();
 | 
						|
                }
 | 
						|
                return GridView.builder(
 | 
						|
                  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
 | 
						|
                    crossAxisCount: isTablet ? 6 : 3,
 | 
						|
                    childAspectRatio: 0.85,
 | 
						|
                    mainAxisSpacing: isPortrait && isTablet ? 36 : 0,
 | 
						|
                  ),
 | 
						|
                  padding: const EdgeInsets.symmetric(vertical: 32),
 | 
						|
                  itemCount: people.length,
 | 
						|
                  itemBuilder: (context, index) {
 | 
						|
                    final person = people[index];
 | 
						|
 | 
						|
                    return Column(
 | 
						|
                      children: [
 | 
						|
                        GestureDetector(
 | 
						|
                          onTap: () {
 | 
						|
                            context.pushRoute(DriftPersonRoute(person: person));
 | 
						|
                          },
 | 
						|
                          child: Material(
 | 
						|
                            shape: const CircleBorder(side: BorderSide.none),
 | 
						|
                            elevation: 3,
 | 
						|
                            child: CircleAvatar(
 | 
						|
                              maxRadius: isTablet ? 100 / 2 : 96 / 2,
 | 
						|
                              backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers),
 | 
						|
                            ),
 | 
						|
                          ),
 | 
						|
                        ),
 | 
						|
                        const SizedBox(height: 12),
 | 
						|
                        GestureDetector(
 | 
						|
                          onTap: () => showNameEditModal(context, person),
 | 
						|
                          child: person.name.isEmpty
 | 
						|
                              ? Text(
 | 
						|
                                  'add_a_name'.tr(),
 | 
						|
                                  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),
 | 
						|
                                  ),
 | 
						|
                                ),
 | 
						|
                        ),
 | 
						|
                      ],
 | 
						|
                    );
 | 
						|
                  },
 | 
						|
                );
 | 
						|
              },
 | 
						|
              error: (error, stack) => const Text("error"),
 | 
						|
              loading: () => const Center(child: CircularProgressIndicator()),
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
        );
 | 
						|
      },
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 |