From a3a02d1b12f40f93e75cbfe11d7f206e31a44c79 Mon Sep 17 00:00:00 2001 From: monsieurtanuki Date: Sun, 24 Apr 2022 22:32:08 +0200 Subject: [PATCH] fix: #1478 - additional cache folder for tintable svg files (#1612) * fix: #1478 - additional cache folder for tintable svg files New files: * `arrow-top-right-thick.svg`: (tintable) optimized version of https://static.openfoodfacts.org/images/icons/dist/arrow-top-right-thick.svg * `vegetarian.svg`: (tintable) version of vegetarian.svg, taken from https://static.openfoodfacts.org/images/icons/dist/vegetarian.svg and optimized Moved files (from `assets/cache` to `assets/cacheTintable`): * `activity-walking.svg` * `agriculture.svg` * `arrow-bottom-right-thick.svg` * `car.svg` * `egg.svg` * `leaf.svg` * `monkey_happy.svg` * `monkey_uncertain.svg` * `monkey_unhappy.svg` * `packaging.svg` * `palm-oil.svg` * `public.svg` * `scale-balance.svg` * `transportation.svg` Impacted files: * `abstract_cache.dart`: refactored with new protected method `getFilename` * `pubspec.yaml`: added an asset folder for tintable svg cached files * `svg_async_asset.svg`: added comments about the new asset folder * `svg_cache.dart`: now we use 2 cache folders - one for colored icons, one for tintable icons that need a color as parameter * fix: #1478 - several asset cache folders Deleted file: * `abstract_async_asset.dart` New file: * `asset_cache_helper.dart`: Asset cache helper class Impacted files: * `abstract_cache.dart`: now we deal with several possible asset cache files (folders) * `raster_async_asset.svg`: now a `StatefulWidget` that uses new class `AssetCacheHelper` * `raster_cache.svg`: now uses new class `AssetCacheHelper` * `svg_async_asset.svg`: now a `StatefulWidget` that uses new class `AssetCacheHelper` * `svg_cache.dart`: now uses new class `AssetCacheHelper` --- .../activity-walking.svg | 0 .../{cache => cacheTintable}/agriculture.svg | 0 .../arrow-bottom-right-thick.svg | 0 .../cacheTintable/arrow-top-right-thick.svg | 1 + .../assets/{cache => cacheTintable}/car.svg | 0 .../assets/{cache => cacheTintable}/egg.svg | 0 .../assets/{cache => cacheTintable}/leaf.svg | 0 .../{cache => cacheTintable}/monkey_happy.svg | 0 .../monkey_uncertain.svg | 0 .../monkey_unhappy.svg | 0 .../{cache => cacheTintable}/packaging.svg | 0 .../{cache => cacheTintable}/palm-oil.svg | 0 .../{cache => cacheTintable}/public.svg | 0 .../scale-balance.svg | 0 .../transportation.svg | 0 .../assets/cacheTintable/vegetarian.svg | 1 + .../category_cards/abstract_async_asset.dart | 28 --------- .../cards/category_cards/abstract_cache.dart | 27 +++++++- .../category_cards/asset_cache_helper.dart | 33 ++++++++++ .../category_cards/raster_async_asset.dart | 47 ++++++++------ .../cards/category_cards/raster_cache.dart | 15 +++-- .../cards/category_cards/svg_async_asset.dart | 61 ++++++++++++------- .../lib/cards/category_cards/svg_cache.dart | 36 ++++++++--- packages/smooth_app/pubspec.yaml | 1 + 24 files changed, 167 insertions(+), 83 deletions(-) rename packages/smooth_app/assets/{cache => cacheTintable}/activity-walking.svg (100%) rename packages/smooth_app/assets/{cache => cacheTintable}/agriculture.svg (100%) rename packages/smooth_app/assets/{cache => cacheTintable}/arrow-bottom-right-thick.svg (100%) create mode 100644 packages/smooth_app/assets/cacheTintable/arrow-top-right-thick.svg rename packages/smooth_app/assets/{cache => cacheTintable}/car.svg (100%) rename packages/smooth_app/assets/{cache => cacheTintable}/egg.svg (100%) rename packages/smooth_app/assets/{cache => cacheTintable}/leaf.svg (100%) rename packages/smooth_app/assets/{cache => cacheTintable}/monkey_happy.svg (100%) rename packages/smooth_app/assets/{cache => cacheTintable}/monkey_uncertain.svg (100%) rename packages/smooth_app/assets/{cache => cacheTintable}/monkey_unhappy.svg (100%) rename packages/smooth_app/assets/{cache => cacheTintable}/packaging.svg (100%) rename packages/smooth_app/assets/{cache => cacheTintable}/palm-oil.svg (100%) rename packages/smooth_app/assets/{cache => cacheTintable}/public.svg (100%) rename packages/smooth_app/assets/{cache => cacheTintable}/scale-balance.svg (100%) rename packages/smooth_app/assets/{cache => cacheTintable}/transportation.svg (100%) create mode 100644 packages/smooth_app/assets/cacheTintable/vegetarian.svg delete mode 100644 packages/smooth_app/lib/cards/category_cards/abstract_async_asset.dart create mode 100644 packages/smooth_app/lib/cards/category_cards/asset_cache_helper.dart diff --git a/packages/smooth_app/assets/cache/activity-walking.svg b/packages/smooth_app/assets/cacheTintable/activity-walking.svg similarity index 100% rename from packages/smooth_app/assets/cache/activity-walking.svg rename to packages/smooth_app/assets/cacheTintable/activity-walking.svg diff --git a/packages/smooth_app/assets/cache/agriculture.svg b/packages/smooth_app/assets/cacheTintable/agriculture.svg similarity index 100% rename from packages/smooth_app/assets/cache/agriculture.svg rename to packages/smooth_app/assets/cacheTintable/agriculture.svg diff --git a/packages/smooth_app/assets/cache/arrow-bottom-right-thick.svg b/packages/smooth_app/assets/cacheTintable/arrow-bottom-right-thick.svg similarity index 100% rename from packages/smooth_app/assets/cache/arrow-bottom-right-thick.svg rename to packages/smooth_app/assets/cacheTintable/arrow-bottom-right-thick.svg diff --git a/packages/smooth_app/assets/cacheTintable/arrow-top-right-thick.svg b/packages/smooth_app/assets/cacheTintable/arrow-top-right-thick.svg new file mode 100644 index 00000000000..c082078c6b9 --- /dev/null +++ b/packages/smooth_app/assets/cacheTintable/arrow-top-right-thick.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/smooth_app/assets/cache/car.svg b/packages/smooth_app/assets/cacheTintable/car.svg similarity index 100% rename from packages/smooth_app/assets/cache/car.svg rename to packages/smooth_app/assets/cacheTintable/car.svg diff --git a/packages/smooth_app/assets/cache/egg.svg b/packages/smooth_app/assets/cacheTintable/egg.svg similarity index 100% rename from packages/smooth_app/assets/cache/egg.svg rename to packages/smooth_app/assets/cacheTintable/egg.svg diff --git a/packages/smooth_app/assets/cache/leaf.svg b/packages/smooth_app/assets/cacheTintable/leaf.svg similarity index 100% rename from packages/smooth_app/assets/cache/leaf.svg rename to packages/smooth_app/assets/cacheTintable/leaf.svg diff --git a/packages/smooth_app/assets/cache/monkey_happy.svg b/packages/smooth_app/assets/cacheTintable/monkey_happy.svg similarity index 100% rename from packages/smooth_app/assets/cache/monkey_happy.svg rename to packages/smooth_app/assets/cacheTintable/monkey_happy.svg diff --git a/packages/smooth_app/assets/cache/monkey_uncertain.svg b/packages/smooth_app/assets/cacheTintable/monkey_uncertain.svg similarity index 100% rename from packages/smooth_app/assets/cache/monkey_uncertain.svg rename to packages/smooth_app/assets/cacheTintable/monkey_uncertain.svg diff --git a/packages/smooth_app/assets/cache/monkey_unhappy.svg b/packages/smooth_app/assets/cacheTintable/monkey_unhappy.svg similarity index 100% rename from packages/smooth_app/assets/cache/monkey_unhappy.svg rename to packages/smooth_app/assets/cacheTintable/monkey_unhappy.svg diff --git a/packages/smooth_app/assets/cache/packaging.svg b/packages/smooth_app/assets/cacheTintable/packaging.svg similarity index 100% rename from packages/smooth_app/assets/cache/packaging.svg rename to packages/smooth_app/assets/cacheTintable/packaging.svg diff --git a/packages/smooth_app/assets/cache/palm-oil.svg b/packages/smooth_app/assets/cacheTintable/palm-oil.svg similarity index 100% rename from packages/smooth_app/assets/cache/palm-oil.svg rename to packages/smooth_app/assets/cacheTintable/palm-oil.svg diff --git a/packages/smooth_app/assets/cache/public.svg b/packages/smooth_app/assets/cacheTintable/public.svg similarity index 100% rename from packages/smooth_app/assets/cache/public.svg rename to packages/smooth_app/assets/cacheTintable/public.svg diff --git a/packages/smooth_app/assets/cache/scale-balance.svg b/packages/smooth_app/assets/cacheTintable/scale-balance.svg similarity index 100% rename from packages/smooth_app/assets/cache/scale-balance.svg rename to packages/smooth_app/assets/cacheTintable/scale-balance.svg diff --git a/packages/smooth_app/assets/cache/transportation.svg b/packages/smooth_app/assets/cacheTintable/transportation.svg similarity index 100% rename from packages/smooth_app/assets/cache/transportation.svg rename to packages/smooth_app/assets/cacheTintable/transportation.svg diff --git a/packages/smooth_app/assets/cacheTintable/vegetarian.svg b/packages/smooth_app/assets/cacheTintable/vegetarian.svg new file mode 100644 index 00000000000..aaa8045f99b --- /dev/null +++ b/packages/smooth_app/assets/cacheTintable/vegetarian.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/smooth_app/lib/cards/category_cards/abstract_async_asset.dart b/packages/smooth_app/lib/cards/category_cards/abstract_async_asset.dart deleted file mode 100644 index 41a7e9e18ef..00000000000 --- a/packages/smooth_app/lib/cards/category_cards/abstract_async_asset.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/material.dart'; - -/// Widget with async load of asset file. -abstract class AbstractAsyncAsset extends StatelessWidget { - const AbstractAsyncAsset( - this.fullFilename, - this.url, { - this.width, - this.height, - }); - - /// Full asset name, e.g. 'assets/cache/ab-agriculture-biologique.74x90.svg' - final String fullFilename; - - /// URL (for debug purpose), e.g. https://static.openfoodfacts.org/images/lang/fr/labels/ab-agriculture-biologique.74x90.svg - final String url; - - final double? width; - final double? height; - - @protected - Widget getEmptySpace() => - SizedBox(width: width ?? height, height: height ?? width); - - @protected - void notFound() => - debugPrint('unexpected case: asset not found $fullFilename ($url)'); -} diff --git a/packages/smooth_app/lib/cards/category_cards/abstract_cache.dart b/packages/smooth_app/lib/cards/category_cards/abstract_cache.dart index 530e532b2e7..7e25e5c42c2 100644 --- a/packages/smooth_app/lib/cards/category_cards/abstract_cache.dart +++ b/packages/smooth_app/lib/cards/category_cards/abstract_cache.dart @@ -35,8 +35,30 @@ abstract class AbstractCache extends StatelessWidget { final double? height; final bool displayAssetWhileWaiting; + /// Returns a list of possible related cached filenames. @protected - String? getFullFilename() { + List getCachedFilenames() { + final List result = []; + final String? filename = getFilename(); + if (filename == null) { + return result; + } + result.add(getCacheFilename(filename)); + return result; + } + + /// Returns the path to the asset cached file (not tintable version). + @protected + String getCacheFilename(final String filename) => 'assets/cache/$filename'; + + /// Returns the path to the asset cached tintable file. + @protected + String getCacheTintableFilename(final String filename) => + 'assets/cacheTintable/$filename'; + + /// Returns the simple filename of the icon url (without the full path). + @protected + String? getFilename() { if (iconUrl == null) { return null; } @@ -44,8 +66,7 @@ abstract class AbstractCache extends StatelessWidget { if (position == -1) { return null; } - final String filename = iconUrl!.substring(position + 1); - return 'assets/cache/$filename'; + return iconUrl!.substring(position + 1); } @protected diff --git a/packages/smooth_app/lib/cards/category_cards/asset_cache_helper.dart b/packages/smooth_app/lib/cards/category_cards/asset_cache_helper.dart new file mode 100644 index 00000000000..dc48cd06764 --- /dev/null +++ b/packages/smooth_app/lib/cards/category_cards/asset_cache_helper.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; + +/// Asset cache helper class +class AssetCacheHelper { + const AssetCacheHelper( + this.cachedFilenames, + this.url, { + this.width, + this.height, + this.color, + }); + + /// Full asset names, e.g. 'assets/cache/ab-agriculture-biologique.74x90.svg' + final List cachedFilenames; + + /// URL (for debug purpose), e.g. https://static.openfoodfacts.org/images/lang/fr/labels/ab-agriculture-biologique.74x90.svg + final String url; + + final double? width; + final double? height; + final Color? color; + + Widget getEmptySpace() => SizedBox( + width: width ?? height, + height: height ?? width, + ); + + void notFound() => + debugPrint('unexpected case: asset not found $cachedFilenames ($url)'); + + Exception loadException() => + Exception('could not load any cached file ($cachedFilenames)'); +} diff --git a/packages/smooth_app/lib/cards/category_cards/raster_async_asset.dart b/packages/smooth_app/lib/cards/category_cards/raster_async_asset.dart index 9bef6fb7b21..d5b15d6ca1e 100644 --- a/packages/smooth_app/lib/cards/category_cards/raster_async_asset.dart +++ b/packages/smooth_app/lib/cards/category_cards/raster_async_asset.dart @@ -1,38 +1,49 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:smooth_app/cards/category_cards/abstract_async_asset.dart'; +import 'package:smooth_app/cards/category_cards/asset_cache_helper.dart'; /// Widget with async load of raster asset file (png, jpeg). -class RasterAsyncAsset extends AbstractAsyncAsset { - const RasterAsyncAsset( - final String fullFilename, - final String url, { - final double? width, - final double? height, - }) : super( - fullFilename, - url, - width: width, - height: height, - ); +class RasterAsyncAsset extends StatefulWidget { + const RasterAsyncAsset(this.assetCacheHelper); + + final AssetCacheHelper assetCacheHelper; + + @override + State createState() => _RasterAsyncAssetState(); +} + +class _RasterAsyncAssetState extends State { + late final Future _loading = _load(); + + Future _load() { + for (final String cachedFilename + in widget.assetCacheHelper.cachedFilenames) { + try { + return rootBundle.load(cachedFilename); + } catch (e) { + // + } + } + throw widget.assetCacheHelper.loadException(); + } @override Widget build(BuildContext context) => FutureBuilder( - future: rootBundle.load(fullFilename), + future: _loading, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done) { if (snapshot.data != null) { return Image.memory( snapshot.data!.buffer.asUint8List(), - width: width, - height: height, + width: widget.assetCacheHelper.width, + height: widget.assetCacheHelper.height, fit: BoxFit.contain, ); } else { - notFound(); + widget.assetCacheHelper.notFound(); } } - return getEmptySpace(); + return widget.assetCacheHelper.getEmptySpace(); }, ); } diff --git a/packages/smooth_app/lib/cards/category_cards/raster_cache.dart b/packages/smooth_app/lib/cards/category_cards/raster_cache.dart index 733ffea8347..76f1f5c6014 100644 --- a/packages/smooth_app/lib/cards/category_cards/raster_cache.dart +++ b/packages/smooth_app/lib/cards/category_cards/raster_cache.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:smooth_app/cards/category_cards/abstract_cache.dart'; +import 'package:smooth_app/cards/category_cards/asset_cache_helper.dart'; import 'package:smooth_app/cards/category_cards/raster_async_asset.dart'; /// Widget that displays a png/jpeg from network (and cache while waiting). @@ -18,8 +19,8 @@ class RasterCache extends AbstractCache { @override Widget build(BuildContext context) { - final String? fullFilename = getFullFilename(); - if (fullFilename == null) { + final List fullFilenames = getCachedFilenames(); + if (fullFilenames.isEmpty) { return getDefaultUnknown(); } return Image.network( @@ -36,8 +37,14 @@ class RasterCache extends AbstractCache { return child; } return displayAssetWhileWaiting - ? RasterAsyncAsset(fullFilename, iconUrl!, - width: width, height: height) + ? RasterAsyncAsset( + AssetCacheHelper( + fullFilenames, + iconUrl!, + width: width, + height: height, + ), + ) : getCircularProgressIndicator(); }, ); diff --git a/packages/smooth_app/lib/cards/category_cards/svg_async_asset.dart b/packages/smooth_app/lib/cards/category_cards/svg_async_asset.dart index 0c47a92369a..1a354a844c3 100644 --- a/packages/smooth_app/lib/cards/category_cards/svg_async_asset.dart +++ b/packages/smooth_app/lib/cards/category_cards/svg_async_asset.dart @@ -1,48 +1,63 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:smooth_app/cards/category_cards/abstract_async_asset.dart'; +import 'package:smooth_app/cards/category_cards/asset_cache_helper.dart'; /// Widget with async load of SVG asset file /// -/// SVG files may need to be optimized before being stored in the cache folder. +/// SVG files may need to be optimized before being stored in the cache folders. +/// There are two cache folders: +/// * assets/cache, where most files should be put +/// * assets/cacheTintable, where only colorless files should be put +/// As an example, vegetarian.svg is in both folders: +/// * the assets/cache version has different colors - no color should be applied +/// * the assets/cacheTintable version works with a color applied to it /// E.g. with https://jakearchibald.github.io/svgomg/ /// C.f. https://github.com/openfoodfacts/smooth-app/issues/52 -class SvgAsyncAsset extends AbstractAsyncAsset { - const SvgAsyncAsset( - final String fullFilename, - final String url, { - final double? width, - final double? height, - this.color, - }) : super( - fullFilename, - url, - width: width, - height: height, - ); +class SvgAsyncAsset extends StatefulWidget { + const SvgAsyncAsset(this.assetCacheHelper); - final Color? color; + final AssetCacheHelper assetCacheHelper; + + @override + State createState() => _SvgAsyncAssetState(); +} + +class _SvgAsyncAssetState extends State { + late final Future _loading = _load(); + + Future _load() async { + for (final String cachedFilename + in widget.assetCacheHelper.cachedFilenames) { + try { + return await rootBundle.loadString(cachedFilename); + } catch (e) { + // + } + } + throw widget.assetCacheHelper.loadException(); + } @override Widget build(BuildContext context) => FutureBuilder( - future: rootBundle.loadString(fullFilename), + future: _loading, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done) { if (snapshot.data != null) { return SvgPicture.string( snapshot.data!, - width: width, - height: height, - color: color, + width: widget.assetCacheHelper.width, + height: widget.assetCacheHelper.height, + color: widget.assetCacheHelper.color, fit: BoxFit.contain, - placeholderBuilder: (BuildContext context) => getEmptySpace(), + placeholderBuilder: (BuildContext context) => + widget.assetCacheHelper.getEmptySpace(), ); } else { - notFound(); + widget.assetCacheHelper.notFound(); } } - return getEmptySpace(); + return widget.assetCacheHelper.getEmptySpace(); }, ); } diff --git a/packages/smooth_app/lib/cards/category_cards/svg_cache.dart b/packages/smooth_app/lib/cards/category_cards/svg_cache.dart index 3f8abc0d5f6..c6936562cfd 100644 --- a/packages/smooth_app/lib/cards/category_cards/svg_cache.dart +++ b/packages/smooth_app/lib/cards/category_cards/svg_cache.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:smooth_app/cards/category_cards/abstract_cache.dart'; +import 'package:smooth_app/cards/category_cards/asset_cache_helper.dart'; import 'package:smooth_app/cards/category_cards/svg_async_asset.dart'; /// Widget that displays a svg from network (and cache while waiting). @@ -20,10 +21,29 @@ class SvgCache extends AbstractCache { final Color? color; + @override + List getCachedFilenames() { + final List result = []; + final String? filename = getFilename(); + if (filename == null) { + return result; + } + final String cacheFilename = getCacheFilename(filename); + final String cacheTintableFilename = getCacheTintableFilename(filename); + if (color == null) { + result.add(cacheFilename); + result.add(cacheTintableFilename); + } else { + result.add(cacheTintableFilename); + result.add(cacheFilename); + } + return result; + } + @override Widget build(BuildContext context) { - final String? fullFilename = getFullFilename(); - if (fullFilename == null) { + final List cachedFilenames = getCachedFilenames(); + if (cachedFilenames.isEmpty) { return getDefaultUnknown(); } return SvgPicture.network( @@ -34,11 +54,13 @@ class SvgCache extends AbstractCache { fit: BoxFit.contain, placeholderBuilder: (BuildContext context) => displayAssetWhileWaiting ? SvgAsyncAsset( - fullFilename, - iconUrl!, - width: width, - height: height, - color: color, + AssetCacheHelper( + cachedFilenames, + iconUrl!, + width: width, + height: height, + color: color, + ), ) : getCircularProgressIndicator(), ); diff --git a/packages/smooth_app/pubspec.yaml b/packages/smooth_app/pubspec.yaml index 608ba7636de..262e940a52a 100644 --- a/packages/smooth_app/pubspec.yaml +++ b/packages/smooth_app/pubspec.yaml @@ -92,6 +92,7 @@ flutter: - assets/app/ - assets/cache/ - assets/categories/ + - assets/cacheTintable/ - assets/data/ - assets/home/ - assets/labels/