diff --git a/app/android/app/src/main/AndroidManifest.xml b/app/android/app/src/main/AndroidManifest.xml index fe900dcf9..518cce220 100644 --- a/app/android/app/src/main/AndroidManifest.xml +++ b/app/android/app/src/main/AndroidManifest.xml @@ -32,6 +32,9 @@ + + + diff --git a/app/ios/Podfile.lock b/app/ios/Podfile.lock index 8ed45dc9d..6d84ae700 100644 --- a/app/ios/Podfile.lock +++ b/app/ios/Podfile.lock @@ -260,6 +260,12 @@ PODS: - SDWebImageWebPCoder - flutter_timezone (0.0.1): - Flutter + - Google-Mobile-Ads-SDK (11.10.0): + - GoogleUserMessagingPlatform (>= 1.1) + - google_mobile_ads (5.2.0): + - Flutter + - Google-Mobile-Ads-SDK (~> 11.10.0) + - webview_flutter_wkwebview - google_sign_in_ios (0.0.1): - AppAuth (>= 1.7.4) - Flutter @@ -304,6 +310,7 @@ PODS: - GoogleToolboxForMac/Defines (= 4.2.1) - "GoogleToolboxForMac/NSData+zlib (4.2.1)": - GoogleToolboxForMac/Defines (= 4.2.1) + - GoogleUserMessagingPlatform (2.6.0) - GoogleUtilities/AppDelegateSwizzler (7.13.3): - GoogleUtilities/Environment - GoogleUtilities/Logger @@ -462,6 +469,7 @@ DEPENDENCIES: - Flutter (from `Flutter`) - flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`) - flutter_timezone (from `.symlinks/plugins/flutter_timezone/ios`) + - google_mobile_ads (from `.symlinks/plugins/google_mobile_ads/ios`) - google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/darwin`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - in_app_review (from `.symlinks/plugins/in_app_review/ios`) @@ -515,11 +523,13 @@ SPEC REPOS: - FirebaseSessions - FirebaseSharedSwift - FirebaseStorage + - Google-Mobile-Ads-SDK - GoogleAppMeasurement - GoogleDataTransport - GoogleMLKit - GoogleSignIn - GoogleToolboxForMac + - GoogleUserMessagingPlatform - GoogleUtilities - GoogleUtilitiesComponents - GTMAppAuth @@ -581,6 +591,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_image_compress_common/ios" flutter_timezone: :path: ".symlinks/plugins/flutter_timezone/ios" + google_mobile_ads: + :path: ".symlinks/plugins/google_mobile_ads/ios" google_sign_in_ios: :path: ".symlinks/plugins/google_sign_in_ios/darwin" image_picker_ios: @@ -676,12 +688,15 @@ SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e flutter_timezone: ffb07bdad3c6276af8dada0f11978d8a1f8a20bb + Google-Mobile-Ads-SDK: 13e6e98edfd78ad8d8a791edb927658cc260a56f + google_mobile_ads: 2a538d8e42b1813809782792e48f8cf4374c2180 google_sign_in_ios: 07375bfbf2620bc93a602c0e27160d6afc6ead38 GoogleAppMeasurement: 9abf64b682732fed36da827aa2a68f0221fd2356 GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleMLKit: 97ac7af399057e99182ee8edfa8249e3226a4065 GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db GoogleToolboxForMac: d1a2cbf009c453f4d6ded37c105e2f67a32206d8 + GoogleUserMessagingPlatform: 0c3a08353e53ce8c2feab7addd0b652cde522450 GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 GoogleUtilitiesComponents: 679b2c881db3b615a2777504623df6122dd20afe GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de diff --git a/app/ios/Runner.xcodeproj/project.pbxproj b/app/ios/Runner.xcodeproj/project.pbxproj index 391a0736d..160b294d6 100644 --- a/app/ios/Runner.xcodeproj/project.pbxproj +++ b/app/ios/Runner.xcodeproj/project.pbxproj @@ -475,7 +475,10 @@ "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestoreGRPCCPPBinary/FirebaseFirestoreGRPCCPPBinary_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestoreGRPCCoreBinary/FirebaseFirestoreGRPCCoreBinary_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestoreInternalBinary/FirebaseFirestoreInternalBinary_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/Google-Mobile-Ads-SDK/GoogleMobileAdsResources.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUserMessagingPlatform/UserMessagingPlatformResources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/firebase_messaging/firebase_messaging_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/google_mobile_ads/google_mobile_ads.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/google_sign_in_ios/google_sign_in_ios_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/mobile_scanner/mobile_scanner_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/permission_handler_apple/permission_handler_apple_privacy.bundle", @@ -488,7 +491,10 @@ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseFirestoreGRPCCPPBinary_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseFirestoreGRPCCoreBinary_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseFirestoreInternalBinary_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleMobileAdsResources.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UserMessagingPlatformResources.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/firebase_messaging_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/google_mobile_ads.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/google_sign_in_ios_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/mobile_scanner_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/permission_handler_apple_privacy.bundle", diff --git a/app/ios/Runner/Info.plist b/app/ios/Runner/Info.plist index 45b7d05d7..9266e2019 100644 --- a/app/ios/Runner/Info.plist +++ b/app/ios/Runner/Info.plist @@ -117,5 +117,7 @@ UIApplicationSupportsIndirectInputEvents + GADApplicationIdentifier + ca-app-pub-7730914075870960~4953718516 diff --git a/app/lib/activation_code/src/bloc/enter_activation_code_bloc.dart b/app/lib/activation_code/src/bloc/enter_activation_code_bloc.dart index 3e566d53c..9f08d173b 100644 --- a/app/lib/activation_code/src/bloc/enter_activation_code_bloc.dart +++ b/app/lib/activation_code/src/bloc/enter_activation_code_bloc.dart @@ -80,12 +80,33 @@ class EnterActivationCodeBloc extends BlocBase { return; } + // Required for testing as long we run the A/B test. + // + // In case you are in A/B test group, you can't deactivate the ads by + // entering 'ads' in the activation code field. + if (_lastEnteredValue?.trim().toLowerCase() == 'ads') { + _toggleAds(); + return; + } + _changeEnterActivationCodeResult(LoadingEnterActivationCodeResult()); final enterActivationCodeResult = await _runAppFunction(enteredValue); _changeEnterActivationCodeResult(enterActivationCodeResult); } + void _toggleAds() { + final currentValue = keyValueStore.getBool('show-ads') ?? false; + keyValueStore.setBool('show-ads', !currentValue); + + _changeEnterActivationCodeResult( + SuccessfulEnterActivationCodeResult( + 'ads', + 'Ads wurden ${!currentValue ? 'aktiviert' : 'deaktiviert'}. Starte die App neu, um die Änderungen zu sehen.', + ), + ); + } + Future _clearCache(BuildContext context) async { await Future.wait([ keyValueStore.clear(), diff --git a/app/lib/ads/ad_banner.dart b/app/lib/ads/ad_banner.dart new file mode 100644 index 000000000..b8f274670 --- /dev/null +++ b/app/lib/ads/ad_banner.dart @@ -0,0 +1,85 @@ +// Copyright (c) 2024 Sharezone UG (haftungsbeschränkt) +// Licensed under the EUPL-1.2-or-later. +// +// You may obtain a copy of the Licence at: +// https://joinup.ec.europa.eu/software/page/eupl +// +// SPDX-License-Identifier: EUPL-1.2 + +import 'package:flutter/widgets.dart'; +import 'package:google_mobile_ads/google_mobile_ads.dart'; +import 'package:provider/provider.dart'; +import 'package:sharezone/ads/ads_controller.dart'; + +class AdBanner extends StatefulWidget { + const AdBanner({ + super.key, + required this.adUnitId, + }); + + final String adUnitId; + + @override + State createState() => _AdBannerState(); +} + +class _AdBannerState extends State { + BannerAd? ad; + bool _isLoaded = false; + + @override + void initState() { + super.initState(); + if (context.read().isQualifiedForAds()) { + WidgetsBinding.instance.addPostFrameCallback((_) async { + final size = + await AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize( + MediaQuery.sizeOf(context).width.truncate()); + + if (size == null) { + // Unable to get width of anchored banner. + return; + } + + if (!mounted) { + return; + } + + ad = BannerAd( + adUnitId: widget.adUnitId, + request: context.read().createAdRequest(), + size: size, + listener: BannerAdListener( + onAdLoaded: (ad) { + debugPrint('$ad loaded.'); + setState(() { + _isLoaded = true; + }); + }, + onAdFailedToLoad: (ad, err) { + debugPrint('BannerAd failed to load: $err'); + ad.dispose(); + }, + ), + )..load(); + }); + } + } + + @override + void dispose() { + ad?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final areAdsVisible = context.watch().areAdsVisible; + if (!areAdsVisible || _isLoaded == false) return Container(); + return SizedBox( + height: ad!.size.height.toDouble(), + width: ad!.size.width.toDouble(), + child: AdWidget(ad: ad!), + ); + } +} diff --git a/app/lib/ads/ad_info_dialog.dart b/app/lib/ads/ad_info_dialog.dart new file mode 100644 index 000000000..b428df9b8 --- /dev/null +++ b/app/lib/ads/ad_info_dialog.dart @@ -0,0 +1,56 @@ +// Copyright (c) 2024 Sharezone UG (haftungsbeschränkt) +// Licensed under the EUPL-1.2-or-later. +// +// You may obtain a copy of the Licence at: +// https://joinup.ec.europa.eu/software/page/eupl +// +// SPDX-License-Identifier: EUPL-1.2 + +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:sharezone/sharezone_plus/page/sharezone_plus_page.dart'; +import 'package:sharezone_widgets/sharezone_widgets.dart'; + +void showAdInfoDialog(BuildContext context) async { + final navigateToPlusPage = await showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Werbung in Sharezone'), + content: Text.rich( + TextSpan( + children: [ + const TextSpan( + text: + 'Innerhalb der nächsten Wochen führen wir ein Experiment mit Werbung in Sharezone durch. Wenn du keine Werbung sehen möchten, kannst du ', + ), + TextSpan( + text: 'Sharezone Plus', + style: const TextStyle( + color: primaryColor, + decoration: TextDecoration.underline, + ), + recognizer: TapGestureRecognizer() + ..onTap = () => Navigator.of(context).pop(true), + ), + const TextSpan( + text: ' erwerben.', + ), + ], + ), + ), + actions: [ + ElevatedButton( + style: ElevatedButton.styleFrom(foregroundColor: Colors.white), + child: const Text('OK'), + onPressed: () => Navigator.of(context).pop(), + ), + ], + ); + }, + ); + + if (navigateToPlusPage == true && context.mounted) { + navigateToSharezonePlusPage(context); + } +} diff --git a/app/lib/ads/ads_controller.dart b/app/lib/ads/ads_controller.dart new file mode 100644 index 000000000..0c3dcd312 --- /dev/null +++ b/app/lib/ads/ads_controller.dart @@ -0,0 +1,217 @@ +// Copyright (c) 2024 Sharezone UG (haftungsbeschränkt) +// Licensed under the EUPL-1.2-or-later. +// +// You may obtain a copy of the Licence at: +// https://joinup.ec.europa.eu/software/page/eupl +// +// SPDX-License-Identifier: EUPL-1.2 + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:google_mobile_ads/google_mobile_ads.dart'; +import 'package:key_value_store/key_value_store.dart'; +import 'package:platform_check/platform_check.dart'; +import 'package:remote_configuration/remote_configuration.dart'; +import 'package:sharezone/sharezone_plus/subscription_service/subscription_service.dart'; + +enum AdFormat { + banner, + native, + interstitial, +} + +class AdsController extends ChangeNotifier { + final SubscriptionService subscriptionService; + final RemoteConfiguration remoteConfiguration; + final KeyValueStore keyValueStore; + + /// Defines if ads are visible for the current user. + bool areAdsVisible = false; + StreamSubscription? _subscription; + + AdsController({ + required this.subscriptionService, + required this.remoteConfiguration, + required this.keyValueStore, + }) { + if (isQualifiedForAds()) { + _initializeMobileAdsSDK(); + _listenToSubscriptionService(); + } + } + + bool isQualifiedForAds() { + if (!PlatformCheck.isMobile) { + // Google AdMob SDK is only available on mobile platforms. + return false; + } + + final isRemoteConfigFlagEnabled = + remoteConfiguration.getBool('ads_enabled'); + final isActivationFlagEnabled = keyValueStore.getBool('show-ads') ?? false; + return isRemoteConfigFlagEnabled || isActivationFlagEnabled; + } + + /// Determines if the user should be shown an info dialog about ads. + /// + /// We show an info dialog about our ads experiment after the first app open. + bool shouldShowInfoDialog() { + if (!isQualifiedForAds()) { + return false; + } + + final hasShownDialog = + keyValueStore.getBool('ads-info-dialog-shown') ?? false; + if (!hasShownDialog) { + keyValueStore.setBool('ads-info-dialog-shown', true); + return true; + } + + return false; + } + + // Returns the test ad unit ID for the given [format]. + // + // Used for testing purposes only. + String getTestAdUnitId(AdFormat format) { + // From: https://developers.google.com/admob/flutter/banner#always_test_with_test_ads + const testAdUnitIdBannerAndroid = 'ca-app-pub-3940256099942544/6300978111'; + const testAdUnitIdBannerIOS = 'ca-app-pub-3940256099942544/2934735716'; + + // From: https://developers.google.com/admob/flutter/native/templates#always_test_with_test_ads + const testAdUnitIdNativeAndroid = 'ca-app-pub-3940256099942544/2247696110'; + const testAdUnitIdNativeIOS = 'ca-app-pub-3940256099942544/3986624511'; + + // From: https://developers.google.com/admob/flutter/interstitial#always_test_with_test_ads + const testAdUnitIdInterstitialAndroid = + 'ca-app-pub-3940256099942544/1033173712'; + const testAdUnitIdInterstitialIOS = + 'ca-app-pub-3940256099942544/4411468910'; + + return switch (PlatformCheck.currentPlatform) { + // Test ad unit IDs from: https://developers.google.com/admob/flutter/ + Platform.android => switch (format) { + AdFormat.banner => testAdUnitIdBannerAndroid, + AdFormat.native => testAdUnitIdNativeAndroid, + AdFormat.interstitial => testAdUnitIdInterstitialAndroid, + }, + Platform.iOS => switch (format) { + AdFormat.banner => testAdUnitIdBannerIOS, + AdFormat.native => testAdUnitIdNativeIOS, + AdFormat.interstitial => testAdUnitIdInterstitialIOS, + }, + _ => 'N/A', + }; + } + + void _listenToSubscriptionService() { + _subscription = subscriptionService + .hasFeatureUnlockedStream(SharezonePlusFeature.removeAds) + .listen((hasUnlocked) { + if (hasUnlocked) { + areAdsVisible = false; + } else { + areAdsVisible = isQualifiedForAds(); + } + notifyListeners(); + }); + } + + Future _initializeMobileAdsSDK() async { + await MobileAds.instance.initialize(); + await MobileAds.instance.updateRequestConfiguration(RequestConfiguration( + // Since we set this value to `true` there is no need to show the consent + // form. + tagForUnderAgeOfConsent: 1, + )); + debugPrint("Google Mobile Ads SDK initialized."); + } + + AdRequest createAdRequest() { + return AdRequest( + nonPersonalizedAds: true, + // keywords, contentUrl, and neighboringContentUrls help Google AdMob to + // show more relevant ads by providing more information about our app. + keywords: remoteConfiguration.getStringList('ad_keywords'), + contentUrl: remoteConfiguration.getString('ad_content_url'), + neighboringContentUrls: + remoteConfiguration.getStringList('ad_neighboring_urls'), + ); + } + + Future maybeShowFullscreenAd() async { + if (!PlatformCheck.isMobile) { + return; + } + + if (areAdsVisible && _shouldShowFullscreenAd()) { + await _showFullscreenAd(); + } + } + + bool _shouldShowFullscreenAd() { + final checkedHwCounter = + keyValueStore.getInt('checked-homework-counter') ?? 0; + + if (checkedHwCounter == 0) { + // For a better user experience, we don't show an ad when the user + // completes their first homework. Technically, counter should then be 1, + // but since we're not waiting for the counter to increment, we need to + // check for 0. + return false; + } + + return checkedHwCounter % 5 == 0; + } + + Future _showFullscreenAd() async { + String adUnitId; + + if (kDebugMode) { + adUnitId = getTestAdUnitId(AdFormat.interstitial); + } else { + adUnitId = switch (PlatformCheck.currentPlatform) { + // Copied from the AdMob Console + Platform.android => 'ca-app-pub-7730914075870960/5232564896', + Platform.iOS => 'ca-app-pub-7730914075870960/4462307071', + _ => 'N/A', + }; + } + + InterstitialAd.load( + adUnitId: adUnitId, + request: createAdRequest(), + adLoadCallback: InterstitialAdLoadCallback( + onAdLoaded: (ad) { + ad.fullScreenContentCallback = FullScreenContentCallback( + onAdFailedToShowFullScreenContent: (ad, err) { + ad.dispose(); + }, + onAdDismissedFullScreenContent: (ad) { + ad.dispose(); + }, + ); + + debugPrint('$ad loaded.'); + ad.show(); + }, + onAdFailedToLoad: (LoadAdError error) { + debugPrint('InterstitialAd failed to load: $error'); + }, + ), + ); + } + + @override + void dispose() { + _subscription?.cancel(); + super.dispose(); + } +} + +extension on RemoteConfiguration { + List getStringList(String key) { + return getString(key).split(',').map((e) => e.trim()).toList(); + } +} diff --git a/app/lib/dashboard/dashboard_page.dart b/app/lib/dashboard/dashboard_page.dart index 2a197fa5e..b5e8ffc26 100644 --- a/app/lib/dashboard/dashboard_page.dart +++ b/app/lib/dashboard/dashboard_page.dart @@ -18,12 +18,16 @@ import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:helper_functions/helper_functions.dart'; import 'package:holidays/holidays.dart' hide State; import 'package:platform_check/platform_check.dart'; +import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:sharezone/ads/ad_info_dialog.dart'; +import 'package:sharezone/ads/ads_controller.dart'; import 'package:sharezone/blackboard/blackboard_page.dart'; import 'package:sharezone/blackboard/blackboard_view.dart'; import 'package:sharezone/dashboard/analytics/dashboard_analytics.dart'; import 'package:sharezone/dashboard/bloc/dashboard_bloc.dart'; import 'package:sharezone/dashboard/models/homework_view.dart'; +import 'package:sharezone/dashboard/sections/ad_section.dart'; import 'package:sharezone/dashboard/timetable/lesson_view.dart'; import 'package:sharezone/dashboard/tips/dashboard_tip_system.dart'; import 'package:sharezone/dashboard/update_reminder/update_reminder_bloc.dart'; @@ -84,6 +88,17 @@ class _DashboardPageState extends State { void initState() { super.initState(); showTipCardIfIsAvailable(context); + maybeShowAdInfoDialog(); + } + + void maybeShowAdInfoDialog() { + final controller = context.read(); + WidgetsBinding.instance.addPostFrameCallback((_) { + final shouldShowDialog = controller.shouldShowInfoDialog(); + if (shouldShowDialog) { + showAdInfoDialog(context); + } + }); } @override @@ -135,6 +150,7 @@ class DashboardPageBody extends StatelessWidget { if (!PlatformCheck.isWeb) _UpdateReminder(), _DashboardTipSection(), const _HomeworkSection(), + const DashboardAds(), _EventsSection(), _BlackboardSection(), const HolidayCountdownSection(), diff --git a/app/lib/dashboard/sections/ad_section.dart b/app/lib/dashboard/sections/ad_section.dart new file mode 100644 index 000000000..2467a1f18 --- /dev/null +++ b/app/lib/dashboard/sections/ad_section.dart @@ -0,0 +1,143 @@ +// Copyright (c) 2024 Sharezone UG (haftungsbeschränkt) +// Licensed under the EUPL-1.2-or-later. +// +// You may obtain a copy of the Licence at: +// https://joinup.ec.europa.eu/software/page/eupl +// +// SPDX-License-Identifier: EUPL-1.2 + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:google_mobile_ads/google_mobile_ads.dart'; +import 'package:platform_check/platform_check.dart'; +import 'package:provider/provider.dart'; +import 'package:sharezone/ads/ads_controller.dart'; +import 'package:sharezone/sharezone_plus/page/sharezone_plus_page.dart'; + +class DashboardAds extends StatefulWidget { + const DashboardAds({super.key}); + + @override + State createState() => _DashboardAdsState(); +} + +class _DashboardAdsState extends State { + NativeAd? nativeAd; + bool _nativeAdIsLoaded = false; + + @override + void initState() { + super.initState(); + if (context.read().isQualifiedForAds()) { + WidgetsBinding.instance.addPostFrameCallback((_) { + loadAd(); + }); + } + } + + String getAdUnitId() { + if (kDebugMode) { + return context.read().getTestAdUnitId(AdFormat.native); + } + + return switch (PlatformCheck.currentPlatform) { + // Copied from the AdMob Console + Platform.android => 'ca-app-pub-7730914075870960/4681798929', + Platform.iOS => 'ca-app-pub-7730914075870960/2027715422', + _ => 'N/A', + }; + } + + /// Loads a native ad. + void loadAd() { + nativeAd = NativeAd( + adUnitId: getAdUnitId(), + listener: NativeAdListener( + onAdLoaded: (ad) { + debugPrint('$NativeAd loaded.'); + setState(() { + _nativeAdIsLoaded = true; + }); + }, + onAdFailedToLoad: (ad, error) { + // Dispose the ad here to free resources. + debugPrint('$NativeAd failed to load: $error'); + ad.dispose(); + }, + ), + request: context.read().createAdRequest(), + nativeTemplateStyle: NativeTemplateStyle( + templateType: TemplateType.small, + mainBackgroundColor: Theme.of(context).scaffoldBackgroundColor, + cornerRadius: 10.0, + callToActionTextStyle: NativeTemplateTextStyle( + textColor: Colors.white, + backgroundColor: Theme.of(context).primaryColor, + size: 16.0, + ), + primaryTextStyle: NativeTemplateTextStyle( + textColor: DefaultTextStyle.of(context).style.color, + style: NativeTemplateFontStyle.normal, + size: 16.0, + ), + ), + )..load(); + } + + @override + dispose() { + nativeAd?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final areAdsVisible = context.watch().areAdsVisible; + if (!areAdsVisible || !_nativeAdIsLoaded) { + return Container(); + } + + return Padding( + padding: const EdgeInsets.fromLTRB(12, 12, 12, 0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(4), + child: GestureDetector( + onTap: () => navigateToSharezonePlusPage(context), + child: Text.rich( + TextSpan( + text: + 'Dank dieser Anzeige ist Sharezone kostenlos. Falls du die Anzeige nicht sehen möchtest, kannst du ', + style: const TextStyle(fontSize: 10, color: Colors.grey), + children: [ + TextSpan( + text: 'Sharezone Plus', + style: TextStyle( + fontSize: 10, + color: Theme.of(context).primaryColor, + ), + ), + const TextSpan( + text: ' erwerben.', + ), + ], + ), + ), + ), + ), + ConstrainedBox( + constraints: const BoxConstraints( + minWidth: 320, // minimum recommended width + minHeight: 90, // minimum recommended height + maxWidth: 500, + maxHeight: 100, + ), + child: AdWidget(ad: nativeAd!), + ), + ], + ), + ); + } +} diff --git a/app/lib/homework/homework_dialog/homework_dialog.dart b/app/lib/homework/homework_dialog/homework_dialog.dart index e967d36ed..d38632142 100644 --- a/app/lib/homework/homework_dialog/homework_dialog.dart +++ b/app/lib/homework/homework_dialog/homework_dialog.dart @@ -19,7 +19,6 @@ import 'package:collection/collection.dart'; import 'package:common_domain_models/common_domain_models.dart'; import 'package:date/date.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart'; - import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart' as bloc_lib show BlocProvider; @@ -27,6 +26,7 @@ import 'package:flutter_bloc/flutter_bloc.dart' hide BlocProvider; import 'package:hausaufgabenheft_logik/hausaufgabenheft_logik.dart' hide Date; import 'package:platform_check/platform_check.dart'; import 'package:provider/provider.dart'; +import 'package:sharezone/ads/ads_controller.dart'; import 'package:sharezone/filesharing/dialog/attach_file.dart'; import 'package:sharezone/filesharing/dialog/course_tile.dart'; import 'package:sharezone/holidays/holiday_bloc.dart'; @@ -238,7 +238,9 @@ class HomeworkDialogMainState extends State { listener: (context, state) { if (state is SavedSuccessfully) { ScaffoldMessenger.of(context).hideCurrentSnackBar(); + final adsController = context.read(); Navigator.pop(context); + adsController.maybeShowFullscreenAd(); } }, builder: (context, state) { diff --git a/app/lib/homework/homework_dialog/homework_dialog_bloc.dart b/app/lib/homework/homework_dialog/homework_dialog_bloc.dart index 9ba682075..de9d9ef5d 100644 --- a/app/lib/homework/homework_dialog/homework_dialog_bloc.dart +++ b/app/lib/homework/homework_dialog/homework_dialog_bloc.dart @@ -18,7 +18,6 @@ import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:files_basics/files_models.dart'; import 'package:files_basics/local_file.dart'; import 'package:filesharing_logic/filesharing_logic_models.dart'; - import 'package:group_domain_models/group_domain_models.dart'; import 'package:hausaufgabenheft_logik/hausaufgabenheft_logik.dart' hide Date; import 'package:meta/meta.dart'; diff --git a/app/lib/homework/student/src/open_homework_list.dart b/app/lib/homework/student/src/open_homework_list.dart index b26f7edec..527db1a1c 100644 --- a/app/lib/homework/student/src/open_homework_list.dart +++ b/app/lib/homework/student/src/open_homework_list.dart @@ -9,6 +9,8 @@ import 'package:bloc_provider/bloc_provider.dart'; import 'package:flutter/material.dart'; import 'package:hausaufgabenheft_logik/hausaufgabenheft_logik.dart'; +import 'package:provider/provider.dart'; +import 'package:sharezone/ads/ads_controller.dart'; import 'package:sharezone/homework/shared/shared.dart'; import 'package:sharezone/homework/student/src/completed_homework_list.dart'; @@ -64,6 +66,9 @@ class OpenHomeworkList extends StatelessWidget { if (newStatus == HomeworkStatus.completed) { dispatchCompletionStatusChange(newStatus, hw.id, bloc); } + + final adsController = context.read(); + adsController.maybeShowFullscreenAd(); }, ), ], diff --git a/app/lib/homework/student/student_homework_page.dart b/app/lib/homework/student/student_homework_page.dart index 0c74c69f5..6062c6938 100644 --- a/app/lib/homework/student/student_homework_page.dart +++ b/app/lib/homework/student/student_homework_page.dart @@ -7,10 +7,14 @@ // SPDX-License-Identifier: EUPL-1.2 import 'package:bloc_provider/bloc_provider.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:hausaufgabenheft_logik/hausaufgabenheft_logik.dart'; +import 'package:platform_check/platform_check.dart'; import 'package:provider/provider.dart'; import 'package:rxdart/rxdart.dart'; +import 'package:sharezone/ads/ad_banner.dart'; +import 'package:sharezone/ads/ads_controller.dart'; import 'package:sharezone/homework/shared/shared.dart'; import 'package:sharezone/homework/student/src/homework_bottom_action_bar.dart'; import 'package:sharezone/navigation/logic/navigation_bloc.dart'; @@ -28,6 +32,19 @@ import 'src/open_homework_list.dart'; class StudentHomeworkPage extends StatelessWidget { const StudentHomeworkPage({super.key}); + String getAdUnitId(BuildContext context) { + if (kDebugMode) { + return context.read().getTestAdUnitId(AdFormat.banner); + } + + return switch (PlatformCheck.currentPlatform) { + // Copied from the AdMob Console + Platform.android => 'ca-app-pub-7730914075870960/6002721082', + Platform.iOS => 'ca-app-pub-7730914075870960/5068913364', + _ => 'N/A', + }; + } + @override Widget build(BuildContext context) { final bloc = BlocProvider.of(context); @@ -55,22 +72,29 @@ class StudentHomeworkPage extends StatelessWidget { body: const StudentHomeworkBody(), navigationItem: NavigationItem.homework, bottomBarConfiguration: BottomBarConfiguration( - bottomBar: AnimatedTabVisibility( - visibleInTabIndicies: const [0], - // Else the Sort shown in the button and the current sort - // could get out of order - maintainState: true, - curve: Curves.easeInOut, - child: HomeworkBottomActionBar( - backgroundColor: bottomBarBackgroundColor, - currentHomeworkSortStream: bloc.stream - .whereType() - .map((s) => s.open.sorting), - showOverflowMenu: true, - onCompletedAllOverdue: () => bloc.add(CompletedAllOverdue()), - onSortingChanged: (sort) => - bloc.add(OpenHwSortingChanged(sort)), - ), + bottomBar: Column( + mainAxisSize: MainAxisSize.min, + children: [ + AdBanner(adUnitId: getAdUnitId(context)), + AnimatedTabVisibility( + visibleInTabIndicies: const [0], + // Else the Sort shown in the button and the current sort + // could get out of order + maintainState: true, + curve: Curves.easeInOut, + child: HomeworkBottomActionBar( + backgroundColor: bottomBarBackgroundColor, + currentHomeworkSortStream: bloc.stream + .whereType() + .map((s) => s.open.sorting), + showOverflowMenu: true, + onCompletedAllOverdue: () => + bloc.add(CompletedAllOverdue()), + onSortingChanged: (sort) => + bloc.add(OpenHwSortingChanged(sort)), + ), + ), + ], ), ), floatingActionButton: diff --git a/app/lib/main/plugin_initializations.dart b/app/lib/main/plugin_initializations.dart index 524b6b939..f10e2a0fb 100644 --- a/app/lib/main/plugin_initializations.dart +++ b/app/lib/main/plugin_initializations.dart @@ -81,7 +81,14 @@ class PluginInitializations { 'firebase_messaging_vapid_key': 'BNT7Da6B6wi-mUBcGrt-9HxeIJZsPTsPpmR8cae_LhgJPcSFb5j0T8o-r-oFV1xAtXVXfRPIZlgUJR3tx8mLbbA', 'stripe_checkout_session_function_url': - 'https://europe-west1-sharezone-c2bd8.cloudfunctions.net/createStripeCheckoutSession' + 'https://europe-west1-sharezone-c2bd8.cloudfunctions.net/createStripeCheckoutSession', + // Setting the ads_enabled to false will also have the positive side + // effect that the ads won't be shown for the first app open which + // provides a better user experience. + 'ads_enabled': false, + 'ad_content_url': 'https://sharezone.net', + 'ad_neighboring_urls': + 'https://sharezone.net/android,https://sharezone.net/ios', }); try { diff --git a/app/lib/main/sharezone_bloc_providers.dart b/app/lib/main/sharezone_bloc_providers.dart index 54c87633b..a8c577302 100644 --- a/app/lib/main/sharezone_bloc_providers.dart +++ b/app/lib/main/sharezone_bloc_providers.dart @@ -31,12 +31,14 @@ import 'package:hausaufgabenheft_logik/hausaufgabenheft_logik_setup.dart'; import 'package:holidays/holidays.dart' hide State; import 'package:http/http.dart' as http; import 'package:key_value_store/in_memory_key_value_store.dart'; +import 'package:key_value_store/key_value_store.dart'; import 'package:provider/provider.dart'; import 'package:provider/single_child_widget.dart'; import 'package:sharezone/account/account_page_bloc_factory.dart'; import 'package:sharezone/account/change_data_bloc.dart'; import 'package:sharezone/account/type_of_user_bloc.dart'; import 'package:sharezone/activation_code/src/bloc/enter_activation_code_bloc_factory.dart'; +import 'package:sharezone/ads/ads_controller.dart'; import 'package:sharezone/blackboard/analytics/blackboard_analytics.dart'; import 'package:sharezone/blackboard/blocs/blackboard_page_bloc.dart'; import 'package:sharezone/calendrical_events/analytics/calendrical_events_page_analytics.dart'; @@ -333,6 +335,10 @@ class _SharezoneBlocProvidersState extends State { firestore: widget.blocDependencies.firestore, functions: widget.blocDependencies.functions, ); + + final keyValueStore = + FlutterKeyValueStore(widget.blocDependencies.sharedPreferences); + // In the past we used BlocProvider for everything (even non-bloc classes). // This forced us to use BlocProvider wrapper classes for non-bloc entities, // Provider allows us to skip using these wrapper classes. @@ -480,7 +486,16 @@ class _SharezoneBlocProvidersState extends State { courseMemberAccessor: FirestoreCourseMemberAccessor(api.references.firestore), ), - ) + ), + ChangeNotifierProvider( + create: (context) => AdsController( + subscriptionService: subscriptionService, + remoteConfiguration: widget.blocDependencies.remoteConfiguration, + keyValueStore: keyValueStore, + ), + lazy: false, + ), + Provider.value(value: keyValueStore) ]; mainBlocProviders = [ diff --git a/app/lib/sharezone_plus/page/sharezone_plus_page.dart b/app/lib/sharezone_plus/page/sharezone_plus_page.dart index 46caa90c6..ce6b41ea0 100644 --- a/app/lib/sharezone_plus/page/sharezone_plus_page.dart +++ b/app/lib/sharezone_plus/page/sharezone_plus_page.dart @@ -13,6 +13,7 @@ import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:platform_check/platform_check.dart'; import 'package:provider/provider.dart'; import 'package:share_plus/share_plus.dart'; +import 'package:sharezone/ads/ads_controller.dart'; import 'package:sharezone/legal/privacy_policy/privacy_policy_page.dart'; import 'package:sharezone/legal/terms_of_service/terms_of_service_page.dart'; import 'package:sharezone/navigation/logic/navigation_bloc.dart'; @@ -112,8 +113,10 @@ class _Advantages extends StatelessWidget { @override Widget build(BuildContext context) { + final areAdsVisible = context.watch().areAdsVisible; return SharezonePlusAdvantages( isHomeworkReminderFeatureVisible: typeOfUser == TypeOfUser.student, + isRemoveAdsFeatureVisible: areAdsVisible, onOpenedAdvantage: (advantage) { final analytics = context.read(); analytics.logOpenedAdvantage(advantage); diff --git a/app/lib/sharezone_plus/subscription_service/subscription_service.dart b/app/lib/sharezone_plus/subscription_service/subscription_service.dart index dbcc8b4ca..8bb71ba80 100644 --- a/app/lib/sharezone_plus/subscription_service/subscription_service.dart +++ b/app/lib/sharezone_plus/subscription_service/subscription_service.dart @@ -106,6 +106,7 @@ class SubscriptionService { } enum SharezonePlusFeature { + removeAds, submissionsList, infoSheetReadByUsersList, homeworkDoneByUsersList, diff --git a/app/lib/timetable/timetable_page/school_class_filter/school_class_filter.dart b/app/lib/timetable/timetable_page/school_class_filter/school_class_filter.dart index 3aacea25f..d606f0219 100644 --- a/app/lib/timetable/timetable_page/school_class_filter/school_class_filter.dart +++ b/app/lib/timetable/timetable_page/school_class_filter/school_class_filter.dart @@ -54,7 +54,7 @@ class _SchoolClassFilterBottomBarState // Funktion keinen Mehrwert und ist somit unnötig, wenn es angezeigt // wird. if (view == null || !view.hasMoreThanOneSchoolClass) { - return const Text(""); + return const SizedBox(); } return Material( diff --git a/app/lib/timetable/timetable_page/timetable_page.dart b/app/lib/timetable/timetable_page/timetable_page.dart index ccca8dbdf..6c0fa65be 100644 --- a/app/lib/timetable/timetable_page/timetable_page.dart +++ b/app/lib/timetable/timetable_page/timetable_page.dart @@ -10,9 +10,14 @@ import 'dart:math'; import 'package:bloc_provider/bloc_provider.dart'; import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:group_domain_models/group_domain_models.dart'; +import 'package:platform_check/platform_check.dart'; +import 'package:provider/provider.dart'; +import 'package:sharezone/ads/ad_banner.dart'; +import 'package:sharezone/ads/ads_controller.dart'; import 'package:sharezone/calendrical_events/models/calendrical_event.dart'; import 'package:sharezone/main/application_bloc.dart'; import 'package:sharezone/navigation/logic/navigation_bloc.dart'; @@ -49,6 +54,19 @@ class TimetablePage extends StatelessWidget { TimetablePage({super.key}); + String getAdUnitId(BuildContext context) { + if (kDebugMode) { + return context.read().getTestAdUnitId(AdFormat.banner); + } + + return switch (PlatformCheck.currentPlatform) { + // Copied from the AdMob Console + Platform.android => 'ca-app-pub-7730914075870960/7645268953', + Platform.iOS => 'ca-app-pub-7730914075870960/6326053086', + _ => 'N/A', + }; + } + @override Widget build(BuildContext context) { final bottomBarBackgroundColor = @@ -94,8 +112,14 @@ class TimetablePage extends StatelessWidget { navigationItem: NavigationItem.timetable, floatingActionButton: _TimetablePageFAB(), bottomBarConfiguration: BottomBarConfiguration( - bottomBar: SchoolClassFilterBottomBar( - backgroundColor: bottomBarBackgroundColor, + bottomBar: Column( + mainAxisSize: MainAxisSize.min, + children: [ + AdBanner(adUnitId: getAdUnitId(context)), + SchoolClassFilterBottomBar( + backgroundColor: bottomBarBackgroundColor, + ), + ], ), ), ), diff --git a/app/pubspec.lock b/app/pubspec.lock index e9f25bc6d..2f8520b9e 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -1107,6 +1107,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.1+4" + google_mobile_ads: + dependency: "direct main" + description: + name: google_mobile_ads + sha256: "4775006383a27a5d86d46f8fb452bfcb17794fc0a46c732979e49a8eb1c8963f" + url: "https://pub.dev" + source: hosted + version: "5.2.0" google_sign_in: dependency: transitive description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 44e654478..b78ea59a3 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -104,6 +104,7 @@ dependencies: flutter_staggered_animations: ^1.1.1 flutter_svg: ^2.0.2 font_awesome_flutter: ^10.4.0 + google_mobile_ads: ^5.2.0 group_domain_implementation: path: ../lib/group_domain_implementation group_domain_models: diff --git a/app/test/dashboard/update_reminder_test.dart b/app/test/dashboard/update_reminder_test.dart index a29a0541a..c01f28e7a 100644 --- a/app/test/dashboard/update_reminder_test.dart +++ b/app/test/dashboard/update_reminder_test.dart @@ -10,14 +10,19 @@ import 'package:bloc_provider/bloc_provider.dart'; import 'package:bloc_provider/multi_bloc_provider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:key_value_store/in_memory_key_value_store.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; +import 'package:provider/provider.dart'; +import 'package:remote_configuration/remote_configuration.dart'; +import 'package:sharezone/ads/ads_controller.dart'; import 'package:sharezone/holidays/holiday_bloc.dart'; import 'package:sharezone/dashboard/bloc/dashboard_bloc.dart'; import 'package:sharezone/dashboard/dashboard_page.dart'; import 'package:sharezone/dashboard/tips/dashboard_tip_system.dart'; import 'package:sharezone/dashboard/update_reminder/update_reminder_bloc.dart'; import 'package:platform_check/platform_check.dart'; +import 'package:sharezone/sharezone_plus/subscription_service/subscription_service.dart'; import 'update_reminder_test.mocks.dart'; @@ -26,6 +31,7 @@ import 'update_reminder_test.mocks.dart'; MockSpec(), MockSpec(), MockSpec(), + MockSpec(), ]) void main() { group('Update-Reminder Card', () { @@ -99,13 +105,20 @@ extension on WidgetTester { } Widget _buildDashboardPage(UpdateReminderBloc updateReminderBloc) { - return MultiBlocProvider( - blocProviders: [ - BlocProvider(bloc: MockDashboardBloc()), - BlocProvider(bloc: updateReminderBloc), - BlocProvider(bloc: MockDashboardTipSystem()), - BlocProvider(bloc: MockHolidayBloc()), - ], - child: (c) => const MaterialApp(home: DashboardPageBody()), + return ChangeNotifierProvider( + create: (context) => AdsController( + subscriptionService: MockSubscriptionService(), + remoteConfiguration: getStubRemoteConfiguration(), + keyValueStore: InMemoryKeyValueStore(), + ), + child: MultiBlocProvider( + blocProviders: [ + BlocProvider(bloc: MockDashboardBloc()), + BlocProvider(bloc: updateReminderBloc), + BlocProvider(bloc: MockDashboardTipSystem()), + BlocProvider(bloc: MockHolidayBloc()), + ], + child: (c) => const MaterialApp(home: DashboardPageBody()), + ), ); } diff --git a/app/test/dashboard/update_reminder_test.mocks.dart b/app/test/dashboard/update_reminder_test.mocks.dart index 71ccdc88f..91563db8f 100644 --- a/app/test/dashboard/update_reminder_test.mocks.dart +++ b/app/test/dashboard/update_reminder_test.mocks.dart @@ -3,25 +3,28 @@ // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i9; +import 'dart:async' as _i10; +import 'package:cloud_functions/cloud_functions.dart' as _i8; import 'package:holidays/holidays.dart' as _i4; import 'package:mockito/mockito.dart' as _i1; -import 'package:sharezone/blackboard/blackboard_view.dart' as _i15; +import 'package:sharezone/blackboard/blackboard_view.dart' as _i16; import 'package:sharezone/changelog/change.dart' as _i3; -import 'package:sharezone/dashboard/bloc/dashboard_bloc.dart' as _i13; -import 'package:sharezone/dashboard/models/homework_view.dart' as _i14; -import 'package:sharezone/dashboard/timetable/lesson_view.dart' as _i17; +import 'package:sharezone/dashboard/bloc/dashboard_bloc.dart' as _i14; +import 'package:sharezone/dashboard/models/homework_view.dart' as _i15; +import 'package:sharezone/dashboard/timetable/lesson_view.dart' as _i18; import 'package:sharezone/dashboard/tips/cache/dashboard_tip_cache.dart' as _i6; -import 'package:sharezone/dashboard/tips/dashboard_tip_system.dart' as _i11; -import 'package:sharezone/dashboard/tips/models/dashboard_tip.dart' as _i12; +import 'package:sharezone/dashboard/tips/dashboard_tip_system.dart' as _i12; +import 'package:sharezone/dashboard/tips/models/dashboard_tip.dart' as _i13; import 'package:sharezone/dashboard/update_reminder/release.dart' as _i2; import 'package:sharezone/dashboard/update_reminder/update_reminder_bloc.dart' - as _i8; + as _i9; import 'package:sharezone/holidays/holiday_bloc.dart' as _i5; import 'package:sharezone/navigation/logic/navigation_bloc.dart' as _i7; -import 'package:sharezone/timetable/src/widgets/events/event_view.dart' as _i16; -import 'package:user/user.dart' as _i10; +import 'package:sharezone/sharezone_plus/subscription_service/subscription_service.dart' + as _i19; +import 'package:sharezone/timetable/src/widgets/events/event_view.dart' as _i17; +import 'package:user/user.dart' as _i11; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -120,40 +123,51 @@ class _FakeNavigationBloc_7 extends _i1.SmartFake ); } +class _FakeFirebaseFunctions_8 extends _i1.SmartFake + implements _i8.FirebaseFunctions { + _FakeFirebaseFunctions_8( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + /// A class which mocks [UpdateReminderBloc]. /// /// See the documentation for Mockito's code generation for more information. class MockUpdateReminderBloc extends _i1.Mock - implements _i8.UpdateReminderBloc { + implements _i9.UpdateReminderBloc { @override - _i9.Future<_i2.Release> Function() get getLatestRelease => + _i10.Future<_i2.Release> Function() get getLatestRelease => (super.noSuchMethod( Invocation.getter(#getLatestRelease), - returnValue: () => _i9.Future<_i2.Release>.value(_FakeRelease_0( + returnValue: () => _i10.Future<_i2.Release>.value(_FakeRelease_0( this, Invocation.getter(#getLatestRelease), )), returnValueForMissingStub: () => - _i9.Future<_i2.Release>.value(_FakeRelease_0( + _i10.Future<_i2.Release>.value(_FakeRelease_0( this, Invocation.getter(#getLatestRelease), )), - ) as _i9.Future<_i2.Release> Function()); + ) as _i10.Future<_i2.Release> Function()); @override - _i9.Future<_i3.Version> Function() get getCurrentVersion => + _i10.Future<_i3.Version> Function() get getCurrentVersion => (super.noSuchMethod( Invocation.getter(#getCurrentVersion), - returnValue: () => _i9.Future<_i3.Version>.value(_FakeVersion_1( + returnValue: () => _i10.Future<_i3.Version>.value(_FakeVersion_1( this, Invocation.getter(#getCurrentVersion), )), returnValueForMissingStub: () => - _i9.Future<_i3.Version>.value(_FakeVersion_1( + _i10.Future<_i3.Version>.value(_FakeVersion_1( this, Invocation.getter(#getCurrentVersion), )), - ) as _i9.Future<_i3.Version> Function()); + ) as _i10.Future<_i3.Version> Function()); @override Duration get updateGracePeriod => (super.noSuchMethod( @@ -182,14 +196,14 @@ class MockUpdateReminderBloc extends _i1.Mock ) as DateTime Function()); @override - _i9.Future shouldRemindToUpdate() => (super.noSuchMethod( + _i10.Future shouldRemindToUpdate() => (super.noSuchMethod( Invocation.method( #shouldRemindToUpdate, [], ), - returnValue: _i9.Future.value(false), - returnValueForMissingStub: _i9.Future.value(false), - ) as _i9.Future); + returnValue: _i10.Future.value(false), + returnValueForMissingStub: _i10.Future.value(false), + ) as _i10.Future); @override void dispose() => super.noSuchMethod( @@ -242,34 +256,34 @@ class MockHolidayBloc extends _i1.Mock implements _i5.HolidayBloc { ) as _i5.HolidayStateGateway); @override - _i9.Stream> get holidays => (super.noSuchMethod( + _i10.Stream> get holidays => (super.noSuchMethod( Invocation.getter(#holidays), - returnValue: _i9.Stream>.empty(), - returnValueForMissingStub: _i9.Stream>.empty(), - ) as _i9.Stream>); + returnValue: _i10.Stream>.empty(), + returnValueForMissingStub: _i10.Stream>.empty(), + ) as _i10.Stream>); @override - _i9.Stream<_i10.StateEnum?> get userState => (super.noSuchMethod( + _i10.Stream<_i11.StateEnum?> get userState => (super.noSuchMethod( Invocation.getter(#userState), - returnValue: _i9.Stream<_i10.StateEnum?>.empty(), - returnValueForMissingStub: _i9.Stream<_i10.StateEnum?>.empty(), - ) as _i9.Stream<_i10.StateEnum?>); + returnValue: _i10.Stream<_i11.StateEnum?>.empty(), + returnValueForMissingStub: _i10.Stream<_i11.StateEnum?>.empty(), + ) as _i10.Stream<_i11.StateEnum?>); @override - _i9.Stream get hasStateSelected => (super.noSuchMethod( + _i10.Stream get hasStateSelected => (super.noSuchMethod( Invocation.getter(#hasStateSelected), - returnValue: _i9.Stream.empty(), - returnValueForMissingStub: _i9.Stream.empty(), - ) as _i9.Stream); + returnValue: _i10.Stream.empty(), + returnValueForMissingStub: _i10.Stream.empty(), + ) as _i10.Stream); @override - _i9.Future Function(_i10.StateEnum?) get changeState => + _i10.Future Function(_i11.StateEnum?) get changeState => (super.noSuchMethod( Invocation.getter(#changeState), - returnValue: (_i10.StateEnum? state) => _i9.Future.value(), - returnValueForMissingStub: (_i10.StateEnum? state) => - _i9.Future.value(), - ) as _i9.Future Function(_i10.StateEnum?)); + returnValue: (_i11.StateEnum? state) => _i10.Future.value(), + returnValueForMissingStub: (_i11.StateEnum? state) => + _i10.Future.value(), + ) as _i10.Future Function(_i11.StateEnum?)); @override void dispose() => super.noSuchMethod( @@ -285,7 +299,7 @@ class MockHolidayBloc extends _i1.Mock implements _i5.HolidayBloc { /// /// See the documentation for Mockito's code generation for more information. class MockDashboardTipSystem extends _i1.Mock - implements _i11.DashboardTipSystem { + implements _i12.DashboardTipSystem { @override _i6.DashboardTipCache get cache => (super.noSuchMethod( Invocation.getter(#cache), @@ -313,11 +327,11 @@ class MockDashboardTipSystem extends _i1.Mock ) as _i7.NavigationBloc); @override - _i9.Stream<_i12.DashboardTip?> get dashboardTip => (super.noSuchMethod( + _i10.Stream<_i13.DashboardTip?> get dashboardTip => (super.noSuchMethod( Invocation.getter(#dashboardTip), - returnValue: _i9.Stream<_i12.DashboardTip?>.empty(), - returnValueForMissingStub: _i9.Stream<_i12.DashboardTip?>.empty(), - ) as _i9.Stream<_i12.DashboardTip?>); + returnValue: _i10.Stream<_i13.DashboardTip?>.empty(), + returnValueForMissingStub: _i10.Stream<_i13.DashboardTip?>.empty(), + ) as _i10.Stream<_i13.DashboardTip?>); @override void dispose() => super.noSuchMethod( @@ -332,7 +346,7 @@ class MockDashboardTipSystem extends _i1.Mock /// A class which mocks [DashboardBloc]. /// /// See the documentation for Mockito's code generation for more information. -class MockDashboardBloc extends _i1.Mock implements _i13.DashboardBloc { +class MockDashboardBloc extends _i1.Mock implements _i14.DashboardBloc { @override DateTime get todayDateTimeWithoutTime => (super.noSuchMethod( Invocation.getter(#todayDateTimeWithoutTime), @@ -347,70 +361,198 @@ class MockDashboardBloc extends _i1.Mock implements _i13.DashboardBloc { ) as DateTime); @override - _i9.Stream> get urgentHomeworks => + _i10.Stream> get urgentHomeworks => (super.noSuchMethod( Invocation.getter(#urgentHomeworks), - returnValue: _i9.Stream>.empty(), - returnValueForMissingStub: _i9.Stream>.empty(), - ) as _i9.Stream>); + returnValue: _i10.Stream>.empty(), + returnValueForMissingStub: _i10.Stream>.empty(), + ) as _i10.Stream>); @override - _i9.Stream> get unreadBlackboardViews => + _i10.Stream> get unreadBlackboardViews => (super.noSuchMethod( Invocation.getter(#unreadBlackboardViews), - returnValue: _i9.Stream>.empty(), + returnValue: _i10.Stream>.empty(), returnValueForMissingStub: - _i9.Stream>.empty(), - ) as _i9.Stream>); + _i10.Stream>.empty(), + ) as _i10.Stream>); @override - _i9.Stream get urgentHomeworksEmpty => (super.noSuchMethod( + _i10.Stream get urgentHomeworksEmpty => (super.noSuchMethod( Invocation.getter(#urgentHomeworksEmpty), - returnValue: _i9.Stream.empty(), - returnValueForMissingStub: _i9.Stream.empty(), - ) as _i9.Stream); + returnValue: _i10.Stream.empty(), + returnValueForMissingStub: _i10.Stream.empty(), + ) as _i10.Stream); @override - _i9.Stream> get upcomingEvents => (super.noSuchMethod( + _i10.Stream> get upcomingEvents => (super.noSuchMethod( Invocation.getter(#upcomingEvents), - returnValue: _i9.Stream>.empty(), - returnValueForMissingStub: _i9.Stream>.empty(), - ) as _i9.Stream>); + returnValue: _i10.Stream>.empty(), + returnValueForMissingStub: _i10.Stream>.empty(), + ) as _i10.Stream>); @override - _i9.Stream get numberOfUrgentHomeworks => (super.noSuchMethod( + _i10.Stream get numberOfUrgentHomeworks => (super.noSuchMethod( Invocation.getter(#numberOfUrgentHomeworks), - returnValue: _i9.Stream.empty(), - returnValueForMissingStub: _i9.Stream.empty(), - ) as _i9.Stream); + returnValue: _i10.Stream.empty(), + returnValueForMissingStub: _i10.Stream.empty(), + ) as _i10.Stream); @override - _i9.Stream> get lessonViews => (super.noSuchMethod( + _i10.Stream> get lessonViews => (super.noSuchMethod( Invocation.getter(#lessonViews), - returnValue: _i9.Stream>.empty(), - returnValueForMissingStub: _i9.Stream>.empty(), - ) as _i9.Stream>); + returnValue: _i10.Stream>.empty(), + returnValueForMissingStub: _i10.Stream>.empty(), + ) as _i10.Stream>); @override - _i9.Stream get numberOfUnreadBlackboardViews => (super.noSuchMethod( + _i10.Stream get numberOfUnreadBlackboardViews => (super.noSuchMethod( Invocation.getter(#numberOfUnreadBlackboardViews), - returnValue: _i9.Stream.empty(), - returnValueForMissingStub: _i9.Stream.empty(), - ) as _i9.Stream); + returnValue: _i10.Stream.empty(), + returnValueForMissingStub: _i10.Stream.empty(), + ) as _i10.Stream); @override - _i9.Stream get unreadBlackboardViewsEmpty => (super.noSuchMethod( + _i10.Stream get unreadBlackboardViewsEmpty => (super.noSuchMethod( Invocation.getter(#unreadBlackboardViewsEmpty), - returnValue: _i9.Stream.empty(), - returnValueForMissingStub: _i9.Stream.empty(), - ) as _i9.Stream); + returnValue: _i10.Stream.empty(), + returnValueForMissingStub: _i10.Stream.empty(), + ) as _i10.Stream); @override - _i9.Stream get numberOfUpcomingEvents => (super.noSuchMethod( + _i10.Stream get numberOfUpcomingEvents => (super.noSuchMethod( Invocation.getter(#numberOfUpcomingEvents), - returnValue: _i9.Stream.empty(), - returnValueForMissingStub: _i9.Stream.empty(), - ) as _i9.Stream); + returnValue: _i10.Stream.empty(), + returnValueForMissingStub: _i10.Stream.empty(), + ) as _i10.Stream); + + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [SubscriptionService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockSubscriptionService extends _i1.Mock + implements _i19.SubscriptionService { + @override + _i10.Stream<_i11.AppUser?> get user => (super.noSuchMethod( + Invocation.getter(#user), + returnValue: _i10.Stream<_i11.AppUser?>.empty(), + returnValueForMissingStub: _i10.Stream<_i11.AppUser?>.empty(), + ) as _i10.Stream<_i11.AppUser?>); + + @override + _i8.FirebaseFunctions get functions => (super.noSuchMethod( + Invocation.getter(#functions), + returnValue: _FakeFirebaseFunctions_8( + this, + Invocation.getter(#functions), + ), + returnValueForMissingStub: _FakeFirebaseFunctions_8( + this, + Invocation.getter(#functions), + ), + ) as _i8.FirebaseFunctions); + + @override + _i10.Stream<_i11.SharezonePlusStatus?> get sharezonePlusStatusStream => + (super.noSuchMethod( + Invocation.getter(#sharezonePlusStatusStream), + returnValue: _i10.Stream<_i11.SharezonePlusStatus?>.empty(), + returnValueForMissingStub: + _i10.Stream<_i11.SharezonePlusStatus?>.empty(), + ) as _i10.Stream<_i11.SharezonePlusStatus?>); + + @override + set sharezonePlusStatusStream( + _i10.Stream<_i11.SharezonePlusStatus?>? _sharezonePlusStatusStream) => + super.noSuchMethod( + Invocation.setter( + #sharezonePlusStatusStream, + _sharezonePlusStatusStream, + ), + returnValueForMissingStub: null, + ); + + @override + bool isSubscriptionActive([_i11.AppUser? appUser]) => (super.noSuchMethod( + Invocation.method( + #isSubscriptionActive, + [appUser], + ), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i10.Stream isSubscriptionActiveStream() => (super.noSuchMethod( + Invocation.method( + #isSubscriptionActiveStream, + [], + ), + returnValue: _i10.Stream.empty(), + returnValueForMissingStub: _i10.Stream.empty(), + ) as _i10.Stream); + + @override + bool hasFeatureUnlocked(_i19.SharezonePlusFeature? feature) => + (super.noSuchMethod( + Invocation.method( + #hasFeatureUnlocked, + [feature], + ), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i10.Stream hasFeatureUnlockedStream( + _i19.SharezonePlusFeature? feature) => + (super.noSuchMethod( + Invocation.method( + #hasFeatureUnlockedStream, + [feature], + ), + returnValue: _i10.Stream.empty(), + returnValueForMissingStub: _i10.Stream.empty(), + ) as _i10.Stream); + + @override + _i10.Future cancelStripeSubscription() => (super.noSuchMethod( + Invocation.method( + #cancelStripeSubscription, + [], + ), + returnValue: _i10.Future.value(), + returnValueForMissingStub: _i10.Future.value(), + ) as _i10.Future); + + @override + _i10.Future showLetParentsBuyButton() => (super.noSuchMethod( + Invocation.method( + #showLetParentsBuyButton, + [], + ), + returnValue: _i10.Future.value(false), + returnValueForMissingStub: _i10.Future.value(false), + ) as _i10.Future); + + @override + _i10.Future getPlusWebsiteBuyToken() => (super.noSuchMethod( + Invocation.method( + #getPlusWebsiteBuyToken, + [], + ), + returnValue: _i10.Future.value(), + returnValueForMissingStub: _i10.Future.value(), + ) as _i10.Future); @override void dispose() => super.noSuchMethod( diff --git a/app/test/homework/homework_dialog_test.dart b/app/test/homework/homework_dialog_test.dart index 644414280..9936e4e33 100644 --- a/app/test/homework/homework_dialog_test.dart +++ b/app/test/homework/homework_dialog_test.dart @@ -25,31 +25,33 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:group_domain_models/group_domain_models.dart'; import 'package:hausaufgabenheft_logik/hausaufgabenheft_logik.dart' hide Date; import 'package:holidays/holidays.dart'; +import 'package:key_value_store/in_memory_key_value_store.dart'; +import 'package:key_value_store/key_value_store.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:path/path.dart'; import 'package:provider/provider.dart'; +import 'package:remote_configuration/remote_configuration.dart'; +import 'package:sharezone/ads/ads_controller.dart'; import 'package:sharezone/holidays/holiday_bloc.dart'; -import 'package:sharezone/sharezone_plus/subscription_service/subscription_service.dart'; -import 'package:sharezone/util/next_schoolday_calculator/next_schoolday_calculator.dart'; -import 'package:test_randomness/test_randomness.dart'; import 'package:sharezone/homework/homework_dialog/homework_dialog.dart'; import 'package:sharezone/homework/homework_dialog/homework_dialog_bloc.dart'; import 'package:sharezone/main/application_bloc.dart'; import 'package:sharezone/markdown/markdown_analytics.dart'; import 'package:sharezone/settings/src/subpages/timetable/time_picker_settings_cache.dart'; +import 'package:sharezone/sharezone_plus/subscription_service/subscription_service.dart'; import 'package:sharezone/util/api.dart'; import 'package:sharezone/util/api/course_gateway.dart'; import 'package:sharezone/util/api/homework_api.dart'; import 'package:sharezone/util/api/user_api.dart'; import 'package:sharezone/util/cache/streaming_key_value_store.dart'; import 'package:sharezone/util/next_lesson_calculator/next_lesson_calculator.dart'; +import 'package:sharezone/util/next_schoolday_calculator/next_schoolday_calculator.dart'; import 'package:sharezone_widgets/sharezone_widgets.dart'; +import 'package:test_randomness/test_randomness.dart'; import 'package:user/user.dart'; import '../analytics/analytics_test.dart'; -import '../dashboard/update_reminder_test.mocks.dart'; -import '../pages/settings/notification_page_test.mocks.dart'; import 'homework_dialog_bloc_test.dart'; @GenerateNiceMocks([ MockSpec(), @@ -58,6 +60,8 @@ import 'homework_dialog_bloc_test.dart'; MockSpec(), MockSpec(), MockSpec(), + MockSpec(), + MockSpec(), ]) import 'homework_dialog_test.mocks.dart'; @@ -329,8 +333,22 @@ void main() { await withClock(clockOverride ?? clock, () async { await tester.pumpWidget( - Provider( - create: (context) => subscriptionService, + MultiProvider( + providers: [ + Provider( + create: (context) => subscriptionService, + ), + ChangeNotifierProvider( + create: (context) => AdsController( + subscriptionService: subscriptionService, + remoteConfiguration: getStubRemoteConfiguration(), + keyValueStore: InMemoryKeyValueStore(), + ), + ), + Provider( + create: (context) => InMemoryKeyValueStore(), + ), + ], child: MultiBlocProvider( blocProviders: [ BlocProvider( diff --git a/app/test/homework/homework_dialog_test.mocks.dart b/app/test/homework/homework_dialog_test.mocks.dart index 78e6fb034..2e03ef189 100644 --- a/app/test/homework/homework_dialog_test.mocks.dart +++ b/app/test/homework/homework_dialog_test.mocks.dart @@ -3,26 +3,31 @@ // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i24; +import 'dart:async' as _i27; import 'package:analytics/analytics.dart' as _i4; import 'package:app_functions/app_functions.dart' as _i19; -import 'package:authentification_base/authentification.dart' as _i27; +import 'package:authentification_base/authentification.dart' as _i30; import 'package:cloud_firestore/cloud_firestore.dart' as _i2; import 'package:cloud_firestore_platform_interface/cloud_firestore_platform_interface.dart' - as _i25; + as _i28; +import 'package:cloud_functions/cloud_functions.dart' as _i23; import 'package:common_domain_models/common_domain_models.dart' as _i8; -import 'package:design/design.dart' as _i30; -import 'package:firebase_auth/firebase_auth.dart' as _i28; -import 'package:flutter/material.dart' as _i29; +import 'package:design/design.dart' as _i33; +import 'package:firebase_auth/firebase_auth.dart' as _i31; +import 'package:flutter/material.dart' as _i32; import 'package:group_domain_models/group_domain_accessors.dart' as _i21; import 'package:group_domain_models/group_domain_models.dart' as _i22; import 'package:hausaufgabenheft_logik/hausaufgabenheft_logik.dart' as _i20; +import 'package:holidays/holidays.dart' as _i24; import 'package:mockito/mockito.dart' as _i1; -import 'package:mockito/src/dummies.dart' as _i23; +import 'package:mockito/src/dummies.dart' as _i26; import 'package:shared_preferences/shared_preferences.dart' as _i6; import 'package:sharezone/filesharing/file_sharing_api.dart' as _i11; -import 'package:sharezone/main/application_bloc.dart' as _i26; +import 'package:sharezone/holidays/holiday_bloc.dart' as _i25; +import 'package:sharezone/main/application_bloc.dart' as _i29; +import 'package:sharezone/sharezone_plus/subscription_service/subscription_service.dart' + as _i34; import 'package:sharezone/util/api.dart' as _i3; import 'package:sharezone/util/api/blackboard_api.dart' as _i10; import 'package:sharezone/util/api/connections_gateway.dart' as _i14; @@ -317,6 +322,39 @@ class _FakeCourseId_24 extends _i1.SmartFake implements _i8.CourseId { ); } +class _FakeFirebaseFunctions_25 extends _i1.SmartFake + implements _i23.FirebaseFunctions { + _FakeFirebaseFunctions_25( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeHolidayService_26 extends _i1.SmartFake + implements _i24.HolidayService { + _FakeHolidayService_26( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeHolidayStateGateway_27 extends _i1.SmartFake + implements _i25.HolidayStateGateway { + _FakeHolidayStateGateway_27( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + /// A class which mocks [DocumentReference]. /// /// See the documentation for Mockito's code generation for more information. @@ -339,11 +377,11 @@ class MockDocumentReference extends _i1.Mock @override String get id => (super.noSuchMethod( Invocation.getter(#id), - returnValue: _i23.dummyValue( + returnValue: _i26.dummyValue( this, Invocation.getter(#id), ), - returnValueForMissingStub: _i23.dummyValue( + returnValueForMissingStub: _i26.dummyValue( this, Invocation.getter(#id), ), @@ -365,11 +403,11 @@ class MockDocumentReference extends _i1.Mock @override String get path => (super.noSuchMethod( Invocation.getter(#path), - returnValue: _i23.dummyValue( + returnValue: _i26.dummyValue( this, Invocation.getter(#path), ), - returnValueForMissingStub: _i23.dummyValue( + returnValueForMissingStub: _i26.dummyValue( this, Invocation.getter(#path), ), @@ -401,33 +439,33 @@ class MockDocumentReference extends _i1.Mock ) as _i2.CollectionReference>); @override - _i24.Future delete() => (super.noSuchMethod( + _i27.Future delete() => (super.noSuchMethod( Invocation.method( #delete, [], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); @override - _i24.Future update(Map? data) => (super.noSuchMethod( + _i27.Future update(Map? data) => (super.noSuchMethod( Invocation.method( #update, [data], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); @override - _i24.Future<_i2.DocumentSnapshot> get([_i25.GetOptions? options]) => + _i27.Future<_i2.DocumentSnapshot> get([_i28.GetOptions? options]) => (super.noSuchMethod( Invocation.method( #get, [options], ), - returnValue: _i24.Future<_i2.DocumentSnapshot>.value( + returnValue: _i27.Future<_i2.DocumentSnapshot>.value( _FakeDocumentSnapshot_2( this, Invocation.method( @@ -435,7 +473,7 @@ class MockDocumentReference extends _i1.Mock [options], ), )), - returnValueForMissingStub: _i24.Future<_i2.DocumentSnapshot>.value( + returnValueForMissingStub: _i27.Future<_i2.DocumentSnapshot>.value( _FakeDocumentSnapshot_2( this, Invocation.method( @@ -443,12 +481,12 @@ class MockDocumentReference extends _i1.Mock [options], ), )), - ) as _i24.Future<_i2.DocumentSnapshot>); + ) as _i27.Future<_i2.DocumentSnapshot>); @override - _i24.Stream<_i2.DocumentSnapshot> snapshots({ + _i27.Stream<_i2.DocumentSnapshot> snapshots({ bool? includeMetadataChanges = false, - _i25.ListenSource? source = _i25.ListenSource.defaultSource, + _i28.ListenSource? source = _i28.ListenSource.defaultSource, }) => (super.noSuchMethod( Invocation.method( @@ -459,14 +497,14 @@ class MockDocumentReference extends _i1.Mock #source: source, }, ), - returnValue: _i24.Stream<_i2.DocumentSnapshot>.empty(), - returnValueForMissingStub: _i24.Stream<_i2.DocumentSnapshot>.empty(), - ) as _i24.Stream<_i2.DocumentSnapshot>); + returnValue: _i27.Stream<_i2.DocumentSnapshot>.empty(), + returnValueForMissingStub: _i27.Stream<_i2.DocumentSnapshot>.empty(), + ) as _i27.Stream<_i2.DocumentSnapshot>); @override - _i24.Future set( + _i27.Future set( T? data, [ - _i25.SetOptions? options, + _i28.SetOptions? options, ]) => (super.noSuchMethod( Invocation.method( @@ -476,9 +514,9 @@ class MockDocumentReference extends _i1.Mock options, ], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); @override _i2.DocumentReference withConverter({ @@ -522,7 +560,7 @@ class MockDocumentReference extends _i1.Mock /// A class which mocks [SharezoneContext]. /// /// See the documentation for Mockito's code generation for more information. -class MockSharezoneContext extends _i1.Mock implements _i26.SharezoneContext { +class MockSharezoneContext extends _i1.Mock implements _i29.SharezoneContext { @override _i3.SharezoneGateway get api => (super.noSuchMethod( Invocation.getter(#api), @@ -606,11 +644,11 @@ class MockSharezoneGateway extends _i1.Mock implements _i3.SharezoneGateway { @override String get uID => (super.noSuchMethod( Invocation.getter(#uID), - returnValue: _i23.dummyValue( + returnValue: _i26.dummyValue( this, Invocation.getter(#uID), ), - returnValueForMissingStub: _i23.dummyValue( + returnValueForMissingStub: _i26.dummyValue( this, Invocation.getter(#uID), ), @@ -697,11 +735,11 @@ class MockSharezoneGateway extends _i1.Mock implements _i3.SharezoneGateway { @override String get memberID => (super.noSuchMethod( Invocation.getter(#memberID), - returnValue: _i23.dummyValue( + returnValue: _i26.dummyValue( this, Invocation.getter(#memberID), ), - returnValueForMissingStub: _i23.dummyValue( + returnValueForMissingStub: _i26.dummyValue( this, Invocation.getter(#memberID), ), @@ -760,14 +798,14 @@ class MockSharezoneGateway extends _i1.Mock implements _i3.SharezoneGateway { ) as _i17.TimetableGateway); @override - _i24.Future dispose() => (super.noSuchMethod( + _i27.Future dispose() => (super.noSuchMethod( Invocation.method( #dispose, [], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); } /// A class which mocks [UserGateway]. @@ -790,60 +828,60 @@ class MockUserGateway extends _i1.Mock implements _i12.UserGateway { @override String get uID => (super.noSuchMethod( Invocation.getter(#uID), - returnValue: _i23.dummyValue( + returnValue: _i26.dummyValue( this, Invocation.getter(#uID), ), - returnValueForMissingStub: _i23.dummyValue( + returnValueForMissingStub: _i26.dummyValue( this, Invocation.getter(#uID), ), ) as String); @override - _i24.Stream<_i18.AppUser?> get userStream => (super.noSuchMethod( + _i27.Stream<_i18.AppUser?> get userStream => (super.noSuchMethod( Invocation.getter(#userStream), - returnValue: _i24.Stream<_i18.AppUser?>.empty(), - returnValueForMissingStub: _i24.Stream<_i18.AppUser?>.empty(), - ) as _i24.Stream<_i18.AppUser?>); + returnValue: _i27.Stream<_i18.AppUser?>.empty(), + returnValueForMissingStub: _i27.Stream<_i18.AppUser?>.empty(), + ) as _i27.Stream<_i18.AppUser?>); @override - _i24.Stream<_i27.AuthUser?> get authUserStream => (super.noSuchMethod( + _i27.Stream<_i30.AuthUser?> get authUserStream => (super.noSuchMethod( Invocation.getter(#authUserStream), - returnValue: _i24.Stream<_i27.AuthUser?>.empty(), - returnValueForMissingStub: _i24.Stream<_i27.AuthUser?>.empty(), - ) as _i24.Stream<_i27.AuthUser?>); + returnValue: _i27.Stream<_i30.AuthUser?>.empty(), + returnValueForMissingStub: _i27.Stream<_i30.AuthUser?>.empty(), + ) as _i27.Stream<_i30.AuthUser?>); @override - _i24.Stream get isSignedInStream => (super.noSuchMethod( + _i27.Stream get isSignedInStream => (super.noSuchMethod( Invocation.getter(#isSignedInStream), - returnValue: _i24.Stream.empty(), - returnValueForMissingStub: _i24.Stream.empty(), - ) as _i24.Stream); + returnValue: _i27.Stream.empty(), + returnValueForMissingStub: _i27.Stream.empty(), + ) as _i27.Stream); @override - _i24.Stream<_i2.DocumentSnapshot> get userDocument => + _i27.Stream<_i2.DocumentSnapshot> get userDocument => (super.noSuchMethod( Invocation.getter(#userDocument), - returnValue: _i24.Stream<_i2.DocumentSnapshot>.empty(), + returnValue: _i27.Stream<_i2.DocumentSnapshot>.empty(), returnValueForMissingStub: - _i24.Stream<_i2.DocumentSnapshot>.empty(), - ) as _i24.Stream<_i2.DocumentSnapshot>); + _i27.Stream<_i2.DocumentSnapshot>.empty(), + ) as _i27.Stream<_i2.DocumentSnapshot>); @override - _i24.Stream<_i27.Provider?> get providerStream => (super.noSuchMethod( + _i27.Stream<_i30.Provider?> get providerStream => (super.noSuchMethod( Invocation.getter(#providerStream), - returnValue: _i24.Stream<_i27.Provider?>.empty(), - returnValueForMissingStub: _i24.Stream<_i27.Provider?>.empty(), - ) as _i24.Stream<_i27.Provider?>); + returnValue: _i27.Stream<_i30.Provider?>.empty(), + returnValueForMissingStub: _i27.Stream<_i30.Provider?>.empty(), + ) as _i27.Stream<_i30.Provider?>); @override - _i24.Future<_i18.AppUser> get() => (super.noSuchMethod( + _i27.Future<_i18.AppUser> get() => (super.noSuchMethod( Invocation.method( #get, [], ), - returnValue: _i24.Future<_i18.AppUser>.value(_FakeAppUser_19( + returnValue: _i27.Future<_i18.AppUser>.value(_FakeAppUser_19( this, Invocation.method( #get, @@ -851,24 +889,24 @@ class MockUserGateway extends _i1.Mock implements _i12.UserGateway { ), )), returnValueForMissingStub: - _i24.Future<_i18.AppUser>.value(_FakeAppUser_19( + _i27.Future<_i18.AppUser>.value(_FakeAppUser_19( this, Invocation.method( #get, [], ), )), - ) as _i24.Future<_i18.AppUser>); + ) as _i27.Future<_i18.AppUser>); @override - _i24.Future logOut() => (super.noSuchMethod( + _i27.Future logOut() => (super.noSuchMethod( Invocation.method( #logOut, [], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); @override bool isAnonymous() => (super.noSuchMethod( @@ -881,45 +919,45 @@ class MockUserGateway extends _i1.Mock implements _i12.UserGateway { ) as bool); @override - _i24.Stream isAnonymousStream() => (super.noSuchMethod( + _i27.Stream isAnonymousStream() => (super.noSuchMethod( Invocation.method( #isAnonymousStream, [], ), - returnValue: _i24.Stream.empty(), - returnValueForMissingStub: _i24.Stream.empty(), - ) as _i24.Stream); + returnValue: _i27.Stream.empty(), + returnValueForMissingStub: _i27.Stream.empty(), + ) as _i27.Stream); @override - _i24.Future linkWithCredential(_i28.AuthCredential? credential) => + _i27.Future linkWithCredential(_i31.AuthCredential? credential) => (super.noSuchMethod( Invocation.method( #linkWithCredential, [credential], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); @override - _i24.Future changeState(_i18.StateEnum? state) => (super.noSuchMethod( + _i27.Future changeState(_i18.StateEnum? state) => (super.noSuchMethod( Invocation.method( #changeState, [state], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); @override - _i24.Future addNotificationToken(String? token) => (super.noSuchMethod( + _i27.Future addNotificationToken(String? token) => (super.noSuchMethod( Invocation.method( #addNotificationToken, [token], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); @override void removeNotificationToken(String? token) => super.noSuchMethod( @@ -931,29 +969,29 @@ class MockUserGateway extends _i1.Mock implements _i12.UserGateway { ); @override - _i24.Future setHomeworkReminderTime(_i29.TimeOfDay? timeOfDay) => + _i27.Future setHomeworkReminderTime(_i32.TimeOfDay? timeOfDay) => (super.noSuchMethod( Invocation.method( #setHomeworkReminderTime, [timeOfDay], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); @override - _i24.Future updateSettings(_i18.UserSettings? userSettings) => + _i27.Future updateSettings(_i18.UserSettings? userSettings) => (super.noSuchMethod( Invocation.method( #updateSettings, [userSettings], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); @override - _i24.Future updateSettingsSingleFiled( + _i27.Future updateSettingsSingleFiled( String? fieldName, dynamic data, ) => @@ -965,12 +1003,12 @@ class MockUserGateway extends _i1.Mock implements _i12.UserGateway { data, ], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); @override - _i24.Future updateUserTip( + _i27.Future updateUserTip( _i18.UserTipKey? userTipKey, bool? value, ) => @@ -982,9 +1020,9 @@ class MockUserGateway extends _i1.Mock implements _i12.UserGateway { value, ], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); @override void setBlackboardNotifications(bool? enabled) => super.noSuchMethod( @@ -1005,17 +1043,17 @@ class MockUserGateway extends _i1.Mock implements _i12.UserGateway { ); @override - _i24.Future changeEmail(String? email) => (super.noSuchMethod( + _i27.Future changeEmail(String? email) => (super.noSuchMethod( Invocation.method( #changeEmail, [email], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); @override - _i24.Future addUser({ + _i27.Future addUser({ required _i18.AppUser? user, bool? merge = false, }) => @@ -1028,40 +1066,40 @@ class MockUserGateway extends _i1.Mock implements _i12.UserGateway { #merge: merge, }, ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); @override - _i24.Future sendVerificationEmail() => (super.noSuchMethod( + _i27.Future sendVerificationEmail() => (super.noSuchMethod( Invocation.method( #sendVerificationEmail, [], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); @override - _i24.Future deleteUser(_i3.SharezoneGateway? gateway) => + _i27.Future deleteUser(_i3.SharezoneGateway? gateway) => (super.noSuchMethod( Invocation.method( #deleteUser, [gateway], ), - returnValue: _i24.Future.value(false), - returnValueForMissingStub: _i24.Future.value(false), - ) as _i24.Future); + returnValue: _i27.Future.value(false), + returnValueForMissingStub: _i27.Future.value(false), + ) as _i27.Future); @override - _i24.Future<_i19.AppFunctionsResult> updateUser( + _i27.Future<_i19.AppFunctionsResult> updateUser( _i18.AppUser? userData) => (super.noSuchMethod( Invocation.method( #updateUser, [userData], ), - returnValue: _i24.Future<_i19.AppFunctionsResult>.value( + returnValue: _i27.Future<_i19.AppFunctionsResult>.value( _FakeAppFunctionsResult_20( this, Invocation.method( @@ -1070,7 +1108,7 @@ class MockUserGateway extends _i1.Mock implements _i12.UserGateway { ), )), returnValueForMissingStub: - _i24.Future<_i19.AppFunctionsResult>.value( + _i27.Future<_i19.AppFunctionsResult>.value( _FakeAppFunctionsResult_20( this, Invocation.method( @@ -1078,17 +1116,17 @@ class MockUserGateway extends _i1.Mock implements _i12.UserGateway { [userData], ), )), - ) as _i24.Future<_i19.AppFunctionsResult>); + ) as _i27.Future<_i19.AppFunctionsResult>); @override - _i24.Future dispose() => (super.noSuchMethod( + _i27.Future dispose() => (super.noSuchMethod( Invocation.method( #dispose, [], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); } /// A class which mocks [HomeworkGateway]. @@ -1098,11 +1136,11 @@ class MockHomeworkGateway extends _i1.Mock implements _i9.HomeworkGateway { @override String get userId => (super.noSuchMethod( Invocation.getter(#userId), - returnValue: _i23.dummyValue( + returnValue: _i26.dummyValue( this, Invocation.getter(#userId), ), - returnValueForMissingStub: _i23.dummyValue( + returnValueForMissingStub: _i26.dummyValue( this, Invocation.getter(#userId), ), @@ -1124,42 +1162,42 @@ class MockHomeworkGateway extends _i1.Mock implements _i9.HomeworkGateway { ) as _i2.CollectionReference>); @override - _i24.Stream<_i18.TypeOfUser?> get typeOfUserStream => (super.noSuchMethod( + _i27.Stream<_i18.TypeOfUser?> get typeOfUserStream => (super.noSuchMethod( Invocation.getter(#typeOfUserStream), - returnValue: _i24.Stream<_i18.TypeOfUser?>.empty(), - returnValueForMissingStub: _i24.Stream<_i18.TypeOfUser?>.empty(), - ) as _i24.Stream<_i18.TypeOfUser?>); + returnValue: _i27.Stream<_i18.TypeOfUser?>.empty(), + returnValueForMissingStub: _i27.Stream<_i18.TypeOfUser?>.empty(), + ) as _i27.Stream<_i18.TypeOfUser?>); @override - _i24.Stream> get homeworkForNowAndInFutureStream => + _i27.Stream> get homeworkForNowAndInFutureStream => (super.noSuchMethod( Invocation.getter(#homeworkForNowAndInFutureStream), - returnValue: _i24.Stream>.empty(), - returnValueForMissingStub: _i24.Stream>.empty(), - ) as _i24.Stream>); + returnValue: _i27.Stream>.empty(), + returnValueForMissingStub: _i27.Stream>.empty(), + ) as _i27.Stream>); @override - _i24.Stream> get homeworkStream => (super.noSuchMethod( + _i27.Stream> get homeworkStream => (super.noSuchMethod( Invocation.getter(#homeworkStream), - returnValue: _i24.Stream>.empty(), - returnValueForMissingStub: _i24.Stream>.empty(), - ) as _i24.Stream>); + returnValue: _i27.Stream>.empty(), + returnValueForMissingStub: _i27.Stream>.empty(), + ) as _i27.Stream>); @override - _i24.Stream<_i20.HomeworkDto> singleHomeworkStream(String? homeworkId) => + _i27.Stream<_i20.HomeworkDto> singleHomeworkStream(String? homeworkId) => (super.noSuchMethod( Invocation.method( #singleHomeworkStream, [homeworkId], ), - returnValue: _i24.Stream<_i20.HomeworkDto>.empty(), - returnValueForMissingStub: _i24.Stream<_i20.HomeworkDto>.empty(), - ) as _i24.Stream<_i20.HomeworkDto>); + returnValue: _i27.Stream<_i20.HomeworkDto>.empty(), + returnValueForMissingStub: _i27.Stream<_i20.HomeworkDto>.empty(), + ) as _i27.Stream<_i20.HomeworkDto>); @override - _i24.Future<_i20.HomeworkDto> singleHomework( + _i27.Future<_i20.HomeworkDto> singleHomework( String? homeworkId, { - _i25.Source? source = _i25.Source.serverAndCache, + _i28.Source? source = _i28.Source.serverAndCache, }) => (super.noSuchMethod( Invocation.method( @@ -1167,7 +1205,7 @@ class MockHomeworkGateway extends _i1.Mock implements _i9.HomeworkGateway { [homeworkId], {#source: source}, ), - returnValue: _i24.Future<_i20.HomeworkDto>.value(_FakeHomeworkDto_21( + returnValue: _i27.Future<_i20.HomeworkDto>.value(_FakeHomeworkDto_21( this, Invocation.method( #singleHomework, @@ -1176,7 +1214,7 @@ class MockHomeworkGateway extends _i1.Mock implements _i9.HomeworkGateway { ), )), returnValueForMissingStub: - _i24.Future<_i20.HomeworkDto>.value(_FakeHomeworkDto_21( + _i27.Future<_i20.HomeworkDto>.value(_FakeHomeworkDto_21( this, Invocation.method( #singleHomework, @@ -1184,10 +1222,10 @@ class MockHomeworkGateway extends _i1.Mock implements _i9.HomeworkGateway { {#source: source}, ), )), - ) as _i24.Future<_i20.HomeworkDto>); + ) as _i27.Future<_i20.HomeworkDto>); @override - _i24.Future deleteHomework( + _i27.Future deleteHomework( _i20.HomeworkDto? homework, { _i11.FileSharingGateway? fileSharingGateway, }) => @@ -1197,9 +1235,9 @@ class MockHomeworkGateway extends _i1.Mock implements _i9.HomeworkGateway { [homework], {#fileSharingGateway: fileSharingGateway}, ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); @override void deleteHomeworkOnlyForCurrentUser(_i20.HomeworkDto? homework) => @@ -1212,7 +1250,7 @@ class MockHomeworkGateway extends _i1.Mock implements _i9.HomeworkGateway { ); @override - _i24.Future<_i2.DocumentReference> addHomeworkToCourse( + _i27.Future<_i2.DocumentReference> addHomeworkToCourse( _i20.HomeworkDto? homework, { List? attachments, required _i11.FileSharingGateway? fileSharingGateway, @@ -1226,7 +1264,7 @@ class MockHomeworkGateway extends _i1.Mock implements _i9.HomeworkGateway { #fileSharingGateway: fileSharingGateway, }, ), - returnValue: _i24.Future<_i2.DocumentReference>.value( + returnValue: _i27.Future<_i2.DocumentReference>.value( _FakeDocumentReference_3( this, Invocation.method( @@ -1239,7 +1277,7 @@ class MockHomeworkGateway extends _i1.Mock implements _i9.HomeworkGateway { ), )), returnValueForMissingStub: - _i24.Future<_i2.DocumentReference>.value( + _i27.Future<_i2.DocumentReference>.value( _FakeDocumentReference_3( this, Invocation.method( @@ -1251,10 +1289,10 @@ class MockHomeworkGateway extends _i1.Mock implements _i9.HomeworkGateway { }, ), )), - ) as _i24.Future<_i2.DocumentReference>); + ) as _i27.Future<_i2.DocumentReference>); @override - _i24.Future addPrivateHomework( + _i27.Future addPrivateHomework( _i20.HomeworkDto? homework, bool? merge, { List? attachments, @@ -1272,9 +1310,9 @@ class MockHomeworkGateway extends _i1.Mock implements _i9.HomeworkGateway { #fileSharingGateway: fileSharingGateway, }, ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); @override void changeIsHomeworkDoneTo( @@ -1293,14 +1331,14 @@ class MockHomeworkGateway extends _i1.Mock implements _i9.HomeworkGateway { ); @override - _i24.Future dispose() => (super.noSuchMethod( + _i27.Future dispose() => (super.noSuchMethod( Invocation.method( #dispose, [], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); } /// A class which mocks [CourseGateway]. @@ -1323,11 +1361,11 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { @override String get memberID => (super.noSuchMethod( Invocation.getter(#memberID), - returnValue: _i23.dummyValue( + returnValue: _i26.dummyValue( this, Invocation.getter(#memberID), ), - returnValueForMissingStub: _i23.dummyValue( + returnValueForMissingStub: _i26.dummyValue( this, Invocation.getter(#memberID), ), @@ -1382,13 +1420,13 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { ) as _i22.Course); @override - _i24.Future<_i19.AppFunctionsResult> joinCourse(String? courseID) => + _i27.Future<_i19.AppFunctionsResult> joinCourse(String? courseID) => (super.noSuchMethod( Invocation.method( #joinCourse, [courseID], ), - returnValue: _i24.Future<_i19.AppFunctionsResult>.value( + returnValue: _i27.Future<_i19.AppFunctionsResult>.value( _FakeAppFunctionsResult_20( this, Invocation.method( @@ -1397,7 +1435,7 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { ), )), returnValueForMissingStub: - _i24.Future<_i19.AppFunctionsResult>.value( + _i27.Future<_i19.AppFunctionsResult>.value( _FakeAppFunctionsResult_20( this, Invocation.method( @@ -1405,16 +1443,16 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { [courseID], ), )), - ) as _i24.Future<_i19.AppFunctionsResult>); + ) as _i27.Future<_i19.AppFunctionsResult>); @override - _i24.Future<_i19.AppFunctionsResult> leaveCourse(String? courseID) => + _i27.Future<_i19.AppFunctionsResult> leaveCourse(String? courseID) => (super.noSuchMethod( Invocation.method( #leaveCourse, [courseID], ), - returnValue: _i24.Future<_i19.AppFunctionsResult>.value( + returnValue: _i27.Future<_i19.AppFunctionsResult>.value( _FakeAppFunctionsResult_20( this, Invocation.method( @@ -1423,7 +1461,7 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { ), )), returnValueForMissingStub: - _i24.Future<_i19.AppFunctionsResult>.value( + _i27.Future<_i19.AppFunctionsResult>.value( _FakeAppFunctionsResult_20( this, Invocation.method( @@ -1431,10 +1469,10 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { [courseID], ), )), - ) as _i24.Future<_i19.AppFunctionsResult>); + ) as _i27.Future<_i19.AppFunctionsResult>); @override - _i24.Future<_i19.AppFunctionsResult> kickMember( + _i27.Future<_i19.AppFunctionsResult> kickMember( String? courseID, String? kickedMemberID, ) => @@ -1446,7 +1484,7 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { kickedMemberID, ], ), - returnValue: _i24.Future<_i19.AppFunctionsResult>.value( + returnValue: _i27.Future<_i19.AppFunctionsResult>.value( _FakeAppFunctionsResult_20( this, Invocation.method( @@ -1458,7 +1496,7 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { ), )), returnValueForMissingStub: - _i24.Future<_i19.AppFunctionsResult>.value( + _i27.Future<_i19.AppFunctionsResult>.value( _FakeAppFunctionsResult_20( this, Invocation.method( @@ -1469,16 +1507,16 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { ], ), )), - ) as _i24.Future<_i19.AppFunctionsResult>); + ) as _i27.Future<_i19.AppFunctionsResult>); @override - _i24.Future<_i19.AppFunctionsResult> editCourse(_i22.Course? course) => + _i27.Future<_i19.AppFunctionsResult> editCourse(_i22.Course? course) => (super.noSuchMethod( Invocation.method( #editCourse, [course], ), - returnValue: _i24.Future<_i19.AppFunctionsResult>.value( + returnValue: _i27.Future<_i19.AppFunctionsResult>.value( _FakeAppFunctionsResult_20( this, Invocation.method( @@ -1487,7 +1525,7 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { ), )), returnValueForMissingStub: - _i24.Future<_i19.AppFunctionsResult>.value( + _i27.Future<_i19.AppFunctionsResult>.value( _FakeAppFunctionsResult_20( this, Invocation.method( @@ -1495,7 +1533,7 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { [course], ), )), - ) as _i24.Future<_i19.AppFunctionsResult>); + ) as _i27.Future<_i19.AppFunctionsResult>); @override _i22.Course? getCourse(String? id) => (super.noSuchMethod( @@ -1507,7 +1545,7 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { ) as _i22.Course?); @override - _i24.Future<_i19.AppFunctionsResult> editCourseSettings( + _i27.Future<_i19.AppFunctionsResult> editCourseSettings( String? courseID, _i22.CourseSettings? courseSettings, ) => @@ -1519,7 +1557,7 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { courseSettings, ], ), - returnValue: _i24.Future<_i19.AppFunctionsResult>.value( + returnValue: _i27.Future<_i19.AppFunctionsResult>.value( _FakeAppFunctionsResult_20( this, Invocation.method( @@ -1531,7 +1569,7 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { ), )), returnValueForMissingStub: - _i24.Future<_i19.AppFunctionsResult>.value( + _i27.Future<_i19.AppFunctionsResult>.value( _FakeAppFunctionsResult_20( this, Invocation.method( @@ -1542,16 +1580,16 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { ], ), )), - ) as _i24.Future<_i19.AppFunctionsResult>); + ) as _i27.Future<_i19.AppFunctionsResult>); @override - _i24.Future<_i19.AppFunctionsResult> deleteCourse(String? courseID) => + _i27.Future<_i19.AppFunctionsResult> deleteCourse(String? courseID) => (super.noSuchMethod( Invocation.method( #deleteCourse, [courseID], ), - returnValue: _i24.Future<_i19.AppFunctionsResult>.value( + returnValue: _i27.Future<_i19.AppFunctionsResult>.value( _FakeAppFunctionsResult_20( this, Invocation.method( @@ -1560,7 +1598,7 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { ), )), returnValueForMissingStub: - _i24.Future<_i19.AppFunctionsResult>.value( + _i27.Future<_i19.AppFunctionsResult>.value( _FakeAppFunctionsResult_20( this, Invocation.method( @@ -1568,12 +1606,12 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { [courseID], ), )), - ) as _i24.Future<_i19.AppFunctionsResult>); + ) as _i27.Future<_i19.AppFunctionsResult>); @override - _i24.Future editCourseGeneralDesign({ + _i27.Future editCourseGeneralDesign({ required String? courseID, - _i30.Design? design, + _i33.Design? design, }) => (super.noSuchMethod( Invocation.method( @@ -1584,14 +1622,14 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { #design: design, }, ), - returnValue: _i24.Future.value(false), - returnValueForMissingStub: _i24.Future.value(false), - ) as _i24.Future); + returnValue: _i27.Future.value(false), + returnValueForMissingStub: _i27.Future.value(false), + ) as _i27.Future); @override - _i24.Future editCoursePersonalDesign({ + _i27.Future editCoursePersonalDesign({ required String? courseID, - _i30.Design? personalDesign, + _i33.Design? personalDesign, }) => (super.noSuchMethod( Invocation.method( @@ -1602,23 +1640,23 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { #personalDesign: personalDesign, }, ), - returnValue: _i24.Future.value(false), - returnValueForMissingStub: _i24.Future.value(false), - ) as _i24.Future); + returnValue: _i27.Future.value(false), + returnValueForMissingStub: _i27.Future.value(false), + ) as _i27.Future); @override - _i24.Future removeCoursePersonalDesign(String? courseID) => + _i27.Future removeCoursePersonalDesign(String? courseID) => (super.noSuchMethod( Invocation.method( #removeCoursePersonalDesign, [courseID], ), - returnValue: _i24.Future.value(false), - returnValueForMissingStub: _i24.Future.value(false), - ) as _i24.Future); + returnValue: _i27.Future.value(false), + returnValueForMissingStub: _i27.Future.value(false), + ) as _i27.Future); @override - _i24.Future<_i19.AppFunctionsResult> memberUpdateRole({ + _i27.Future<_i19.AppFunctionsResult> memberUpdateRole({ required String? courseID, required String? newMemberID, required _i22.MemberRole? newRole, @@ -1633,7 +1671,7 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { #newRole: newRole, }, ), - returnValue: _i24.Future<_i19.AppFunctionsResult>.value( + returnValue: _i27.Future<_i19.AppFunctionsResult>.value( _FakeAppFunctionsResult_20( this, Invocation.method( @@ -1647,7 +1685,7 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { ), )), returnValueForMissingStub: - _i24.Future<_i19.AppFunctionsResult>.value( + _i27.Future<_i19.AppFunctionsResult>.value( _FakeAppFunctionsResult_20( this, Invocation.method( @@ -1660,7 +1698,7 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { }, ), )), - ) as _i24.Future<_i19.AppFunctionsResult>); + ) as _i27.Future<_i19.AppFunctionsResult>); @override _i22.MemberRole? getRoleFromCourseNoSync(String? courseID) => @@ -1673,49 +1711,49 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { ) as _i22.MemberRole?); @override - _i24.Stream<_i22.Course?> streamCourse(String? courseID) => + _i27.Stream<_i22.Course?> streamCourse(String? courseID) => (super.noSuchMethod( Invocation.method( #streamCourse, [courseID], ), - returnValue: _i24.Stream<_i22.Course?>.empty(), - returnValueForMissingStub: _i24.Stream<_i22.Course?>.empty(), - ) as _i24.Stream<_i22.Course?>); + returnValue: _i27.Stream<_i22.Course?>.empty(), + returnValueForMissingStub: _i27.Stream<_i22.Course?>.empty(), + ) as _i27.Stream<_i22.Course?>); @override - _i24.Stream> streamCourses() => (super.noSuchMethod( + _i27.Stream> streamCourses() => (super.noSuchMethod( Invocation.method( #streamCourses, [], ), - returnValue: _i24.Stream>.empty(), - returnValueForMissingStub: _i24.Stream>.empty(), - ) as _i24.Stream>); + returnValue: _i27.Stream>.empty(), + returnValueForMissingStub: _i27.Stream>.empty(), + ) as _i27.Stream>); @override - _i24.Stream> getGroupInfoStream( + _i27.Stream> getGroupInfoStream( _i16.SchoolClassGateway? schoolClassGateway) => (super.noSuchMethod( Invocation.method( #getGroupInfoStream, [schoolClassGateway], ), - returnValue: _i24.Stream>.empty(), + returnValue: _i27.Stream>.empty(), returnValueForMissingStub: - _i24.Stream>.empty(), - ) as _i24.Stream>); + _i27.Stream>.empty(), + ) as _i27.Stream>); @override - _i24.Future> getCourses() => (super.noSuchMethod( + _i27.Future> getCourses() => (super.noSuchMethod( Invocation.method( #getCourses, [], ), - returnValue: _i24.Future>.value(<_i22.Course>[]), + returnValue: _i27.Future>.value(<_i22.Course>[]), returnValueForMissingStub: - _i24.Future>.value(<_i22.Course>[]), - ) as _i24.Future>); + _i27.Future>.value(<_i22.Course>[]), + ) as _i27.Future>); @override List<_i22.Course> getCurrentCourses() => (super.noSuchMethod( @@ -1759,3 +1797,211 @@ class MockCourseGateway extends _i1.Mock implements _i15.CourseGateway { returnValueForMissingStub: false, ) as bool); } + +/// A class which mocks [SubscriptionService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockSubscriptionService extends _i1.Mock + implements _i34.SubscriptionService { + @override + _i27.Stream<_i18.AppUser?> get user => (super.noSuchMethod( + Invocation.getter(#user), + returnValue: _i27.Stream<_i18.AppUser?>.empty(), + returnValueForMissingStub: _i27.Stream<_i18.AppUser?>.empty(), + ) as _i27.Stream<_i18.AppUser?>); + + @override + _i23.FirebaseFunctions get functions => (super.noSuchMethod( + Invocation.getter(#functions), + returnValue: _FakeFirebaseFunctions_25( + this, + Invocation.getter(#functions), + ), + returnValueForMissingStub: _FakeFirebaseFunctions_25( + this, + Invocation.getter(#functions), + ), + ) as _i23.FirebaseFunctions); + + @override + _i27.Stream<_i18.SharezonePlusStatus?> get sharezonePlusStatusStream => + (super.noSuchMethod( + Invocation.getter(#sharezonePlusStatusStream), + returnValue: _i27.Stream<_i18.SharezonePlusStatus?>.empty(), + returnValueForMissingStub: + _i27.Stream<_i18.SharezonePlusStatus?>.empty(), + ) as _i27.Stream<_i18.SharezonePlusStatus?>); + + @override + set sharezonePlusStatusStream( + _i27.Stream<_i18.SharezonePlusStatus?>? _sharezonePlusStatusStream) => + super.noSuchMethod( + Invocation.setter( + #sharezonePlusStatusStream, + _sharezonePlusStatusStream, + ), + returnValueForMissingStub: null, + ); + + @override + bool isSubscriptionActive([_i18.AppUser? appUser]) => (super.noSuchMethod( + Invocation.method( + #isSubscriptionActive, + [appUser], + ), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i27.Stream isSubscriptionActiveStream() => (super.noSuchMethod( + Invocation.method( + #isSubscriptionActiveStream, + [], + ), + returnValue: _i27.Stream.empty(), + returnValueForMissingStub: _i27.Stream.empty(), + ) as _i27.Stream); + + @override + bool hasFeatureUnlocked(_i34.SharezonePlusFeature? feature) => + (super.noSuchMethod( + Invocation.method( + #hasFeatureUnlocked, + [feature], + ), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i27.Stream hasFeatureUnlockedStream( + _i34.SharezonePlusFeature? feature) => + (super.noSuchMethod( + Invocation.method( + #hasFeatureUnlockedStream, + [feature], + ), + returnValue: _i27.Stream.empty(), + returnValueForMissingStub: _i27.Stream.empty(), + ) as _i27.Stream); + + @override + _i27.Future cancelStripeSubscription() => (super.noSuchMethod( + Invocation.method( + #cancelStripeSubscription, + [], + ), + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); + + @override + _i27.Future showLetParentsBuyButton() => (super.noSuchMethod( + Invocation.method( + #showLetParentsBuyButton, + [], + ), + returnValue: _i27.Future.value(false), + returnValueForMissingStub: _i27.Future.value(false), + ) as _i27.Future); + + @override + _i27.Future getPlusWebsiteBuyToken() => (super.noSuchMethod( + Invocation.method( + #getPlusWebsiteBuyToken, + [], + ), + returnValue: _i27.Future.value(), + returnValueForMissingStub: _i27.Future.value(), + ) as _i27.Future); + + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [HolidayBloc]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockHolidayBloc extends _i1.Mock implements _i25.HolidayBloc { + @override + _i24.HolidayService get holidayManager => (super.noSuchMethod( + Invocation.getter(#holidayManager), + returnValue: _FakeHolidayService_26( + this, + Invocation.getter(#holidayManager), + ), + returnValueForMissingStub: _FakeHolidayService_26( + this, + Invocation.getter(#holidayManager), + ), + ) as _i24.HolidayService); + + @override + set getCurrentTime(DateTime Function()? _getCurrentTime) => + super.noSuchMethod( + Invocation.setter( + #getCurrentTime, + _getCurrentTime, + ), + returnValueForMissingStub: null, + ); + + @override + _i25.HolidayStateGateway get stateGateway => (super.noSuchMethod( + Invocation.getter(#stateGateway), + returnValue: _FakeHolidayStateGateway_27( + this, + Invocation.getter(#stateGateway), + ), + returnValueForMissingStub: _FakeHolidayStateGateway_27( + this, + Invocation.getter(#stateGateway), + ), + ) as _i25.HolidayStateGateway); + + @override + _i27.Stream> get holidays => (super.noSuchMethod( + Invocation.getter(#holidays), + returnValue: _i27.Stream>.empty(), + returnValueForMissingStub: _i27.Stream>.empty(), + ) as _i27.Stream>); + + @override + _i27.Stream<_i18.StateEnum?> get userState => (super.noSuchMethod( + Invocation.getter(#userState), + returnValue: _i27.Stream<_i18.StateEnum?>.empty(), + returnValueForMissingStub: _i27.Stream<_i18.StateEnum?>.empty(), + ) as _i27.Stream<_i18.StateEnum?>); + + @override + _i27.Stream get hasStateSelected => (super.noSuchMethod( + Invocation.getter(#hasStateSelected), + returnValue: _i27.Stream.empty(), + returnValueForMissingStub: _i27.Stream.empty(), + ) as _i27.Stream); + + @override + _i27.Future Function(_i18.StateEnum?) get changeState => + (super.noSuchMethod( + Invocation.getter(#changeState), + returnValue: (_i18.StateEnum? state) => _i27.Future.value(), + returnValueForMissingStub: (_i18.StateEnum? state) => + _i27.Future.value(), + ) as _i27.Future Function(_i18.StateEnum?)); + + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); +} diff --git a/app/test/settings/support/support_page_test.mocks.dart b/app/test/settings/support/support_page_test.mocks.dart index 0959b9b93..d2fa0d745 100644 --- a/app/test/settings/support/support_page_test.mocks.dart +++ b/app/test/settings/support/support_page_test.mocks.dart @@ -29,23 +29,6 @@ import 'package:sharezone/support/support_page_controller.dart' as _i2; class MockSupportPageController extends _i1.Mock implements _i2.SupportPageController { @override - bool get hasPlusSupportUnlocked => (super.noSuchMethod( - Invocation.getter(#hasPlusSupportUnlocked), - returnValue: false, - returnValueForMissingStub: false, - ) as bool); - - @override - set hasPlusSupportUnlocked(bool? _hasPlusSupportUnlocked) => - super.noSuchMethod( - Invocation.setter( - #hasPlusSupportUnlocked, - _hasPlusSupportUnlocked, - ), - returnValueForMissingStub: null, - ); - - @override bool get isUserInGroupOnboarding => (super.noSuchMethod( Invocation.getter(#isUserInGroupOnboarding), returnValue: false, @@ -89,6 +72,13 @@ class MockSupportPageController extends _i1.Mock returnValueForMissingStub: null, ); + @override + bool get hasPlusSupportUnlocked => (super.noSuchMethod( + Invocation.getter(#hasPlusSupportUnlocked), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + @override bool get isUserSignedIn => (super.noSuchMethod( Invocation.getter(#isUserSignedIn), diff --git a/app/test/sharezone_plus/sharezone_plus_page_test.dart b/app/test/sharezone_plus/sharezone_plus_page_test.dart index 3f3a6e871..62d66c2f4 100644 --- a/app/test/sharezone_plus/sharezone_plus_page_test.dart +++ b/app/test/sharezone_plus/sharezone_plus_page_test.dart @@ -8,13 +8,20 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:key_value_store/in_memory_key_value_store.dart'; +import 'package:mockito/annotations.dart'; import 'package:provider/provider.dart'; +import 'package:remote_configuration/remote_configuration.dart'; +import 'package:sharezone/ads/ads_controller.dart'; import 'package:sharezone/sharezone_plus/page/sharezone_plus_page.dart'; import 'package:sharezone/sharezone_plus/page/sharezone_plus_page_controller.dart'; +import 'package:sharezone/sharezone_plus/subscription_service/subscription_service.dart'; import 'package:sharezone_plus_page_ui/sharezone_plus_page_ui.dart'; import 'package:sharezone_widgets/sharezone_widgets.dart'; import 'package:user/user.dart'; +import 'sharezone_plus_page_test.mocks.dart'; + class MockSharezonePlusPageController extends ChangeNotifier implements SharezonePlusPageController { @override @@ -93,6 +100,7 @@ class MockSharezonePlusPageController extends ChangeNotifier } } +@GenerateNiceMocks([MockSpec()]) void main() { group(SharezonePlusPage, () { late MockSharezonePlusPageController controller; @@ -103,10 +111,22 @@ void main() { Future pumpPlusPage(WidgetTester tester) async { await tester.pumpWidget( - ChangeNotifierProvider( - create: (context) => controller, - child: - const MaterialApp(home: Scaffold(body: SharezonePlusPageMain())), + MultiProvider( + providers: [ + ChangeNotifierProvider( + create: (context) => controller, + ), + ChangeNotifierProvider( + create: (context) => AdsController( + subscriptionService: MockSubscriptionService(), + remoteConfiguration: getStubRemoteConfiguration(), + keyValueStore: InMemoryKeyValueStore(), + ), + ), + ], + child: const MaterialApp( + home: Scaffold(body: SharezonePlusPageMain()), + ), ), ); } diff --git a/app/test/sharezone_plus/sharezone_plus_page_test.mocks.dart b/app/test/sharezone_plus/sharezone_plus_page_test.mocks.dart new file mode 100644 index 000000000..6eb6fa4f3 --- /dev/null +++ b/app/test/sharezone_plus/sharezone_plus_page_test.mocks.dart @@ -0,0 +1,163 @@ +// Mocks generated by Mockito 5.4.4 from annotations +// in sharezone/test/sharezone_plus/sharezone_plus_page_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i4; + +import 'package:cloud_functions/cloud_functions.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; +import 'package:sharezone/sharezone_plus/subscription_service/subscription_service.dart' + as _i3; +import 'package:user/user.dart' as _i5; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeFirebaseFunctions_0 extends _i1.SmartFake + implements _i2.FirebaseFunctions { + _FakeFirebaseFunctions_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [SubscriptionService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockSubscriptionService extends _i1.Mock + implements _i3.SubscriptionService { + @override + _i4.Stream<_i5.AppUser?> get user => (super.noSuchMethod( + Invocation.getter(#user), + returnValue: _i4.Stream<_i5.AppUser?>.empty(), + returnValueForMissingStub: _i4.Stream<_i5.AppUser?>.empty(), + ) as _i4.Stream<_i5.AppUser?>); + + @override + _i2.FirebaseFunctions get functions => (super.noSuchMethod( + Invocation.getter(#functions), + returnValue: _FakeFirebaseFunctions_0( + this, + Invocation.getter(#functions), + ), + returnValueForMissingStub: _FakeFirebaseFunctions_0( + this, + Invocation.getter(#functions), + ), + ) as _i2.FirebaseFunctions); + + @override + _i4.Stream<_i5.SharezonePlusStatus?> get sharezonePlusStatusStream => + (super.noSuchMethod( + Invocation.getter(#sharezonePlusStatusStream), + returnValue: _i4.Stream<_i5.SharezonePlusStatus?>.empty(), + returnValueForMissingStub: _i4.Stream<_i5.SharezonePlusStatus?>.empty(), + ) as _i4.Stream<_i5.SharezonePlusStatus?>); + + @override + set sharezonePlusStatusStream( + _i4.Stream<_i5.SharezonePlusStatus?>? _sharezonePlusStatusStream) => + super.noSuchMethod( + Invocation.setter( + #sharezonePlusStatusStream, + _sharezonePlusStatusStream, + ), + returnValueForMissingStub: null, + ); + + @override + bool isSubscriptionActive([_i5.AppUser? appUser]) => (super.noSuchMethod( + Invocation.method( + #isSubscriptionActive, + [appUser], + ), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i4.Stream isSubscriptionActiveStream() => (super.noSuchMethod( + Invocation.method( + #isSubscriptionActiveStream, + [], + ), + returnValue: _i4.Stream.empty(), + returnValueForMissingStub: _i4.Stream.empty(), + ) as _i4.Stream); + + @override + bool hasFeatureUnlocked(_i3.SharezonePlusFeature? feature) => + (super.noSuchMethod( + Invocation.method( + #hasFeatureUnlocked, + [feature], + ), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i4.Stream hasFeatureUnlockedStream( + _i3.SharezonePlusFeature? feature) => + (super.noSuchMethod( + Invocation.method( + #hasFeatureUnlockedStream, + [feature], + ), + returnValue: _i4.Stream.empty(), + returnValueForMissingStub: _i4.Stream.empty(), + ) as _i4.Stream); + + @override + _i4.Future cancelStripeSubscription() => (super.noSuchMethod( + Invocation.method( + #cancelStripeSubscription, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future showLetParentsBuyButton() => (super.noSuchMethod( + Invocation.method( + #showLetParentsBuyButton, + [], + ), + returnValue: _i4.Future.value(false), + returnValueForMissingStub: _i4.Future.value(false), + ) as _i4.Future); + + @override + _i4.Future getPlusWebsiteBuyToken() => (super.noSuchMethod( + Invocation.method( + #getPlusWebsiteBuyToken, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); +} diff --git a/app/test_goldens/settings/support/support_page_test.mocks.dart b/app/test_goldens/settings/support/support_page_test.mocks.dart index ef568d818..4d88b328d 100644 --- a/app/test_goldens/settings/support/support_page_test.mocks.dart +++ b/app/test_goldens/settings/support/support_page_test.mocks.dart @@ -29,23 +29,6 @@ import 'package:sharezone/support/support_page_controller.dart' as _i2; class MockSupportPageController extends _i1.Mock implements _i2.SupportPageController { @override - bool get hasPlusSupportUnlocked => (super.noSuchMethod( - Invocation.getter(#hasPlusSupportUnlocked), - returnValue: false, - returnValueForMissingStub: false, - ) as bool); - - @override - set hasPlusSupportUnlocked(bool? _hasPlusSupportUnlocked) => - super.noSuchMethod( - Invocation.setter( - #hasPlusSupportUnlocked, - _hasPlusSupportUnlocked, - ), - returnValueForMissingStub: null, - ); - - @override bool get isUserInGroupOnboarding => (super.noSuchMethod( Invocation.getter(#isUserInGroupOnboarding), returnValue: false, @@ -89,6 +72,13 @@ class MockSupportPageController extends _i1.Mock returnValueForMissingStub: null, ); + @override + bool get hasPlusSupportUnlocked => (super.noSuchMethod( + Invocation.getter(#hasPlusSupportUnlocked), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + @override bool get isUserSignedIn => (super.noSuchMethod( Invocation.getter(#isUserSignedIn), diff --git a/app/test_goldens/sharezone_plus/sharezone_plus_page_test.dart b/app/test_goldens/sharezone_plus/sharezone_plus_page_test.dart index c7a7dec3a..8f375c623 100644 --- a/app/test_goldens/sharezone_plus/sharezone_plus_page_test.dart +++ b/app/test_goldens/sharezone_plus/sharezone_plus_page_test.dart @@ -11,10 +11,13 @@ import 'package:bloc_provider/multi_bloc_provider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:golden_toolkit/golden_toolkit.dart'; +import 'package:key_value_store/in_memory_key_value_store.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; +import 'package:remote_configuration/remote_configuration.dart'; import 'package:rxdart/subjects.dart'; +import 'package:sharezone/ads/ads_controller.dart'; import 'package:sharezone/feedback/unread_messages/has_unread_feedback_messages_provider.dart'; import 'package:sharezone/main/application_bloc.dart'; import 'package:sharezone/navigation/analytics/navigation_analytics.dart'; @@ -24,6 +27,7 @@ import 'package:sharezone/navigation/scaffold/portable/bottom_navigation_bar/nav import 'package:sharezone/navigation/scaffold/portable/bottom_navigation_bar/navigation_experiment/navigation_experiment_option.dart'; import 'package:sharezone/sharezone_plus/page/sharezone_plus_page.dart'; import 'package:sharezone/sharezone_plus/page/sharezone_plus_page_controller.dart'; +import 'package:sharezone/sharezone_plus/subscription_service/subscription_service.dart'; import 'package:sharezone/util/api.dart'; import 'package:sharezone/util/api/user_api.dart'; import 'package:sharezone_widgets/sharezone_widgets.dart'; @@ -40,6 +44,7 @@ import 'sharezone_plus_page_test.mocks.dart'; MockSpec(), MockSpec(), MockSpec(), + MockSpec(), ]) void main() { group(SharezonePlusPage, () { @@ -97,6 +102,14 @@ void main() { Provider.value(value: TypeOfUser.student), ChangeNotifierProvider.value( value: hasUnreadFeedbackMessagesProvider), + ChangeNotifierProvider( + create: (context) => AdsController( + subscriptionService: MockSubscriptionService(), + // ignore: invalid_use_of_visible_for_testing_member + remoteConfiguration: getStubRemoteConfiguration(), + keyValueStore: InMemoryKeyValueStore(), + ), + ) ], child: MultiBlocProvider( blocProviders: [ diff --git a/app/test_goldens/sharezone_plus/sharezone_plus_page_test.mocks.dart b/app/test_goldens/sharezone_plus/sharezone_plus_page_test.mocks.dart index 5c202fe38..486f987ae 100644 --- a/app/test_goldens/sharezone_plus/sharezone_plus_page_test.mocks.dart +++ b/app/test_goldens/sharezone_plus/sharezone_plus_page_test.mocks.dart @@ -3,36 +3,39 @@ // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i24; -import 'dart:ui' as _i25; +import 'dart:async' as _i25; +import 'dart:ui' as _i26; import 'package:analytics/analytics.dart' as _i5; import 'package:app_functions/app_functions.dart' as _i20; -import 'package:authentification_base/authentification.dart' as _i33; -import 'package:cloud_firestore/cloud_firestore.dart' as _i34; +import 'package:authentification_base/authentification.dart' as _i34; +import 'package:cloud_firestore/cloud_firestore.dart' as _i35; +import 'package:cloud_functions/cloud_functions.dart' as _i22; import 'package:common_domain_models/common_domain_models.dart' as _i9; import 'package:feedback_shared_implementation/feedback_shared_implementation.dart' as _i21; -import 'package:firebase_auth/firebase_auth.dart' as _i35; +import 'package:firebase_auth/firebase_auth.dart' as _i36; import 'package:flutter/material.dart' as _i1; import 'package:mockito/mockito.dart' as _i2; -import 'package:mockito/src/dummies.dart' as _i32; +import 'package:mockito/src/dummies.dart' as _i33; import 'package:rxdart/rxdart.dart' as _i3; import 'package:shared_preferences/shared_preferences.dart' as _i7; import 'package:sharezone/feedback/unread_messages/has_unread_feedback_messages_provider.dart' - as _i36; + as _i37; import 'package:sharezone/filesharing/file_sharing_api.dart' as _i12; -import 'package:sharezone/main/application_bloc.dart' as _i31; +import 'package:sharezone/main/application_bloc.dart' as _i32; import 'package:sharezone/navigation/analytics/navigation_analytics.dart' - as _i30; -import 'package:sharezone/navigation/logic/navigation_bloc.dart' as _i26; -import 'package:sharezone/navigation/models/navigation_item.dart' as _i27; + as _i31; +import 'package:sharezone/navigation/logic/navigation_bloc.dart' as _i27; +import 'package:sharezone/navigation/models/navigation_item.dart' as _i28; import 'package:sharezone/navigation/scaffold/portable/bottom_navigation_bar/navigation_experiment/navigation_experiment_cache.dart' - as _i28; -import 'package:sharezone/navigation/scaffold/portable/bottom_navigation_bar/navigation_experiment/navigation_experiment_option.dart' as _i29; +import 'package:sharezone/navigation/scaffold/portable/bottom_navigation_bar/navigation_experiment/navigation_experiment_option.dart' + as _i30; import 'package:sharezone/sharezone_plus/page/sharezone_plus_page_controller.dart' - as _i22; + as _i23; +import 'package:sharezone/sharezone_plus/subscription_service/subscription_service.dart' + as _i38; import 'package:sharezone/util/api.dart' as _i4; import 'package:sharezone/util/api/blackboard_api.dart' as _i11; import 'package:sharezone/util/api/connections_gateway.dart' as _i15; @@ -43,7 +46,7 @@ import 'package:sharezone/util/api/timetable_gateway.dart' as _i18; import 'package:sharezone/util/api/user_api.dart' as _i13; import 'package:sharezone/util/navigation_service.dart' as _i8; import 'package:sharezone_common/references.dart' as _i14; -import 'package:sharezone_plus_page_ui/sharezone_plus_page_ui.dart' as _i23; +import 'package:sharezone_plus_page_ui/sharezone_plus_page_ui.dart' as _i24; import 'package:streaming_shared_preferences/streaming_shared_preferences.dart' as _i6; import 'package:user/user.dart' as _i19; @@ -275,11 +278,22 @@ class _FakeFeedbackApi_19 extends _i2.SmartFake implements _i21.FeedbackApi { ); } +class _FakeFirebaseFunctions_20 extends _i2.SmartFake + implements _i22.FirebaseFunctions { + _FakeFirebaseFunctions_20( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + /// A class which mocks [SharezonePlusPageController]. /// /// See the documentation for Mockito's code generation for more information. class MockSharezonePlusPageController extends _i2.Mock - implements _i22.SharezonePlusPageController { + implements _i23.SharezonePlusPageController { @override set monthlySubscriptionPrice(String? _monthlySubscriptionPrice) => super.noSuchMethod( @@ -351,14 +365,14 @@ class MockSharezonePlusPageController extends _i2.Mock ); @override - _i23.PurchasePeriod get selectedPurchasePeriod => (super.noSuchMethod( + _i24.PurchasePeriod get selectedPurchasePeriod => (super.noSuchMethod( Invocation.getter(#selectedPurchasePeriod), - returnValue: _i23.PurchasePeriod.monthly, - returnValueForMissingStub: _i23.PurchasePeriod.monthly, - ) as _i23.PurchasePeriod); + returnValue: _i24.PurchasePeriod.monthly, + returnValueForMissingStub: _i24.PurchasePeriod.monthly, + ) as _i24.PurchasePeriod); @override - set selectedPurchasePeriod(_i23.PurchasePeriod? _selectedPurchasePeriod) => + set selectedPurchasePeriod(_i24.PurchasePeriod? _selectedPurchasePeriod) => super.noSuchMethod( Invocation.setter( #selectedPurchasePeriod, @@ -398,44 +412,44 @@ class MockSharezonePlusPageController extends _i2.Mock ); @override - _i24.Future buy() => (super.noSuchMethod( + _i25.Future buy() => (super.noSuchMethod( Invocation.method( #buy, [], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i25.Future.value(), + returnValueForMissingStub: _i25.Future.value(), + ) as _i25.Future); @override - _i24.Future isBuyingEnabled() => (super.noSuchMethod( + _i25.Future isBuyingEnabled() => (super.noSuchMethod( Invocation.method( #isBuyingEnabled, [], ), - returnValue: _i24.Future.value(false), - returnValueForMissingStub: _i24.Future.value(false), - ) as _i24.Future); + returnValue: _i25.Future.value(false), + returnValueForMissingStub: _i25.Future.value(false), + ) as _i25.Future); @override - _i24.Future cancelSubscription() => (super.noSuchMethod( + _i25.Future cancelSubscription() => (super.noSuchMethod( Invocation.method( #cancelSubscription, [], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i25.Future.value(), + returnValueForMissingStub: _i25.Future.value(), + ) as _i25.Future); @override - _i24.Future getBuyWebsiteToken() => (super.noSuchMethod( + _i25.Future getBuyWebsiteToken() => (super.noSuchMethod( Invocation.method( #getBuyWebsiteToken, [], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i25.Future.value(), + returnValueForMissingStub: _i25.Future.value(), + ) as _i25.Future); @override bool canCancelSubscription(_i19.SubscriptionSource? source) => @@ -449,7 +463,7 @@ class MockSharezonePlusPageController extends _i2.Mock ) as bool); @override - void setPeriodOption(_i23.PurchasePeriod? period) => super.noSuchMethod( + void setPeriodOption(_i24.PurchasePeriod? period) => super.noSuchMethod( Invocation.method( #setPeriodOption, [period], @@ -494,7 +508,7 @@ class MockSharezonePlusPageController extends _i2.Mock ); @override - void addListener(_i25.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i26.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -503,7 +517,7 @@ class MockSharezonePlusPageController extends _i2.Mock ); @override - void removeListener(_i25.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i26.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], @@ -524,7 +538,7 @@ class MockSharezonePlusPageController extends _i2.Mock /// A class which mocks [NavigationBloc]. /// /// See the documentation for Mockito's code generation for more information. -class MockNavigationBloc extends _i2.Mock implements _i26.NavigationBloc { +class MockNavigationBloc extends _i2.Mock implements _i27.NavigationBloc { @override _i1.GlobalKey<_i1.State<_i1.StatefulWidget>> get scaffoldKey => (super.noSuchMethod( @@ -571,25 +585,25 @@ class MockNavigationBloc extends _i2.Mock implements _i26.NavigationBloc { ) as _i1.GlobalKey<_i1.State<_i1.StatefulWidget>>); @override - _i24.Stream<_i27.NavigationItem> get currentItemStream => (super.noSuchMethod( + _i25.Stream<_i28.NavigationItem> get currentItemStream => (super.noSuchMethod( Invocation.getter(#currentItemStream), - returnValue: _i24.Stream<_i27.NavigationItem>.empty(), - returnValueForMissingStub: _i24.Stream<_i27.NavigationItem>.empty(), - ) as _i24.Stream<_i27.NavigationItem>); + returnValue: _i25.Stream<_i28.NavigationItem>.empty(), + returnValueForMissingStub: _i25.Stream<_i28.NavigationItem>.empty(), + ) as _i25.Stream<_i28.NavigationItem>); @override - _i27.NavigationItem get currentItem => (super.noSuchMethod( + _i28.NavigationItem get currentItem => (super.noSuchMethod( Invocation.getter(#currentItem), - returnValue: _i27.NavigationItem.overview, - returnValueForMissingStub: _i27.NavigationItem.overview, - ) as _i27.NavigationItem); + returnValue: _i28.NavigationItem.overview, + returnValueForMissingStub: _i28.NavigationItem.overview, + ) as _i28.NavigationItem); @override - dynamic Function(_i27.NavigationItem) get navigateTo => (super.noSuchMethod( + dynamic Function(_i28.NavigationItem) get navigateTo => (super.noSuchMethod( Invocation.getter(#navigateTo), - returnValue: (_i27.NavigationItem __p0) => null, - returnValueForMissingStub: (_i27.NavigationItem __p0) => null, - ) as dynamic Function(_i27.NavigationItem)); + returnValue: (_i28.NavigationItem __p0) => null, + returnValueForMissingStub: (_i28.NavigationItem __p0) => null, + ) as dynamic Function(_i28.NavigationItem)); @override void dispose() => super.noSuchMethod( @@ -605,24 +619,24 @@ class MockNavigationBloc extends _i2.Mock implements _i26.NavigationBloc { /// /// See the documentation for Mockito's code generation for more information. class MockNavigationExperimentCache extends _i2.Mock - implements _i28.NavigationExperimentCache { + implements _i29.NavigationExperimentCache { @override - _i3.ValueStream<_i29.NavigationExperimentOption> get currentNavigation => + _i3.ValueStream<_i30.NavigationExperimentOption> get currentNavigation => (super.noSuchMethod( Invocation.getter(#currentNavigation), - returnValue: _FakeValueStream_1<_i29.NavigationExperimentOption>( + returnValue: _FakeValueStream_1<_i30.NavigationExperimentOption>( this, Invocation.getter(#currentNavigation), ), returnValueForMissingStub: - _FakeValueStream_1<_i29.NavigationExperimentOption>( + _FakeValueStream_1<_i30.NavigationExperimentOption>( this, Invocation.getter(#currentNavigation), ), - ) as _i3.ValueStream<_i29.NavigationExperimentOption>); + ) as _i3.ValueStream<_i30.NavigationExperimentOption>); @override - void setNavigation(_i29.NavigationExperimentOption? option) => + void setNavigation(_i30.NavigationExperimentOption? option) => super.noSuchMethod( Invocation.method( #setNavigation, @@ -645,9 +659,9 @@ class MockNavigationExperimentCache extends _i2.Mock /// /// See the documentation for Mockito's code generation for more information. class MockNavigationAnalytics extends _i2.Mock - implements _i30.NavigationAnalytics { + implements _i31.NavigationAnalytics { @override - void logBottomNavigationBarEvent(_i27.NavigationItem? item) => + void logBottomNavigationBarEvent(_i28.NavigationItem? item) => super.noSuchMethod( Invocation.method( #logBottomNavigationBarEvent, @@ -657,7 +671,7 @@ class MockNavigationAnalytics extends _i2.Mock ); @override - void logDrawerEvent(_i27.NavigationItem? item) => super.noSuchMethod( + void logDrawerEvent(_i28.NavigationItem? item) => super.noSuchMethod( Invocation.method( #logDrawerEvent, [item], @@ -705,7 +719,7 @@ class MockNavigationAnalytics extends _i2.Mock /// A class which mocks [SharezoneContext]. /// /// See the documentation for Mockito's code generation for more information. -class MockSharezoneContext extends _i2.Mock implements _i31.SharezoneContext { +class MockSharezoneContext extends _i2.Mock implements _i32.SharezoneContext { @override _i4.SharezoneGateway get api => (super.noSuchMethod( Invocation.getter(#api), @@ -789,11 +803,11 @@ class MockSharezoneGateway extends _i2.Mock implements _i4.SharezoneGateway { @override String get uID => (super.noSuchMethod( Invocation.getter(#uID), - returnValue: _i32.dummyValue( + returnValue: _i33.dummyValue( this, Invocation.getter(#uID), ), - returnValueForMissingStub: _i32.dummyValue( + returnValueForMissingStub: _i33.dummyValue( this, Invocation.getter(#uID), ), @@ -880,11 +894,11 @@ class MockSharezoneGateway extends _i2.Mock implements _i4.SharezoneGateway { @override String get memberID => (super.noSuchMethod( Invocation.getter(#memberID), - returnValue: _i32.dummyValue( + returnValue: _i33.dummyValue( this, Invocation.getter(#memberID), ), - returnValueForMissingStub: _i32.dummyValue( + returnValueForMissingStub: _i33.dummyValue( this, Invocation.getter(#memberID), ), @@ -943,14 +957,14 @@ class MockSharezoneGateway extends _i2.Mock implements _i4.SharezoneGateway { ) as _i18.TimetableGateway); @override - _i24.Future dispose() => (super.noSuchMethod( + _i25.Future dispose() => (super.noSuchMethod( Invocation.method( #dispose, [], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i25.Future.value(), + returnValueForMissingStub: _i25.Future.value(), + ) as _i25.Future); } /// A class which mocks [UserGateway]. @@ -973,60 +987,60 @@ class MockUserGateway extends _i2.Mock implements _i13.UserGateway { @override String get uID => (super.noSuchMethod( Invocation.getter(#uID), - returnValue: _i32.dummyValue( + returnValue: _i33.dummyValue( this, Invocation.getter(#uID), ), - returnValueForMissingStub: _i32.dummyValue( + returnValueForMissingStub: _i33.dummyValue( this, Invocation.getter(#uID), ), ) as String); @override - _i24.Stream<_i19.AppUser?> get userStream => (super.noSuchMethod( + _i25.Stream<_i19.AppUser?> get userStream => (super.noSuchMethod( Invocation.getter(#userStream), - returnValue: _i24.Stream<_i19.AppUser?>.empty(), - returnValueForMissingStub: _i24.Stream<_i19.AppUser?>.empty(), - ) as _i24.Stream<_i19.AppUser?>); + returnValue: _i25.Stream<_i19.AppUser?>.empty(), + returnValueForMissingStub: _i25.Stream<_i19.AppUser?>.empty(), + ) as _i25.Stream<_i19.AppUser?>); @override - _i24.Stream<_i33.AuthUser?> get authUserStream => (super.noSuchMethod( + _i25.Stream<_i34.AuthUser?> get authUserStream => (super.noSuchMethod( Invocation.getter(#authUserStream), - returnValue: _i24.Stream<_i33.AuthUser?>.empty(), - returnValueForMissingStub: _i24.Stream<_i33.AuthUser?>.empty(), - ) as _i24.Stream<_i33.AuthUser?>); + returnValue: _i25.Stream<_i34.AuthUser?>.empty(), + returnValueForMissingStub: _i25.Stream<_i34.AuthUser?>.empty(), + ) as _i25.Stream<_i34.AuthUser?>); @override - _i24.Stream get isSignedInStream => (super.noSuchMethod( + _i25.Stream get isSignedInStream => (super.noSuchMethod( Invocation.getter(#isSignedInStream), - returnValue: _i24.Stream.empty(), - returnValueForMissingStub: _i24.Stream.empty(), - ) as _i24.Stream); + returnValue: _i25.Stream.empty(), + returnValueForMissingStub: _i25.Stream.empty(), + ) as _i25.Stream); @override - _i24.Stream<_i34.DocumentSnapshot> get userDocument => + _i25.Stream<_i35.DocumentSnapshot> get userDocument => (super.noSuchMethod( Invocation.getter(#userDocument), - returnValue: _i24.Stream<_i34.DocumentSnapshot>.empty(), + returnValue: _i25.Stream<_i35.DocumentSnapshot>.empty(), returnValueForMissingStub: - _i24.Stream<_i34.DocumentSnapshot>.empty(), - ) as _i24.Stream<_i34.DocumentSnapshot>); + _i25.Stream<_i35.DocumentSnapshot>.empty(), + ) as _i25.Stream<_i35.DocumentSnapshot>); @override - _i24.Stream<_i33.Provider?> get providerStream => (super.noSuchMethod( + _i25.Stream<_i34.Provider?> get providerStream => (super.noSuchMethod( Invocation.getter(#providerStream), - returnValue: _i24.Stream<_i33.Provider?>.empty(), - returnValueForMissingStub: _i24.Stream<_i33.Provider?>.empty(), - ) as _i24.Stream<_i33.Provider?>); + returnValue: _i25.Stream<_i34.Provider?>.empty(), + returnValueForMissingStub: _i25.Stream<_i34.Provider?>.empty(), + ) as _i25.Stream<_i34.Provider?>); @override - _i24.Future<_i19.AppUser> get() => (super.noSuchMethod( + _i25.Future<_i19.AppUser> get() => (super.noSuchMethod( Invocation.method( #get, [], ), - returnValue: _i24.Future<_i19.AppUser>.value(_FakeAppUser_17( + returnValue: _i25.Future<_i19.AppUser>.value(_FakeAppUser_17( this, Invocation.method( #get, @@ -1034,24 +1048,24 @@ class MockUserGateway extends _i2.Mock implements _i13.UserGateway { ), )), returnValueForMissingStub: - _i24.Future<_i19.AppUser>.value(_FakeAppUser_17( + _i25.Future<_i19.AppUser>.value(_FakeAppUser_17( this, Invocation.method( #get, [], ), )), - ) as _i24.Future<_i19.AppUser>); + ) as _i25.Future<_i19.AppUser>); @override - _i24.Future logOut() => (super.noSuchMethod( + _i25.Future logOut() => (super.noSuchMethod( Invocation.method( #logOut, [], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i25.Future.value(), + returnValueForMissingStub: _i25.Future.value(), + ) as _i25.Future); @override bool isAnonymous() => (super.noSuchMethod( @@ -1064,45 +1078,45 @@ class MockUserGateway extends _i2.Mock implements _i13.UserGateway { ) as bool); @override - _i24.Stream isAnonymousStream() => (super.noSuchMethod( + _i25.Stream isAnonymousStream() => (super.noSuchMethod( Invocation.method( #isAnonymousStream, [], ), - returnValue: _i24.Stream.empty(), - returnValueForMissingStub: _i24.Stream.empty(), - ) as _i24.Stream); + returnValue: _i25.Stream.empty(), + returnValueForMissingStub: _i25.Stream.empty(), + ) as _i25.Stream); @override - _i24.Future linkWithCredential(_i35.AuthCredential? credential) => + _i25.Future linkWithCredential(_i36.AuthCredential? credential) => (super.noSuchMethod( Invocation.method( #linkWithCredential, [credential], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i25.Future.value(), + returnValueForMissingStub: _i25.Future.value(), + ) as _i25.Future); @override - _i24.Future changeState(_i19.StateEnum? state) => (super.noSuchMethod( + _i25.Future changeState(_i19.StateEnum? state) => (super.noSuchMethod( Invocation.method( #changeState, [state], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i25.Future.value(), + returnValueForMissingStub: _i25.Future.value(), + ) as _i25.Future); @override - _i24.Future addNotificationToken(String? token) => (super.noSuchMethod( + _i25.Future addNotificationToken(String? token) => (super.noSuchMethod( Invocation.method( #addNotificationToken, [token], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i25.Future.value(), + returnValueForMissingStub: _i25.Future.value(), + ) as _i25.Future); @override void removeNotificationToken(String? token) => super.noSuchMethod( @@ -1114,29 +1128,29 @@ class MockUserGateway extends _i2.Mock implements _i13.UserGateway { ); @override - _i24.Future setHomeworkReminderTime(_i1.TimeOfDay? timeOfDay) => + _i25.Future setHomeworkReminderTime(_i1.TimeOfDay? timeOfDay) => (super.noSuchMethod( Invocation.method( #setHomeworkReminderTime, [timeOfDay], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i25.Future.value(), + returnValueForMissingStub: _i25.Future.value(), + ) as _i25.Future); @override - _i24.Future updateSettings(_i19.UserSettings? userSettings) => + _i25.Future updateSettings(_i19.UserSettings? userSettings) => (super.noSuchMethod( Invocation.method( #updateSettings, [userSettings], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i25.Future.value(), + returnValueForMissingStub: _i25.Future.value(), + ) as _i25.Future); @override - _i24.Future updateSettingsSingleFiled( + _i25.Future updateSettingsSingleFiled( String? fieldName, dynamic data, ) => @@ -1148,12 +1162,12 @@ class MockUserGateway extends _i2.Mock implements _i13.UserGateway { data, ], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i25.Future.value(), + returnValueForMissingStub: _i25.Future.value(), + ) as _i25.Future); @override - _i24.Future updateUserTip( + _i25.Future updateUserTip( _i19.UserTipKey? userTipKey, bool? value, ) => @@ -1165,9 +1179,9 @@ class MockUserGateway extends _i2.Mock implements _i13.UserGateway { value, ], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i25.Future.value(), + returnValueForMissingStub: _i25.Future.value(), + ) as _i25.Future); @override void setBlackboardNotifications(bool? enabled) => super.noSuchMethod( @@ -1188,17 +1202,17 @@ class MockUserGateway extends _i2.Mock implements _i13.UserGateway { ); @override - _i24.Future changeEmail(String? email) => (super.noSuchMethod( + _i25.Future changeEmail(String? email) => (super.noSuchMethod( Invocation.method( #changeEmail, [email], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i25.Future.value(), + returnValueForMissingStub: _i25.Future.value(), + ) as _i25.Future); @override - _i24.Future addUser({ + _i25.Future addUser({ required _i19.AppUser? user, bool? merge = false, }) => @@ -1211,40 +1225,40 @@ class MockUserGateway extends _i2.Mock implements _i13.UserGateway { #merge: merge, }, ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i25.Future.value(), + returnValueForMissingStub: _i25.Future.value(), + ) as _i25.Future); @override - _i24.Future sendVerificationEmail() => (super.noSuchMethod( + _i25.Future sendVerificationEmail() => (super.noSuchMethod( Invocation.method( #sendVerificationEmail, [], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i25.Future.value(), + returnValueForMissingStub: _i25.Future.value(), + ) as _i25.Future); @override - _i24.Future deleteUser(_i4.SharezoneGateway? gateway) => + _i25.Future deleteUser(_i4.SharezoneGateway? gateway) => (super.noSuchMethod( Invocation.method( #deleteUser, [gateway], ), - returnValue: _i24.Future.value(false), - returnValueForMissingStub: _i24.Future.value(false), - ) as _i24.Future); + returnValue: _i25.Future.value(false), + returnValueForMissingStub: _i25.Future.value(false), + ) as _i25.Future); @override - _i24.Future<_i20.AppFunctionsResult> updateUser( + _i25.Future<_i20.AppFunctionsResult> updateUser( _i19.AppUser? userData) => (super.noSuchMethod( Invocation.method( #updateUser, [userData], ), - returnValue: _i24.Future<_i20.AppFunctionsResult>.value( + returnValue: _i25.Future<_i20.AppFunctionsResult>.value( _FakeAppFunctionsResult_18( this, Invocation.method( @@ -1253,7 +1267,7 @@ class MockUserGateway extends _i2.Mock implements _i13.UserGateway { ), )), returnValueForMissingStub: - _i24.Future<_i20.AppFunctionsResult>.value( + _i25.Future<_i20.AppFunctionsResult>.value( _FakeAppFunctionsResult_18( this, Invocation.method( @@ -1261,24 +1275,24 @@ class MockUserGateway extends _i2.Mock implements _i13.UserGateway { [userData], ), )), - ) as _i24.Future<_i20.AppFunctionsResult>); + ) as _i25.Future<_i20.AppFunctionsResult>); @override - _i24.Future dispose() => (super.noSuchMethod( + _i25.Future dispose() => (super.noSuchMethod( Invocation.method( #dispose, [], ), - returnValue: _i24.Future.value(), - returnValueForMissingStub: _i24.Future.value(), - ) as _i24.Future); + returnValue: _i25.Future.value(), + returnValueForMissingStub: _i25.Future.value(), + ) as _i25.Future); } /// A class which mocks [HasUnreadFeedbackMessagesProvider]. /// /// See the documentation for Mockito's code generation for more information. class MockHasUnreadFeedbackMessagesProvider extends _i2.Mock - implements _i36.HasUnreadFeedbackMessagesProvider { + implements _i37.HasUnreadFeedbackMessagesProvider { @override _i21.FeedbackApi get feedbackApi => (super.noSuchMethod( Invocation.getter(#feedbackApi), @@ -1329,7 +1343,7 @@ class MockHasUnreadFeedbackMessagesProvider extends _i2.Mock ); @override - void addListener(_i25.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i26.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -1338,7 +1352,7 @@ class MockHasUnreadFeedbackMessagesProvider extends _i2.Mock ); @override - void removeListener(_i25.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i26.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], @@ -1355,3 +1369,131 @@ class MockHasUnreadFeedbackMessagesProvider extends _i2.Mock returnValueForMissingStub: null, ); } + +/// A class which mocks [SubscriptionService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockSubscriptionService extends _i2.Mock + implements _i38.SubscriptionService { + @override + _i25.Stream<_i19.AppUser?> get user => (super.noSuchMethod( + Invocation.getter(#user), + returnValue: _i25.Stream<_i19.AppUser?>.empty(), + returnValueForMissingStub: _i25.Stream<_i19.AppUser?>.empty(), + ) as _i25.Stream<_i19.AppUser?>); + + @override + _i22.FirebaseFunctions get functions => (super.noSuchMethod( + Invocation.getter(#functions), + returnValue: _FakeFirebaseFunctions_20( + this, + Invocation.getter(#functions), + ), + returnValueForMissingStub: _FakeFirebaseFunctions_20( + this, + Invocation.getter(#functions), + ), + ) as _i22.FirebaseFunctions); + + @override + _i25.Stream<_i19.SharezonePlusStatus?> get sharezonePlusStatusStream => + (super.noSuchMethod( + Invocation.getter(#sharezonePlusStatusStream), + returnValue: _i25.Stream<_i19.SharezonePlusStatus?>.empty(), + returnValueForMissingStub: + _i25.Stream<_i19.SharezonePlusStatus?>.empty(), + ) as _i25.Stream<_i19.SharezonePlusStatus?>); + + @override + set sharezonePlusStatusStream( + _i25.Stream<_i19.SharezonePlusStatus?>? _sharezonePlusStatusStream) => + super.noSuchMethod( + Invocation.setter( + #sharezonePlusStatusStream, + _sharezonePlusStatusStream, + ), + returnValueForMissingStub: null, + ); + + @override + bool isSubscriptionActive([_i19.AppUser? appUser]) => (super.noSuchMethod( + Invocation.method( + #isSubscriptionActive, + [appUser], + ), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i25.Stream isSubscriptionActiveStream() => (super.noSuchMethod( + Invocation.method( + #isSubscriptionActiveStream, + [], + ), + returnValue: _i25.Stream.empty(), + returnValueForMissingStub: _i25.Stream.empty(), + ) as _i25.Stream); + + @override + bool hasFeatureUnlocked(_i38.SharezonePlusFeature? feature) => + (super.noSuchMethod( + Invocation.method( + #hasFeatureUnlocked, + [feature], + ), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i25.Stream hasFeatureUnlockedStream( + _i38.SharezonePlusFeature? feature) => + (super.noSuchMethod( + Invocation.method( + #hasFeatureUnlockedStream, + [feature], + ), + returnValue: _i25.Stream.empty(), + returnValueForMissingStub: _i25.Stream.empty(), + ) as _i25.Stream); + + @override + _i25.Future cancelStripeSubscription() => (super.noSuchMethod( + Invocation.method( + #cancelStripeSubscription, + [], + ), + returnValue: _i25.Future.value(), + returnValueForMissingStub: _i25.Future.value(), + ) as _i25.Future); + + @override + _i25.Future showLetParentsBuyButton() => (super.noSuchMethod( + Invocation.method( + #showLetParentsBuyButton, + [], + ), + returnValue: _i25.Future.value(false), + returnValueForMissingStub: _i25.Future.value(false), + ) as _i25.Future); + + @override + _i25.Future getPlusWebsiteBuyToken() => (super.noSuchMethod( + Invocation.method( + #getPlusWebsiteBuyToken, + [], + ), + returnValue: _i25.Future.value(), + returnValueForMissingStub: _i25.Future.value(), + ) as _i25.Future); + + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); +} diff --git a/lib/hausaufgabenheft_logik/lib/src/student/bloc/student_homework_page_bloc.dart b/lib/hausaufgabenheft_logik/lib/src/student/bloc/student_homework_page_bloc.dart index 06632445e..f770f76e5 100644 --- a/lib/hausaufgabenheft_logik/lib/src/student/bloc/student_homework_page_bloc.dart +++ b/lib/hausaufgabenheft_logik/lib/src/student/bloc/student_homework_page_bloc.dart @@ -13,8 +13,9 @@ import 'package:bloc_base/bloc_base.dart' as bloc_base; import 'package:common_domain_models/common_domain_models.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:hausaufgabenheft_logik/hausaufgabenheft_logik.dart'; -import 'package:hausaufgabenheft_logik/src/student/views/student_open_homework_list_view_factory.dart'; import 'package:hausaufgabenheft_logik/src/shared/homework_sorting_cache.dart'; +import 'package:hausaufgabenheft_logik/src/student/views/student_open_homework_list_view_factory.dart'; +import 'package:key_value_store/key_value_store.dart'; import 'package:rxdart/rxdart.dart'; import '../views/student_homework_view_factory.dart'; @@ -29,6 +30,7 @@ class StudentHomeworkPageBloc final StudentHomeworkViewFactory _viewFactory; final StudentOpenHomeworkListViewFactory _openHomeworkListViewFactory; final _currentSortStream = BehaviorSubject>(); + final KeyValueStore _keyValueStore; LazyLoadingController? _lazyLoadingController; /// Whether [close] or [dispose] has been called; @@ -41,11 +43,13 @@ class StudentHomeworkPageBloc required StudentOpenHomeworkListViewFactory openHomeworkListViewFactory, required this.numberOfInitialCompletedHomeworksToLoad, required DateTime Function() getCurrentDateTime, + required KeyValueStore keyValueStore, }) : _homeworkApi = homeworkApi, _openHomeworkListViewFactory = openHomeworkListViewFactory, _homeworkSortingCache = homeworkSortingCache, _viewFactory = viewFactory, _getCurrentDateTime = getCurrentDateTime, + _keyValueStore = keyValueStore, super(Uninitialized()) { on((event, emit) { _mapLoadHomeworksToState(); @@ -119,6 +123,16 @@ class StudentHomeworkPageBloc event.newValue == true ? CompletionStatus.completed : CompletionStatus.open); + + if (event.newValue == true) { + _incrementCheckedHomeworkCounter(); + } + } + + void _incrementCheckedHomeworkCounter() { + final currentCounter = + _keyValueStore.getInt('checked-homework-counter') ?? 0; + _keyValueStore.setInt('checked-homework-counter', currentCounter + 1); } Future _mapHomeworkMarkOverdueToState(CompletedAllOverdue event) async { diff --git a/lib/hausaufgabenheft_logik/lib/src/student/create_student_homework_page_bloc.dart b/lib/hausaufgabenheft_logik/lib/src/student/create_student_homework_page_bloc.dart index 4df8f5d03..4d5c849b7 100644 --- a/lib/hausaufgabenheft_logik/lib/src/student/create_student_homework_page_bloc.dart +++ b/lib/hausaufgabenheft_logik/lib/src/student/create_student_homework_page_bloc.dart @@ -38,6 +38,7 @@ StudentHomeworkPageBloc createStudentHomeworkPageBloc( numberOfInitialCompletedHomeworksToLoad: config.nrOfInitialCompletedHomeworksToLoad, homeworkSortingCache: HomeworkSortingCache(dependencies.keyValueStore), + keyValueStore: dependencies.keyValueStore, getCurrentDateTime: dependencies.getCurrentDateTime ?? () => clock.now(), ); } diff --git a/lib/hausaufgabenheft_logik/test/homework_page_bloc_test.dart b/lib/hausaufgabenheft_logik/test/homework_page_bloc_test.dart index 730fe74c2..aed932180 100644 --- a/lib/hausaufgabenheft_logik/test/homework_page_bloc_test.dart +++ b/lib/hausaufgabenheft_logik/test/homework_page_bloc_test.dart @@ -68,6 +68,22 @@ void main() { expect(success.open.sections.first.title, 'Morgen'); }); + test( + 'Counter in key value store is incremented when homework is marked as completed', + () async { + await addToRepository([createHomework(id: 'hw')]); + + bloc = createBloc(repository, keyValueStore: kvs); + bloc.add(LoadHomeworks()); + await pumpEventQueue(); + + bloc.add(CompletionStatusChanged('hw', true)); + await pumpEventQueue(); + + final updatedCounter = kvs.getInt('checked-homework-counter') ?? 0; + expect(updatedCounter, 1); + }); + test( 'Regressions Test für #954 "Wenn man Hausaufgaben nach Fach sortiert und dann eine Aufgabe abhakt wechselt die App automatisch wieder in die Ansicht nach Datum."', () async { diff --git a/lib/remote_configuration/lib/src/get_remote_configuration.dart b/lib/remote_configuration/lib/src/get_remote_configuration.dart index 2f6e3db2d..82ca96323 100644 --- a/lib/remote_configuration/lib/src/get_remote_configuration.dart +++ b/lib/remote_configuration/lib/src/get_remote_configuration.dart @@ -6,9 +6,17 @@ // // SPDX-License-Identifier: EUPL-1.2 +import 'package:flutter/foundation.dart'; + import 'implementation/firebase_remote_configuration.dart' as implementation; +import 'implementation/stub_remote_configuration.dart' as stub; import 'remote_configuration.dart'; RemoteConfiguration getRemoteConfiguration() { return implementation.getRemoteConfiguration(); } + +@visibleForTesting +RemoteConfiguration getStubRemoteConfiguration() { + return stub.getRemoteConfiguration(); +} diff --git a/lib/sharezone_plus/sharezone_plus_page_ui/lib/src/sharezone_plus_advantages.dart b/lib/sharezone_plus/sharezone_plus_page_ui/lib/src/sharezone_plus_advantages.dart index 792d61d01..6680a1047 100644 --- a/lib/sharezone_plus/sharezone_plus_page_ui/lib/src/sharezone_plus_advantages.dart +++ b/lib/sharezone_plus/sharezone_plus_page_ui/lib/src/sharezone_plus_advantages.dart @@ -15,11 +15,13 @@ class SharezonePlusAdvantages extends StatelessWidget { const SharezonePlusAdvantages({ super.key, required this.isHomeworkReminderFeatureVisible, + this.isRemoveAdsFeatureVisible = false, this.onOpenedAdvantage, this.onGitHubOpen, }); final bool isHomeworkReminderFeatureVisible; + final bool isRemoveAdsFeatureVisible; final ValueChanged? onOpenedAdvantage; final VoidCallback? onGitHubOpen; @@ -27,6 +29,7 @@ class SharezonePlusAdvantages extends StatelessWidget { Widget build(BuildContext context) { return Column( children: [ + if (isRemoveAdsFeatureVisible) _RemoveAds(onOpen: onOpenedAdvantage), _Grades(onOpen: onOpenedAdvantage), _MoreColors(onOpen: onOpenedAdvantage), _QuickHomeworkDueDate(onOpen: onOpenedAdvantage), @@ -51,6 +54,27 @@ class SharezonePlusAdvantages extends StatelessWidget { } } +class _RemoveAds extends StatelessWidget { + const _RemoveAds({ + required this.onOpen, + }); + + final ValueChanged? onOpen; + + @override + Widget build(BuildContext context) { + return _AdvantageTile( + onOpen: () { + if (onOpen != null) onOpen!('remove_ads'); + }, + icon: const Icon(Icons.block), + title: const Text('Werbung entfernen'), + description: const Text( + 'Genieße Sharezone komplett werbefrei.\n\nHinweis: Wir testen derzeit die Anzeige von Werbung. Es ist möglich, dass wir in Zukunft die Werbung wieder für alle Nutzer entfernen.'), + ); + } +} + class _Grades extends StatelessWidget { const _Grades({ required this.onOpen,