Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 2424 - new "select existing images" button #3641

Merged
merged 3 commits into from
Jan 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:smooth_app/background/background_task_crop.dart';
import 'package:smooth_app/background/background_task_details.dart';
import 'package:smooth_app/background/background_task_image.dart';
import 'package:smooth_app/background/background_task_manager.dart';
Expand Down Expand Up @@ -47,6 +48,7 @@ abstract class AbstractBackgroundTask {
BackgroundTaskDetails.fromJson(map) ??
BackgroundTaskImage.fromJson(map) ??
BackgroundTaskUnselect.fromJson(map) ??
BackgroundTaskCrop.fromJson(map) ??
BackgroundTaskRefreshLater.fromJson(map);

/// Executes the background task: upload, download, update locally.
Expand Down
228 changes: 228 additions & 0 deletions packages/smooth_app/lib/background/background_task_crop.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:provider/provider.dart';
import 'package:smooth_app/background/abstract_background_task.dart';
import 'package:smooth_app/background/background_task_image.dart';
import 'package:smooth_app/background/background_task_refresh_later.dart';
import 'package:smooth_app/data_models/operation_type.dart';
import 'package:smooth_app/database/local_database.dart';
import 'package:smooth_app/database/transient_file.dart';
import 'package:smooth_app/query/product_query.dart';

/// Background task about product image crop from existing file.
class BackgroundTaskCrop extends AbstractBackgroundTask {
const BackgroundTaskCrop._({
required super.processName,
required super.uniqueId,
required super.barcode,
required super.languageCode,
required super.user,
required super.country,
required super.stamp,
required this.imageId,
required this.imageField,
required this.croppedPath,
required this.rotationDegrees,
required this.cropX1,
required this.cropY1,
required this.cropX2,
required this.cropY2,
});

BackgroundTaskCrop._fromJson(Map<String, dynamic> json)
: this._(
processName: json['processName'] as String,
uniqueId: json['uniqueId'] as String,
barcode: json['barcode'] as String,
languageCode: json['languageCode'] as String,
user: json['user'] as String,
country: json['country'] as String,
imageId: json['imageId'] as int,
imageField: json['imageField'] as String,
croppedPath: json['croppedPath'] as String,
rotationDegrees: json['rotation'] as int,
cropX1: json['x1'] as int? ?? 0,
cropY1: json['y1'] as int? ?? 0,
cropX2: json['x2'] as int? ?? 0,
cropY2: json['y2'] as int? ?? 0,
stamp: json['stamp'] as String,
);

/// Task ID.
static const String _PROCESS_NAME = 'IMAGE_CROP';

static const OperationType _operationType = OperationType.crop;

final int imageId;
final String imageField;
final String croppedPath;
final int rotationDegrees;
final int cropX1;
final int cropY1;
final int cropX2;
final int cropY2;

@override
Map<String, dynamic> toJson() => <String, dynamic>{
'processName': processName,
'uniqueId': uniqueId,
'barcode': barcode,
'languageCode': languageCode,
'user': user,
'country': country,
'imageId': imageId,
'imageField': imageField,
'croppedPath': croppedPath,
'stamp': stamp,
'rotation': rotationDegrees,
'x1': cropX1,
'y1': cropY1,
'x2': cropX2,
'y2': cropY2,
};

/// Returns the deserialized background task if possible, or null.
static AbstractBackgroundTask? fromJson(final Map<String, dynamic> map) {
try {
final AbstractBackgroundTask result = BackgroundTaskCrop._fromJson(map);
if (result.processName == _PROCESS_NAME) {
return result;
}
} catch (e) {
//
}
return null;
}

/// Adds the background task about uploading a product image.
static Future<void> addTask(
final String barcode, {
required final int imageId,
required final ImageField imageField,
required final File croppedFile,
required final int rotation,
required final int x1,
required final int y1,
required final int x2,
required final int y2,
required final State<StatefulWidget> widget,
}) async {
final LocalDatabase localDatabase = widget.context.read<LocalDatabase>();
final String uniqueId = await _operationType.getNewKey(
localDatabase,
barcode,
);
final AbstractBackgroundTask task = _getNewTask(
barcode,
imageId,
imageField,
croppedFile,
uniqueId,
rotation,
x1,
y1,
x2,
y2,
);
await task.addToManager(localDatabase, widget: widget);
}

@override
String? getSnackBarMessage(final AppLocalizations appLocalizations) =>
appLocalizations.product_task_background_schedule;

/// Returns a new background task about cropping an existing image.
static BackgroundTaskCrop _getNewTask(
final String barcode,
final int imageId,
final ImageField imageField,
final File croppedFile,
final String uniqueId,
final int rotationDegrees,
final int cropX1,
final int cropY1,
final int cropX2,
final int cropY2,
) =>
BackgroundTaskCrop._(
uniqueId: uniqueId,
barcode: barcode,
processName: _PROCESS_NAME,
imageId: imageId,
imageField: imageField.offTag,
croppedPath: croppedFile.path,
rotationDegrees: rotationDegrees,
cropX1: cropX1,
cropY1: cropY1,
cropX2: cropX2,
cropY2: cropY2,
languageCode: ProductQuery.getLanguage().code,
user: jsonEncode(ProductQuery.getUser().toJson()),
country: ProductQuery.getCountry()!.offTag,
stamp: BackgroundTaskImage.getStamp(
barcode,
imageField.offTag,
ProductQuery.getLanguage().code,
),
);

@override
Future<void> preExecute(final LocalDatabase localDatabase) async =>
TransientFile.putImage(
ImageField.fromOffTag(imageField)!,
barcode,
localDatabase,
File(croppedPath),
);

@override
Future<void> postExecute(
final LocalDatabase localDatabase,
final bool success,
) async {
try {
File(croppedPath).deleteSync();
} catch (e) {
// not likely, but let's not spoil the task for that either.
}
TransientFile.removeImage(
ImageField.fromOffTag(imageField)!,
barcode,
localDatabase,
);
localDatabase.notifyListeners();
if (success) {
await BackgroundTaskRefreshLater.addTask(
barcode,
localDatabase: localDatabase,
);
}
}

/// Uploads the product image.
@override
Future<void> upload() async {
final ImageField imageField = ImageField.fromOffTag(this.imageField)!;
final OpenFoodFactsLanguage language = getLanguage();
final User user = getUser();
final String? imageUrl = await OpenFoodAPIClient.setProductImageCrop(
barcode: barcode,
imageField: imageField,
language: language,
imgid: '$imageId',
angle: ImageAngleExtension.fromInt(rotationDegrees)!,
x1: cropX1,
y1: cropY1,
x2: cropX2,
y2: cropY2,
user: user,
);
if (imageUrl == null) {
throw Exception('Could not select picture');
}
}
}
3 changes: 3 additions & 0 deletions packages/smooth_app/lib/data_models/operation_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'package:smooth_app/helpers/database_helper.dart';
/// * possibly, which barcode (not useful yet)
enum OperationType {
image('I'),
crop('C'),
unselect('U'),
refreshLater('R'),
details('D');
Expand Down Expand Up @@ -47,6 +48,8 @@ enum OperationType {
return appLocalizations.background_task_operation_image;
case OperationType.unselect:
return 'Unselect a product image';
case OperationType.crop:
return 'Crop an existing image';
case OperationType.refreshLater:
return 'Waiting 10 min before refreshing product to get all automatic edits';
}
Expand Down

This file was deleted.

16 changes: 16 additions & 0 deletions packages/smooth_app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1526,6 +1526,22 @@
"@edit_photo_unselect_button_label": {
"description": "Edit 'unselect photo' button label"
},
"edit_photo_select_existing_button_label": "Select an existing image",
"@edit_photo_select_existing_button_label": {
"description": "Edit 'select existing image' button label"
},
"edit_photo_select_existing_all_label": "Existing images",
"@edit_photo_select_existing_all_label": {
"description": "Page title"
},
"edit_photo_select_existing_download_label": "Retrieving existing images...",
"@edit_photo_select_existing_download_label": {
"description": "Dialog label"
},
"edit_photo_select_existing_downloaded_none": "There are no images previously uploaded related to this product.",
"@edit_photo_select_existing_downloaded_none": {
"description": "Error message"
},
"category_picker_screen_title": "Categories",
"@category_picker_screen_title": {
"description": "Categories picker screen title"
Expand Down