diff --git a/lib/state/booster_inventory_notifier.dart b/lib/state/booster_inventory_notifier.dart new file mode 100644 index 0000000..ee6f869 --- /dev/null +++ b/lib/state/booster_inventory_notifier.dart @@ -0,0 +1,28 @@ +// lib/state/booster_inventory_notifier.dart +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../data/save_repository.dart'; +import '../game/models/booster.dart'; +import 'providers.dart'; + +/// Live booster counts backed by [SaveRepository]. State is an immutable map. +class BoosterInventoryNotifier extends Notifier> { + SaveRepository get _save => ref.read(saveRepositoryProvider); + + @override + Map build() => _snapshot(); + + Map _snapshot() => + {for (final t in BoosterType.values) t: _save.boosterCount(t)}; + + Future grant(BoosterType type, [int n = 1]) async { + await _save.grantBooster(type, n); + state = _snapshot(); + } + + Future consume(BoosterType type) async { + final ok = await _save.consumeBooster(type); + if (ok) state = _snapshot(); + return ok; + } +} diff --git a/lib/state/providers.dart b/lib/state/providers.dart index 40ba84e..8eb630e 100644 --- a/lib/state/providers.dart +++ b/lib/state/providers.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../data/content_repository.dart'; import '../data/save_repository.dart'; import '../data/streak.dart'; +import '../game/models/booster.dart'; import '../game/models/season.dart'; import '../services/ad_service.dart'; import '../services/analytics_service.dart'; @@ -13,6 +14,7 @@ import '../services/music_service.dart'; import '../services/review_service.dart'; import '../services/store_reviewer.dart'; import 'ads_notifier.dart'; +import 'booster_inventory_notifier.dart'; import 'endless_best_notifier.dart'; import 'music_notifier.dart'; import 'sound_notifier.dart'; @@ -143,3 +145,8 @@ final activeThemeProvider = Provider((ref) { final flow = ref.watch(seasonFlowProvider); return flow?.pack.theme ?? SeasonTheme.fallback; }); + +final boosterInventoryProvider = + NotifierProvider>( + BoosterInventoryNotifier.new, +); diff --git a/test/state/booster_inventory_test.dart b/test/state/booster_inventory_test.dart new file mode 100644 index 0000000..15953f2 --- /dev/null +++ b/test/state/booster_inventory_test.dart @@ -0,0 +1,32 @@ +import 'package:block_seasons/data/save_repository.dart'; +import 'package:block_seasons/game/models/booster.dart'; +import 'package:block_seasons/state/booster_inventory_notifier.dart'; +import 'package:block_seasons/state/providers.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + Future container() async { + SharedPreferences.setMockInitialValues({}); + final repo = SaveRepository(await SharedPreferences.getInstance()); + return ProviderContainer( + overrides: [saveRepositoryProvider.overrideWithValue(repo)], + ); + } + + test('exposes counts and updates on grant/consume', () async { + final c = await container(); + final notifier = c.read(boosterInventoryProvider.notifier); + + expect(c.read(boosterInventoryProvider)[BoosterType.hammer], 0); + + await notifier.grant(BoosterType.hammer, 2); + expect(c.read(boosterInventoryProvider)[BoosterType.hammer], 2); + + expect(await notifier.consume(BoosterType.hammer), isTrue); + expect(c.read(boosterInventoryProvider)[BoosterType.hammer], 1); + }); +}