Skip to content

Commit bac1b6e

Browse files
committed
Fixed "no album displayed" bug
Added new image deletion mode Changed confirmation dialogs style Added "Select All" option
1 parent 802b209 commit bac1b6e

8 files changed

Lines changed: 208 additions & 76 deletions

File tree

lib/api/ImageAPI.dart

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,38 @@ Future<Map<String,dynamic>> fetchImages(String albumID, int page) async {
4242
}
4343
}
4444

45+
Future<dynamic> getImageInfo(int imageId) async {
46+
Map<String, String> queries = {
47+
"format": "json",
48+
"method": "pwg.images.getInfo",
49+
};
50+
FormData formData = FormData.fromMap({
51+
"image_id": imageId
52+
});
53+
try {
54+
Response response = await API.dio.post('ws.php',
55+
data: formData,
56+
queryParameters: queries
57+
);
58+
59+
if (response.statusCode == 200) {
60+
return json.decode(response.data);
61+
} else {
62+
return {
63+
'stat': 'fail',
64+
'result': response.statusMessage
65+
};
66+
}
67+
} catch (e) {
68+
var error = e as DioError;
69+
return {
70+
'stat': 'fail',
71+
'result': error.message
72+
};
73+
}
74+
}
75+
76+
4577
Future<bool> _requestPermissions() async {
4678
var permission = await Permission.storage.status;
4779
print(permission.isGranted);
@@ -149,6 +181,7 @@ Future<int> deleteImages(BuildContext context, List<int> imageIdList) async {
149181
ScaffoldMessenger.of(context).showSnackBar(
150182
errorSnackBar(context, '${response['result']}'),
151183
);
184+
break;
152185
} else {
153186
nbSuccess++;
154187
}
@@ -187,6 +220,71 @@ Future<dynamic> deleteImage(int imageId) async {
187220
}
188221
}
189222

223+
Future<int> removeImages(BuildContext context, List<int> imageIdList, String catId) async {
224+
int nbSuccess = 0;
225+
for(int id in imageIdList) {
226+
var response = await removeImage(id, catId);
227+
if(response['stat'] == 'fail') {
228+
print(response);
229+
ScaffoldMessenger.of(context).hideCurrentSnackBar();
230+
ScaffoldMessenger.of(context).showSnackBar(
231+
errorSnackBar(context, '${response['result']}'),
232+
);
233+
break;
234+
} else {
235+
nbSuccess++;
236+
}
237+
}
238+
return nbSuccess;
239+
}
240+
Future<dynamic> removeImage(int imageId, String catId) async {
241+
var imageInfo = await getImageInfo(imageId);
242+
if(imageInfo['stat'] == 'fail') return imageInfo;
243+
244+
List<String> categories = imageInfo['result']['categories'].map<String>(
245+
(cat) => cat['id'].toString()
246+
).toList();
247+
categories.removeWhere((cat) => cat == catId);
248+
249+
if(categories.isEmpty) {
250+
return await deleteImage(imageId);
251+
}
252+
253+
Map<String, String> queries = {
254+
"format": "json",
255+
"method": "pwg.images.setInfo",
256+
};
257+
FormData formData = FormData.fromMap({
258+
"image_id": imageId,
259+
"categories": categories,
260+
"multiple_value_mode": "replace"
261+
});
262+
263+
try {
264+
Response response = await API.dio.post('ws.php',
265+
data: formData,
266+
queryParameters: queries
267+
);
268+
269+
print(response.data);
270+
271+
if (response.statusCode == 200) {
272+
return json.decode(response.data);
273+
} else {
274+
return {
275+
'stat': 'fail',
276+
'result': response.statusMessage
277+
};
278+
}
279+
} catch (e) {
280+
var error = e as DioError;
281+
return {
282+
'stat': 'fail',
283+
'result': error.message
284+
};
285+
}
286+
}
287+
190288
Future<int> moveImages(BuildContext context, List<dynamic> images, int category) async {
191289
int nbMoved = 0;
192290
for(var image in images) {
@@ -196,6 +294,7 @@ Future<int> moveImages(BuildContext context, List<dynamic> images, int category)
196294
ScaffoldMessenger.of(context).showSnackBar(
197295
errorSnackBar(context, '${response['result']}')
198296
);
297+
break;
199298
} else {
200299
nbMoved++;
201300
}
@@ -251,6 +350,7 @@ Future<int> assignImages(BuildContext context, List<dynamic> images, int categor
251350
ScaffoldMessenger.of(context).showSnackBar(
252351
errorSnackBar(context, '${response['result']}')
253352
);
353+
break;
254354
} else {
255355
nbAssigned++;
256356
}

lib/constants/SettingsConstants.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,12 @@ class Constants {
3333

3434
static double CONFIRM_DIALOG_MAX_WIDTH = 500.0;
3535

36+
static double ALBUM_MIN_WIDTH = 400.0;
37+
3638
static double PORTRAIT_IMAGE_COUNT_MIN = 1.0;
3739
static double PORTRAIT_IMAGE_COUNT_MAX = 6.0;
3840
static double LANDSCAPE_IMAGE_COUNT_MIN = 4.0;
3941
static double LANDSCAPE_IMAGE_COUNT_MAX = 10.0;
40-
4142
}
4243

4344
AppLocalizations appStrings(context) {

lib/views/CategoryViewPage.dart

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -198,12 +198,11 @@ class _CategoryViewPageState extends State<CategoryViewPage> with SingleTickerPr
198198
}
199199
}
200200
void _onDeleteSelection() async {
201-
print(_imageDeletionMode());
202-
if(await confirmDeleteDialog(context,
203-
content: appStrings(context).deleteImageCount_title(_selectedItems.length),
204-
)) {
205-
print('Delete ${_selectedItems.keys}');
206-
201+
int choice = await confirmRemoveImagesFromAlbumDialog(context,
202+
content: appStrings(context).deleteImage_message(_selectedItems.length),
203+
count: _selectedItems.length,
204+
);
205+
if(choice != -1) {
207206
List<int> selection = [];
208207
selection.addAll(_selectedItems.keys.toList());
209208

@@ -212,11 +211,19 @@ class _CategoryViewPageState extends State<CategoryViewPage> with SingleTickerPr
212211
_selectedItems.clear();
213212
});
214213

215-
int nbSuccess = await deleteImages(context, selection);
214+
int nbSuccess = 0;
215+
switch(choice) {
216+
case 0: nbSuccess = await deleteImages(context, selection);
217+
break;
218+
case 1: nbSuccess = await removeImages(context, selection, widget.category);
219+
break;
220+
default: break;
221+
}
216222

217223
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
218224
content: Text(appStrings(context).deleteImageSuccess_message(nbSuccess)),
219225
));
226+
220227
setState(() {});
221228
}
222229
}
@@ -233,25 +240,6 @@ class _CategoryViewPageState extends State<CategoryViewPage> with SingleTickerPr
233240
});
234241
}
235242

236-
int _imageDeletionMode() {
237-
int nbUnique = 0;
238-
_selectedItems.values.forEach((image) {
239-
print(image["categories"]);
240-
if(image["categories"].length == 1) nbUnique++;
241-
});
242-
if(nbUnique == 0) {
243-
print(appStrings(context).deleteCategory_allImages(_selectedItems.length));
244-
print(appStrings(context).removeSingleImage_title);
245-
return 0;
246-
}
247-
if(nbUnique == _selectedItems.length) {
248-
print(appStrings(context).deleteCategory_allImages(_selectedItems.length));
249-
return 1;
250-
}
251-
print(appStrings(context).deleteCategory_allImages(_selectedItems.length));
252-
print(appStrings(context).removeSingleImage_title);
253-
return 2;
254-
}
255243

256244
handleAlbumSnapshot(AsyncSnapshot albumSnapshot, int nbImages) {
257245
if(albumSnapshot.data['stat'] == 'fail') {
@@ -490,6 +478,10 @@ class _CategoryViewPageState extends State<CategoryViewPage> with SingleTickerPr
490478

491479
Widget createPageContent(dynamic albums, int nbImages) {
492480
ThemeData _theme = Theme.of(context);
481+
482+
int albumCrossAxisCount = MediaQuery.of(context).size.width <= Constants.ALBUM_MIN_WIDTH ? 1
483+
: (MediaQuery.of(context).size.width/Constants.ALBUM_MIN_WIDTH).floor();
484+
493485
return RefreshIndicator(
494486
displacement: 20,
495487
notificationPredicate: (notification) {
@@ -502,7 +494,7 @@ class _CategoryViewPageState extends State<CategoryViewPage> with SingleTickerPr
502494
albums.length > 0 ?
503495
GridView.builder(
504496
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
505-
crossAxisCount: (MediaQuery.of(context).size.width/400).floor(),
497+
crossAxisCount: albumCrossAxisCount,
506498
mainAxisSpacing: 10,
507499
crossAxisSpacing: 10,
508500
childAspectRatio: albumGridAspectRatio(context),

lib/views/ImageViewPage.dart

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -167,19 +167,40 @@ class _ImageViewPageState extends State<ImageViewPage> {
167167
}
168168
}
169169
void _onDeleteImage() async {
170-
if(await confirmDeleteDialog(context,
170+
int choice = await confirmRemoveImagesFromAlbumDialog(context,
171171
content: appStrings(context).deleteImage_message(1),
172-
)) {
173-
var response = await deleteImage(images[_page]['id']);
174-
if(response['stat'] == 'fail') {
175-
ScaffoldMessenger.of(context).showSnackBar(
176-
errorSnackBar(context, '${response['result']}')
177-
);
178-
} else {
179-
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
180-
content: Text(appStrings(context).deleteImageHUD_deleting(1)),
181-
));
172+
count: 1,
173+
);
174+
175+
if(choice != -1) {
176+
switch(choice) {
177+
case 0:
178+
var response = await deleteImage(images[_page]['id']);
179+
if(response['stat'] == 'fail') {
180+
ScaffoldMessenger.of(context).showSnackBar(
181+
errorSnackBar(context, '${response['result']}')
182+
);
183+
} else {
184+
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
185+
content: Text(appStrings(context).deleteImageHUD_deleting(1)),
186+
));
187+
}
188+
break;
189+
case 1:
190+
var response = await removeImage(images[_page]['id'], widget.category);
191+
if(response['stat'] == 'fail') {
192+
ScaffoldMessenger.of(context).showSnackBar(
193+
errorSnackBar(context, '${response['result']}')
194+
);
195+
} else {
196+
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
197+
content: Text(appStrings(context).removeImageHUD_removing(1)),
198+
));
199+
}
200+
break;
201+
default: break;
182202
}
203+
183204
int page = _page;
184205
if(page == images.length-1) {
185206
if (page == 0) {

lib/views/LoginViewPage.dart

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import 'package:piwigo_ng/constants/SettingsConstants.dart';
1313
import 'package:piwigo_ng/views/PrivacyPolicyViewPage.dart';
1414
import 'package:piwigo_ng/views/components/buttons.dart';
1515

16+
import 'components/dialogs/error_dialog.dart';
17+
1618

1719
class LoginViewPage extends StatefulWidget {
1820
LoginViewPage({Key key}) : super(key: key);
@@ -65,7 +67,10 @@ class _LoginViewPageState extends State<LoginViewPage> {
6567
barrierDismissible: true,
6668
context: context,
6769
builder: (BuildContext context) {
68-
return connectionErrorDialog(message);
70+
return ErrorDialog(
71+
errorTitle: appStrings(context).loginError_title,
72+
errorMessage: message
73+
);
6974
}
7075
);
7176
}
@@ -171,7 +176,10 @@ class _LoginViewPageState extends State<LoginViewPage> {
171176
barrierDismissible: true,
172177
context: context,
173178
builder: (BuildContext context) {
174-
return connectionErrorDialog(errorMessage);
179+
return ErrorDialog(
180+
errorTitle: appStrings(context).loginError_title,
181+
errorMessage: errorMessage
182+
);
175183
}
176184
);
177185
}
@@ -377,22 +385,6 @@ class _LoginViewPageState extends State<LoginViewPage> {
377385
),
378386
);
379387
}
380-
381-
AlertDialog connectionErrorDialog(String error) {
382-
return AlertDialog(
383-
title: Text('Connection failed'),
384-
content: Text('$error'),
385-
actions: [
386-
IconButton(
387-
color: Color(0xffff0e00),
388-
onPressed: () {
389-
Navigator.pop(context);
390-
},
391-
icon: Icon(Icons.close, color: Color(0xffffffff),)
392-
),
393-
],
394-
);
395-
}
396388
}
397389

398390

lib/views/RootCategoryViewPage.dart

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -90,24 +90,7 @@ class _RootCategoryViewPageState extends State<RootCategoryViewPage> with Single
9090
child: SingleChildScrollView(
9191
child: Column(
9292
children: [
93-
GridView.builder(
94-
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
95-
crossAxisCount: (MediaQuery.of(context).size.width/400).floor(),
96-
mainAxisSpacing: 10,
97-
crossAxisSpacing: 10,
98-
childAspectRatio: albumGridAspectRatio(context),
99-
),
100-
padding: EdgeInsets.all(10),
101-
itemCount: albums.length,
102-
shrinkWrap: true,
103-
physics: NeverScrollableScrollPhysics(),
104-
itemBuilder: (BuildContext context, int index) {
105-
var album = albums[index];
106-
return AlbumListItem(album, isAdmin: widget.isAdmin, onClose: () {
107-
setState(() {});
108-
});
109-
},
110-
),
93+
_albumGrid(albums),
11194
Center(
11295
child: Container(
11396
padding: EdgeInsets.all(10),
@@ -144,4 +127,28 @@ class _RootCategoryViewPageState extends State<RootCategoryViewPage> with Single
144127
) : Container(),
145128
);
146129
}
130+
131+
Widget _albumGrid(dynamic albums) {
132+
int albumCrossAxisCount = MediaQuery.of(context).size.width <= Constants.ALBUM_MIN_WIDTH ? 1
133+
: (MediaQuery.of(context).size.width/Constants.ALBUM_MIN_WIDTH).floor();
134+
135+
return GridView.builder(
136+
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
137+
crossAxisCount: albumCrossAxisCount,
138+
mainAxisSpacing: 10,
139+
crossAxisSpacing: 10,
140+
childAspectRatio: albumGridAspectRatio(context),
141+
),
142+
padding: EdgeInsets.all(10),
143+
itemCount: albums.length,
144+
shrinkWrap: true,
145+
physics: NeverScrollableScrollPhysics(),
146+
itemBuilder: (BuildContext context, int index) {
147+
var album = albums[index];
148+
return AlbumListItem(album, isAdmin: widget.isAdmin, onClose: () {
149+
setState(() {});
150+
});
151+
},
152+
);
153+
}
147154
}

0 commit comments

Comments
 (0)