migrated to new iap service

This commit is contained in:
Vadim 2025-07-17 23:02:07 +02:00
parent 91a1942191
commit c5ef878ddb
11 changed files with 51 additions and 63 deletions

View file

@ -7,9 +7,7 @@ import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
class _MockEquipmentProfilesStorageService extends Mock implements EquipmentProfilesStorageService {} class _MockIapStorageService extends Mock implements IapStorageService {}
class _MockFilmsStorageService extends Mock implements FilmsStorageService {}
class MockIAPProviders extends StatefulWidget { class MockIAPProviders extends StatefulWidget {
final TogglableMap<EquipmentProfile> equipmentProfiles; final TogglableMap<EquipmentProfile> equipmentProfiles;
@ -37,42 +35,38 @@ class MockIAPProviders extends StatefulWidget {
} }
class _MockIAPProvidersState extends State<MockIAPProviders> { class _MockIAPProvidersState extends State<MockIAPProviders> {
late final _MockEquipmentProfilesStorageService mockEquipmentProfilesStorageService; late final _MockIapStorageService mockIapStorageService;
late final _MockFilmsStorageService mockFilmsStorageService;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
registerFallbackValue(defaultEquipmentProfile); registerFallbackValue(defaultEquipmentProfile);
mockEquipmentProfilesStorageService = _MockEquipmentProfilesStorageService(); mockIapStorageService = _MockIapStorageService();
when(() => mockEquipmentProfilesStorageService.init()).thenAnswer((_) async {}); when(() => mockIapStorageService.init()).thenAnswer((_) async {});
when(() => mockEquipmentProfilesStorageService.getProfiles())
.thenAnswer((_) => Future.value(widget.equipmentProfiles)); when(() => mockIapStorageService.getEquipmentProfiles()).thenAnswer((_) => Future.value(widget.equipmentProfiles));
when(() => mockEquipmentProfilesStorageService.selectedEquipmentProfileId) when(() => mockIapStorageService.selectedEquipmentProfileId).thenReturn(widget.selectedEquipmentProfileId);
.thenReturn(widget.selectedEquipmentProfileId); when(() => mockIapStorageService.addEquipmentProfile(any<EquipmentProfile>())).thenAnswer((_) async {});
when(() => mockEquipmentProfilesStorageService.addProfile(any<EquipmentProfile>())).thenAnswer((_) async {});
when( when(
() => mockEquipmentProfilesStorageService.updateProfile( () => mockIapStorageService.updateEquipmentProfile(
id: any<String>(named: 'id'), id: any<String>(named: 'id'),
name: any<String>(named: 'name'), name: any<String>(named: 'name'),
isUsed: any<bool>(named: 'isUsed'), isUsed: any<bool>(named: 'isUsed'),
), ),
).thenAnswer((_) async {}); ).thenAnswer((_) async {});
when(() => mockEquipmentProfilesStorageService.deleteProfile(any<String>())).thenAnswer((_) async {}); when(() => mockIapStorageService.deleteEquipmentProfile(any<String>())).thenAnswer((_) async {});
mockFilmsStorageService = _MockFilmsStorageService(); when(() => mockIapStorageService.getPredefinedFilms()).thenAnswer((_) => Future.value(widget.predefinedFilms));
when(() => mockFilmsStorageService.init()).thenAnswer((_) async {}); when(() => mockIapStorageService.getCustomFilms()).thenAnswer((_) => Future.value(widget.customFilms));
when(() => mockFilmsStorageService.getPredefinedFilms()).thenAnswer((_) => Future.value(widget.predefinedFilms)); when(() => mockIapStorageService.selectedFilmId).thenReturn(widget.selectedFilmId);
when(() => mockFilmsStorageService.getCustomFilms()).thenAnswer((_) => Future.value(widget.customFilms));
when(() => mockFilmsStorageService.selectedFilmId).thenReturn(widget.selectedFilmId);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return EquipmentProfilesProvider( return EquipmentProfilesProvider(
storageService: mockEquipmentProfilesStorageService, storageService: mockIapStorageService,
child: FilmsProvider( child: FilmsProvider(
storageService: mockFilmsStorageService, storageService: mockIapStorageService,
child: widget.child, child: widget.child,
), ),
); );

View file

@ -42,13 +42,9 @@ class _ApplicationWrapperState extends State<ApplicationWrapper> {
late final UserPreferencesService userPreferencesService; late final UserPreferencesService userPreferencesService;
late final bool hasLightSensor; late final bool hasLightSensor;
final equipmentProfilesStorageService = EquipmentProfilesStorageService(); final iapStorageService = IapStorageService();
final equipmentProfilesStorageServiceCompleter = Completer<void>(); final equipmentProfilesStorageServiceCompleter = Completer<void>();
final filmsStorageService = FilmsStorageService();
final filmsStorageServiceCompleter = Completer<void>(); final filmsStorageServiceCompleter = Completer<void>();
final logbookPhotosStorageService = LogbookPhotosStorageService();
final logbookPhotosStorageServiceCompleter = Completer<void>(); final logbookPhotosStorageServiceCompleter = Completer<void>();
late final Future<void> _initFuture; late final Future<void> _initFuture;
@ -81,13 +77,13 @@ class _ApplicationWrapperState extends State<ApplicationWrapper> {
child: RemoteConfigProvider( child: RemoteConfigProvider(
remoteConfigService: remoteConfigService, remoteConfigService: remoteConfigService,
child: EquipmentProfilesProvider( child: EquipmentProfilesProvider(
storageService: equipmentProfilesStorageService, storageService: iapStorageService,
onInitialized: equipmentProfilesStorageServiceCompleter.complete, onInitialized: equipmentProfilesStorageServiceCompleter.complete,
child: FilmsProvider( child: FilmsProvider(
storageService: filmsStorageService, storageService: iapStorageService,
onInitialized: filmsStorageServiceCompleter.complete, onInitialized: filmsStorageServiceCompleter.complete,
child: LogbookPhotosProvider( child: LogbookPhotosProvider(
storageService: logbookPhotosStorageService, storageService: iapStorageService,
onInitialized: logbookPhotosStorageServiceCompleter.complete, onInitialized: logbookPhotosStorageServiceCompleter.complete,
child: UserPreferencesProvider( child: UserPreferencesProvider(
hasLightSensor: hasLightSensor, hasLightSensor: hasLightSensor,
@ -112,9 +108,7 @@ class _ApplicationWrapperState extends State<ApplicationWrapper> {
const LightSensorService(LocalPlatform()).hasSensor(), const LightSensorService(LocalPlatform()).hasSensor(),
const CameraInfoService(analytics).mainCameraEfl(), const CameraInfoService(analytics).mainCameraEfl(),
remoteConfigService.activeAndFetchFeatures(), remoteConfigService.activeAndFetchFeatures(),
equipmentProfilesStorageService.init(), iapStorageService.init(),
filmsStorageService.init(),
logbookPhotosStorageService.init(),
]).then((value) { ]).then((value) {
userPreferencesService = UserPreferencesService((value[0] as SharedPreferences?)!) userPreferencesService = UserPreferencesService((value[0] as SharedPreferences?)!)
..cameraFocalLength = value[2] as int?; ..cameraFocalLength = value[2] as int?;

View file

@ -14,7 +14,7 @@ class EquipmentProfilesProvider extends StatefulWidget {
isoValues: IsoValue.values, isoValues: IsoValue.values,
); );
final EquipmentProfilesStorageService storageService; final IapStorageService storageService;
final VoidCallback? onInitialized; final VoidCallback? onInitialized;
final Widget child; final Widget child;
@ -57,21 +57,21 @@ class EquipmentProfilesProviderState extends State<EquipmentProfilesProvider> {
Future<void> _init() async { Future<void> _init() async {
_selectedId = widget.storageService.selectedEquipmentProfileId; _selectedId = widget.storageService.selectedEquipmentProfileId;
_customProfiles.addAll(await widget.storageService.getProfiles()); _customProfiles.addAll(await widget.storageService.getEquipmentProfiles());
_discardSelectedIfNotIncluded(); _discardSelectedIfNotIncluded();
if (mounted) setState(() {}); if (mounted) setState(() {});
widget.onInitialized?.call(); widget.onInitialized?.call();
} }
Future<void> addProfile(EquipmentProfile profile) async { Future<void> addProfile(EquipmentProfile profile) async {
await widget.storageService.addProfile(profile); await widget.storageService.addEquipmentProfile(profile);
_customProfiles[profile.id] = (value: profile, isUsed: true); _customProfiles[profile.id] = (value: profile, isUsed: true);
setState(() {}); setState(() {});
} }
Future<void> updateProfile(EquipmentProfile profile) async { Future<void> updateProfile(EquipmentProfile profile) async {
final oldProfile = _customProfiles[profile.id]!.value; final oldProfile = _customProfiles[profile.id]!.value;
await widget.storageService.updateProfile( await widget.storageService.updateEquipmentProfile(
id: profile.id, id: profile.id,
name: oldProfile.name != profile.name ? profile.name : null, name: oldProfile.name != profile.name ? profile.name : null,
apertureValues: oldProfile.apertureValues != profile.apertureValues ? profile.apertureValues : null, apertureValues: oldProfile.apertureValues != profile.apertureValues ? profile.apertureValues : null,
@ -86,7 +86,7 @@ class EquipmentProfilesProviderState extends State<EquipmentProfilesProvider> {
} }
Future<void> deleteProfile(EquipmentProfile profile) async { Future<void> deleteProfile(EquipmentProfile profile) async {
await widget.storageService.deleteProfile(profile.id); await widget.storageService.deleteEquipmentProfile(profile.id);
if (profile.id == _selectedId) { if (profile.id == _selectedId) {
_selectedId = EquipmentProfilesProvider.defaultProfile.id; _selectedId = EquipmentProfilesProvider.defaultProfile.id;
widget.storageService.selectedEquipmentProfileId = EquipmentProfilesProvider.defaultProfile.id; widget.storageService.selectedEquipmentProfileId = EquipmentProfilesProvider.defaultProfile.id;
@ -111,7 +111,7 @@ class EquipmentProfilesProviderState extends State<EquipmentProfilesProvider> {
} else { } else {
return; return;
} }
await widget.storageService.updateProfile(id: profile.id, isUsed: enabled); await widget.storageService.updateEquipmentProfile(id: profile.id, isUsed: enabled);
_discardSelectedIfNotIncluded(); _discardSelectedIfNotIncluded();
setState(() {}); setState(() {});
} }

View file

@ -5,7 +5,7 @@ import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
class FilmsProvider extends StatefulWidget { class FilmsProvider extends StatefulWidget {
final FilmsStorageService storageService; final IapStorageService storageService;
final VoidCallback? onInitialized; final VoidCallback? onInitialized;
final Widget child; final Widget child;

View file

@ -9,7 +9,7 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'package:uuid/v8.dart'; import 'package:uuid/v8.dart';
class LogbookPhotosProvider extends StatefulWidget { class LogbookPhotosProvider extends StatefulWidget {
final LogbookPhotosStorageService storageService; final IapStorageService storageService;
final VoidCallback? onInitialized; final VoidCallback? onInitialized;
final Widget child; final Widget child;

View file

@ -5,7 +5,7 @@ import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
class _MockEquipmentProfilesStorageService extends Mock implements EquipmentProfilesStorageService {} class _MockEquipmentProfilesStorageService extends Mock implements IapStorageService {}
void main() { void main() {
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();
@ -17,16 +17,16 @@ void main() {
setUp(() { setUp(() {
registerFallbackValue(_customProfiles.first); registerFallbackValue(_customProfiles.first);
when(() => storageService.addProfile(any<EquipmentProfile>())).thenAnswer((_) async {}); when(() => storageService.addEquipmentProfile(any<EquipmentProfile>())).thenAnswer((_) async {});
when( when(
() => storageService.updateProfile( () => storageService.updateEquipmentProfile(
id: any<String>(named: 'id'), id: any<String>(named: 'id'),
name: any<String>(named: 'name'), name: any<String>(named: 'name'),
isUsed: any<bool>(named: 'isUsed'), isUsed: any<bool>(named: 'isUsed'),
), ),
).thenAnswer((_) async {}); ).thenAnswer((_) async {});
when(() => storageService.deleteProfile(any<String>())).thenAnswer((_) async {}); when(() => storageService.deleteEquipmentProfile(any<String>())).thenAnswer((_) async {});
when(() => storageService.getProfiles()).thenAnswer((_) => Future.value(_customProfiles.toTogglableMap())); when(() => storageService.getEquipmentProfiles()).thenAnswer((_) => Future.value(_customProfiles.toTogglableMap()));
}); });
tearDown(() { tearDown(() {
@ -69,7 +69,7 @@ void main() {
() { () {
setUp(() { setUp(() {
when(() => storageService.selectedEquipmentProfileId).thenReturn(_customProfiles.first.id); when(() => storageService.selectedEquipmentProfileId).thenReturn(_customProfiles.first.id);
when(() => storageService.getProfiles()).thenAnswer((_) => Future.value(_customProfiles.toTogglableMap())); when(() => storageService.getEquipmentProfiles()).thenAnswer((_) => Future.value(_customProfiles.toTogglableMap()));
}); });
testWidgets( testWidgets(
@ -116,7 +116,7 @@ void main() {
expectEquipmentProfilesInUseCount(_customProfiles.length + 1 - 1); expectEquipmentProfilesInUseCount(_customProfiles.length + 1 - 1);
expectSelectedEquipmentProfileName(''); expectSelectedEquipmentProfileName('');
verify(() => storageService.updateProfile(id: _customProfiles.first.id, isUsed: false)).called(1); verify(() => storageService.updateEquipmentProfile(id: _customProfiles.first.id, isUsed: false)).called(1);
verify(() => storageService.selectedEquipmentProfileId = '').called(1); verify(() => storageService.selectedEquipmentProfileId = '').called(1);
}, },
); );
@ -124,7 +124,7 @@ void main() {
testWidgets( testWidgets(
'EquipmentProfilesProvider CRUD', 'EquipmentProfilesProvider CRUD',
(tester) async { (tester) async {
when(() => storageService.getProfiles()).thenAnswer((_) async => {}); when(() => storageService.getEquipmentProfiles()).thenAnswer((_) async => {});
when(() => storageService.selectedEquipmentProfileId).thenReturn(''); when(() => storageService.selectedEquipmentProfileId).thenReturn('');
await pumpTestWidget(tester, IAPProductStatus.purchased); await pumpTestWidget(tester, IAPProductStatus.purchased);
@ -136,7 +136,7 @@ void main() {
await tester.pump(); await tester.pump();
expectEquipmentProfilesCount(2); expectEquipmentProfilesCount(2);
expectSelectedEquipmentProfileName(''); expectSelectedEquipmentProfileName('');
verify(() => storageService.addProfile(any<EquipmentProfile>())).called(1); verify(() => storageService.addEquipmentProfile(any<EquipmentProfile>())).called(1);
/// Add the other profiles and select the 1st one /// Add the other profiles and select the 1st one
for (final profile in _customProfiles.skip(1)) { for (final profile in _customProfiles.skip(1)) {
@ -153,7 +153,7 @@ void main() {
await tester.pump(); await tester.pump();
expectEquipmentProfilesCount(1 + _customProfiles.length); expectEquipmentProfilesCount(1 + _customProfiles.length);
expectSelectedEquipmentProfileName(updatedName); expectSelectedEquipmentProfileName(updatedName);
verify(() => storageService.updateProfile(id: _customProfiles.first.id, name: updatedName)).called(1); verify(() => storageService.updateEquipmentProfile(id: _customProfiles.first.id, name: updatedName)).called(1);
/// Delete a non-selected profile /// Delete a non-selected profile
await tester.equipmentProfilesProvider.deleteProfile(_customProfiles.last); await tester.equipmentProfilesProvider.deleteProfile(_customProfiles.last);
@ -161,7 +161,7 @@ void main() {
expectEquipmentProfilesCount(1 + _customProfiles.length - 1); expectEquipmentProfilesCount(1 + _customProfiles.length - 1);
expectSelectedEquipmentProfileName(updatedName); expectSelectedEquipmentProfileName(updatedName);
verifyNever(() => storageService.selectedEquipmentProfileId = ''); verifyNever(() => storageService.selectedEquipmentProfileId = '');
verify(() => storageService.deleteProfile(_customProfiles.last.id)).called(1); verify(() => storageService.deleteEquipmentProfile(_customProfiles.last.id)).called(1);
/// Delete the selected profile /// Delete the selected profile
await tester.equipmentProfilesProvider.deleteProfile(_customProfiles.first); await tester.equipmentProfilesProvider.deleteProfile(_customProfiles.first);
@ -169,7 +169,7 @@ void main() {
expectEquipmentProfilesCount(1 + _customProfiles.length - 2); expectEquipmentProfilesCount(1 + _customProfiles.length - 2);
expectSelectedEquipmentProfileName(''); expectSelectedEquipmentProfileName('');
verify(() => storageService.selectedEquipmentProfileId = '').called(1); verify(() => storageService.selectedEquipmentProfileId = '').called(1);
verify(() => storageService.deleteProfile(_customProfiles.first.id)).called(1); verify(() => storageService.deleteEquipmentProfile(_customProfiles.first.id)).called(1);
}, },
); );
} }

View file

@ -5,7 +5,7 @@ import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
class _MockFilmsStorageService extends Mock implements FilmsStorageService {} class _MockFilmsStorageService extends Mock implements IapStorageService {}
void main() { void main() {
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();

View file

@ -5,7 +5,7 @@ import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
class _MockLogbookPhotosStorageService extends Mock implements LogbookPhotosStorageService {} class _MockLogbookPhotosStorageService extends Mock implements IapStorageService {}
void main() { void main() {
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();

View file

@ -12,14 +12,14 @@ import 'package:mocktail/mocktail.dart';
import '../../../../../application_mock.dart'; import '../../../../../application_mock.dart';
import 'utils.dart'; import 'utils.dart';
class _MockEquipmentProfilesStorageService extends Mock implements EquipmentProfilesStorageService {} class _MockEquipmentProfilesStorageService extends Mock implements IapStorageService {}
void main() { void main() {
late final _MockEquipmentProfilesStorageService storageService; late final _MockEquipmentProfilesStorageService storageService;
setUpAll(() { setUpAll(() {
storageService = _MockEquipmentProfilesStorageService(); storageService = _MockEquipmentProfilesStorageService();
when(() => storageService.getProfiles()).thenAnswer((_) async => _mockEquipmentProfiles.toTogglableMap()); when(() => storageService.getEquipmentProfiles()).thenAnswer((_) async => _mockEquipmentProfiles.toTogglableMap());
when(() => storageService.selectedEquipmentProfileId).thenReturn(''); when(() => storageService.selectedEquipmentProfileId).thenReturn('');
}); });
@ -88,7 +88,7 @@ void main() {
testWidgets( testWidgets(
'Equipment profile picker shows only profiles in use', 'Equipment profile picker shows only profiles in use',
(tester) async { (tester) async {
when(() => storageService.getProfiles()) when(() => storageService.getEquipmentProfiles())
.thenAnswer((_) async => _mockEquipmentProfiles.skip(1).toList().toTogglableMap()); .thenAnswer((_) async => _mockEquipmentProfiles.skip(1).toList().toTogglableMap());
await pumpApplication(tester); await pumpApplication(tester);
await tester.openAnimatedPicker<EquipmentProfilePicker>(); await tester.openAnimatedPicker<EquipmentProfilePicker>();

View file

@ -10,7 +10,7 @@ import 'package:mocktail/mocktail.dart';
import '../../../../../application_mock.dart'; import '../../../../../application_mock.dart';
import 'utils.dart'; import 'utils.dart';
class _MockFilmsStorageService extends Mock implements FilmsStorageService {} class _MockFilmsStorageService extends Mock implements IapStorageService {}
void main() { void main() {
late final _MockFilmsStorageService mockFilmsStorageService; late final _MockFilmsStorageService mockFilmsStorageService;

View file

@ -8,7 +8,7 @@ import 'package:mocktail/mocktail.dart';
import '../../../function_mock.dart'; import '../../../function_mock.dart';
class _MockEquipmentProfilesStorageService extends Mock implements EquipmentProfilesStorageService {} class _MockEquipmentProfilesStorageService extends Mock implements IapStorageService {}
void main() { void main() {
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();
@ -17,15 +17,15 @@ void main() {
setUp(() { setUp(() {
registerFallbackValue(_customProfiles.first); registerFallbackValue(_customProfiles.first);
when(() => storageService.addProfile(any<EquipmentProfile>())).thenAnswer((_) async {}); when(() => storageService.addEquipmentProfile(any<EquipmentProfile>())).thenAnswer((_) async {});
when( when(
() => storageService.updateProfile( () => storageService.updateEquipmentProfile(
id: any<String>(named: 'id'), id: any<String>(named: 'id'),
name: any<String>(named: 'name'), name: any<String>(named: 'name'),
), ),
).thenAnswer((_) async {}); ).thenAnswer((_) async {});
when(() => storageService.deleteProfile(any<String>())).thenAnswer((_) async {}); when(() => storageService.deleteEquipmentProfile(any<String>())).thenAnswer((_) async {});
when(() => storageService.getProfiles()).thenAnswer((_) => Future.value(_customProfiles.toTogglableMap())); when(() => storageService.getEquipmentProfiles()).thenAnswer((_) => Future.value(_customProfiles.toTogglableMap()));
}); });
tearDown(() { tearDown(() {