-
-
Notifications
You must be signed in to change notification settings - Fork 280
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: Extract ingredients/packaging: loading / loaded / extracting states #5384
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know it's your way of coding, putting several classes in the same file, and my way of coding is rather one class per file. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I will move There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm ok with subwidgets, I just don't like big files - and giant widgets ;) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ 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:shimmer/shimmer.dart'; | ||
import 'package:smooth_app/background/background_task_details.dart'; | ||
import 'package:smooth_app/data_models/up_to_date_mixin.dart'; | ||
import 'package:smooth_app/database/local_database.dart'; | ||
|
@@ -18,6 +19,7 @@ import 'package:smooth_app/pages/product/explanation_widget.dart'; | |
import 'package:smooth_app/pages/product/multilingual_helper.dart'; | ||
import 'package:smooth_app/pages/product/ocr_helper.dart'; | ||
import 'package:smooth_app/pages/product/product_image_button.dart'; | ||
import 'package:smooth_app/themes/smooth_theme_colors.dart'; | ||
import 'package:smooth_app/widgets/smooth_scaffold.dart'; | ||
|
||
/// Editing with OCR a product field and the corresponding image. | ||
|
@@ -43,6 +45,7 @@ class _EditOcrPageState extends State<EditOcrPage> with UpToDateMixin { | |
late final MultilingualHelper _multilingualHelper; | ||
|
||
OcrHelper get _helper => widget.helper; | ||
bool _extractingData = false; | ||
|
||
@override | ||
void initState() { | ||
|
@@ -62,7 +65,8 @@ class _EditOcrPageState extends State<EditOcrPage> with UpToDateMixin { | |
/// | ||
/// When done, populates the related page field. | ||
Future<void> _extractData() async { | ||
// TODO(monsieurtanuki): hide the "extract" button while extracting, or display a loading dialog on top | ||
setState(() => _extractingData = true); | ||
|
||
try { | ||
final String? extractedText = await _helper.getExtractedText( | ||
widget.product, | ||
|
@@ -72,6 +76,8 @@ class _EditOcrPageState extends State<EditOcrPage> with UpToDateMixin { | |
return; | ||
} | ||
|
||
setState(() => _extractingData = false); | ||
|
||
if (extractedText == null || extractedText.isEmpty) { | ||
await LoadingDialog.error( | ||
context: context, | ||
|
@@ -83,9 +89,7 @@ class _EditOcrPageState extends State<EditOcrPage> with UpToDateMixin { | |
if (_controller.text != extractedText) { | ||
setState(() => _controller.text = extractedText); | ||
} | ||
} catch (e) { | ||
// | ||
} | ||
} catch (_) {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So you're not resetting There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice catch 😆 |
||
} | ||
|
||
/// Updates the product field on the server. | ||
|
@@ -282,17 +286,11 @@ class _EditOcrPageState extends State<EditOcrPage> with UpToDateMixin { | |
setState: setState, | ||
product: upToDateProduct, | ||
), | ||
if (transientFile.isServerImage()) | ||
SmoothActionButtonsBar.single( | ||
action: SmoothActionButton( | ||
text: | ||
_helper.getActionExtractText(appLocalizations), | ||
onPressed: () async => _extractData(), | ||
), | ||
) | ||
else if (transientFile.isImageAvailable()) | ||
// TODO(monsieurtanuki): what if slow upload? text instead? | ||
const CircularProgressIndicator.adaptive(), | ||
_EditOcrMainAction( | ||
onPressed: _extractData, | ||
helper: _helper, | ||
state: _extractState(transientFile), | ||
), | ||
const SizedBox(height: MEDIUM_SPACE), | ||
TextField( | ||
controller: _controller, | ||
|
@@ -377,4 +375,224 @@ class _EditOcrPageState extends State<EditOcrPage> with UpToDateMixin { | |
} | ||
return result; | ||
} | ||
|
||
OcrState _extractState(TransientFile transientFile) { | ||
if (_extractingData) { | ||
return OcrState.EXTRACTING_DATA; | ||
} else if (transientFile.isServerImage()) { | ||
return OcrState.IMAGE_LOADED; | ||
} else if (transientFile.isImageAvailable()) { | ||
return OcrState.IMAGE_LOADING; | ||
} else { | ||
return OcrState.OTHER; | ||
} | ||
} | ||
} | ||
Comment on lines
+379
to
+390
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks brillant! |
||
|
||
class _EditOcrMainAction extends StatelessWidget { | ||
const _EditOcrMainAction({ | ||
required this.onPressed, | ||
required this.helper, | ||
required this.state, | ||
}); | ||
|
||
final VoidCallback onPressed; | ||
final OcrHelper helper; | ||
final OcrState state; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
final AppLocalizations appLocalizations = AppLocalizations.of(context); | ||
|
||
final Widget? child = switch (state) { | ||
OcrState.IMAGE_LOADING => _EditOcrActionLoadingContent( | ||
helper: helper, | ||
appLocalizations: appLocalizations, | ||
), | ||
OcrState.IMAGE_LOADED => _ExtractMainActionContentLoaded( | ||
helper: helper, | ||
appLocalizations: appLocalizations, | ||
onPressed: onPressed, | ||
), | ||
OcrState.EXTRACTING_DATA => _EditOcrActionExtractingContent( | ||
helper: helper, | ||
appLocalizations: appLocalizations, | ||
), | ||
OcrState.OTHER => null, | ||
}; | ||
|
||
if (child == null) { | ||
return EMPTY_WIDGET; | ||
} | ||
|
||
final SmoothColorsThemeExtension theme = | ||
Theme.of(context).extension<SmoothColorsThemeExtension>()!; | ||
|
||
return SizedBox( | ||
height: 45.0 * (_computeFontScaleFactor(context)), | ||
width: double.infinity, | ||
child: DecoratedBox( | ||
decoration: BoxDecoration( | ||
borderRadius: ANGULAR_BORDER_RADIUS, | ||
color: theme.primarySemiDark, | ||
border: Border.all( | ||
color: theme.primaryBlack, | ||
width: 2.0, | ||
), | ||
), | ||
child: Material( | ||
type: MaterialType.transparency, | ||
child: ProgressIndicatorTheme( | ||
data: const ProgressIndicatorThemeData( | ||
color: Colors.white, | ||
), | ||
child: IconTheme( | ||
data: const IconThemeData(color: Colors.white), | ||
child: DefaultTextStyle( | ||
style: const TextStyle( | ||
fontWeight: FontWeight.bold, | ||
fontSize: 15.5, | ||
color: Colors.white, | ||
), | ||
child: child, | ||
), | ||
), | ||
), | ||
), | ||
), | ||
); | ||
} | ||
|
||
double _computeFontScaleFactor(BuildContext context) { | ||
final double fontSize = DefaultTextStyle.of(context).style.fontSize ?? 15.0; | ||
final double scaledFontSize = | ||
MediaQuery.textScalerOf(context).scale(fontSize); | ||
|
||
return scaledFontSize / fontSize; | ||
} | ||
} | ||
|
||
class _EditOcrActionExtractingContent extends StatelessWidget { | ||
const _EditOcrActionExtractingContent({ | ||
required this.helper, | ||
required this.appLocalizations, | ||
}); | ||
|
||
final OcrHelper helper; | ||
final AppLocalizations appLocalizations; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return Semantics( | ||
label: helper.getActionLoadingPhoto(appLocalizations), | ||
excludeSemantics: true, | ||
child: Shimmer( | ||
gradient: const LinearGradient( | ||
begin: Alignment.centerLeft, | ||
end: Alignment.centerRight, | ||
colors: <Color>[ | ||
Colors.black, | ||
Colors.white, | ||
Colors.black, | ||
], | ||
), | ||
child: Padding( | ||
padding: const EdgeInsets.symmetric(horizontal: MEDIUM_SPACE), | ||
child: Row( | ||
mainAxisAlignment: MainAxisAlignment.center, | ||
crossAxisAlignment: CrossAxisAlignment.center, | ||
children: <Widget>[ | ||
const CircularProgressIndicator.adaptive(), | ||
Expanded( | ||
child: Text( | ||
helper.getActionExtractingData(appLocalizations), | ||
textAlign: TextAlign.center, | ||
), | ||
), | ||
const CircularProgressIndicator.adaptive(), | ||
Comment on lines
+505
to
+512
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 2 progress indicators? A bit too much, isn't it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can see it, on the video, with the Shimmer effect, actually it's OK. |
||
], | ||
), | ||
), | ||
), | ||
); | ||
} | ||
} | ||
|
||
class _ExtractMainActionContentLoaded extends StatelessWidget { | ||
const _ExtractMainActionContentLoaded({ | ||
required this.helper, | ||
required this.appLocalizations, | ||
required this.onPressed, | ||
}); | ||
|
||
final OcrHelper helper; | ||
final AppLocalizations appLocalizations; | ||
final VoidCallback onPressed; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return Semantics( | ||
excludeSemantics: true, | ||
value: helper.getActionExtractText(appLocalizations), | ||
button: true, | ||
child: InkWell( | ||
onTap: onPressed, | ||
borderRadius: ANGULAR_BORDER_RADIUS, | ||
child: Padding( | ||
padding: const EdgeInsets.symmetric(horizontal: MEDIUM_SPACE), | ||
child: Row( | ||
mainAxisAlignment: MainAxisAlignment.center, | ||
crossAxisAlignment: CrossAxisAlignment.center, | ||
children: <Widget>[ | ||
Expanded( | ||
child: Text( | ||
helper.getActionExtractText(appLocalizations), | ||
), | ||
), | ||
const Icon( | ||
Icons.download, | ||
semanticLabel: '', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is that worth it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, otherwise, the screen reader tries to say the character in font (that's the default behavior) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Hmm, I guess that wouldn't hurt to say "download". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's not needed, because we have the text just before. |
||
), | ||
], | ||
), | ||
), | ||
), | ||
); | ||
} | ||
} | ||
|
||
class _EditOcrActionLoadingContent extends StatelessWidget { | ||
const _EditOcrActionLoadingContent({ | ||
required this.helper, | ||
required this.appLocalizations, | ||
}); | ||
|
||
final OcrHelper helper; | ||
final AppLocalizations appLocalizations; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return Padding( | ||
padding: const EdgeInsets.symmetric(horizontal: MEDIUM_SPACE), | ||
child: Row( | ||
mainAxisAlignment: MainAxisAlignment.center, | ||
crossAxisAlignment: CrossAxisAlignment.center, | ||
children: <Widget>[ | ||
Expanded( | ||
child: Text( | ||
helper.getActionLoadingPhoto(appLocalizations), | ||
), | ||
), | ||
const CircularProgressIndicator.adaptive(), | ||
], | ||
), | ||
); | ||
} | ||
} | ||
|
||
enum OcrState { | ||
IMAGE_LOADING, | ||
IMAGE_LOADED, | ||
EXTRACTING_DATA, | ||
OTHER, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm really not convinced it's such a brillant idea to change translation tags, as I fear it wipes out all the previous translations. For a very limited added-value: fixing an invisible useless typo.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, it's now merged :/
@teleomon Is-it somehow possible to move an old translation to a new one?