2023-03-21 01:55:47 +00:00
|
|
|
import 'package:flutter/material.dart' hide Visibility;
|
2024-12-05 04:00:58 +00:00
|
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
2022-12-27 03:00:28 +00:00
|
|
|
|
2024-12-05 04:00:58 +00:00
|
|
|
import '../controls/async_value_widget.dart';
|
|
|
|
import '../controls/error_message_widget.dart';
|
2023-03-20 14:06:44 +00:00
|
|
|
import '../controls/login_aware_cached_network_image.dart';
|
2024-12-05 04:00:58 +00:00
|
|
|
import '../models/auth/profile.dart';
|
2022-12-27 03:00:28 +00:00
|
|
|
import '../models/gallery_data.dart';
|
|
|
|
import '../models/image_entry.dart';
|
2023-03-21 01:55:47 +00:00
|
|
|
import '../models/visibility.dart';
|
2024-12-10 11:54:45 +00:00
|
|
|
import '../riverpod_controllers/account_services.dart';
|
2024-12-05 04:00:58 +00:00
|
|
|
import '../riverpod_controllers/gallery_services.dart';
|
2022-12-29 05:24:44 +00:00
|
|
|
import '../serializers/friendica/image_entry_friendica_extensions.dart';
|
2023-04-22 12:45:06 +00:00
|
|
|
import 'media_viewer_screen.dart';
|
2022-12-27 03:00:28 +00:00
|
|
|
|
2024-12-05 04:00:58 +00:00
|
|
|
class ExistingImageSelectorScreen extends ConsumerStatefulWidget {
|
2023-03-21 01:55:47 +00:00
|
|
|
final Visibility? visibilityFilter;
|
|
|
|
|
|
|
|
const ExistingImageSelectorScreen({
|
|
|
|
super.key,
|
|
|
|
this.visibilityFilter,
|
|
|
|
});
|
|
|
|
|
2022-12-27 03:00:28 +00:00
|
|
|
@override
|
2024-12-05 04:00:58 +00:00
|
|
|
ConsumerState<ExistingImageSelectorScreen> createState() =>
|
2022-12-27 03:00:28 +00:00
|
|
|
_ExistingImageSelectorScreenState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _ExistingImageSelectorScreenState
|
2024-12-05 04:00:58 +00:00
|
|
|
extends ConsumerState<ExistingImageSelectorScreen> {
|
2022-12-27 03:00:28 +00:00
|
|
|
GalleryData? selectedGallery;
|
|
|
|
final selectedImages = <ImageEntry>[];
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2024-12-10 11:54:45 +00:00
|
|
|
final profile = ref.watch(activeProfileProvider);
|
2024-12-05 04:00:58 +00:00
|
|
|
final galleryListAsyncValue = ref.watch(galleryListProvider(profile));
|
|
|
|
final title = switch (galleryListAsyncValue) {
|
|
|
|
AsyncData() => selectedImages.isEmpty
|
|
|
|
? 'Select Image(s)'
|
|
|
|
: '${selectedImages.length} selected',
|
|
|
|
AsyncLoading() => 'Loading...',
|
|
|
|
_ => 'Error loading',
|
|
|
|
};
|
|
|
|
final List<Widget> actions = switch (galleryListAsyncValue) {
|
|
|
|
AsyncData() => [
|
|
|
|
IconButton(
|
|
|
|
onPressed: selectedImages.isEmpty
|
|
|
|
? null
|
|
|
|
: () {
|
|
|
|
Navigator.of(context).pop(selectedImages);
|
|
|
|
},
|
|
|
|
tooltip: 'Attach selected files',
|
|
|
|
icon: const Icon(Icons.attach_file),
|
|
|
|
),
|
|
|
|
IconButton(
|
|
|
|
onPressed: selectedImages.isEmpty
|
|
|
|
? null
|
|
|
|
: () {
|
|
|
|
setState(() {
|
|
|
|
selectedImages.clear();
|
|
|
|
});
|
|
|
|
},
|
|
|
|
tooltip: 'Clear Selection',
|
|
|
|
icon: const Icon(Icons.remove_circle_outline),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
_ => [],
|
|
|
|
};
|
|
|
|
final body = AsyncValueWidget(galleryListAsyncValue,
|
|
|
|
valueBuilder: (context, ref, galleriesResult) {
|
|
|
|
return galleriesResult.fold(
|
|
|
|
onSuccess: (galleries) {
|
|
|
|
return Center(
|
|
|
|
child: Column(
|
|
|
|
children: [
|
|
|
|
DropdownButton<GalleryData>(
|
|
|
|
value: selectedGallery,
|
|
|
|
items: galleries
|
|
|
|
.map((g) =>
|
|
|
|
DropdownMenuItem(value: g, child: Text(g.name)))
|
|
|
|
.toList(),
|
|
|
|
onChanged: (value) {
|
|
|
|
setState(() {
|
|
|
|
selectedGallery = value;
|
|
|
|
});
|
|
|
|
}),
|
|
|
|
const VerticalDivider(),
|
|
|
|
selectedGallery == null
|
|
|
|
? buildInfoBody()
|
|
|
|
: Expanded(child: buildGrid(context, profile)),
|
|
|
|
],
|
|
|
|
));
|
|
|
|
},
|
|
|
|
onError: (error) =>
|
|
|
|
ErrorMessageWidget(message: 'Error getting galleries: $error'));
|
|
|
|
});
|
2022-12-27 03:00:28 +00:00
|
|
|
return SafeArea(
|
|
|
|
child: Scaffold(
|
|
|
|
appBar: AppBar(
|
|
|
|
title: Text(title),
|
2024-12-05 04:00:58 +00:00
|
|
|
actions: actions,
|
2022-12-27 03:00:28 +00:00
|
|
|
),
|
2024-12-05 04:00:58 +00:00
|
|
|
body: body,
|
2022-12-27 03:00:28 +00:00
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-01-29 21:46:22 +00:00
|
|
|
Widget buildInfoBody() {
|
2023-10-31 01:44:16 +00:00
|
|
|
return const Center(
|
2023-01-29 22:21:47 +00:00
|
|
|
child: Column(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
2023-10-31 01:44:16 +00:00
|
|
|
children: [Text('Select a gallery')],
|
2023-01-29 22:21:47 +00:00
|
|
|
),
|
|
|
|
);
|
2023-01-29 21:46:22 +00:00
|
|
|
}
|
|
|
|
|
2023-01-29 22:21:47 +00:00
|
|
|
Widget buildGrid(
|
|
|
|
BuildContext context,
|
2024-12-05 04:00:58 +00:00
|
|
|
Profile profile,
|
2023-01-29 22:21:47 +00:00
|
|
|
) {
|
2022-12-29 05:24:44 +00:00
|
|
|
const thumbnailDimension = 350.0;
|
2023-01-29 21:46:22 +00:00
|
|
|
final galleryName = selectedGallery!.name;
|
2024-12-05 04:00:58 +00:00
|
|
|
final expectedPhotoCount = selectedGallery!.count;
|
|
|
|
return AsyncValueWidget(
|
|
|
|
ref.watch(galleryImagesProvider(profile, galleryName)),
|
|
|
|
valueBuilder: (context, ref, imagesResult) {
|
|
|
|
return imagesResult.fold(
|
|
|
|
onSuccess: (images) {
|
|
|
|
return GridView.builder(
|
|
|
|
itemCount: images.length,
|
|
|
|
padding: const EdgeInsets.all(5.0),
|
|
|
|
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
|
|
|
maxCrossAxisExtent: thumbnailDimension),
|
|
|
|
itemBuilder: (context, index) {
|
|
|
|
final image = images[index];
|
2024-12-12 21:10:34 +00:00
|
|
|
// TODO May need to take out since there is a count discrepency for OAuth users
|
2024-12-05 04:00:58 +00:00
|
|
|
if (images.length < expectedPhotoCount &&
|
|
|
|
index == images.length - 1) {
|
|
|
|
ref
|
|
|
|
.read(galleryImagesProvider(profile, galleryName)
|
|
|
|
.notifier)
|
|
|
|
.updateGalleryImages(
|
|
|
|
withNextPage: true,
|
|
|
|
nextPageOnly: true,
|
|
|
|
);
|
2022-12-27 03:00:28 +00:00
|
|
|
}
|
2024-12-05 04:00:58 +00:00
|
|
|
final selected = selectedImages.contains(image);
|
|
|
|
|
|
|
|
final tileWidget = Card(
|
|
|
|
child: Stack(
|
|
|
|
children: [
|
|
|
|
LoginAwareCachedNetworkImage(
|
|
|
|
imageUrl: image.thumbnailUrl,
|
|
|
|
),
|
|
|
|
if (selected)
|
|
|
|
const Positioned(
|
|
|
|
child: Icon(
|
|
|
|
Icons.check_circle,
|
|
|
|
color: Colors.green,
|
|
|
|
)),
|
|
|
|
Positioned(
|
|
|
|
bottom: 5.0,
|
|
|
|
right: 5.0,
|
|
|
|
child: Icon(
|
|
|
|
image.visibility.type == VisibilityType.public
|
|
|
|
? Icons.public
|
|
|
|
: Icons.lock),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
return Padding(
|
|
|
|
padding: const EdgeInsets.all(2.0),
|
|
|
|
child: GestureDetector(
|
|
|
|
onTap: () {
|
|
|
|
setState(() {
|
|
|
|
if (selected) {
|
|
|
|
selectedImages.remove(image);
|
|
|
|
} else {
|
|
|
|
selectedImages.add(image);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
onDoubleTap: () {
|
|
|
|
Navigator.of(context).push(
|
|
|
|
MaterialPageRoute(
|
|
|
|
builder: (context) => MediaViewerScreen(
|
|
|
|
attachments: [image.toMediaAttachment()],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
child: tileWidget,
|
2023-01-29 22:21:47 +00:00
|
|
|
),
|
2024-12-05 04:00:58 +00:00
|
|
|
);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
onError: (error) =>
|
|
|
|
ErrorMessageWidget(message: 'Error getting image list: $error'));
|
|
|
|
});
|
2022-12-27 03:00:28 +00:00
|
|
|
}
|
|
|
|
}
|