diff --git a/lib/src/upgrade_messages.dart b/lib/src/upgrade_messages.dart index 0b03a731..d5005361 100644 --- a/lib/src/upgrade_messages.dart +++ b/lib/src/upgrade_messages.dart @@ -88,13 +88,11 @@ class UpgraderMessages { Locale? locale; if (context != null) { locale = Localizations.maybeLocaleOf(context); - } else { - // Get the system locale - locale = PlatformDispatcher.instance.locale; } - final code = locale == null || locale.languageCode.isEmpty - ? 'en' - : locale.languageCode; + // Get the system locale + locale ??= PlatformDispatcher.instance.locale; + + final code = locale.languageCode.isEmpty ? 'en' : locale.languageCode; return code; } diff --git a/lib/src/upgrade_state.dart b/lib/src/upgrade_state.dart index 2ae5af69..f8a1b1ff 100644 --- a/lib/src/upgrade_state.dart +++ b/lib/src/upgrade_state.dart @@ -5,6 +5,7 @@ import 'package:package_info_plus/package_info_plus.dart'; import 'package:version/version.dart'; import 'upgrade_device.dart'; +import 'upgrade_messages.dart'; import 'upgrade_os.dart'; import 'upgrader_version_info.dart'; @@ -17,7 +18,9 @@ class UpgraderState { this.debugDisplayAlways = false, this.debugDisplayOnce = false, this.debugLogging = false, + this.durationUntilAlertAgain = const Duration(days: 3), this.languageCodeOverride, + this.messages, this.minAppVersion, this.packageInfo, required this.upgraderDevice, @@ -40,9 +43,16 @@ class UpgraderState { /// Enable print statements for debugging. final bool debugLogging; - /// The country code that will override the system locale. Optional. Used only for Android. + /// Duration until alerting user again. + final Duration durationUntilAlertAgain; + + /// The country code that will override the system locale. Optional. Used + /// only for Android. final String? languageCodeOverride; + /// The localized messages used for display in upgrader. + final UpgraderMessages? messages; + /// The minimum app version supported by this app. Earlier versions of this app /// will be forced to update to the current version. Optional. final Version? minAppVersion; @@ -67,7 +77,9 @@ class UpgraderState { bool? debugDisplayAlways, bool? debugDisplayOnce, bool? debugLogging, + Duration? durationUntilAlertAgain, String? languageCodeOverride, + UpgraderMessages? messages, Version? minAppVersion, PackageInfo? packageInfo, UpgraderDevice? upgraderDevice, @@ -80,7 +92,10 @@ class UpgraderState { debugDisplayAlways: debugDisplayAlways ?? this.debugDisplayAlways, debugDisplayOnce: debugDisplayOnce ?? this.debugDisplayOnce, debugLogging: debugLogging ?? this.debugLogging, + durationUntilAlertAgain: + durationUntilAlertAgain ?? this.durationUntilAlertAgain, languageCodeOverride: languageCodeOverride ?? this.languageCodeOverride, + messages: messages ?? this.messages, minAppVersion: minAppVersion ?? this.minAppVersion, packageInfo: packageInfo ?? this.packageInfo, upgraderDevice: upgraderDevice ?? this.upgraderDevice, @@ -94,6 +109,7 @@ class UpgraderState { UpgraderState copyWithNull({ bool? countryCodeOverride, bool? languageCodeOverride, + bool? messages, bool? minAppVersion, bool? packageInfo, bool? versionInfo, @@ -105,8 +121,10 @@ class UpgraderState { debugDisplayAlways: debugDisplayAlways, debugDisplayOnce: debugDisplayOnce, debugLogging: debugLogging, + durationUntilAlertAgain: durationUntilAlertAgain, languageCodeOverride: languageCodeOverride == true ? null : this.languageCodeOverride, + messages: messages == true ? null : this.messages, minAppVersion: minAppVersion == true ? null : this.minAppVersion, packageInfo: packageInfo == true ? null : this.packageInfo, upgraderDevice: upgraderDevice, diff --git a/lib/src/upgrader.dart b/lib/src/upgrader.dart index 0e150738..1240b205 100644 --- a/lib/src/upgrader.dart +++ b/lib/src/upgrader.dart @@ -34,18 +34,26 @@ typedef WillDisplayUpgradeCallback = void Function({ /// Creates a shared instance of [Upgrader]. Upgrader _sharedInstance = Upgrader(); -/// A class to configure the upgrade dialog. +/// An upgrade controllerthat maintains a [state] that is used to +/// trigger an alert or other UI to evaluate upgrading criteria. +/// +/// See also: +/// +/// * [UpgraderMessages], the default localized messages used for display. +/// * [UpgraderState], the [Upgrader] state. class Upgrader with WidgetsBindingObserver { + /// Creates an uprade controller that maintains a [state] that is used to + /// trigger an alert or other UI to evaluate upgrading criteria. Upgrader({ - this.messages, bool debugDisplayAlways = false, bool debugDisplayOnce = false, bool debugLogging = false, - this.durationUntilAlertAgain = const Duration(days: 3), + Duration durationUntilAlertAgain = const Duration(days: 3), this.willDisplayUpgrade, http.Client? client, String? countryCode, String? languageCode, + UpgraderMessages? messages, String? minAppVersion, UpgraderStoreController? storeController, UpgraderDevice? upgraderDevice, @@ -56,28 +64,22 @@ class Upgrader with WidgetsBindingObserver { debugDisplayAlways: debugDisplayAlways, debugDisplayOnce: debugDisplayOnce, debugLogging: debugLogging, + durationUntilAlertAgain: durationUntilAlertAgain, languageCodeOverride: languageCode, + messages: messages, minAppVersion: - _parseVersion(minAppVersion, 'minAppVersion', debugLogging), + parseVersion(minAppVersion, 'minAppVersion', debugLogging), upgraderDevice: upgraderDevice ?? UpgraderDevice(), upgraderOS: upgraderOS ?? UpgraderOS(), ), storeController = storeController ?? UpgraderStoreController() { - if (debugLogging) print("upgrader: instantiated"); + if (_state.debugLogging) { + print("upgrader: instantiated"); + } } - /// The [Upgrader] state. - UpgraderState _state; - UpgraderState get state => _state; - /// The controller that provides the store details for each platform. - final UpgraderStoreController storeController; - - /// Duration until alerting user again - final Duration durationUntilAlertAgain; - - /// The localized messages used for display in upgrader. - UpgraderMessages? messages; + UpgraderStoreController storeController; /// Called when [Upgrader] determines that an upgrade may or may not be /// displayed. The [value] parameter will be true when it should be displayed, @@ -85,78 +87,31 @@ class Upgrader with WidgetsBindingObserver { /// is logging metrics for your app. WillDisplayUpgradeCallback? willDisplayUpgrade; - bool _initCalled = false; - String? _installedVersion; - Version? _updateAvailable; - DateTime? _lastTimeAlerted; - Version? _lastVersionAlerted; - Version? _userIgnoredVersion; - bool _hasAlerted = false; + /// A shared instance of [Upgrader]. + static Upgrader get sharedInstance => _sharedInstance; - /// Track the initialization future so that [initialize] can be called multiple times. - Future? _futureInit; + /// The [Upgrader] state. + UpgraderState _state; + UpgraderState get state => _state; /// A stream that provides a new state each time an evaluation should be performed. /// The values will always be the state. Stream get stateStream => _streamController.stream; final _streamController = StreamController.broadcast(); - /// A shared instance of [Upgrader]. - static Upgrader get sharedInstance => _sharedInstance; + /// Track the initialization future so that [initialize] can be called multiple times. + Future? _futureInit; + + bool _initCalled = false; + Version? _updateAvailable; + DateTime? _lastTimeAlerted; + Version? _lastVersionAlerted; + Version? _userIgnoredVersion; + bool _hasAlerted = false; static const notInitializedExceptionMessage = 'upgrader: initialize() not called. Must be called first.'; - String? get currentAppStoreListingURL => - state.versionInfo?.appStoreListingURL; - - String? get currentAppStoreVersion => - state.versionInfo?.appStoreVersion?.toString(); - - String? get currentInstalledVersion => _installedVersion; - - String? get releaseNotes => state.versionInfo?.releaseNotes; - - void installPackageInfo({PackageInfo? packageInfo}) { - updateState(state.copyWith(packageInfo: packageInfo)); - _initCalled = false; - } - - /// The minAppVersion in the Upgrader state. - String? get minAppVersion => state.minAppVersion.toString(); - - set minAppVersion(String? version) { - if (version == null) { - updateState(state.copyWithNull(minAppVersion: true)); - } else { - final parsedVersion = - _parseVersion(version, 'minAppVersion', state.debugLogging); - if (parsedVersion != null) { - updateState(state.copyWith(minAppVersion: parsedVersion)); - } - } - } - - static Version? _parseVersion( - String? version, String name, bool debugLogging) { - if (version == null) return null; - try { - return Version.parse(version); - } catch (e) { - // if (state.debugLogging) { - print('upgrader: _parseVersion $name exception: $e'); - // } - return null; - } - } - - // void installAppStoreVersion(String version) => _appStoreVersion = version; - - // void installAppStoreListingURL(String url) => _appStoreListingURL = url; - - /// The latest version info for this app. - UpgraderVersionInfo? get versionInfo => state.versionInfo; - /// Initialize [Upgrader] by getting saved preferences, getting platform package info, and getting /// released version info. Future initialize() async { @@ -190,9 +145,7 @@ class Upgrader with WidgetsBindingObserver { } } - _installedVersion = state.packageInfo!.version; - - updateState(state.copyWith(versionInfo: await updateVersionInfo())); + await updateVersionInfo(); // Add an observer of application events, so that when the app returns // from the background, the version info is updated. @@ -203,9 +156,18 @@ class Upgrader with WidgetsBindingObserver { return _futureInit!; } - /// Updates the Upgrader state. - void updateState(UpgraderState newState) { + /// Updates the Upgrader state, which updates the stream, which triggers a + /// call to [shouldDisplayUpgrade]. + void updateState(UpgraderState newState, + {bool updateTheVersionInfo = false}) { _state = newState; + + if (updateTheVersionInfo) { + Future.delayed(Duration.zero).then((value) async { + await updateVersionInfo(); + }); + return; + } updateStream(); } @@ -229,50 +191,62 @@ class Upgrader with WidgetsBindingObserver { // When app has resumed from background. if (lifecycleState == AppLifecycleState.resumed) { - updateState(state.copyWith(versionInfo: await updateVersionInfo())); + await updateVersionInfo(); } } - /// Update the version info for this app. + /// Update the version info for this app by using an [UpgraderStore] to get + /// the [UpgraderVersionInfo]. Future updateVersionInfo() async { if (state.packageInfo == null || state.packageInfo!.packageName.isEmpty) { + updateState(state.copyWithNull(versionInfo: null)); return null; } // Determine the store to be used for this app. final store = storeController.getUpgraderStore(state.upgraderOS); - if (store == null) return null; + if (store == null) { + updateState(state.copyWithNull(versionInfo: null)); + return null; + } // Determine the installed version of this app. late Version installedVersion; try { - installedVersion = Version.parse(_installedVersion!); + installedVersion = Version.parse(state.packageInfo!.version); } catch (e) { if (state.debugLogging) { print('upgrader: installedVersion exception: $e'); - return null; } + updateState(state.copyWithNull(versionInfo: null)); + return null; } + final locale = findLocale(); + // Determine the country code of the locale, defaulting to `US`. - final country = state.countryCodeOverride ?? findCountryCode(); + final country = + state.countryCodeOverride ?? findCountryCode(locale: locale); if (state.debugLogging) { print('upgrader: countryCode: $country'); } // Determine the language code of the locale, defaulting to `en`. - final language = state.languageCodeOverride ?? findLanguageCode(); + final language = + state.languageCodeOverride ?? findLanguageCode(locale: locale); if (state.debugLogging) { print('upgrader: languageCode: $language'); } // Get the version info from the store. - final versionInfo = store.getVersionInfo( + final versionInfo = await store.getVersionInfo( state: state, installedVersion: installedVersion, country: country, language: language); + updateState(state.copyWith(versionInfo: versionInfo)); + return versionInfo; } @@ -284,7 +258,7 @@ class Upgrader with WidgetsBindingObserver { bool verifyInit() { if (!_initCalled) { - throw ('upgrader: initialize() not called. Must be called first.'); + throw (notInitializedExceptionMessage); } return true; } @@ -304,40 +278,6 @@ class Upgrader with WidgetsBindingObserver { return msg; } - /// Determine which [UpgraderMessages] object to use. It will be either the one passed - /// to [Upgrader], or one based on the app locale. - UpgraderMessages determineMessages(BuildContext context) { - { - late UpgraderMessages appMessages; - if (messages != null) { - appMessages = messages!; - } else { - String? languageCode; - try { - // Get the current locale in the app. - final locale = Localizations.localeOf(context); - // Get the current language code in the app. - languageCode = locale.languageCode; - if (state.debugLogging) { - print('upgrader: current locale: $locale'); - } - } catch (e) { - // ignored, really. - } - - appMessages = UpgraderMessages(code: languageCode); - } - - if (appMessages.languageCode.isEmpty) { - print('upgrader: error -> languageCode is empty'); - } else if (state.debugLogging) { - print('upgrader: languageCode: ${appMessages.languageCode}'); - } - - return appMessages; - } - } - bool blocked() { return belowMinAppVersion() || versionInfo?.isCriticalUpdate == true; } @@ -370,7 +310,7 @@ class Upgrader with WidgetsBindingObserver { if (willDisplayUpgrade != null) { willDisplayUpgrade!( display: rv, - installedVersion: _installedVersion, + installedVersion: state.packageInfo?.version, versionInfo: versionInfo, ); } @@ -382,9 +322,9 @@ class Upgrader with WidgetsBindingObserver { bool belowMinAppVersion() { var rv = false; final minVersion = state.minAppVersion ?? versionInfo?.minAppVersion; - if (minVersion != null) { + if (minVersion != null && state.packageInfo != null) { try { - final installedVersion = Version.parse(_installedVersion!); + final installedVersion = Version.parse(state.packageInfo!.version); rv = installedVersion < minVersion; } catch (e) { if (state.debugLogging) { @@ -401,7 +341,7 @@ class Upgrader with WidgetsBindingObserver { } final lastAlertedDuration = DateTime.now().difference(_lastTimeAlerted!); - final rv = lastAlertedDuration < durationUntilAlertAgain; + final rv = lastAlertedDuration < state.durationUntilAlertAgain; if (rv && state.debugLogging) { print('upgrader: isTooSoon: true'); } @@ -419,16 +359,17 @@ class Upgrader with WidgetsBindingObserver { bool isUpdateAvailable() { if (state.debugLogging) { - print('upgrader: installedVersion: $_installedVersion'); + print('upgrader: installedVersion: ${state.packageInfo?.version}'); print('upgrader: minAppVersion: ${state.minAppVersion}'); } - if (versionInfo?.appStoreVersion == null || _installedVersion == null) { + if (versionInfo?.appStoreVersion == null || + state.packageInfo?.version == null) { if (state.debugLogging) print('upgrader: isUpdateAvailable: false'); return false; } try { - final installedVersion = Version.parse(_installedVersion!); + final installedVersion = Version.parse(state.packageInfo!.version); final available = versionInfo!.appStoreVersion! > installedVersion; _updateAvailable = available ? versionInfo?.appStoreVersion : null; @@ -442,35 +383,31 @@ class Upgrader with WidgetsBindingObserver { return isAvailable; } - /// Determine the current country code, either from the context, or - /// from the system-reported default locale of the device. The default - /// is `US`. - String? findCountryCode({BuildContext? context}) { + Locale findLocale({BuildContext? context}) { Locale? locale; if (context != null) { locale = Localizations.maybeLocaleOf(context); - } else { - // Get the system locale - locale = PlatformDispatcher.instance.locale; } - final code = locale == null || locale.countryCode == null - ? 'US' - : locale.countryCode; + locale ??= PlatformDispatcher.instance.locale; + if (state.debugLogging) { + print('upgrader: current locale: $locale'); + } + return locale; + } + + /// Determine the current country code, either from the context, or + /// from the system-reported default locale of the device. The default + /// is `US`. + String? findCountryCode({required Locale locale}) { + final code = locale.countryCode ?? 'US'; return code; } /// Determine the current language code, either from the context, or /// from the system-reported default locale of the device. The default /// is `en`. - String? findLanguageCode({BuildContext? context}) { - Locale? locale; - if (context != null) { - locale = Localizations.maybeLocaleOf(context); - } else { - // Get the system locale - locale = PlatformDispatcher.instance.locale; - } - final code = locale == null ? 'en' : locale.languageCode; + String? findLanguageCode({required Locale locale}) { + final code = locale.languageCode; return code; } @@ -483,6 +420,28 @@ class Upgrader with WidgetsBindingObserver { return; } + /// Determine which [UpgraderMessages] object to use. It will be either the one passed + /// to [Upgrader], or one based on the app locale. + UpgraderMessages determineMessages(BuildContext context) { + if (state.messages != null) return state.messages!; + + String? languageCode = state.languageCodeOverride; + if (languageCode == null) { + final locale = findLocale(context: context); + languageCode = locale.languageCode; + } + + final appMessages = UpgraderMessages(code: languageCode); + + if (appMessages.languageCode.isEmpty) { + print('upgrader: error -> languageCode is empty'); + } else if (state.debugLogging) { + print('upgrader: languageCode: ${appMessages.languageCode}'); + } + + return appMessages; + } + Future saveIgnored() async { var prefs = await SharedPreferences.getInstance(); @@ -511,7 +470,6 @@ class Upgrader with WidgetsBindingObserver { if (lastTimeAlerted != null) { _lastTimeAlerted = DateTime.parse(lastTimeAlerted); } - final versionAlerted = prefs.getString('lastVersionAlerted'); if (versionAlerted != null) { try { @@ -536,6 +494,7 @@ class Upgrader with WidgetsBindingObserver { return true; } + /// Launch the app store from the app store listing URL. void sendUserToAppStore() async { final appStoreListingURL = versionInfo?.appStoreListingURL; if (appStoreListingURL == null || appStoreListingURL.isEmpty) { @@ -560,6 +519,59 @@ class Upgrader with WidgetsBindingObserver { print('upgrader: launch to app store failed: $e'); } } - } else {} + } } + + static Version? parseVersion( + String? version, String name, bool debugLogging) { + if (version == null) return null; + try { + return Version.parse(version); + } catch (e) { + // if (state.debugLogging) { + print('upgrader: _parseVersion $name exception: $e'); + // } + return null; + } + } +} + +extension UpgraderExt on Upgrader { + String? get currentAppStoreListingURL => + state.versionInfo?.appStoreListingURL; + + String? get currentAppStoreVersion => + state.versionInfo?.appStoreVersion?.toString(); + + String? get currentInstalledVersion => state.packageInfo?.version; + + String? get releaseNotes => state.versionInfo?.releaseNotes; + + void installPackageInfo({PackageInfo? packageInfo}) { + updateState(state.copyWith(packageInfo: packageInfo), + updateTheVersionInfo: true); + } + + /// The minAppVersion in the Upgrader state. + String? get minAppVersion => state.minAppVersion.toString(); + + set minAppVersion(String? version) { + if (version == null) { + updateState( + state.copyWithNull( + minAppVersion: true, + ), + updateTheVersionInfo: true); + } else { + final parsedVersion = + Upgrader.parseVersion(version, 'minAppVersion', state.debugLogging); + if (parsedVersion != null) { + updateState(state.copyWith(minAppVersion: parsedVersion), + updateTheVersionInfo: true); + } + } + } + + /// The latest version info for this app. + UpgraderVersionInfo? get versionInfo => state.versionInfo; } diff --git a/test/upgrade_card_test.dart b/test/upgrade_card_test.dart index d84c7f09..8ee71f43 100644 --- a/test/upgrade_card_test.dart +++ b/test/upgrade_card_test.dart @@ -77,18 +77,18 @@ void main() { // Pump the UI so the upgrade card is displayed await tester.pumpAndSettle(); - expect(upgrader.messages, isNull); - upgrader.messages = UpgraderMessages(); - expect(upgrader.messages, isNotNull); + expect(upgrader.state.messages, isNull); + upgrader.updateState(upgrader.state.copyWith(messages: UpgraderMessages())); + expect(upgrader.state.messages, isNotNull); - expect(find.text(upgrader.messages!.releaseNotes), findsOneWidget); + expect(find.text(upgrader.state.messages!.releaseNotes), findsOneWidget); expect(find.text(upgrader.releaseNotes!), findsOneWidget); - await tester.tap(find.text(upgrader.messages!.buttonTitleUpdate)); + await tester.tap(find.text(upgrader.state.messages!.buttonTitleUpdate)); await tester.pumpAndSettle(); expect(called, true); expect(notCalled, true); - expect(find.text(upgrader.messages!.buttonTitleUpdate), findsNothing); + expect(find.text(upgrader.state.messages!.buttonTitleUpdate), findsNothing); }, skip: false); testWidgets('test UpgradeCard ignore', (WidgetTester tester) async { @@ -132,16 +132,16 @@ void main() { // Pump the UI so the upgrade card is displayed await tester.pumpAndSettle(); - expect(upgrader.messages, isNull); - upgrader.messages = UpgraderMessages(); - expect(upgrader.messages, isNotNull); + expect(upgrader.state.messages, isNull); + upgrader.updateState(upgrader.state.copyWith(messages: UpgraderMessages())); + expect(upgrader.state.messages, isNotNull); - await tester.tap(find.text(upgrader.messages!.buttonTitleIgnore)); + await tester.tap(find.text(upgrader.state.messages!.buttonTitleIgnore)); await tester.pumpAndSettle(); expect(called, true); expect(notCalled, true); - expect(find.text(upgrader.messages!.buttonTitleIgnore), findsNothing); + expect(find.text(upgrader.state.messages!.buttonTitleIgnore), findsNothing); }, skip: false); testWidgets('test UpgradeCard later', (WidgetTester tester) async { @@ -185,15 +185,15 @@ void main() { // Pump the UI so the upgrade card is displayed await tester.pumpAndSettle(const Duration(milliseconds: 5000)); - expect(upgrader.messages, isNull); - upgrader.messages = UpgraderMessages(); - expect(upgrader.messages, isNotNull); + expect(upgrader.state.messages, isNull); + upgrader.updateState(upgrader.state.copyWith(messages: UpgraderMessages())); + expect(upgrader.state.messages, isNotNull); - await tester.tap(find.text(upgrader.messages!.buttonTitleLater)); + await tester.tap(find.text(upgrader.state.messages!.buttonTitleLater)); await tester.pumpAndSettle(); expect(called, true); expect(notCalled, true); - expect(find.text(upgrader.messages!.buttonTitleLater), findsNothing); + expect(find.text(upgrader.state.messages!.buttonTitleLater), findsNothing); }, skip: false); } diff --git a/test/upgrader_test.dart b/test/upgrader_test.dart index 67e72c88..42b097bd 100644 --- a/test/upgrader_test.dart +++ b/test/upgrader_test.dart @@ -180,21 +180,23 @@ void main() { expect(upgrader.isUpdateAvailable(), true); expect(upgrader.isTooSoon(), false); - expect(upgrader.messages, isNull); - upgrader.messages = UpgraderMessages(); - expect(upgrader.messages, isNotNull); + expect(upgrader.state.messages, isNull); + upgrader.updateState(upgrader.state.copyWith(messages: UpgraderMessages())); + upgrader.updateState(upgrader.state.copyWith(messages: UpgraderMessages())); + expect(upgrader.state.messages, isNotNull); - expect(upgrader.messages?.buttonTitleIgnore, 'IGNORE'); - expect(upgrader.messages?.buttonTitleLater, 'LATER'); - expect(upgrader.messages?.buttonTitleUpdate, 'UPDATE NOW'); - expect(upgrader.messages?.releaseNotes, 'Release Notes'); + expect(upgrader.state.messages?.buttonTitleIgnore, 'IGNORE'); + expect(upgrader.state.messages?.buttonTitleLater, 'LATER'); + expect(upgrader.state.messages?.buttonTitleUpdate, 'UPDATE NOW'); + expect(upgrader.state.messages?.releaseNotes, 'Release Notes'); - upgrader.messages = MyUpgraderMessages(); + upgrader + .updateState(upgrader.state.copyWith(messages: MyUpgraderMessages())); - expect(upgrader.messages!.buttonTitleIgnore, 'aaa'); - expect(upgrader.messages!.buttonTitleLater, 'bbb'); - expect(upgrader.messages!.buttonTitleUpdate, 'ccc'); - expect(upgrader.messages!.releaseNotes, 'ddd'); + expect(upgrader.state.messages!.buttonTitleIgnore, 'aaa'); + expect(upgrader.state.messages!.buttonTitleLater, 'bbb'); + expect(upgrader.state.messages!.buttonTitleUpdate, 'ccc'); + expect(upgrader.state.messages!.releaseNotes, 'ddd'); var called = false; var notCalled = true; @@ -229,24 +231,27 @@ void main() { expect(upgrader.isTooSoon(), true); - expect(find.text(upgrader.messages!.title), findsOneWidget); - expect(find.text(upgrader.body(upgrader.messages!)), findsOneWidget); - expect(find.text(upgrader.messages!.releaseNotes), findsOneWidget); + expect(find.text(upgrader.state.messages!.title), findsOneWidget); + expect(find.text(upgrader.body(upgrader.state.messages!)), findsOneWidget); + expect(find.text(upgrader.state.messages!.releaseNotes), findsOneWidget); expect(find.text(upgrader.releaseNotes!), findsOneWidget); - expect(find.text(upgrader.messages!.prompt), findsOneWidget); + expect(find.text(upgrader.state.messages!.prompt), findsOneWidget); expect(find.byType(TextButton), findsNWidgets(3)); - expect(find.text(upgrader.messages!.buttonTitleIgnore), findsOneWidget); - expect(find.text(upgrader.messages!.buttonTitleLater), findsOneWidget); - expect(find.text(upgrader.messages!.buttonTitleUpdate), findsOneWidget); - expect(find.text(upgrader.messages!.releaseNotes), findsOneWidget); + expect( + find.text(upgrader.state.messages!.buttonTitleIgnore), findsOneWidget); + expect( + find.text(upgrader.state.messages!.buttonTitleLater), findsOneWidget); + expect( + find.text(upgrader.state.messages!.buttonTitleUpdate), findsOneWidget); + expect(find.text(upgrader.state.messages!.releaseNotes), findsOneWidget); expect(find.byKey(dialogKey), findsOneWidget); - await tester.tap(find.text(upgrader.messages!.buttonTitleUpdate)); + await tester.tap(find.text(upgrader.state.messages!.buttonTitleUpdate)); await tester.pumpAndSettle(); - expect(find.text(upgrader.messages!.buttonTitleIgnore), findsNothing); - expect(find.text(upgrader.messages!.buttonTitleLater), findsNothing); - expect(find.text(upgrader.messages!.buttonTitleUpdate), findsNothing); - expect(find.text(upgrader.messages!.releaseNotes), findsNothing); + expect(find.text(upgrader.state.messages!.buttonTitleIgnore), findsNothing); + expect(find.text(upgrader.state.messages!.buttonTitleLater), findsNothing); + expect(find.text(upgrader.state.messages!.buttonTitleUpdate), findsNothing); + expect(find.text(upgrader.state.messages!.releaseNotes), findsNothing); expect(called, true); expect(notCalled, true); // }); @@ -277,19 +282,20 @@ void main() { expect(upgrader.isUpdateAvailable(), true); expect(upgrader.isTooSoon(), false); - expect(upgrader.messages, isNull); - upgrader.messages = UpgraderMessages(); - expect(upgrader.messages, isNotNull); + expect(upgrader.state.messages, isNull); + upgrader.updateState(upgrader.state.copyWith(messages: UpgraderMessages())); + expect(upgrader.state.messages, isNotNull); - expect(upgrader.messages!.buttonTitleIgnore, 'IGNORE'); - expect(upgrader.messages!.buttonTitleLater, 'LATER'); - expect(upgrader.messages!.buttonTitleUpdate, 'UPDATE NOW'); + expect(upgrader.state.messages!.buttonTitleIgnore, 'IGNORE'); + expect(upgrader.state.messages!.buttonTitleLater, 'LATER'); + expect(upgrader.state.messages!.buttonTitleUpdate, 'UPDATE NOW'); - upgrader.messages = MyUpgraderMessages(); + upgrader + .updateState(upgrader.state.copyWith(messages: MyUpgraderMessages())); - expect(upgrader.messages!.buttonTitleIgnore, 'aaa'); - expect(upgrader.messages!.buttonTitleLater, 'bbb'); - expect(upgrader.messages!.buttonTitleUpdate, 'ccc'); + expect(upgrader.state.messages!.buttonTitleIgnore, 'aaa'); + expect(upgrader.state.messages!.buttonTitleLater, 'bbb'); + expect(upgrader.state.messages!.buttonTitleUpdate, 'ccc'); var called = false; var notCalled = true; @@ -324,11 +330,11 @@ void main() { expect(upgrader.isTooSoon(), true); - expect(find.text(upgrader.messages!.title), findsOneWidget); - expect(find.text(upgrader.body(upgrader.messages!)), findsOneWidget); - expect(find.text(upgrader.messages!.releaseNotes), findsOneWidget); + expect(find.text(upgrader.state.messages!.title), findsOneWidget); + expect(find.text(upgrader.body(upgrader.state.messages!)), findsOneWidget); + expect(find.text(upgrader.state.messages!.releaseNotes), findsOneWidget); expect(find.text(upgrader.releaseNotes!), findsOneWidget); - expect(find.text(upgrader.messages!.prompt), findsOneWidget); + expect(find.text(upgrader.state.messages!.prompt), findsOneWidget); expect(find.byType(CupertinoDialogAction), findsNWidgets(3)); expect( find.byWidgetPredicate((widget) => @@ -336,16 +342,19 @@ void main() { widget.textStyle == cupertinoButtonTextStyle), findsNWidgets(3), ); - expect(find.text(upgrader.messages!.buttonTitleIgnore), findsOneWidget); - expect(find.text(upgrader.messages!.buttonTitleLater), findsOneWidget); - expect(find.text(upgrader.messages!.buttonTitleUpdate), findsOneWidget); + expect( + find.text(upgrader.state.messages!.buttonTitleIgnore), findsOneWidget); + expect( + find.text(upgrader.state.messages!.buttonTitleLater), findsOneWidget); + expect( + find.text(upgrader.state.messages!.buttonTitleUpdate), findsOneWidget); expect(find.byKey(const Key('upgrader_alert_dialog')), findsOneWidget); - await tester.tap(find.text(upgrader.messages!.buttonTitleUpdate)); + await tester.tap(find.text(upgrader.state.messages!.buttonTitleUpdate)); await tester.pumpAndSettle(); - expect(find.text(upgrader.messages!.buttonTitleIgnore), findsNothing); - expect(find.text(upgrader.messages!.buttonTitleLater), findsNothing); - expect(find.text(upgrader.messages!.buttonTitleUpdate), findsNothing); + expect(find.text(upgrader.state.messages!.buttonTitleIgnore), findsNothing); + expect(find.text(upgrader.state.messages!.buttonTitleLater), findsNothing); + expect(find.text(upgrader.state.messages!.buttonTitleUpdate), findsNothing); expect(called, true); expect(notCalled, true); }, skip: false); @@ -369,9 +378,9 @@ void main() { expect(upgrader.isTooSoon(), false); - expect(upgrader.messages, isNull); - upgrader.messages = UpgraderMessages(); - expect(upgrader.messages, isNotNull); + expect(upgrader.state.messages, isNull); + upgrader.updateState(upgrader.state.copyWith(messages: UpgraderMessages())); + expect(upgrader.state.messages, isNotNull); var called = false; var notCalled = true; @@ -398,9 +407,9 @@ void main() { // Pump the UI so the upgrader can display its dialog await tester.pumpAndSettle(); - await tester.tap(find.text(upgrader.messages!.buttonTitleIgnore)); + await tester.tap(find.text(upgrader.state.messages!.buttonTitleIgnore)); await tester.pumpAndSettle(); - expect(find.text(upgrader.messages!.buttonTitleIgnore), findsNothing); + expect(find.text(upgrader.state.messages!.buttonTitleIgnore), findsNothing); expect(called, true); expect(notCalled, true); }, skip: false); @@ -424,9 +433,9 @@ void main() { expect(upgrader.isTooSoon(), false); - expect(upgrader.messages, isNull); - upgrader.messages = UpgraderMessages(); - expect(upgrader.messages, isNotNull); + expect(upgrader.state.messages, isNull); + upgrader.updateState(upgrader.state.copyWith(messages: UpgraderMessages())); + expect(upgrader.state.messages, isNotNull); var called = false; var notCalled = true; @@ -453,9 +462,9 @@ void main() { // Pump the UI so the upgrader can display its dialog await tester.pumpAndSettle(); - await tester.tap(find.text(upgrader.messages!.buttonTitleLater)); + await tester.tap(find.text(upgrader.state.messages!.buttonTitleLater)); await tester.pumpAndSettle(); - expect(find.text(upgrader.messages!.buttonTitleLater), findsNothing); + expect(find.text(upgrader.state.messages!.buttonTitleLater), findsNothing); expect(called, true); expect(notCalled, true); }, skip: false); @@ -478,9 +487,9 @@ void main() { expect(upgrader.isTooSoon(), false); - expect(upgrader.messages, isNull); - upgrader.messages = UpgraderMessages(); - expect(upgrader.messages, isNotNull); + expect(upgrader.state.messages, isNull); + upgrader.updateState(upgrader.state.copyWith(messages: UpgraderMessages())); + expect(upgrader.state.messages, isNotNull); var called = false; final upgradeAlert = wrapper( @@ -564,13 +573,14 @@ void main() { // Pump the UI so the upgrade card is displayed await tester.pumpAndSettle(const Duration(milliseconds: 5000)); - expect(upgrader.messages, isNull); - upgrader.messages = UpgraderMessages(); - expect(upgrader.messages, isNotNull); + expect(upgrader.state.messages, isNull); + upgrader.updateState(upgrader.state.copyWith(messages: UpgraderMessages())); + expect(upgrader.state.messages, isNotNull); - expect(find.text(upgrader.messages!.buttonTitleIgnore), findsNothing); - expect(find.text(upgrader.messages!.buttonTitleLater), findsNothing); - expect(find.text(upgrader.messages!.buttonTitleUpdate), findsOneWidget); + expect(find.text(upgrader.state.messages!.buttonTitleIgnore), findsNothing); + expect(find.text(upgrader.state.messages!.buttonTitleLater), findsNothing); + expect( + find.text(upgrader.state.messages!.buttonTitleUpdate), findsOneWidget); }, skip: false); testWidgets('test upgrader minAppVersion description android', @@ -660,11 +670,11 @@ void main() { // Pump the UI so the upgrade card is displayed await tester.pumpAndSettle(); - expect(upgrader.messages, isNull); - upgrader.messages = UpgraderMessages(); - expect(upgrader.messages, isNotNull); + expect(upgrader.state.messages, isNull); + upgrader.updateState(upgrader.state.copyWith(messages: UpgraderMessages())); + expect(upgrader.state.messages, isNotNull); - final laterButton = find.text(upgrader.messages!.buttonTitleLater); + final laterButton = find.text(upgrader.state.messages!.buttonTitleLater); expect(laterButton, findsNothing); expect(called, false); @@ -791,41 +801,41 @@ void main() { test('durationUntilAlertAgain defaults to 3 days', () async { final upgrader = Upgrader(); - expect(upgrader.durationUntilAlertAgain, const Duration(days: 3)); + expect(upgrader.state.durationUntilAlertAgain, const Duration(days: 3)); }, skip: false); test('durationUntilAlertAgain is 0 days', () async { final upgrader = Upgrader(durationUntilAlertAgain: const Duration(seconds: 0)); - expect(upgrader.durationUntilAlertAgain, const Duration(seconds: 0)); + expect(upgrader.state.durationUntilAlertAgain, const Duration(seconds: 0)); UpgradeAlert(upgrader: upgrader); - expect(upgrader.durationUntilAlertAgain, const Duration(seconds: 0)); + expect(upgrader.state.durationUntilAlertAgain, const Duration(seconds: 0)); UpgradeCard(upgrader: upgrader); - expect(upgrader.durationUntilAlertAgain, const Duration(seconds: 0)); + expect(upgrader.state.durationUntilAlertAgain, const Duration(seconds: 0)); }, skip: false); test('durationUntilAlertAgain card is valid', () async { final upgrader = Upgrader(durationUntilAlertAgain: const Duration(days: 3)); UpgradeCard(upgrader: upgrader); - expect(upgrader.durationUntilAlertAgain, const Duration(days: 3)); + expect(upgrader.state.durationUntilAlertAgain, const Duration(days: 3)); final upgrader2 = Upgrader(durationUntilAlertAgain: const Duration(days: 10)); UpgradeCard(upgrader: upgrader2); - expect(upgrader2.durationUntilAlertAgain, const Duration(days: 10)); + expect(upgrader2.state.durationUntilAlertAgain, const Duration(days: 10)); }, skip: false); test('durationUntilAlertAgain alert is valid', () async { final upgrader = Upgrader(durationUntilAlertAgain: const Duration(days: 3)); UpgradeAlert(upgrader: upgrader); - expect(upgrader.durationUntilAlertAgain, const Duration(days: 3)); + expect(upgrader.state.durationUntilAlertAgain, const Duration(days: 3)); final upgrader2 = Upgrader(durationUntilAlertAgain: const Duration(days: 10)); UpgradeAlert(upgrader: upgrader2); - expect(upgrader2.durationUntilAlertAgain, const Duration(days: 10)); + expect(upgrader2.state.durationUntilAlertAgain, const Duration(days: 10)); }, skip: false); group('shouldDisplayUpgrade', () {