diff --git a/lib/app_configuration/widgets/app_config_form_fields.dart b/lib/app_configuration/widgets/app_config_form_fields.dart index d6d03b39..50ebb7fd 100644 --- a/lib/app_configuration/widgets/app_config_form_fields.dart +++ b/lib/app_configuration/widgets/app_config_form_fields.dart @@ -13,6 +13,7 @@ class AppConfigIntField extends StatelessWidget { required this.value, required this.onChanged, this.controller, + this.enabled = true, super.key, }); @@ -31,6 +32,9 @@ class AppConfigIntField extends StatelessWidget { /// Optional text editing controller for more control. final TextEditingController? controller; + /// Whether the input field is enabled. Defaults to true. + final bool enabled; + @override Widget build(BuildContext context) { final theme = Theme.of(context); @@ -50,6 +54,7 @@ class AppConfigIntField extends StatelessWidget { ), const SizedBox(height: AppSpacing.xs), TextFormField( + enabled: enabled, controller: controller, initialValue: controller == null ? value.toString() : null, keyboardType: TextInputType.number, diff --git a/lib/app_configuration/widgets/feed_ad_settings_form.dart b/lib/app_configuration/widgets/feed_ad_settings_form.dart index 49f52b60..211dd5d9 100644 --- a/lib/app_configuration/widgets/feed_ad_settings_form.dart +++ b/lib/app_configuration/widgets/feed_ad_settings_form.dart @@ -43,6 +43,37 @@ class _FeedAdSettingsFormState extends State vsync: this, ); _initializeControllers(); + _tabController.addListener(_onTabChanged); + } + + void _onTabChanged() { + if (_tabController.indexIsChanging) return; + + final selectedRole = AppUserRole.values[_tabController.index]; + if (selectedRole == AppUserRole.premiumUser) { + final adConfig = widget.remoteConfig.adConfig; + final feedAdConfig = adConfig.feedAdConfiguration; + + // If the values for premium are not 0, update the config. + // This enforces the business rule that premium users do not see ads. + if (feedAdConfig.frequencyConfig.premiumAdFrequency != 0 || + feedAdConfig.frequencyConfig.premiumAdPlacementInterval != 0) { + final updatedFrequencyConfig = feedAdConfig.frequencyConfig.copyWith( + premiumAdFrequency: 0, + premiumAdPlacementInterval: 0, + ); + final updatedFeedAdConfig = feedAdConfig.copyWith( + frequencyConfig: updatedFrequencyConfig, + ); + widget.onConfigChanged( + widget.remoteConfig.copyWith( + adConfig: adConfig.copyWith( + feedAdConfiguration: updatedFeedAdConfig, + ), + ), + ); + } + } } @override @@ -109,7 +140,9 @@ class _FeedAdSettingsFormState extends State @override void dispose() { - _tabController.dispose(); + _tabController + ..removeListener(_onTabChanged) + ..dispose(); for (final controller in _adFrequencyControllers.values) { controller.dispose(); } @@ -257,6 +290,9 @@ class _FeedAdSettingsFormState extends State AppUserRole role, FeedAdConfiguration config, ) { + // Premium users do not see ads, so their settings are disabled. + final isEnabled = role != AppUserRole.premiumUser; + return Column( children: [ AppConfigIntField( @@ -273,6 +309,7 @@ class _FeedAdSettingsFormState extends State ); }, controller: _adFrequencyControllers[role], + enabled: isEnabled, ), AppConfigIntField( label: l10n.adPlacementIntervalLabel, @@ -292,6 +329,7 @@ class _FeedAdSettingsFormState extends State ); }, controller: _adPlacementIntervalControllers[role], + enabled: isEnabled, ), ], ); @@ -338,9 +376,11 @@ class _FeedAdSettingsFormState extends State ), ); case AppUserRole.premiumUser: + // Premium users should not see ads, so their frequency is always 0. + // The UI field is disabled, but this is a safeguard. return config.copyWith( frequencyConfig: config.frequencyConfig.copyWith( - premiumAdFrequency: value, + premiumAdFrequency: 0, ), ); } @@ -365,9 +405,11 @@ class _FeedAdSettingsFormState extends State ), ); case AppUserRole.premiumUser: + // Premium users should not see ads, so their interval is always 0. + // The UI field is disabled, but this is a safeguard. return config.copyWith( frequencyConfig: config.frequencyConfig.copyWith( - premiumAdPlacementInterval: value, + premiumAdPlacementInterval: 0, ), ); } diff --git a/lib/app_configuration/widgets/interstitial_ad_settings_form.dart b/lib/app_configuration/widgets/interstitial_ad_settings_form.dart index a4f252b7..24c57acc 100644 --- a/lib/app_configuration/widgets/interstitial_ad_settings_form.dart +++ b/lib/app_configuration/widgets/interstitial_ad_settings_form.dart @@ -42,6 +42,40 @@ class _InterstitialAdSettingsFormState extends State vsync: this, ); _initializeControllers(); + _tabController.addListener(_onTabChanged); + } + + void _onTabChanged() { + if (_tabController.indexIsChanging) return; + + final selectedRole = AppUserRole.values[_tabController.index]; + if (selectedRole == AppUserRole.premiumUser) { + final adConfig = widget.remoteConfig.adConfig; + final interstitialAdConfig = adConfig.interstitialAdConfiguration; + + // If the value for premium is not 0, update the config. + // This enforces the business rule that premium users do not see ads. + if (interstitialAdConfig + .feedInterstitialAdFrequencyConfig + .premiumUserTransitionsBeforeShowingInterstitialAds != + 0) { + final updatedFrequencyConfig = interstitialAdConfig + .feedInterstitialAdFrequencyConfig + .copyWith( + premiumUserTransitionsBeforeShowingInterstitialAds: 0, + ); + final updatedInterstitialAdConfig = interstitialAdConfig.copyWith( + feedInterstitialAdFrequencyConfig: updatedFrequencyConfig, + ); + widget.onConfigChanged( + widget.remoteConfig.copyWith( + adConfig: adConfig.copyWith( + interstitialAdConfiguration: updatedInterstitialAdConfig, + ), + ), + ); + } + } } @override @@ -96,7 +130,9 @@ class _InterstitialAdSettingsFormState extends State @override void dispose() { - _tabController.dispose(); + _tabController + ..removeListener(_onTabChanged) + ..dispose(); for (final controller in _transitionsBeforeShowingInterstitialAdsControllers.values) { controller.dispose(); @@ -190,6 +226,9 @@ class _InterstitialAdSettingsFormState extends State AppUserRole role, InterstitialAdConfiguration config, ) { + // Premium users do not see ads, so their settings are disabled. + final isEnabled = role != AppUserRole.premiumUser; + return Column( children: [ AppConfigIntField( @@ -211,6 +250,7 @@ class _InterstitialAdSettingsFormState extends State ); }, controller: _transitionsBeforeShowingInterstitialAdsControllers[role], + enabled: isEnabled, ), ], ); @@ -255,8 +295,10 @@ class _InterstitialAdSettingsFormState extends State standardUserTransitionsBeforeShowingInterstitialAds: value, ); case AppUserRole.premiumUser: + // Premium users should not see ads, so their frequency is always 0. + // The UI field is disabled, but this is a safeguard. newFrequencyConfig = currentFrequencyConfig.copyWith( - premiumUserTransitionsBeforeShowingInterstitialAds: value, + premiumUserTransitionsBeforeShowingInterstitialAds: 0, ); } diff --git a/lib/local_ads_management/view/archived_local_ads_page.dart b/lib/local_ads_management/view/archived_local_ads_page.dart index 94e29789..029b66cb 100644 --- a/lib/local_ads_management/view/archived_local_ads_page.dart +++ b/lib/local_ads_management/view/archived_local_ads_page.dart @@ -51,11 +51,19 @@ class _ArchivedLocalAdsViewState extends State<_archivedlocaladsview> with SingleTickerProviderStateMixin { late TabController _tabController; + // Statically define the tab order to ensure consistency. + final List _tabs = [ + AdType.native, + AdType.banner, + AdType.interstitial, + AdType.video, + ]; + @override void initState() { super.initState(); _tabController = TabController( - length: AdType.values.length, + length: _tabs.length, vsync: this, ); } @@ -76,9 +84,7 @@ class _ArchivedLocalAdsViewState extends State<_archivedlocaladsview> controller: _tabController, tabAlignment: TabAlignment.start, isScrollable: true, - tabs: AdType.values - .map((type) => Tab(text: type.l10n(context))) - .toList(), + tabs: _tabs.map((type) => Tab(text: type.l10n(context))).toList(), ), ), body: BlocListener( @@ -149,9 +155,9 @@ class _ArchivedLocalAdsViewState extends State<_archivedlocaladsview> }, child: TabBarView( controller: _tabController, - children: AdType.values.map((type) { - return _ArchivedLocalAdsDataTable(adType: type); - }).toList(), + children: _tabs + .map((type) => _ArchivedLocalAdsDataTable(adType: type)) + .toList(), ), ), ); diff --git a/pubspec.lock b/pubspec.lock index 1eb68cd4..3987dfff 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -90,7 +90,7 @@ packages: description: path: "." ref: HEAD - resolved-ref: dd37d11352124113978ebeda3b509d932db61cf5 + resolved-ref: b6a3fb4ad862901a56b424fd92a9702f30513404 url: "https://github.com/flutter-news-app-full-source-code/core.git" source: git version: "0.0.0" @@ -269,10 +269,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: eb059dfe59f08546e9787f895bd01652076f996bcbf485a8609ef990419ad227 + sha256: b1488741c9ce37b72e026377c69a59c47378493156fc38efb5a54f6def3f92a3 url: "https://pub.dev" source: hosted - version: "16.2.1" + version: "16.2.2" google_fonts: dependency: "direct main" description: @@ -488,10 +488,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: a2608114b1ffdcbc9c120eb71a0e207c71da56202852d4aab8a5e30a82269e74 + sha256: bd14436108211b0d4ee5038689a56d4ae3620fd72fd6036e113bf1345bc74d9e url: "https://pub.dev" source: hosted - version: "2.4.12" + version: "2.4.13" shared_preferences_foundation: dependency: transitive description:

AltStyle によって変換されたページ (->オリジナル) /