mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-25 17:00:39 +00:00
implemented RemoteConfigProvider
This commit is contained in:
parent
822ba8c7f8
commit
d7162c7529
5 changed files with 211 additions and 10 deletions
|
@ -44,10 +44,10 @@ class ApplicationWrapper extends StatelessWidget {
|
|||
hapticsService: const HapticsService(),
|
||||
lightSensorService: const LightSensorService(LocalPlatform()),
|
||||
permissionsService: const PermissionsService(),
|
||||
remoteConfigService: const RemoteConfigService(),
|
||||
userPreferencesService: userPreferencesService,
|
||||
volumeEventsService: const VolumeEventsService(LocalPlatform()),
|
||||
child: RemoteConfig(
|
||||
child: RemoteConfigProvider(
|
||||
remoteConfigService: const RemoteConfigService(),
|
||||
child: EquipmentProfileProvider(
|
||||
storageService: iapService,
|
||||
child: FilmsProvider(
|
||||
|
|
|
@ -34,9 +34,48 @@ class RemoteConfigService {
|
|||
}
|
||||
}
|
||||
|
||||
dynamic getValue(Feature feature) => FirebaseRemoteConfig.instance.getValue(feature.name).toValue(feature);
|
||||
|
||||
Map<Feature, dynamic> getAll() {
|
||||
final Map<Feature, dynamic> result = {};
|
||||
for (final value in FirebaseRemoteConfig.instance.getAll().entries) {
|
||||
try {
|
||||
final feature = Feature.values.firstWhere((f) => f.name == value.key);
|
||||
result[feature] = MapEntry(feature, value.value.toValue(feature));
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Stream<Set<Feature>> onConfigUpdated() => FirebaseRemoteConfig.instance.onConfigUpdated.asyncMap(
|
||||
(event) async {
|
||||
await FirebaseRemoteConfig.instance.activate();
|
||||
final Set<Feature> updatedFeatures = {};
|
||||
for (final key in event.updatedKeys) {
|
||||
try {
|
||||
updatedFeatures.add(Feature.values.firstWhere((element) => element.name == key));
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
}
|
||||
}
|
||||
return updatedFeatures;
|
||||
},
|
||||
);
|
||||
|
||||
bool isEnabled(Feature feature) => FirebaseRemoteConfig.instance.getBool(feature.name);
|
||||
|
||||
void _logError(dynamic throwable, {StackTrace? stackTrace}) {
|
||||
FirebaseCrashlytics.instance.recordError(throwable, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
extension on RemoteConfigValue {
|
||||
dynamic toValue(Feature feature) {
|
||||
switch (feature) {
|
||||
case Feature.unlockProFeaturesText:
|
||||
return asBool();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,78 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/data/models/feature.dart';
|
||||
import 'package:lightmeter/providers/services_provider.dart';
|
||||
import 'package:lightmeter/data/remote_config_service.dart';
|
||||
|
||||
class RemoteConfig extends InheritedWidget {
|
||||
const RemoteConfig({
|
||||
class RemoteConfigProvider extends StatefulWidget {
|
||||
final RemoteConfigService remoteConfigService;
|
||||
final Widget child;
|
||||
|
||||
const RemoteConfigProvider({
|
||||
required this.remoteConfigService,
|
||||
required this.child,
|
||||
super.key,
|
||||
required super.child,
|
||||
});
|
||||
|
||||
@override
|
||||
State<RemoteConfigProvider> createState() => RemoteConfigProviderState();
|
||||
}
|
||||
|
||||
class RemoteConfigProviderState extends State<RemoteConfigProvider> {
|
||||
late final Map<Feature, dynamic> _config = widget.remoteConfigService.getAll();
|
||||
late final StreamSubscription<Set<Feature>> _updatesSubscription;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_updatesSubscription = widget.remoteConfigService.onConfigUpdated().listen(_updateFeatures);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_updatesSubscription.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RemoteConfig(
|
||||
config: _config,
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
|
||||
void _updateFeatures(Set<Feature> updatedFeatures) {
|
||||
for (final feature in updatedFeatures) {
|
||||
_config[feature] = widget.remoteConfigService.getValue(feature);
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
class RemoteConfig extends InheritedModel<Feature> {
|
||||
final Map<Feature, dynamic> _config;
|
||||
|
||||
const RemoteConfig({
|
||||
super.key,
|
||||
required Map<Feature, dynamic> config,
|
||||
required super.child,
|
||||
}) : _config = config;
|
||||
|
||||
static bool isEnabled(BuildContext context, Feature feature) {
|
||||
return ServicesProvider.of(context).remoteConfigService.isEnabled(feature);
|
||||
return InheritedModel.inheritFrom<RemoteConfig>(context)!._config[feature] as bool;
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(RemoteConfig oldWidget) => true;
|
||||
|
||||
@override
|
||||
bool updateShouldNotifyDependent(RemoteConfig oldWidget, Set<Feature> features) {
|
||||
for (final feature in features) {
|
||||
if (oldWidget._config[feature] != _config[feature]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import 'package:lightmeter/data/caffeine_service.dart';
|
|||
import 'package:lightmeter/data/haptics_service.dart';
|
||||
import 'package:lightmeter/data/light_sensor_service.dart';
|
||||
import 'package:lightmeter/data/permissions_service.dart';
|
||||
import 'package:lightmeter/data/remote_config_service.dart';
|
||||
import 'package:lightmeter/data/shared_prefs_service.dart';
|
||||
import 'package:lightmeter/data/volume_events_service.dart';
|
||||
import 'package:lightmeter/environment.dart';
|
||||
|
@ -17,7 +16,6 @@ class ServicesProvider extends InheritedWidget {
|
|||
final HapticsService hapticsService;
|
||||
final LightSensorService lightSensorService;
|
||||
final PermissionsService permissionsService;
|
||||
final RemoteConfigService remoteConfigService;
|
||||
final UserPreferencesService userPreferencesService;
|
||||
final VolumeEventsService volumeEventsService;
|
||||
|
||||
|
@ -28,7 +26,6 @@ class ServicesProvider extends InheritedWidget {
|
|||
required this.hapticsService,
|
||||
required this.lightSensorService,
|
||||
required this.permissionsService,
|
||||
required this.remoteConfigService,
|
||||
required this.userPreferencesService,
|
||||
required this.volumeEventsService,
|
||||
required super.child,
|
||||
|
|
104
test/providers/remote_config_provider_test.dart
Normal file
104
test/providers/remote_config_provider_test.dart
Normal file
|
@ -0,0 +1,104 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:lightmeter/data/models/feature.dart';
|
||||
import 'package:lightmeter/data/remote_config_service.dart';
|
||||
import 'package:lightmeter/providers/remote_config_provider.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class _MockRemoteConfigService extends Mock implements RemoteConfigService {}
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
late _MockRemoteConfigService mockRemoteConfigService;
|
||||
|
||||
setUpAll(() {
|
||||
mockRemoteConfigService = _MockRemoteConfigService();
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
when(() => mockRemoteConfigService.getValue(Feature.unlockProFeaturesText)).thenReturn(false);
|
||||
when(() => mockRemoteConfigService.getAll()).thenReturn({Feature.unlockProFeaturesText: false});
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
reset(mockRemoteConfigService);
|
||||
});
|
||||
|
||||
Future<void> pumpTestWidget(WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
RemoteConfigProvider(
|
||||
remoteConfigService: mockRemoteConfigService,
|
||||
child: const _Application(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets(
|
||||
'RemoteConfigProvider init',
|
||||
(tester) async {
|
||||
when(() => mockRemoteConfigService.onConfigUpdated()).thenAnswer((_) => const Stream.empty());
|
||||
|
||||
await pumpTestWidget(tester);
|
||||
expect(find.text('unlockProFeaturesText: false'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'RemoteConfigProvider updates stream',
|
||||
(tester) async {
|
||||
final StreamController<Set<Feature>> remoteConfigUpdateController = StreamController<Set<Feature>>();
|
||||
when(() => mockRemoteConfigService.onConfigUpdated()).thenAnswer((_) => remoteConfigUpdateController.stream);
|
||||
|
||||
await pumpTestWidget(tester);
|
||||
expect(find.text('unlockProFeaturesText: false'), findsOneWidget);
|
||||
|
||||
when(() => mockRemoteConfigService.getValue(Feature.unlockProFeaturesText)).thenReturn(true);
|
||||
remoteConfigUpdateController.add({Feature.unlockProFeaturesText});
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('unlockProFeaturesText: true'), findsOneWidget);
|
||||
|
||||
await remoteConfigUpdateController.close();
|
||||
},
|
||||
);
|
||||
|
||||
test('RemoteConfig.updateShouldNotifyDependent', () {
|
||||
const config = RemoteConfig(config: {Feature.unlockProFeaturesText: false}, child: SizedBox());
|
||||
expect(
|
||||
config.updateShouldNotifyDependent(config, {}),
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
config.updateShouldNotifyDependent(
|
||||
const RemoteConfig(config: {Feature.unlockProFeaturesText: false}, child: SizedBox()),
|
||||
{Feature.unlockProFeaturesText},
|
||||
),
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
config.updateShouldNotifyDependent(
|
||||
const RemoteConfig(config: {Feature.unlockProFeaturesText: true}, child: SizedBox()),
|
||||
{Feature.unlockProFeaturesText},
|
||||
),
|
||||
true,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
class _Application extends StatelessWidget {
|
||||
const _Application();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
child: Text(
|
||||
"${Feature.unlockProFeaturesText.name}: ${RemoteConfig.isEnabled(context, Feature.unlockProFeaturesText)}",
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue