diff --git a/lib/providers/user_preferences_provider.dart b/lib/providers/user_preferences_provider.dart index a3542ac9..8712a57b 100644 --- a/lib/providers/user_preferences_provider.dart +++ b/lib/providers/user_preferences_provider.dart @@ -286,6 +286,8 @@ class MapThemeProfilesProvider extends Notifier { static const String immutableDefaultProfileId = 'immutable-default-map-theme'; static const String immutableValorantProfileId = 'immutable-valorant-map-theme'; + static const String immutableLotusMossProfileId = + 'immutable-lotus-moss-map-theme'; static const String appPreferencesSingletonKey = 'app_preferences'; static const int customProfilesSoftCap = 10; @@ -315,9 +317,25 @@ class MapThemeProfilesProvider extends Notifier { isBuiltIn: true, ); + static final MapThemePalette immutableLotusMossPalette = MapThemePalette( + baseColorValue: 0xFF18221A, + detailColorValue: 0xFF7FA36B, + highlightColorValue: 0xFFE3C567, + ); + + static final MapThemeProfile immutableLotusMossProfile = MapThemeProfile( + id: immutableLotusMossProfileId, + name: 'Lotus Moss', + palette: immutableLotusMossPalette, + isBuiltIn: true, + ); + + /// Every store gets each of these on launch ([bootstrap] writes any that + /// are missing), so adding one here ships it to existing users too. static final List immutableBuiltInProfiles = [ immutableDefaultProfile, immutableValorantProfile, + immutableLotusMossProfile, ]; @override diff --git a/test/lotus_moss_builtin_test.dart b/test/lotus_moss_builtin_test.dart new file mode 100644 index 00000000..5135cb0c --- /dev/null +++ b/test/lotus_moss_builtin_test.dart @@ -0,0 +1,117 @@ +import 'dart:io'; + +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:hive_ce/hive.dart'; +import 'package:icarus/const/hive_boxes.dart'; +import 'package:icarus/hive/hive_registration.dart'; +import 'package:icarus/providers/user_preferences_provider.dart'; + +void main() { + late Directory tempDir; + + setUpAll(() { + registerIcarusAdapters(Hive); + }); + + setUp(() async { + tempDir = await Directory.systemTemp.createTemp('icarus-lotus-moss-'); + Hive.init(tempDir.path); + await Hive.openBox(HiveBoxNames.mapThemeProfilesBox); + await Hive.openBox(HiveBoxNames.appPreferencesBox); + }); + + tearDown(() async { + await Hive.close(); + if (await tempDir.exists()) await tempDir.delete(recursive: true); + }); + + test('a store from before Lotus Moss gains it at launch and keeps the rest', + () async { + // What a 4.6.3 store holds: the two older built-ins, a custom profile, + // and that custom profile as the default for new strategies. + final profiles = + Hive.box(HiveBoxNames.mapThemeProfilesBox); + final custom = MapThemeProfile( + id: 'custom-id', + name: 'Tournament', + palette: MapThemePalette( + baseColorValue: 0xFF0F172A, + detailColorValue: 0xFF38BDF8, + highlightColorValue: 0xFFF97316, + ), + isBuiltIn: false, + ); + await profiles.putAll({ + MapThemeProfilesProvider.immutableDefaultProfileId: + MapThemeProfilesProvider.immutableDefaultProfile, + MapThemeProfilesProvider.immutableValorantProfileId: + MapThemeProfilesProvider.immutableValorantProfile, + custom.id: custom, + }); + await Hive.box(HiveBoxNames.appPreferencesBox).put( + MapThemeProfilesProvider.appPreferencesSingletonKey, + AppPreferences(defaultThemeProfileIdForNewStrategies: custom.id), + ); + + await MapThemeProfilesProvider.bootstrap(); + + final lotus = + profiles.get(MapThemeProfilesProvider.immutableLotusMossProfileId)!; + expect(lotus.name, 'Lotus Moss'); + expect(lotus.isBuiltIn, isTrue); + expect(lotus.palette, MapThemeProfilesProvider.immutableLotusMossPalette); + expect(profiles.get(custom.id)!.palette, custom.palette); + + final container = ProviderContainer(); + addTearDown(container.dispose); + final state = container.read(mapThemeProfilesProvider); + expect( + state.profiles.map((profile) => profile.name), + ['Default', 'Valorant', 'Lotus Moss', 'Tournament'], + ); + expect(state.defaultProfileIdForNewStrategies, custom.id); + }); + + test('Lotus Moss is a built-in: it cannot be renamed, recolored, or deleted', + () async { + await MapThemeProfilesProvider.bootstrap(); + final container = ProviderContainer(); + addTearDown(container.dispose); + final notifier = container.read(mapThemeProfilesProvider.notifier); + const id = MapThemeProfilesProvider.immutableLotusMossProfileId; + + expect( + await notifier.renameProfile(profileId: id, newName: 'Moss'), isFalse); + expect( + await notifier.updateProfilePalette( + profileId: id, + palette: MapThemeProfilesProvider.immutableDefaultPalette, + ), + isFalse, + ); + await notifier.deleteProfile(id); + + final lotus = container + .read(mapThemeProfilesProvider) + .profiles + .singleWhere((profile) => profile.id == id); + expect(lotus.name, 'Lotus Moss'); + expect(lotus.palette, MapThemeProfilesProvider.immutableLotusMossPalette); + }); + + test('a strategy set to Lotus Moss draws in its colors', () async { + await MapThemeProfilesProvider.bootstrap(); + final container = ProviderContainer(); + addTearDown(container.dispose); + + container.read(strategyThemeProvider.notifier).fromStrategy( + profileId: MapThemeProfilesProvider.immutableLotusMossProfileId, + ); + + expect( + container.read(effectiveMapThemePaletteProvider), + MapThemeProfilesProvider.immutableLotusMossPalette, + ); + }); +}