Skip to content

Commit 516e885

Browse files
committed
Fullscreen image actions
1 parent d1ce5fa commit 516e885

6 files changed

Lines changed: 180 additions & 55 deletions

File tree

lib/app.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,9 @@ Route<dynamic> generateRoute(RouteSettings settings) {
113113
);
114114
case ImageSearchViewPage.routeName:
115115
return SlideUpPageRoute(
116-
page: const ImageSearchViewPage(),
116+
page: ImageSearchViewPage(
117+
isAdmin: arguments['isAdmin'] ?? isAdmin,
118+
),
117119
settings: settings,
118120
);
119121
case UploadViewPage.routeName:

lib/components/modals/delete_images_modal.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ class DeleteImagesModal extends StatelessWidget {
99
const DeleteImagesModal({
1010
Key? key,
1111
required this.imageList,
12-
required this.album,
12+
this.album,
1313
}) : super(key: key);
1414

1515
final List<ImageModel> imageList;
16-
final AlbumModel album;
16+
final AlbumModel? album;
1717

1818
bool get _safeDelete {
1919
for (ImageModel image in imageList) {
@@ -27,7 +27,9 @@ class DeleteImagesModal extends StatelessWidget {
2727
bool get _canMakeOrphans {
2828
for (ImageModel image in imageList) {
2929
List<int> albums = image.categories.map<int>((a) => a['id']).toList();
30-
albums.removeWhere((a) => a == album.id);
30+
if (album != null) {
31+
albums.removeWhere((a) => a == album!.id);
32+
}
3133
if (albums.isEmpty) {
3234
return true;
3335
}

lib/services/upload_provider.dart

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import 'dart:async';
2+
3+
import 'package:flutter/material.dart';
4+
import 'package:image_picker/image_picker.dart';
5+
import 'package:piwigo_ng/models/album_model.dart';
6+
7+
class UploadProvider extends ChangeNotifier {
8+
final List<UploadItem> _uploadList = [];
9+
10+
UploadProvider();
11+
12+
void addItem(UploadItem item) {
13+
_uploadList.add(item);
14+
}
15+
16+
void removeItem(UploadItem item) {
17+
_uploadList.remove(item);
18+
}
19+
20+
List<UploadItem> get uploadList => _uploadList;
21+
}
22+
23+
class UploadItem {
24+
final XFile file;
25+
final StreamController<double> progress;
26+
final AlbumModel destination;
27+
28+
UploadItem({
29+
required this.file,
30+
required this.destination,
31+
required this.progress,
32+
});
33+
}

lib/utils/image_actions.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ Future<bool?> onEditPhotos(BuildContext context, List<ImageModel> images) async
6060
}
6161

6262
Future<dynamic> onMovePhotos(BuildContext context, List<ImageModel> images, AlbumModel album) async {
63+
AlbumModel origin = album;
64+
if (images.length == 1) {
65+
origin = AlbumModel(
66+
id: images.first.categories.first['id'],
67+
name: '',
68+
);
69+
}
6370
return showModalBottomSheet<dynamic>(
6471
context: context,
6572
isScrollControlled: true,
@@ -69,7 +76,7 @@ Future<dynamic> onMovePhotos(BuildContext context, List<ImageModel> images, Albu
6976
title: appStrings.moveImage_title,
7077
subtitle: appStrings.moveImage_selectAlbum(images.length, images.first),
7178
isImage: true,
72-
album: album,
79+
album: origin,
7380
onSelected: (selectedAlbum) async {
7481
final int? choice = await showModalBottomSheet<int>(
7582
context: context,

lib/views/image/image_search_view_page.dart

Lines changed: 130 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,23 @@ import 'package:piwigo_ng/api/api_error.dart';
44
import 'package:piwigo_ng/api/images.dart';
55
import 'package:piwigo_ng/components/popup_list_item.dart';
66
import 'package:piwigo_ng/components/scroll_widgets/image_grid_view.dart';
7+
import 'package:piwigo_ng/models/album_model.dart';
78
import 'package:piwigo_ng/models/image_model.dart';
9+
import 'package:piwigo_ng/utils/image_actions.dart';
810
import 'package:piwigo_ng/utils/localizations.dart';
911
import 'package:piwigo_ng/utils/page_routes.dart';
12+
import 'package:piwigo_ng/views/image/image_view_page.dart';
1013
import 'package:pull_to_refresh/pull_to_refresh.dart';
1114

1215
import '../../components/fields/app_field.dart';
1316

1417
class ImageSearchViewPage extends StatefulWidget {
15-
const ImageSearchViewPage({Key? key}) : super(key: key);
18+
const ImageSearchViewPage({Key? key, this.isAdmin = false}) : super(key: key);
1619

1720
static const String routeName = '/images/search';
1821

22+
final bool isAdmin;
23+
1924
@override
2025
State<ImageSearchViewPage> createState() => _ImageSearchViewPageState();
2126
}
@@ -54,6 +59,8 @@ class _ImageSearchViewPageState extends State<ImageSearchViewPage> {
5459
super.dispose();
5560
}
5661

62+
bool get _hasNonFavorites => _selectedList.where((image) => !image.favorite).isNotEmpty;
63+
5764
Future<bool> _onWillPop() async {
5865
if (_selectedList.isNotEmpty) {
5966
setState(() {
@@ -104,11 +111,26 @@ class _ImageSearchViewPageState extends State<ImageSearchViewPage> {
104111
_refreshController.loadComplete();
105112
}
106113

107-
void _onEdit() {}
108-
void _onMove() {}
109-
void _onDelete() {}
110-
void _onDownload() {}
111-
void _onShare() {}
114+
void _onTapPhoto(ImageModel image) => Navigator.of(context).pushNamed(
115+
ImageViewPage.routeName,
116+
arguments: {
117+
'images': _searchList,
118+
'startId': image.id,
119+
'album': AlbumModel(
120+
id: -1,
121+
name: 'Search',
122+
nbImages: _nbImages,
123+
nbTotalImages: _nbImages,
124+
),
125+
},
126+
).whenComplete(() => _onRefresh());
127+
void _onEditPhotos() => onEditPhotos(context, _selectedList).then((success) {
128+
if (success == true) {
129+
_selectedList.clear();
130+
_onRefresh();
131+
}
132+
});
133+
void _onLikePhotos() => onLikePhotos(_selectedList, _hasNonFavorites).whenComplete(() => _onRefresh());
112134

113135
@override
114136
Widget build(BuildContext context) {
@@ -180,36 +202,65 @@ class _ImageSearchViewPageState extends State<ImageSearchViewPage> {
180202
),
181203
),
182204
actions: [
183-
if (_selectedList.isNotEmpty)
205+
AnimatedSize(
206+
duration: const Duration(milliseconds: 300),
207+
curve: Curves.ease,
208+
child: SizedBox(
209+
width: _selectedList.isEmpty ? (widget.isAdmin ? 0.0 : 16.0) : null,
210+
child: AnimatedScale(
211+
duration: const Duration(milliseconds: 300),
212+
curve: Curves.ease,
213+
scale: _selectedList.isEmpty ? 0 : 1,
214+
child: AnimatedOpacity(
215+
duration: const Duration(milliseconds: 300),
216+
curve: Curves.ease,
217+
opacity: _selectedList.isEmpty ? 0 : 1,
218+
child: IconButton(
219+
onPressed: () => setState(() {
220+
_selectedList.clear();
221+
}),
222+
icon: Icon(Icons.cancel),
223+
),
224+
),
225+
),
226+
),
227+
),
228+
if (widget.isAdmin)
184229
PopupMenuButton(
230+
padding: EdgeInsets.zero,
231+
enabled: _selectedList.isNotEmpty,
185232
position: PopupMenuPosition.under,
186233
itemBuilder: (context) => [
187-
if (_selectedList.isNotEmpty)
188-
PopupMenuItem(
189-
onTap: () => setState(() {
190-
_selectedList.clear();
191-
}),
192-
child: PopupListItem(
193-
icon: Icons.cancel,
194-
text: appStrings.categoryImageList_deselectButton,
195-
),
234+
PopupMenuItem(
235+
onTap: () => Future.delayed(
236+
const Duration(seconds: 0),
237+
() => share(_selectedList),
196238
),
197-
if (_selectedList.isNotEmpty)
198-
PopupMenuItem(
199-
onTap: _onShare,
200-
child: PopupListItem(
201-
icon: Icons.share,
202-
text: appStrings.imageOptions_share,
203-
),
239+
child: PopupListItem(
240+
icon: Icons.share,
241+
text: appStrings.imageOptions_share,
204242
),
205-
if (_selectedList.isNotEmpty)
206-
PopupMenuItem(
207-
onTap: _onDownload,
208-
child: PopupListItem(
209-
icon: Icons.download,
210-
text: appStrings.downloadImage_title(_selectedList.length),
211-
),
243+
),
244+
PopupMenuItem(
245+
onTap: () => Future.delayed(
246+
const Duration(seconds: 0),
247+
() => _onLikePhotos(),
248+
),
249+
child: PopupListItem(
250+
icon: _hasNonFavorites ? Icons.favorite_border : Icons.favorite,
251+
text: _hasNonFavorites ? appStrings.imageOptions_addFavorites : appStrings.imageOptions_removeFavorites,
252+
),
253+
),
254+
PopupMenuItem(
255+
onTap: () => Future.delayed(
256+
const Duration(seconds: 0),
257+
() => downloadImages(_selectedList),
258+
),
259+
child: PopupListItem(
260+
icon: Icons.download,
261+
text: appStrings.downloadImage_title(_selectedList.length),
212262
),
263+
),
213264
],
214265
),
215266
],
@@ -251,6 +302,7 @@ class _ImageSearchViewPageState extends State<ImageSearchViewPage> {
251302
onDeselectImage: (image) => setState(() {
252303
_selectedList.remove(image);
253304
}),
305+
onTapImage: _onTapPhoto,
254306
);
255307
}
256308
return Center(child: CircularProgressIndicator());
@@ -265,26 +317,54 @@ class _ImageSearchViewPageState extends State<ImageSearchViewPage> {
265317
curve: Curves.easeInOut,
266318
height: _selectedList.isEmpty ? 0 : 56.0,
267319
child: Row(
268-
children: [
269-
Expanded(
270-
child: IconButton(
271-
onPressed: _onEdit,
272-
icon: Icon(Icons.edit),
273-
),
274-
),
275-
Expanded(
276-
child: IconButton(
277-
onPressed: _onMove,
278-
icon: Icon(Icons.drive_file_move),
279-
),
280-
),
281-
Expanded(
282-
child: IconButton(
283-
onPressed: _onDelete,
284-
icon: Icon(Icons.delete),
285-
),
286-
),
287-
],
320+
children: widget.isAdmin
321+
? [
322+
Expanded(
323+
child: IconButton(
324+
onPressed: _onEditPhotos,
325+
icon: Icon(Icons.edit),
326+
),
327+
),
328+
Expanded(
329+
child: IconButton(
330+
onPressed: () {}, // todo: on move
331+
icon: Icon(Icons.drive_file_move),
332+
),
333+
),
334+
Expanded(
335+
child: IconButton(
336+
onPressed: () {}, // todo: on delete
337+
icon: Icon(Icons.delete),
338+
),
339+
),
340+
]
341+
: [
342+
Expanded(
343+
child: IconButton(
344+
onPressed: () => share(_selectedList),
345+
icon: Icon(Icons.share),
346+
),
347+
),
348+
Expanded(
349+
child: IconButton(
350+
onPressed: _onLikePhotos,
351+
icon: Builder(
352+
builder: (context) {
353+
if (_hasNonFavorites) {
354+
return Icon(Icons.favorite_border);
355+
}
356+
return Icon(Icons.favorite);
357+
},
358+
),
359+
),
360+
),
361+
Expanded(
362+
child: IconButton(
363+
onPressed: () => downloadImages(_selectedList),
364+
icon: Icon(Icons.download),
365+
),
366+
),
367+
],
288368
),
289369
),
290370
);

lib/views/image/image_view_page.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ class _ImageViewPageState extends State<ImageViewPage> {
8686
/// Load more images with API pagination
8787
/// Triggers when changing to last page
8888
Future<void> _loadMoreImages() async {
89+
if (_album.id == -1) return;
8990
if (_album.nbImages <= _imageList.length) return;
9091
ApiResult<List<ImageModel>> result = await fetchImages(_album.id, _imagePage + 1);
9192
if (result.hasError || !result.hasData) return;

0 commit comments

Comments
 (0)