Skip to content

Commit

Permalink
feat: Custom domain + environment support for the TagLine (#5364)
Browse files Browse the repository at this point in the history
* Support custom domain + env for the TagLine

* Update packages/smooth_app/lib/data_models/tagline/tagline_provider.dart

---------

Co-authored-by: Pierre Slamich <[email protected]>
  • Loading branch information
g123k and teolemon authored Jun 13, 2024
1 parent b5b308c commit 08071d2
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 90 deletions.
66 changes: 61 additions & 5 deletions packages/smooth_app/lib/data_models/tagline/tagline_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ import 'dart:isolate';
import 'package:collection/collection.dart';
import 'package:flutter/widgets.dart';
import 'package:http/http.dart' as http;
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:smooth_app/data_models/preferences/user_preferences.dart';
import 'package:smooth_app/data_models/tagline/tagline_model.dart';
import 'package:smooth_app/pages/preferences/user_preferences_dev_mode.dart';
import 'package:smooth_app/query/product_query.dart';
import 'package:smooth_app/services/smooth_services.dart';

part 'tagline_json.dart';

Expand All @@ -19,10 +23,21 @@ part 'tagline_json.dart';
/// To be notified of changes, listen to this [ChangeNotifier] and more
/// particularly to the [state] property
class TagLineProvider extends ChangeNotifier {
TagLineProvider() : _state = const TagLineLoading() {
TagLineProvider(UserPreferences preferences)
: _state = const TagLineLoading(),
_preferences = preferences,
_domain = preferences.getDevModeString(
UserPreferencesDevMode.userPreferencesTestEnvDomain) ??
'',
_prodEnv = preferences
.getFlag(UserPreferencesDevMode.userPreferencesFlagProd) ??
true {
_preferences.addListener(_onPreferencesChanged);
loadTagLine();
}

final UserPreferences _preferences;

TagLineState _state;

bool get hasContent => _state is TagLineLoaded;
Expand Down Expand Up @@ -56,8 +71,10 @@ class TagLineProvider extends ChangeNotifier {
() => _parseJSONAndGetLocalizedContent(jsonString!, locale));
if (tagLine == null) {
_emit(const TagLineError('Unable to parse the JSON file'));
Logs.e('Unable to parse the Tagline file');
} else {
_emit(TagLineLoaded(tagLine));
Logs.i('TagLine reloaded');
}
}

Expand All @@ -83,12 +100,25 @@ class TagLineProvider extends ChangeNotifier {
}
}

/// API URL: [https://world.openfoodfacts.org/files/tagline-off-ios-v3.json]
/// or [https://world.openfoodfacts.org/files/tagline-off-android-v3.json]
/// API URL: [https://world.openfoodfacts.[org/net]/resources/files/tagline-off-ios-v3.json]
/// or [https://world.openfoodfacts.[org/net]/resources/files/tagline-off-android-v3.json]
Future<String?> _fetchTagLine() async {
try {
final http.Response response =
await http.get(Uri.https('world.openfoodfacts.org', _tagLineUrl));
final UriProductHelper uriProductHelper = ProductQuery.uriProductHelper;
final Map<String, String> headers = <String, String>{};
final Uri uri = uriProductHelper.getUri(path: _tagLineUrl);

if (uriProductHelper.userInfoForPatch != null) {
headers['Authorization'] =
'Basic ${base64Encode(utf8.encode(uriProductHelper.userInfoForPatch!))}';
}

final http.Response response = await http.get(uri, headers: headers);

if (response.statusCode == 404) {
Logs.e("Remote file $uri doesn't exist!");
throw Exception('Incorrect URL= $uri');
}

final String json = const Utf8Decoder().convert(response.bodyBytes);

Expand Down Expand Up @@ -125,6 +155,32 @@ class TagLineProvider extends ChangeNotifier {
file
.lastModifiedSync()
.isAfter(DateTime.now().add(const Duration(days: -1)));

bool? _prodEnv;
String? _domain;

/// [ProductQuery.uriProductHelper] is not synced yet,
/// so we have to check it manually
Future<void> _onPreferencesChanged() async {
final String domain = _preferences.getDevModeString(
UserPreferencesDevMode.userPreferencesTestEnvDomain) ??
'';
final bool prodEnv =
_preferences.getFlag(UserPreferencesDevMode.userPreferencesFlagProd) ??
true;

if (domain != _domain || prodEnv != _prodEnv) {
_domain = domain;
_prodEnv = prodEnv;
loadTagLine(forceUpdate: true);
}
}

@override
void dispose() {
_preferences.removeListener(_onPreferencesChanged);
super.dispose();
}
}

sealed class TagLineState {
Expand Down
22 changes: 13 additions & 9 deletions packages/smooth_app/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ late TextContrastProvider _textContrastProvider;
final ContinuousScanModel _continuousScanModel = ContinuousScanModel();
final PermissionListener _permissionListener =
PermissionListener(permission: Permission.camera);
final TagLineProvider _tagLineProvider = TagLineProvider();
bool _init1done = false;

// Had to split init in 2 methods, for test/screenshots reasons.
Expand Down Expand Up @@ -224,15 +223,20 @@ class _SmoothAppState extends State<SmoothApp> {
provide<UserManagementProvider>(_userManagementProvider),
provide<ContinuousScanModel>(_continuousScanModel),
provide<PermissionListener>(_permissionListener),
provide<TagLineProvider>(_tagLineProvider, lazy: true),
],
child: AnimationsLoader(
child: AppNavigator(
observers: <NavigatorObserver>[
SentryNavigatorObserver(),
matomoObserver,
],
child: Builder(builder: _buildApp),
child: ChangeNotifierProvider<TagLineProvider>(
create: (BuildContext context) => TagLineProvider(
context.read<UserPreferences>(),
),
lazy: true,
child: AnimationsLoader(
child: AppNavigator(
observers: <NavigatorObserver>[
SentryNavigatorObserver(),
matomoObserver,
],
child: Builder(builder: _buildApp),
),
),
),
);
Expand Down
147 changes: 71 additions & 76 deletions packages/smooth_app/lib/pages/scan/scan_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import 'package:smooth_app/data_models/continuous_scan_model.dart';
import 'package:smooth_app/data_models/preferences/user_preferences.dart';
import 'package:smooth_app/data_models/tagline/tagline_provider.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_card.dart';
Expand Down Expand Up @@ -61,88 +60,84 @@ class _ScanPageState extends State<ScanPage> {
Theme.of(context).brightness == Brightness.light && Platform.isIOS
? Brightness.dark
: null,
body: ChangeNotifierProvider<TagLineProvider>(
lazy: true,
create: (_) => TagLineProvider(),
child: Container(
color: Colors.white,
child: SafeArea(
child: Container(
color: Theme.of(context).colorScheme.background,
child: Column(
children: <Widget>[
if (hasACamera)
Expanded(
flex: 100 - _carouselHeightPct,
child: Consumer<PermissionListener>(
builder: (
BuildContext context,
PermissionListener listener,
_,
) {
switch (listener.value.status) {
case DevicePermissionStatus.checking:
return EMPTY_WIDGET;
case DevicePermissionStatus.granted:
// TODO(m123): change
return const CameraScannerPage();
default:
return const _PermissionDeniedCard();
}
},
),
),
body: Container(
color: Colors.white,
child: SafeArea(
child: Container(
color: Theme.of(context).colorScheme.background,
child: Column(
children: <Widget>[
if (hasACamera)
Expanded(
flex: _carouselHeightPct,
child: Padding(
padding: const EdgeInsetsDirectional.only(bottom: 10.0),
child: SmoothProductCarousel(
containSearchCard: true,
onPageChangedTo: (int page, String? barcode) async {
if (barcode == null) {
// We only notify for new products
return;
}
flex: 100 - _carouselHeightPct,
child: Consumer<PermissionListener>(
builder: (
BuildContext context,
PermissionListener listener,
_,
) {
switch (listener.value.status) {
case DevicePermissionStatus.checking:
return EMPTY_WIDGET;
case DevicePermissionStatus.granted:
// TODO(m123): change
return const CameraScannerPage();
default:
return const _PermissionDeniedCard();
}
},
),
),
Expanded(
flex: _carouselHeightPct,
child: Padding(
padding: const EdgeInsetsDirectional.only(bottom: 10.0),
child: SmoothProductCarousel(
containSearchCard: true,
onPageChangedTo: (int page, String? barcode) async {
if (barcode == null) {
// We only notify for new products
return;
}

// Both are Future methods, but it doesn't matter to wait here
SmoothHapticFeedback.lightNotification();
// Both are Future methods, but it doesn't matter to wait here
SmoothHapticFeedback.lightNotification();

if (_userPreferences.playCameraSound) {
await _initSoundManagerIfNecessary();
await _musicPlayer!.stop();
await _musicPlayer!.play(
AssetSource('audio/beep.wav'),
volume: 0.5,
ctx: const AudioContext(
android: AudioContextAndroid(
isSpeakerphoneOn: false,
stayAwake: false,
contentType: AndroidContentType.sonification,
usageType: AndroidUsageType.notification,
audioFocus:
AndroidAudioFocus.gainTransientMayDuck,
),
iOS: AudioContextIOS(
category: AVAudioSessionCategory.soloAmbient,
options: <AVAudioSessionOptions>[
AVAudioSessionOptions.mixWithOthers,
],
),
if (_userPreferences.playCameraSound) {
await _initSoundManagerIfNecessary();
await _musicPlayer!.stop();
await _musicPlayer!.play(
AssetSource('audio/beep.wav'),
volume: 0.5,
ctx: const AudioContext(
android: AudioContextAndroid(
isSpeakerphoneOn: false,
stayAwake: false,
contentType: AndroidContentType.sonification,
usageType: AndroidUsageType.notification,
audioFocus:
AndroidAudioFocus.gainTransientMayDuck,
),
);
}

SemanticsService.announce(
appLocalizations.scan_announce_new_barcode(barcode),
direction,
assertiveness: Assertiveness.assertive,
iOS: AudioContextIOS(
category: AVAudioSessionCategory.soloAmbient,
options: <AVAudioSessionOptions>[
AVAudioSessionOptions.mixWithOthers,
],
),
),
);
},
),
}

SemanticsService.announce(
appLocalizations.scan_announce_new_barcode(barcode),
direction,
assertiveness: Assertiveness.assertive,
);
},
),
),
],
),
),
],
),
),
),
Expand Down

0 comments on commit 08071d2

Please sign in to comment.