Skip to content

Commit ea49608

Browse files
committed
edit images
1 parent 343d68d commit ea49608

31 files changed

Lines changed: 1632 additions & 985 deletions

android/app/src/main/AndroidManifest.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
<uses-permission android:name="android.permission.CAMERA" />
77
<uses-permission android:name="android.permission.INTERNET"/>
88
<uses-permission android:name="android.permission.VIBRATE" />
9-
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" tools:node="remove" />
109

1110
<application
1211
android:label="Piwigo NG"

lib/api/api_error.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
enum ApiErrors {
2+
error,
23
wrongLoginId,
34
wrongServerUrl,
45
fetchAlbumsError,

lib/api/images.dart

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,33 @@ import 'package:share_plus/share_plus.dart';
1919
import 'albums.dart';
2020
import 'api_client.dart';
2121

22+
Future<ApiResult<ImageModel>> getImage(int imageId) async {
23+
Map<String, dynamic> queries = {
24+
'format': 'json',
25+
'method': 'pwg.images.getInfo',
26+
'image_id': imageId,
27+
};
28+
29+
try {
30+
Response response = await ApiClient.get(queryParameters: queries);
31+
32+
if (response.statusCode == 200) {
33+
var data = json.decode(response.data);
34+
if (data['stat'] == 'fail') {
35+
return ApiResult(error: ApiErrors.error);
36+
}
37+
var jsonImage = data['result'];
38+
ImageModel image = ImageModel.fromJson(jsonImage);
39+
return ApiResult<ImageModel>(data: image);
40+
}
41+
} on DioError catch (e) {
42+
debugPrint('Fetch images: ${e.message}');
43+
} on Error catch (e) {
44+
debugPrint('Fetch images: $e\n${e.stackTrace}');
45+
}
46+
return ApiResult(error: ApiErrors.error);
47+
}
48+
2249
Future<ApiResult<List<ImageModel>>> fetchImages(int albumID, int page) async {
2350
Map<String, dynamic> queries = {
2451
"format": "json",
@@ -354,31 +381,33 @@ Future<bool> assignImage(int imageId, List<int> categories) async {
354381
return false;
355382
}
356383

357-
Future<int> editImages(List<ImageModel> images, List<int> tags, int level) async {
384+
Future<int> editImages(List<ImageModel> images, [Map<String, dynamic> info = const {}]) async {
358385
int nbEdited = 0;
359386
for (ImageModel image in images) {
360-
bool response = await editImage(image, tags, level);
387+
bool response = await editImage(image, info);
361388
if (response == true) {
362389
nbEdited++;
363390
}
364391
}
365392
return nbEdited;
366393
}
367394

368-
Future<bool> editImage(ImageModel image, List<int> tags, int level) async {
395+
Future<bool> editImage(ImageModel image, [Map<String, dynamic> info = const {}]) async {
369396
final Map<String, String> queries = {
370397
'format': 'json',
371398
'method': 'pwg.images.setInfo',
372399
};
373400
Map<String, dynamic> form = {
374401
'image_id': image.id,
375-
'name': image.name,
376-
'comment': image.comment,
377-
'tag_ids': tags,
378402
'single_value_mode': 'replace',
379403
'multiple_value_mode': 'append',
380404
};
381-
if (level != -1) form['level'] = level;
405+
if (info['title'] != null) form['name'] = info['title'];
406+
if (info['description'] != null) form['comment'] = info['description'];
407+
if (info['author'] != null) form['author'] = info['author'];
408+
if (info['level'] != null) form['level'] = info['level'];
409+
if (info['tags'] != null) form['tag_ids'] = info['tags'];
410+
382411
final FormData formData = FormData.fromMap(form);
383412

384413
try {

lib/api/tags.dart

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import 'dart:convert';
2+
3+
import 'package:dio/dio.dart';
4+
import 'package:flutter/material.dart';
5+
import 'package:piwigo_ng/api/api_error.dart';
6+
import 'package:piwigo_ng/models/tag_model.dart';
7+
8+
import 'api_client.dart';
9+
10+
Future<ApiResult<List<TagModel>>> getTags() async {
11+
Map<String, String> queries = {
12+
'format': 'json',
13+
'method': 'pwg.tags.getAdminList',
14+
};
15+
16+
Response response = await ApiClient.get(queryParameters: queries);
17+
18+
try {
19+
if (response.statusCode == 200) {
20+
var data = json.decode(response.data);
21+
if (data['stat'] == 'fail') {
22+
return ApiResult(error: ApiErrors.error);
23+
}
24+
List<TagModel> tags = data['result']['tags'].map<TagModel>((tag) => TagModel.fromJson(tag)).toList();
25+
return ApiResult(data: tags);
26+
}
27+
} on DioError catch (e) {
28+
debugPrint('Get tags: ${e.message}');
29+
} on Error catch (e) {
30+
debugPrint('Get tags: $e\n${e.stackTrace}');
31+
}
32+
return ApiResult(error: ApiErrors.error);
33+
}
34+
35+
Future<ApiResult<TagModel>> createTag(String name) async {
36+
Map<String, String> queries = {
37+
'format': 'json',
38+
'method': 'pwg.tags.add',
39+
'name': name,
40+
};
41+
Response response = await ApiClient.get(queryParameters: queries);
42+
43+
try {
44+
if (response.statusCode == 200) {
45+
var data = json.decode(response.data);
46+
print(data);
47+
if (data['stat'] == 'fail') {
48+
return ApiResult(error: ApiErrors.error);
49+
}
50+
return ApiResult(
51+
data: TagModel.fromJson(data['result']),
52+
);
53+
}
54+
} on DioError catch (e) {
55+
debugPrint('Fetch tags: ${e.message}');
56+
} on Error catch (e) {
57+
debugPrint('Fetch tags: $e\n${e.stackTrace}');
58+
}
59+
return ApiResult(error: ApiErrors.error);
60+
}

lib/app.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'package:piwigo_ng/utils/themes.dart';
1010
import 'package:piwigo_ng/views/album/album_view_page.dart';
1111
import 'package:piwigo_ng/views/album/root_album_view_page.dart';
1212
import 'package:piwigo_ng/views/authentication/login_view_page.dart';
13+
import 'package:piwigo_ng/views/image/edit_image_page.dart';
1314
import 'package:piwigo_ng/views/image/image_search_view_page.dart';
1415
import 'package:piwigo_ng/views/image/image_view_page.dart';
1516
import 'package:piwigo_ng/views/settings/privacy_policy_view_page.dart';
@@ -123,12 +124,19 @@ Route<dynamic> generateRoute(RouteSettings settings) {
123124
case ImageViewPage.routeName:
124125
return MaterialPageRoute(
125126
builder: (_) => ImageViewPage(
126-
imageList: arguments['images'] ?? [],
127+
images: arguments['images'] ?? [],
127128
startId: arguments['startId'],
128129
album: arguments['album'],
129130
),
130131
settings: settings,
131132
);
133+
case EditImagePage.routeName:
134+
return MaterialPageRoute<bool>(
135+
builder: (_) => EditImagePage(
136+
images: arguments['images'] ?? [],
137+
),
138+
settings: settings,
139+
);
132140
case SettingsViewPage.routeName:
133141
return MaterialPageRoute(
134142
builder: (_) => SettingsViewPage(),
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:intl/intl.dart';
3+
import 'package:piwigo_ng/models/image_model.dart';
4+
import 'package:piwigo_ng/services/preferences_service.dart';
5+
6+
class ImageDetailsCard extends StatelessWidget {
7+
const ImageDetailsCard({Key? key, required this.image, this.onRemove}) : super(key: key);
8+
9+
final ImageModel image;
10+
final Function()? onRemove;
11+
12+
@override
13+
Widget build(BuildContext context) {
14+
return Stack(
15+
children: [
16+
Padding(
17+
padding: const EdgeInsets.all(8.0),
18+
child: Row(
19+
children: [
20+
_imageThumbnail(context),
21+
Expanded(
22+
child: Container(
23+
margin: EdgeInsets.symmetric(vertical: 8.0),
24+
padding: EdgeInsets.only(top: 8.0, bottom: 8.0, right: 8.0),
25+
decoration: BoxDecoration(
26+
borderRadius: BorderRadius.horizontal(right: Radius.circular(10.0)),
27+
color: Theme.of(context).cardColor,
28+
),
29+
child: Column(
30+
crossAxisAlignment: CrossAxisAlignment.stretch,
31+
children: [
32+
Text(
33+
image.file,
34+
maxLines: 2,
35+
overflow: TextOverflow.ellipsis,
36+
style: Theme.of(context).textTheme.bodyMedium,
37+
),
38+
if (image.dateAvailable != null)
39+
Flexible(
40+
child: Padding(
41+
padding: const EdgeInsets.only(top: 4.0),
42+
child: Text(
43+
DateFormat.yMMMMd().format(DateTime.parse(image.dateAvailable!)), // todo locale
44+
maxLines: 2,
45+
style: Theme.of(context).textTheme.bodyMedium,
46+
),
47+
),
48+
),
49+
],
50+
),
51+
),
52+
),
53+
],
54+
),
55+
),
56+
_removeButton(context),
57+
],
58+
);
59+
}
60+
61+
Widget _imageThumbnail(context) => Container(
62+
padding: EdgeInsets.all(8.0),
63+
decoration: BoxDecoration(
64+
borderRadius: BorderRadius.circular(10.0),
65+
color: Theme.of(context).cardColor,
66+
),
67+
child: AspectRatio(
68+
aspectRatio: 1.0,
69+
child: ClipRRect(
70+
borderRadius: BorderRadius.circular(5.0),
71+
child: Builder(builder: (context) {
72+
final String? imageUrl = image.getDerivativeFromString(Preferences.getImageThumbnailSize)?.url;
73+
return Image.network(
74+
imageUrl ?? '',
75+
fit: BoxFit.cover,
76+
errorBuilder: (context, o, s) {
77+
debugPrint("$o\n$s");
78+
return Center(child: Icon(Icons.image_not_supported));
79+
},
80+
loadingBuilder: (context, child, loadingProgress) {
81+
if (loadingProgress == null) return child;
82+
return Center(
83+
child: CircularProgressIndicator(
84+
value: loadingProgress.expectedTotalBytes != null
85+
? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes!
86+
: null,
87+
),
88+
);
89+
},
90+
);
91+
}),
92+
),
93+
),
94+
);
95+
96+
Widget _removeButton(context) => Positioned(
97+
bottom: 0.0,
98+
right: 16.0,
99+
child: GestureDetector(
100+
behavior: HitTestBehavior.opaque,
101+
onTap: onRemove,
102+
child: Container(
103+
padding: EdgeInsets.all(2.0),
104+
decoration: BoxDecoration(
105+
shape: BoxShape.circle,
106+
color: Theme.of(context).cardColor,
107+
),
108+
child: Icon(Icons.remove_circle_outline),
109+
),
110+
),
111+
);
112+
}

lib/components/cards/tag_chip.dart

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:piwigo_ng/models/tag_model.dart';
3+
4+
class TagChip extends StatelessWidget {
5+
const TagChip({Key? key, required this.tag, this.onTap, this.icon}) : super(key: key);
6+
7+
final Widget? icon;
8+
final TagModel tag;
9+
final Function()? onTap;
10+
11+
@override
12+
Widget build(BuildContext context) {
13+
return GestureDetector(
14+
onTap: onTap,
15+
child: Container(
16+
padding: const EdgeInsets.all(2.0),
17+
decoration: ShapeDecoration(
18+
shape: StadiumBorder(),
19+
color: Theme.of(context).chipTheme.backgroundColor,
20+
),
21+
child: Row(
22+
mainAxisSize: MainAxisSize.min,
23+
children: [
24+
Padding(
25+
padding: const EdgeInsets.symmetric(horizontal: 8.0),
26+
child: Text(
27+
tag.name,
28+
style: Theme.of(context).textTheme.bodyMedium,
29+
),
30+
),
31+
if (icon != null) icon!,
32+
],
33+
),
34+
),
35+
);
36+
}
37+
}

0 commit comments

Comments
 (0)