import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:logging/logging.dart'; import '../controls/async_value_widget.dart'; import '../controls/error_message_widget.dart'; import '../controls/padding.dart'; import '../controls/standard_appbar.dart'; import '../controls/status_and_refresh_button.dart'; import '../models/auth/profile.dart'; import '../riverpod_controllers/account_services.dart'; import '../riverpod_controllers/gallery_services.dart'; class GalleryBrowsersScreen extends ConsumerWidget { static final _logger = Logger('$GalleryBrowsersScreen'); const GalleryBrowsersScreen({super.key}); String? validNameChecker(String? text) { final newName = text ?? ''; if (newName.isEmpty) { return 'Name cannot be empty'; } if (!RegExp( r"^[a-zA-Z0-9 ]+$", ).hasMatch(newName)) { return 'Name must be only letters and numbers'; } return null; } Future renameGallery( BuildContext context, WidgetRef ref, Profile profile, String galleryName, ) async { final newName = await showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { var controller = TextEditingController(text: galleryName); return Form( child: AlertDialog( title: const Text('Rename Gallery'), content: TextFormField( controller: controller, autovalidateMode: AutovalidateMode.always, validator: validNameChecker, ), actions: [ ElevatedButton( child: const Text('OK'), onPressed: () { if (validNameChecker(controller.text) != null) { return; } Navigator.pop(context, controller.text); // showDialog() returns true }, ), ElevatedButton( child: const Text('Cancel'), onPressed: () { Navigator.pop( context, galleryName); // showDialog() returns true }, ), ], ), ); }, ) ?? ''; if (newName.isEmpty || newName == galleryName) { return; } await ref .read(galleryProvider(profile, galleryName).notifier) .rename(newName); } @override Widget build(BuildContext context, WidgetRef ref) { _logger.finer('Building'); final profile = ref.watch(activeProfileProvider); final loading = switch (ref.watch(galleryListProvider(profile))) { AsyncData() => false, _ => true, }; return Scaffold( appBar: StandardAppBar.build(context, 'Galleries', actions: [ StatusAndRefreshButton( executing: loading, refreshFunction: () async => await ref .read(galleryListProvider(profile).notifier) .updateGalleries(), busyColor: Theme.of(context).colorScheme.surface, ), ]), body: RefreshIndicator( onRefresh: () async { await ref .read(galleryListProvider(profile).notifier) .updateGalleries(); }, child: buildBody(context, ref, profile), ), ); } Widget buildBody(BuildContext context, WidgetRef ref, Profile profile) { return AsyncValueWidget(ref.watch(galleryListProvider(profile)), loadingBuilder: (_, __) => const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Text('Loading galleries'), VerticalPadding(), CircularProgressIndicator(), ], ), ), valueBuilder: (context, ref, galleryListResult) { return galleryListResult.fold( onSuccess: (galleries) { if (galleries.isEmpty) { return const SingleChildScrollView( child: Center( child: Text('No Galleries'), ), ); } return ListView.separated( itemBuilder: (context, index) { final gallery = galleries[index]; return ListTile( onTap: () => context.push('/gallery/show', extra: gallery.name), title: Text(gallery.name), subtitle: Text( '#Photos: ${gallery.count}, Created: ${gallery.created}', style: Theme.of(context).textTheme.bodySmall, ), trailing: ElevatedButton( onPressed: () async => await renameGallery( context, ref, profile, gallery.name, ), child: const Text('Rename'), ), ); }, separatorBuilder: (context, index) { return const Divider(); }, itemCount: galleries.length, ); }, onError: (error) => ErrorMessageWidget(message: error.message), ); }); } }