Skip to content

Commit

Permalink
feat: Product page: notify when there are pending operations (#5947)
Browse files Browse the repository at this point in the history
* Product page: notify when there are pending operations

* Fix warning
  • Loading branch information
g123k authored Nov 27, 2024
1 parent 452b05a commit 57e13ea
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ class _ProductPictureWithImageProvider extends StatelessWidget {
color: lightTheme ? Colors.white : Colors.black,
child: ClipRRect(
child: Opacity(
opacity: lightTheme ? 0.2 : 0.55,
opacity: lightTheme ? 0.3 : 0.55,
child: ImageFiltered(
imageFilter: ImageFilter.blur(sigmaX: 8.0, sigmaY: 8.0),
child: Image(
Expand Down
24 changes: 24 additions & 0 deletions packages/smooth_app/lib/helpers/color_extension.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import 'package:flutter/painting.dart';

/// Code from https://stackoverflow.com/questions/58360989
extension ColorExtension on Color {
Color darken([double amount = .1]) {
assert(amount >= 0 && amount <= 1);

final HSLColor hsl = HSLColor.fromColor(this);
final HSLColor hslDark =
hsl.withLightness((hsl.lightness - amount).clamp(0.0, 1.0));

return hslDark.toColor();
}

Color lighten([double amount = .1]) {
assert(amount >= 0 && amount <= 1);

final HSLColor hsl = HSLColor.fromColor(this);
final HSLColor hslLight =
hsl.withLightness((hsl.lightness + amount).clamp(0.0, 1.0));

return hslLight.toColor();
}
}
8 changes: 8 additions & 0 deletions packages/smooth_app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -3277,6 +3277,14 @@
"@product_page_action_bar_item_disable": {
"description": "Accessibility label to disable action (= make it invisible)"
},
"product_page_pending_operations_banner_title": "Uploading your edits…",
"@product_page_pending_operations_banner_title": {
"description": "When a product has pending edits (being sent to the server), there is a message on the product page (here is the title of the message)."
},
"product_page_pending_operations_banner_message": "The data displayed on this page **does not yet reflect your modifications**.\nPlease wait a few seconds…",
"@product_page_pending_operations_banner_message": {
"description": "When a product has pending edits (being sent to the server), there is a message on the product page. Please keep the ** syntax to make the text bold."
},
"product_add_a_language": "Add a language",
"@product_add_a_language": {
"description": "Button to add a language (eg: for photos) to a product"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,11 @@ class _SmoothAutocompleteTextFieldState
void _setLoading(bool loading) {
if (_loading != loading) {
WidgetsBinding.instance.addPostFrameCallback(
(_) => setState(() => _loading = loading),
(_) {
if (context.mounted) {
setState(() => _loading = loading);
}
},
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,10 @@ class _ProductHeaderName extends StatelessWidget {
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 17.0,
height: 0.9,
height: 1.0,
),
strutStyle: const StrutStyle(
forceStrutHeight: true,
),
),
Text(
Expand Down Expand Up @@ -286,6 +289,9 @@ class _ProductCompatibilityScore extends StatelessWidget {
BuildContext context,
ProductPageCompatibility compatibility,
) {
final String compatibilityLabel =
AppLocalizations.of(context).product_page_compatibility_score;

return IntrinsicHeight(
child: Row(
children: <Widget>[
Expand Down Expand Up @@ -314,26 +320,32 @@ class _ProductCompatibilityScore extends StatelessWidget {
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(
padding: const EdgeInsetsDirectional.only(
top: 6.0,
bottom: 8.0,
bottom: SMALL_SPACE,
start: 6.0,
end: 6.0,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
'${compatibility.score}%',
maxLines: 1,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 12.0,
height: 0.9,
fontWeight: FontWeight.bold,
),
),
Text(
AppLocalizations.of(context)
.product_page_compatibility_score,
style: const TextStyle(
fontSize: 9.0,
compatibilityLabel,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
style: TextStyle(
fontSize: _getCompatibilityFontSize(compatibilityLabel),
height: 0.9,
fontWeight: FontWeight.w500,
),
Expand All @@ -347,6 +359,24 @@ class _ProductCompatibilityScore extends StatelessWidget {
);
}

double _getCompatibilityFontSize(String compatibilityLabel) {
final int length = compatibilityLabel.length;

if (length < 13) {
return 9.0;
} else if (length == 13) {
return 8.5;
} else if (length == 14) {
return 7.5;
} else if (length == 15) {
return 7.0;
} else if (length == 16) {
return 6.5;
} else {
return 6.0;
}
}

double computeWidth(BuildContext context) {
return math.min(
80.0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:provider/single_child_widget.dart';
import 'package:smooth_app/data_models/preferences/user_preferences.dart';
import 'package:smooth_app/data_models/product_list.dart';
import 'package:smooth_app/data_models/product_preferences.dart';
import 'package:smooth_app/data_models/up_to_date_changes.dart';
import 'package:smooth_app/data_models/up_to_date_mixin.dart';
import 'package:smooth_app/database/dao_product_last_access.dart';
import 'package:smooth_app/database/dao_product_list.dart';
Expand All @@ -20,6 +21,7 @@ import 'package:smooth_app/pages/prices/prices_card.dart';
import 'package:smooth_app/pages/product/common/product_refresher.dart';
import 'package:smooth_app/pages/product/product_page/footer/new_product_footer.dart';
import 'package:smooth_app/pages/product/product_page/new_product_header.dart';
import 'package:smooth_app/pages/product/product_page/new_product_page_loading_indicator.dart';
import 'package:smooth_app/pages/product/product_questions_widget.dart';
import 'package:smooth_app/pages/product/reorderable_knowledge_panel_page.dart';
import 'package:smooth_app/pages/product/reordered_knowledge_panel_cards.dart';
Expand Down Expand Up @@ -83,14 +85,17 @@ class ProductPageState extends State<ProductPage>
Theme.of(context).extension<SmoothColorsThemeExtension>()!;

_productPreferences = context.watch<ProductPreferences>();
context.watch<LocalDatabase>();
final LocalDatabase localDatabase = context.watch<LocalDatabase>();
refreshUpToDate();

final MatchedProductV2 matchedProductV2 = MatchedProductV2(
upToDateProduct,
_productPreferences,
);

final bool hasPendingOperations = UpToDateChanges(localDatabase)
.hasNotTerminatedOperations(upToDateProduct.barcode!);

return MultiProvider(
providers: <SingleChildWidget>[
Provider<Product>.value(value: upToDateProduct),
Expand Down Expand Up @@ -135,9 +140,11 @@ class ProductPageState extends State<ProductPage>
setState(() => bottomPadding = size.height);
}
},
child: ProductQuestionsWidget(
upToDateProduct,
),
child: !hasPendingOperations
? const ProductPageLoadingIndicator()
: ProductQuestionsWidget(
upToDateProduct,
),
),
),
],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import 'package:smooth_app/helpers/color_extension.dart';
import 'package:smooth_app/pages/product/product_page/new_product_page.dart';
import 'package:smooth_app/resources/app_animations.dart';
import 'package:smooth_app/themes/theme_provider.dart';
import 'package:smooth_app/widgets/smooth_banner.dart';

class ProductPageLoadingIndicator extends StatelessWidget {
const ProductPageLoadingIndicator({super.key});

@override
Widget build(BuildContext context) {
final AppLocalizations appLocalizations = AppLocalizations.of(context);

final bool lightTheme = context.lightTheme();
final Color color = context.watch<ProductPageCompatibility>().color ??
(lightTheme ? Colors.grey : Colors.grey[600]!);

return SmoothBanner(
icon: CloudUploadAnimation(
size: MediaQuery.sizeOf(context).width * 0.10,
),
iconAlignment: AlignmentDirectional.center,
iconBackgroundColor: color,
title: appLocalizations.product_page_pending_operations_banner_title,
titleColor: lightTheme ? null : Colors.white,
contentBackgroundColor:
lightTheme ? color.lighten(0.6) : color.darken(0.3),
contentColor: lightTheme ? null : Colors.grey[200],
topShadow: true,
content: appLocalizations.product_page_pending_operations_banner_message,
);
}
}
18 changes: 18 additions & 0 deletions packages/smooth_app/lib/resources/app_animations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import 'package:rive/rive.dart';
// ignore: implementation_imports
import 'package:rive/src/rive_core/component.dart';
import 'package:scanner_shared/scanner_shared.dart';
import 'package:smooth_app/cards/category_cards/svg_cache.dart';
import 'package:smooth_app/helpers/haptic_feedback_helper.dart';
Expand Down Expand Up @@ -85,16 +87,19 @@ class BarcodeAnimation extends StatelessWidget {
class CloudUploadAnimation extends StatelessWidget {
const CloudUploadAnimation({
required this.size,
this.color,
super.key,
}) : _circleColor = null;

const CloudUploadAnimation.circle({
required this.size,
this.color,
Color? circleColor,
super.key,
}) : _circleColor = circleColor ?? Colors.black54;

final double size;
final Color? color;
final Color? _circleColor;

@override
Expand All @@ -105,6 +110,19 @@ class CloudUploadAnimation extends StatelessWidget {
AnimationsLoader.of(context)!,
artboard: 'Cloud upload',
animations: const <String>['Animation'],
onInit: (Artboard artboard) {
if (color != null) {
artboard.forEachComponent(
(Component child) {
if (child is Stroke) {
child.paint.color = color!;
} else if (child is SolidColor) {
child.color = color!;
}
},
);
}
},
),
);

Expand Down
38 changes: 26 additions & 12 deletions packages/smooth_app/lib/widgets/smooth_banner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,25 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
import 'package:smooth_app/widgets/smooth_close_button.dart';
import 'package:smooth_app/widgets/smooth_text.dart';

class SmoothBanner extends StatelessWidget {
const SmoothBanner({
required this.icon,
required this.title,
required this.content,
this.titleColor,
this.contentColor,
this.iconAlignment,
this.iconColor,
this.iconBackgroundColor,
this.contentBackgroundColor,
this.onDismissClicked,
this.topShadow = false,
super.key,
});

final AlignmentGeometry? iconAlignment;
final Widget icon;
final String title;
final String content;
Expand All @@ -21,6 +29,12 @@ class SmoothBanner extends StatelessWidget {
final ValueChanged<SmoothBannerDismissEvent>? onDismissClicked;
final bool topShadow;

final Color? iconColor;
final Color? iconBackgroundColor;
final Color? titleColor;
final Color? contentColor;
final Color? contentBackgroundColor;

static const Color _titleColor = Color(0xFF373737);

@override
Expand All @@ -34,15 +48,15 @@ class SmoothBanner extends StatelessWidget {
child: Container(
width: double.infinity,
height: double.infinity,
color: const Color(0xFFE4E4E4),
color: iconBackgroundColor ?? const Color(0xFFE4E4E4),
padding: const EdgeInsetsDirectional.symmetric(
horizontal: LARGE_SPACE,
vertical: MEDIUM_SPACE,
),
alignment: AlignmentDirectional.topCenter,
alignment: iconAlignment ?? AlignmentDirectional.topCenter,
child: IconTheme(
data: const IconThemeData(
color: Color(0xFF373737),
data: IconThemeData(
color: iconColor ?? const Color(0xFF373737),
),
child: icon,
),
Expand All @@ -53,7 +67,7 @@ class SmoothBanner extends StatelessWidget {
flex: 85,
child: Container(
width: double.infinity,
color: const Color(0xFFECECEC),
color: contentBackgroundColor ?? const Color(0xFFECECEC),
padding: EdgeInsetsDirectional.only(
start: MEDIUM_SPACE,
end: MEDIUM_SPACE,
Expand All @@ -70,10 +84,10 @@ class SmoothBanner extends StatelessWidget {
Expanded(
child: Text(
title,
style: const TextStyle(
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
color: _titleColor,
color: titleColor ?? _titleColor,
),
),
),
Expand All @@ -83,7 +97,7 @@ class SmoothBanner extends StatelessWidget {
onClose: () => onDismissClicked!.call(
SmoothBannerDismissEvent.fromButton,
),
circleColor: _titleColor,
circleColor: titleColor ?? _titleColor,
crossColor: Colors.white,
circleSize: 26.0,
crossSize: 12.0,
Expand All @@ -95,11 +109,11 @@ class SmoothBanner extends StatelessWidget {
),
if (onDismissClicked == null)
const SizedBox(height: VERY_SMALL_SPACE),
Text(
content,
style: const TextStyle(
TextWithBoldParts(
text: content,
textStyle: TextStyle(
fontSize: 14.0,
color: Color(0xFF373737),
color: contentColor ?? const Color(0xFF373737),
),
),
],
Expand Down

0 comments on commit 57e13ea

Please sign in to comment.