Skip to content

Commit

Permalink
feat: 5326 - users can now select proof from gallery in "add price" page
Browse files Browse the repository at this point in the history
Impacted files:
* `image_crop_page.dart`: now we can force the image source
* `price_model.dart`: now we can set a proof after init time
* `price_proof_card.dart`: now we can select the proof image among "camera", "gallery" and "my proofs"; minor refactoring
* `prices_proofs_page.dart`: added a "select proof?" parameter to pop the selected proof if needed
* `user_preferences_account.dart`: minor refactoring
  • Loading branch information
monsieurtanuki committed Oct 9, 2024
1 parent 2c411c0 commit 389b0ff
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 58 deletions.
22 changes: 17 additions & 5 deletions packages/smooth_app/lib/pages/image_crop_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,23 @@ import 'package:smooth_app/pages/crop_parameters.dart';
import 'package:smooth_app/pages/product_crop_helper.dart';

/// Safely picks an image file from gallery or camera, regarding access denied.
Future<XFile?> pickImageFile(final BuildContext context) async {
Future<XFile?> pickImageFile(
final BuildContext context, {
final UserPictureSource? forcedSource,
}) async {
/// Picks an image file from gallery or camera.
Future<XFile?> innerPickImageFile(
final BuildContext context, {
bool ignorePlatformException = false,
}) async {
final UserPictureSource? source = await _getUserPictureSource(context);
if (source == null) {
return null;
final UserPictureSource? source;
if (forcedSource != null) {
source = forcedSource;
} else {
source = await _getUserPictureSource(context);
if (source == null) {
return null;
}
}
final ImagePicker picker = ImagePicker();
if (source == UserPictureSource.GALLERY) {
Expand Down Expand Up @@ -277,8 +285,12 @@ Future<CropParameters?> confirmAndUploadNewImage(
final BuildContext context, {
required final CropHelper cropHelper,
required final bool isLoggedInMandatory,
final UserPictureSource? forcedSource,
}) async {
final XFile? fullPhoto = await pickImageFile(context);
final XFile? fullPhoto = await pickImageFile(
context,
forcedSource: forcedSource,
);
if (fullPhoto == null) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,9 @@ class UserPreferencesAccount extends AbstractUserPreferences {
appLocalizations.user_search_proofs_title,
() async => Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context) => const PricesProofsPage(),
builder: (BuildContext context) => const PricesProofsPage(
selectProof: false,
),
),
),
Icons.receipt,
Expand Down
36 changes: 24 additions & 12 deletions packages/smooth_app/lib/pages/prices/price_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class PriceModel with ChangeNotifier {
required final List<OsmLocation>? locations,
required final Currency currency,
final PriceMetaProduct? initialProduct,
}) : proof = null,
}) : _proof = null,
_proofType = proofType,
_date = DateTime.now(),
_currency = currency,
Expand All @@ -29,12 +29,19 @@ class PriceModel with ChangeNotifier {
];

PriceModel.proof({
required Proof this.proof,
}) : _proofType = proof.type!,
_date = proof.date!,
_locations = null,
_currency = proof.currency!,
priceAmountModels = <PriceAmountModel>[];
required Proof proof,
}) : priceAmountModels = <PriceAmountModel>[] {
setProof(proof);
}

void setProof(final Proof proof) {
_proof = proof;
_cropParameters = null;
_proofType = proof.type!;
_date = proof.date!;
_locations = null;
_currency = proof.currency!;
}

/// Checks if a proof cannot be reused for prices adding.
///
Expand All @@ -49,6 +56,8 @@ class PriceModel with ChangeNotifier {
proof.imageThumbPath == null ||
proof.filePath == null;

bool get hasImage => _proof != null || _cropParameters != null;

final List<PriceAmountModel> priceAmountModels;

CropParameters? _cropParameters;
Expand All @@ -57,21 +66,24 @@ class PriceModel with ChangeNotifier {

set cropParameters(final CropParameters? value) {
_cropParameters = value;
_proof = null;
notifyListeners();
}

final Proof? proof;
Proof? _proof;

Proof? get proof => _proof;

ProofType _proofType;
late ProofType _proofType;

ProofType get proofType => proof != null ? proof!.type! : _proofType;
ProofType get proofType => _proof != null ? _proof!.type! : _proofType;

set proofType(final ProofType proofType) {
_proofType = proofType;
notifyListeners();
}

DateTime _date;
late DateTime _date;

DateTime get date => _date;

Expand All @@ -96,7 +108,7 @@ class PriceModel with ChangeNotifier {
? OsmLocation.fromPrice(proof!.location!)
: _locations!.firstOrNull;

Currency _currency;
late Currency _currency;

Currency get currency => _currency;

Expand Down
141 changes: 110 additions & 31 deletions packages/smooth_app/lib/pages/prices/price_proof_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ 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/data_models/preferences/user_preferences.dart';
import 'package:smooth_app/generic_lib/buttons/smooth_large_button_with_icon.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_card.dart';
import 'package:smooth_app/helpers/camera_helper.dart';
import 'package:smooth_app/pages/crop_parameters.dart';
import 'package:smooth_app/pages/image_crop_page.dart';
import 'package:smooth_app/pages/prices/price_model.dart';
import 'package:smooth_app/pages/prices/prices_proofs_page.dart';
import 'package:smooth_app/pages/proof_crop_helper.dart';
import 'package:smooth_app/query/product_query.dart';

Expand All @@ -29,20 +32,17 @@ class PriceProofCard extends StatelessWidget {
children: <Widget>[
Text(appLocalizations.prices_proof_subtitle),
if (model.proof != null)
LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) =>
Image(
image: NetworkImage(
model.proof!
.getFileUrl(
uriProductHelper: ProductQuery.uriPricesHelper,
isThumbnail: true,
)!
.toString(),
),
Image(
image: NetworkImage(
model.proof!
.getFileUrl(
uriProductHelper: ProductQuery.uriPricesHelper,
isThumbnail: true,
)!
.toString(),
),
),
if (model.cropParameters != null)
)
else if (model.cropParameters != null)
LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) =>
Image(
Expand All @@ -53,29 +53,24 @@ class PriceProofCard extends StatelessWidget {
height: constraints.maxWidth,
),
),
//Text(model.cropParameters!.smallCroppedFile.path),
SmoothLargeButtonWithIcon(
text: model.proof == null && model.cropParameters == null
text: !model.hasImage
? appLocalizations.prices_proof_find
: model.proofType == ProofType.receipt
? appLocalizations.prices_proof_receipt
: appLocalizations.prices_proof_price_tag,
icon: model.proof == null && model.cropParameters == null
? _iconTodo
: _iconDone,
onPressed: model.proof != null
? null
: () async {
final CropParameters? cropParameters =
await confirmAndUploadNewImage(
context,
cropHelper: ProofCropHelper(model: model),
isLoggedInMandatory: true,
);
if (cropParameters != null) {
model.cropParameters = cropParameters;
}
},
icon: !model.hasImage ? _iconTodo : _iconDone,
onPressed: () async {
final _ProofSource? proofSource =
await _ProofSource.select(context);
if (proofSource == null) {
return;
}
if (!context.mounted) {
return;
}
return proofSource.process(context, model);
},
),
LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) => Row(
Expand Down Expand Up @@ -112,3 +107,87 @@ class PriceProofCard extends StatelessWidget {
);
}
}

enum _ProofSource {
camera,
gallery,
history;

Future<void> process(
final BuildContext context,
final PriceModel model,
) async {
switch (this) {
case _ProofSource.history:
final Proof? proof = await Navigator.of(context).push<Proof>(
MaterialPageRoute<Proof>(
builder: (BuildContext context) => const PricesProofsPage(
selectProof: true,
),
),
);
if (proof != null) {
model.setProof(proof);
model.notifyListeners();
}
return;
case _ProofSource.camera:
case _ProofSource.gallery:
final UserPictureSource source = this == _ProofSource.gallery
? UserPictureSource.GALLERY
: UserPictureSource.CAMERA;
final CropParameters? cropParameters = await confirmAndUploadNewImage(
context,
cropHelper: ProofCropHelper(model: model),
isLoggedInMandatory: true,
forcedSource: source,
);
if (cropParameters != null) {
model.cropParameters = cropParameters;
}
}
}

static Future<_ProofSource?> select(final BuildContext context) async {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
return showCupertinoModalPopup<_ProofSource>(
context: context,
builder: (final BuildContext context) => CupertinoActionSheet(
title: Text(appLocalizations.prices_proof_find),
cancelButton: CupertinoActionSheetAction(
onPressed: () => Navigator.of(context).pop(),
child: Text(
appLocalizations.cancel,
),
),
actions: <Widget>[
if (CameraHelper.hasACamera)
CupertinoActionSheetAction(
onPressed: () => Navigator.of(context).pop(
_ProofSource.camera,
),
child: Text(
appLocalizations.settings_app_camera,
),
),
CupertinoActionSheetAction(
onPressed: () => Navigator.of(context).pop(
_ProofSource.gallery,
),
child: Text(
appLocalizations.gallery_source_label,
),
),
CupertinoActionSheetAction(
onPressed: () => Navigator.of(context).pop(
_ProofSource.history,
),
child: Text(
appLocalizations.user_search_proofs_title,
),
),
],
),
);
}
}
29 changes: 20 additions & 9 deletions packages/smooth_app/lib/pages/prices/prices_proofs_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ import 'package:smooth_app/widgets/smooth_scaffold.dart';

/// Page that displays the latest proofs of the current user.
class PricesProofsPage extends StatefulWidget {
const PricesProofsPage();
const PricesProofsPage({
required this.selectProof,
});

/// Do we want to select a proof (true), or just to see its details (false)?
final bool selectProof;

@override
State<PricesProofsPage> createState() => _PricesProofsPageState();
Expand Down Expand Up @@ -119,15 +124,21 @@ class _PricesProofsPageState extends State<PricesProofsPage>
);
}
return InkWell(
onTap: () async => Navigator.push<void>(
context,
MaterialPageRoute<void>(
builder: (BuildContext context) =>
PriceProofPage(
proof,
onTap: () async {
if (widget.selectProof) {
Navigator.of(context).pop(proof);
return;
}
return Navigator.push<void>(
context,
MaterialPageRoute<void>(
builder: (BuildContext context) =>
PriceProofPage(
proof,
),
),
),
), // PriceProofPage
);
}, // PriceProofPage
child: _PriceProofImage(proof,
squareSize: squareSize),
);
Expand Down

0 comments on commit 389b0ff

Please sign in to comment.