From 79e7ccf1baac6cb07d548d300b0d07d1ee0956eb Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 10:07:09 +0100 Subject: [PATCH 01/27] build(deps): update git dependencies to use version tags - Replace git refs with version tags for auth-api, auth-client, auth-inmemory, auth-repository, core, data-api, data-client, data-inmemory, data-repository, http-client, kv-storage-service, kv-storage-shared-preferences, and ui-kit dependencies - Update package version to 1.0.0 - Remove publish_to field --- pubspec.lock | 52 ++++++++++++++++++++++++++-------------------------- pubspec.yaml | 29 +++++++++++++++-------------- 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index ab302b18..878a02f7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -13,8 +13,8 @@ packages: dependency: "direct main" description: path: "." - ref: "41a7ea2bea938e422acdac0ecfbf3a614b3fb855" - resolved-ref: "41a7ea2bea938e422acdac0ecfbf3a614b3fb855" + ref: "v1.0.0" + resolved-ref: "7360a5a1af202bdc291138c81ddc817979454b78" url: "https://github.com/flutter-news-app-full-source-code/auth-api.git" source: git version: "0.0.0" @@ -22,8 +22,8 @@ packages: dependency: "direct main" description: path: "." - ref: fec23f405d58d4915638783d972b542d3faf96b2 - resolved-ref: fec23f405d58d4915638783d972b542d3faf96b2 + ref: "v1.0.0" + resolved-ref: a90e24285874e4c39cb331bbbeae2423901f3289 url: "https://github.com/flutter-news-app-full-source-code/auth-client.git" source: git version: "0.0.0" @@ -31,8 +31,8 @@ packages: dependency: "direct main" description: path: "." - ref: "7f1a582419ae37cb43efe705e3299f2092c54a8b" - resolved-ref: "7f1a582419ae37cb43efe705e3299f2092c54a8b" + ref: "v1.0.0" + resolved-ref: "5316331378ee90e13dbbb0966a964a4b02e4dfcf" url: "https://github.com/flutter-news-app-full-source-code/auth-inmemory" source: git version: "0.0.0" @@ -40,8 +40,8 @@ packages: dependency: "direct main" description: path: "." - ref: "1f1272b586b045903c164be5a39d95383809ba09" - resolved-ref: "1f1272b586b045903c164be5a39d95383809ba09" + ref: "v1.0.0" + resolved-ref: "0d751f835d563c970945a7dab828e1bcb4181049" url: "https://github.com/flutter-news-app-full-source-code/auth-repository.git" source: git version: "0.0.0" @@ -89,11 +89,11 @@ packages: dependency: "direct main" description: path: "." - ref: "828b984517edec069266579c624c7af9fbe0e2ac" - resolved-ref: "828b984517edec069266579c624c7af9fbe0e2ac" + ref: "v1.2.0" + resolved-ref: d052799c1ebb7bcdd1a725a2a7919948c14fa001 url: "https://github.com/flutter-news-app-full-source-code/core.git" source: git - version: "0.0.0" + version: "1.2.0" crypto: dependency: transitive description: @@ -106,8 +106,8 @@ packages: dependency: "direct main" description: path: "." - ref: f90ab664c6333995757026a2c96e96d304da6246 - resolved-ref: f90ab664c6333995757026a2c96e96d304da6246 + ref: "v1.0.0" + resolved-ref: b838da6b8c29ba3a54a68fe11402e7fe3886d135 url: "https://github.com/flutter-news-app-full-source-code/data-api.git" source: git version: "0.0.0" @@ -115,8 +115,8 @@ packages: dependency: "direct main" description: path: "." - ref: "960a00882045ee252274d9d57bfca606c1fe5d64" - resolved-ref: "960a00882045ee252274d9d57bfca606c1fe5d64" + ref: "v1.0.0" + resolved-ref: b9a8a8c2c660928c22f2d38d657000bcae4c74d6 url: "https://github.com/flutter-news-app-full-source-code/data-client.git" source: git version: "0.0.0" @@ -124,8 +124,8 @@ packages: dependency: "direct main" description: path: "." - ref: aaf4e18b759ca8b33d060fd4aa8509238d5d3eaa - resolved-ref: aaf4e18b759ca8b33d060fd4aa8509238d5d3eaa + ref: "v1.0.0" + resolved-ref: cd7a39e12f321c4c0c6bb5041c5d91cb32bc1586 url: "https://github.com/flutter-news-app-full-source-code/data-inmemory.git" source: git version: "0.0.0" @@ -133,8 +133,8 @@ packages: dependency: "direct main" description: path: "." - ref: f8f01f1191286efbba41fa2bb369fb16eb652ccf - resolved-ref: f8f01f1191286efbba41fa2bb369fb16eb652ccf + ref: "v1.0.0" + resolved-ref: "7f9242d810d60fefd2f883b19e1650e8e4eb41a3" url: "https://github.com/flutter-news-app-full-source-code/data-repository.git" source: git version: "0.0.0" @@ -293,8 +293,8 @@ packages: dependency: "direct main" description: path: "." - ref: "57f6bcfc0f209ecc135fdcb688194045ec87e6e8" - resolved-ref: "57f6bcfc0f209ecc135fdcb688194045ec87e6e8" + ref: "v1.0.1" + resolved-ref: ce550196f78ee2e95aa9e985759179265983689d url: "https://github.com/flutter-news-app-full-source-code/http-client.git" source: git version: "0.0.0" @@ -326,7 +326,7 @@ packages: dependency: "direct main" description: path: "." - ref: "3bcba6b11fe0480e06e1bedf6eab4159a7c59c53" + ref: "v1.0.0" resolved-ref: "3bcba6b11fe0480e06e1bedf6eab4159a7c59c53" url: "https://github.com/flutter-news-app-full-source-code/kv-storage-service.git" source: git @@ -335,8 +335,8 @@ packages: dependency: "direct main" description: path: "." - ref: "4cefd202c5fc0c53856782fea7760baa1d0733f7" - resolved-ref: "4cefd202c5fc0c53856782fea7760baa1d0733f7" + ref: "v1.0.0" + resolved-ref: ed94c16ae5ced56216f1adcc14b995e418d5c464 url: "https://github.com/flutter-news-app-full-source-code/kv-storage-shared-preferences.git" source: git version: "0.0.0" @@ -597,8 +597,8 @@ packages: dependency: "direct main" description: path: "." - ref: d9c66362ec87fd7b5e3d3dee35828da6cbc1f66a - resolved-ref: d9c66362ec87fd7b5e3d3dee35828da6cbc1f66a + ref: "v1.0.0" + resolved-ref: "5e84f206a0e047f6afd608d510aec998c6a37dd3" url: "https://github.com/flutter-news-app-full-source-code/ui-kit.git" source: git version: "0.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 47536a06..e289ba5d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,8 @@ name: flutter_news_app_web_dashboard_full_source_code description: "A Flutter web application for comprehensive content management and administration within the Flutter News App Full Source Code Toolkit." -publish_to: none repository: https://github.com/flutter-news-app-full-source-code/flutter-news-app-web-dashboard-full-source-code +publish_to: none +version: 1.0.0 environment: sdk: ^3.9.0 @@ -10,42 +11,42 @@ dependencies: auth_api: git: url: https://github.com/flutter-news-app-full-source-code/auth-api.git - ref: 41a7ea2bea938e422acdac0ecfbf3a614b3fb855 + ref: v1.0.0 auth_client: git: url: https://github.com/flutter-news-app-full-source-code/auth-client.git - ref: fec23f405d58d4915638783d972b542d3faf96b2 + ref: v1.0.0 auth_inmemory: git: url: https://github.com/flutter-news-app-full-source-code/auth-inmemory - ref: 7f1a582419ae37cb43efe705e3299f2092c54a8b + ref: v1.0.0 auth_repository: git: url: https://github.com/flutter-news-app-full-source-code/auth-repository.git - ref: 1f1272b586b045903c164be5a39d95383809ba09 + ref: v1.0.0 bloc: ^9.0.0 bloc_concurrency: ^0.3.0 collection: ^1.19.1 core: git: url: https://github.com/flutter-news-app-full-source-code/core.git - ref: 828b984517edec069266579c624c7af9fbe0e2ac + ref: v1.2.0 data_api: git: url: https://github.com/flutter-news-app-full-source-code/data-api.git - ref: f90ab664c6333995757026a2c96e96d304da6246 + ref: v1.0.0 data_client: git: url: https://github.com/flutter-news-app-full-source-code/data-client.git - ref: 960a00882045ee252274d9d57bfca606c1fe5d64 + ref: v1.0.0 data_inmemory: git: url: https://github.com/flutter-news-app-full-source-code/data-inmemory.git - ref: aaf4e18b759ca8b33d060fd4aa8509238d5d3eaa + ref: v1.0.0 data_repository: git: url: https://github.com/flutter-news-app-full-source-code/data-repository.git - ref: f8f01f1191286efbba41fa2bb369fb16eb652ccf + ref: v1.0.0 data_table_2: ^2.6.0 device_preview: ^1.2.0 equatable: ^2.0.7 @@ -61,16 +62,16 @@ dependencies: http_client: git: url: https://github.com/flutter-news-app-full-source-code/http-client.git - ref: 57f6bcfc0f209ecc135fdcb688194045ec87e6e8 + ref: v1.0.1 intl: ^0.20.2 kv_storage_service: git: url: https://github.com/flutter-news-app-full-source-code/kv-storage-service.git - ref: 3bcba6b11fe0480e06e1bedf6eab4159a7c59c53 + ref: v1.0.0 kv_storage_shared_preferences: git: url: https://github.com/flutter-news-app-full-source-code/kv-storage-shared-preferences.git - ref: 4cefd202c5fc0c53856782fea7760baa1d0733f7 + ref: v1.0.0 logging: ^1.3.0 pinput: ^5.0.1 rxdart: ^0.28.0 @@ -79,7 +80,7 @@ dependencies: ui_kit: git: url: https://github.com/flutter-news-app-full-source-code/ui-kit.git - ref: d9c66362ec87fd7b5e3d3dee35828da6cbc1f66a + ref: v1.0.0 uuid: ^4.5.1 From 847722174d42e9cbfa22029f6df25b3757f6c7ac Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 10:07:19 +0100 Subject: [PATCH 02/27] docs: add CHANGELOG.md file - Create a new CHANGELOG.md file to document changes to the project --- CHANGELOG.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..e69de29b From b763f1b6931a2c6767229d089c3afad7576ed11d Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 10:52:47 +0100 Subject: [PATCH 03/27] chore(deps): remove empty line in pubspec.yaml --- pubspec.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index e289ba5d..85729a8a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -83,7 +83,6 @@ dependencies: ref: v1.0.0 uuid: ^4.5.1 - dev_dependencies: very_good_analysis: ^9.0.0 From 74b0aa7f816cb244763a53836736a21d348856fa Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 10:53:02 +0100 Subject: [PATCH 04/27] feat(ui): add user preset limits form - Implement UserPresetLimitsForm widget for configuring user preset limits - Add fields for guest, authenticated, and premium user saved filters limits - Integrate with RemoteConfig and provide callback for config changes - Use TextFormFieldWithDescription for input fields with labels and descriptions --- user_preset_limits_form.dart | 92 ++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 user_preset_limits_form.dart diff --git a/user_preset_limits_form.dart b/user_preset_limits_form.dart new file mode 100644 index 00000000..9bbbb4d6 --- /dev/null +++ b/user_preset_limits_form.dart @@ -0,0 +1,92 @@ +import 'package:core/core.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart'; +import 'package:ui_kit/ui_kit.dart'; + +/// {@template user_preset_limits_form} +/// A form for configuring user preset limits within the [RemoteConfig]. +/// +/// This form provides fields to set the maximum number of saved filters +/// for guest, authenticated, and premium users. +/// {@endtemplate} +class UserPresetLimitsForm extends StatelessWidget { + /// {@macro user_preset_limits_form} + const UserPresetLimitsForm({ + required this.remoteConfig, + required this.onConfigChanged, + super.key, + }); + + /// The current [RemoteConfig] object. + final RemoteConfig remoteConfig; + + /// Callback to notify parent of changes to the [RemoteConfig]. + final ValueChanged onConfigChanged; + + @override + Widget build(BuildContext context) { + final l10n = AppLocalizationsX(context).l10n; + final userPreferenceConfig = remoteConfig.userPreferenceConfig; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextFormFieldWithDescription( + label: l10n.guestUserRole, + description: l10n.savedFiltersLimitDescription, + initialValue: userPreferenceConfig.guestSavedFiltersLimit.toString(), + onChanged: (value) { + final newLimit = int.tryParse(value); + if (newLimit != null) { + onConfigChanged( + remoteConfig.copyWith( + userPreferenceConfig: userPreferenceConfig.copyWith( + guestSavedFiltersLimit: newLimit, + ), + ), + ); + } + }, + ), + const SizedBox(height: AppSpacing.lg), + TextFormFieldWithDescription( + label: l10n.standardUserRole, + description: l10n.savedFiltersLimitDescription, + initialValue: userPreferenceConfig.authenticatedSavedFiltersLimit + .toString(), + onChanged: (value) { + final newLimit = int.tryParse(value); + if (newLimit != null) { + onConfigChanged( + remoteConfig.copyWith( + userPreferenceConfig: userPreferenceConfig.copyWith( + authenticatedSavedFiltersLimit: newLimit, + ), + ), + ); + } + }, + ), + const SizedBox(height: AppSpacing.lg), + TextFormFieldWithDescription( + label: l10n.premiumUserRole, + description: l10n.savedFiltersLimitDescription, + initialValue: userPreferenceConfig.premiumSavedFiltersLimit + .toString(), + onChanged: (value) { + final newLimit = int.tryParse(value); + if (newLimit != null) { + onConfigChanged( + remoteConfig.copyWith( + userPreferenceConfig: userPreferenceConfig.copyWith( + premiumSavedFiltersLimit: newLimit, + ), + ), + ); + } + }, + ), + ], + ); + } +} From 6a67ad5eeb9ab348b16103b0809c19bf5f6c227a Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 10:56:05 +0100 Subject: [PATCH 05/27] refactor(app-configuration): move user preset limits form to lib directory - Rename user_preset_limits_form.dart to lib/app_configuration/widgets/user_preset_limits_form.dart - This change improves the project structure and follows the recommended directory layout for Angular Dart applications --- .../app_configuration/widgets/user_preset_limits_form.dart | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename user_preset_limits_form.dart => lib/app_configuration/widgets/user_preset_limits_form.dart (100%) diff --git a/user_preset_limits_form.dart b/lib/app_configuration/widgets/user_preset_limits_form.dart similarity index 100% rename from user_preset_limits_form.dart rename to lib/app_configuration/widgets/user_preset_limits_form.dart From e753d38a170682c9a4e84cd98f40ec2143eb2f04 Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 10:56:13 +0100 Subject: [PATCH 06/27] feat(app_configuration): add user presets configuration tab - Implement UserPresetsConfigurationTab widget - Add UserPresetLimitsForm to the tab for configuring user preset limits - Update dependencies to include core, flutter, ui_kit, and app_configuration packages --- .../tabs/user_presets_configuration_tab.dart | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 lib/app_configuration/view/tabs/user_presets_configuration_tab.dart diff --git a/lib/app_configuration/view/tabs/user_presets_configuration_tab.dart b/lib/app_configuration/view/tabs/user_presets_configuration_tab.dart new file mode 100644 index 00000000..edeab122 --- /dev/null +++ b/lib/app_configuration/view/tabs/user_presets_configuration_tab.dart @@ -0,0 +1,47 @@ +import 'package:core/core.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/user_preset_limits_form.dart'; +import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart'; +import 'package:ui_kit/ui_kit.dart'; + +/// {@template user_presets_configuration_tab} +/// A widget representing the "User Presets" tab in the App Configuration page. +/// +/// This tab allows configuration of user preset limits, such as saved filters. +/// {@endtemplate} +class UserPresetsConfigurationTab extends StatelessWidget { + /// {@macro user_presets_configuration_tab} + const UserPresetsConfigurationTab({ + required this.remoteConfig, + required this.onConfigChanged, + super.key, + }); + + /// The current [RemoteConfig] object. + final RemoteConfig remoteConfig; + + /// Callback to notify parent of changes to the [RemoteConfig]. + final ValueChanged onConfigChanged; + + @override + Widget build(BuildContext context) { + final l10n = AppLocalizationsX(context).l10n; + + return ListView( + padding: const EdgeInsets.all(AppSpacing.lg), + children: [ + ExpansionTile( + title: Text(l10n.userPresetLimitsTitle), + initiallyExpanded: true, + childrenPadding: const EdgeInsets.all(AppSpacing.lg), + children: [ + UserPresetLimitsForm( + remoteConfig: remoteConfig, + onConfigChanged: onConfigChanged, + ), + ], + ), + ], + ); + } +} From f5344d1b216fed1371a262a1480a6fbab9e768c6 Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 10:58:00 +0100 Subject: [PATCH 07/27] feat(app_configuration): add user presets configuration tab - Import UserPresetsConfigurationTab from new file - Increase TabController length from 3 to 4 - Add new tab for user presets in the tab bar - Implement UserPresetsConfigurationTab in the tab view --- .../view/app_configuration_page.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/app_configuration/view/app_configuration_page.dart b/lib/app_configuration/view/app_configuration_page.dart index 20281e82..898b124e 100644 --- a/lib/app_configuration/view/app_configuration_page.dart +++ b/lib/app_configuration/view/app_configuration_page.dart @@ -4,6 +4,7 @@ import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuratio import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/view/tabs/advertisements_configuration_tab.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/view/tabs/feed_configuration_tab.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/view/tabs/general_configuration_tab.dart'; +import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/view/tabs/user_presets_configuration_tab.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/shared/widgets/about_icon.dart'; import 'package:ui_kit/ui_kit.dart'; @@ -30,7 +31,7 @@ class _AppConfigurationPageState extends State @override void initState() { super.initState(); - _tabController = TabController(length: 3, vsync: this); + _tabController = TabController(length: 4, vsync: this); context.read().add(const AppConfigurationLoaded()); } @@ -67,6 +68,7 @@ class _AppConfigurationPageState extends State tabs: [ Tab(text: l10n.generalTab), Tab(text: l10n.feedTab), + Tab(text: l10n.userPresetsTab), Tab(text: l10n.advertisementsTab), ], ), @@ -148,6 +150,14 @@ class _AppConfigurationPageState extends State ); }, ), + UserPresetsConfigurationTab( + remoteConfig: remoteConfig, + onConfigChanged: (newConfig) { + context.read().add( + AppConfigurationFieldChanged(remoteConfig: newConfig), + ); + }, + ), AdvertisementsConfigurationTab( remoteConfig: remoteConfig, onConfigChanged: (newConfig) { From 5908712ade30f0ed07a91e01ffd7e01ff4ac22cc Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 11:05:00 +0100 Subject: [PATCH 08/27] chore: changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29b..2668ec18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ +##1.0.0 + +- **feat**: Added a "User Presets" tab to the App Configuration page to configure saved filter limits. +- **BREAKING** feat!: migrated from date based versioning to semantic versioning. \ No newline at end of file From 81f8091f1b8868876be0e29fabb6da7baac0319a Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 11:23:07 +0100 Subject: [PATCH 09/27] feat(l10n): add user presets and filter limit translations - Add Arabic and English translations for user presets tab and filter limit settings - Include new strings for user presets tab title, headlines filter preset limits, and saved filters limit description - Update existing translation files for both Arabic and English --- lib/l10n/app_localizations.dart | 18 ++++++++++++++++++ lib/l10n/app_localizations_ar.dart | 11 +++++++++++ lib/l10n/app_localizations_en.dart | 11 +++++++++++ lib/l10n/arb/app_ar.arb | 12 ++++++++++++ lib/l10n/arb/app_en.arb | 12 ++++++++++++ 5 files changed, 64 insertions(+) diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 61fadf6c..290a9096 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -290,12 +290,24 @@ abstract class AppLocalizations { /// **'General'** String get generalTab; + /// Tab title for User Presets settings + /// + /// In en, this message translates to: + /// **'User Presets'** + String get userPresetsTab; + /// Title for the User Content & Feed Limits section /// /// In en, this message translates to: /// **'User Content & Feed Limits'** String get userContentLimitsTitle; + /// Title for the Headlines Filter Preset Limits section + /// + /// In en, this message translates to: + /// **'Headlines Filter Preset Limits'** + String get headlinesFilterPresetLimitsTitle; + /// Description for the User Content & Feed Limits section /// /// In en, this message translates to: @@ -1580,6 +1592,12 @@ abstract class AppLocalizations { /// **'Maximum number of headlines this user role can save.'** String get savedHeadlinesLimitDescription; + /// Description for Saved Filters Limit + /// + /// In en, this message translates to: + /// **'Maximum number of saved filters this user role can create.'** + String get savedFiltersLimitDescription; + /// Label for Ad Frequency /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_ar.dart b/lib/l10n/app_localizations_ar.dart index 369d3342..07d67bbe 100644 --- a/lib/l10n/app_localizations_ar.dart +++ b/lib/l10n/app_localizations_ar.dart @@ -117,9 +117,16 @@ class AppLocalizationsAr extends AppLocalizations { @override String get generalTab => 'عام'; + @override + String get userPresetsTab => 'الإعدادات المسبقة'; + @override String get userContentLimitsTitle => 'حدود المحتوى والموجز للمستخدم'; + @override + String get headlinesFilterPresetLimitsTitle => + 'حدود الإعدادات المسبقة لمرشحات الأخبار'; + @override String get userContentLimitsDescription => 'تعيين حدود للعناصر المتابعة والعناوين المحفوظة لكل فئة من فئات المستخدمين.'; @@ -832,6 +839,10 @@ class AppLocalizationsAr extends AppLocalizations { String get savedHeadlinesLimitDescription => 'الحد الأقصى لعدد العناوين الرئيسية التي يمكن لهذا الدور المستخدم حفظها.'; + @override + String get savedFiltersLimitDescription => + 'الحد الأقصى لعدد المرشحات المحفوظة التي يمكن لهذا الدور إنشاؤها.'; + @override String get adFrequencyLabel => 'تكرار الإعلان'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 18ee64ec..9feda86e 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -116,9 +116,16 @@ class AppLocalizationsEn extends AppLocalizations { @override String get generalTab => 'General'; + @override + String get userPresetsTab => 'User Presets'; + @override String get userContentLimitsTitle => 'User Content & Feed Limits'; + @override + String get headlinesFilterPresetLimitsTitle => + 'Headlines Filter Preset Limits'; + @override String get userContentLimitsDescription => 'Set limits on followed items and saved headlines for each user tier.'; @@ -830,6 +837,10 @@ class AppLocalizationsEn extends AppLocalizations { String get savedHeadlinesLimitDescription => 'Maximum number of headlines this user role can save.'; + @override + String get savedFiltersLimitDescription => + 'Maximum number of saved filters this user role can create.'; + @override String get adFrequencyLabel => 'Ad Frequency'; diff --git a/lib/l10n/arb/app_ar.arb b/lib/l10n/arb/app_ar.arb index 3db46135..90953ebc 100644 --- a/lib/l10n/arb/app_ar.arb +++ b/lib/l10n/arb/app_ar.arb @@ -143,10 +143,18 @@ "@generalTab": { "description": "عنوان تبويب الإعدادات العامة" }, + "userPresetsTab": "الإعدادات المسبقة", + "@userPresetsTab": { + "description": "عنوان تبويب إعدادات الإعدادات المسبقة للمستخدم" + }, "userContentLimitsTitle": "حدود المحتوى والموجز للمستخدم", "@userContentLimitsTitle": { "description": "عنوان قسم حدود المحتوى والموجز للمستخدم" }, + "headlinesFilterPresetLimitsTitle": "حدود الإعدادات المسبقة لمرشحات الأخبار", + "@headlinesFilterPresetLimitsTitle": { + "description": "عنوان قسم حدود الإعدادات المسبقة لمرشحات الأخبار" + }, "userContentLimitsDescription": "تعيين حدود للعناصر المتابعة والعناوين المحفوظة لكل فئة من فئات المستخدمين.", "@userContentLimitsDescription": { "description": "وصف قسم حدود المحتوى والموجز للمستخدم" @@ -1030,6 +1038,10 @@ "@savedHeadlinesLimitDescription": { "description": "وصف حد العناوين المحفوظة" }, + "savedFiltersLimitDescription": "الحد الأقصى لعدد المرشحات المحفوظة التي يمكن لهذا الدور إنشاؤها.", + "@savedFiltersLimitDescription": { + "description": "وصف لحد المرشحات المحفوظة" + }, "adFrequencyLabel": "تكرار الإعلان", "@adFrequencyLabel": { "description": "تسمية تكرار الإعلان" diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 8b7d9ff8..04dcb37e 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -143,10 +143,18 @@ "@generalTab": { "description": "Tab title for General settings" }, + "userPresetsTab": "User Presets", + "@userPresetsTab": { + "description": "Tab title for User Presets settings" + }, "userContentLimitsTitle": "User Content & Feed Limits", "@userContentLimitsTitle": { "description": "Title for the User Content & Feed Limits section" }, + "headlinesFilterPresetLimitsTitle": "Headlines Filter Preset Limits", + "@headlinesFilterPresetLimitsTitle": { + "description": "Title for the Headlines Filter Preset Limits section" + }, "userContentLimitsDescription": "Set limits on followed items and saved headlines for each user tier.", "@userContentLimitsDescription": { "description": "Description for the User Content & Feed Limits section" @@ -1030,6 +1038,10 @@ "@savedHeadlinesLimitDescription": { "description": "Description for Saved Headlines Limit" }, + "savedFiltersLimitDescription": "Maximum number of saved filters this user role can create.", + "@savedFiltersLimitDescription": { + "description": "Description for Saved Filters Limit" + }, "adFrequencyLabel": "Ad Frequency", "@adFrequencyLabel": { "description": "Label for Ad Frequency" From ff48e8d406d611344ee09024b5c1426d10d400b6 Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 11:25:05 +0100 Subject: [PATCH 10/27] fix(app_configuration): update preset tab label - Change ExpansionTile title from 'userPresetLimitsTitle' to 'userPresetsTab' --- .../view/tabs/user_presets_configuration_tab.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/app_configuration/view/tabs/user_presets_configuration_tab.dart b/lib/app_configuration/view/tabs/user_presets_configuration_tab.dart index edeab122..b37b8ab0 100644 --- a/lib/app_configuration/view/tabs/user_presets_configuration_tab.dart +++ b/lib/app_configuration/view/tabs/user_presets_configuration_tab.dart @@ -31,7 +31,7 @@ class UserPresetsConfigurationTab extends StatelessWidget { padding: const EdgeInsets.all(AppSpacing.lg), children: [ ExpansionTile( - title: Text(l10n.userPresetLimitsTitle), + title: Text(l10n.userPresetsTab), initiallyExpanded: true, childrenPadding: const EdgeInsets.all(AppSpacing.lg), children: [ From 2c90f0eea884ba645a42719331b161720a16571a Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 11:28:05 +0100 Subject: [PATCH 11/27] fix(l10n): correct arb key for saved headlines filters limit description - Update AR and EN ARB files to use the correct key for the description of saved headlines filters limit - Change 'savedFiltersLimitDescription' to 'savedHeadlinesFiltersLimitDescription' to match the context of headlines filters --- lib/l10n/app_localizations.dart | 4 ++-- lib/l10n/app_localizations_ar.dart | 4 ++-- lib/l10n/app_localizations_en.dart | 4 ++-- lib/l10n/arb/app_ar.arb | 4 ++-- lib/l10n/arb/app_en.arb | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 290a9096..e3e0ac03 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -1595,8 +1595,8 @@ abstract class AppLocalizations { /// Description for Saved Filters Limit /// /// In en, this message translates to: - /// **'Maximum number of saved filters this user role can create.'** - String get savedFiltersLimitDescription; + /// **'Maximum number of saved headlines filters this user role can create.'** + String get savedHeadlinesFiltersLimitDescription; /// Label for Ad Frequency /// diff --git a/lib/l10n/app_localizations_ar.dart b/lib/l10n/app_localizations_ar.dart index 07d67bbe..59cc7acd 100644 --- a/lib/l10n/app_localizations_ar.dart +++ b/lib/l10n/app_localizations_ar.dart @@ -840,8 +840,8 @@ class AppLocalizationsAr extends AppLocalizations { 'الحد الأقصى لعدد العناوين الرئيسية التي يمكن لهذا الدور المستخدم حفظها.'; @override - String get savedFiltersLimitDescription => - 'الحد الأقصى لعدد المرشحات المحفوظة التي يمكن لهذا الدور إنشاؤها.'; + String get savedHeadlinesFiltersLimitDescription => + 'الحد الأقصى لعدد مرشحات العناوين المحفوظة التي يمكن لهذا الدور إنشاؤها.'; @override String get adFrequencyLabel => 'تكرار الإعلان'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 9feda86e..0a73f179 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -838,8 +838,8 @@ class AppLocalizationsEn extends AppLocalizations { 'Maximum number of headlines this user role can save.'; @override - String get savedFiltersLimitDescription => - 'Maximum number of saved filters this user role can create.'; + String get savedHeadlinesFiltersLimitDescription => + 'Maximum number of saved headlines filters this user role can create.'; @override String get adFrequencyLabel => 'Ad Frequency'; diff --git a/lib/l10n/arb/app_ar.arb b/lib/l10n/arb/app_ar.arb index 90953ebc..fd30e0b5 100644 --- a/lib/l10n/arb/app_ar.arb +++ b/lib/l10n/arb/app_ar.arb @@ -1038,8 +1038,8 @@ "@savedHeadlinesLimitDescription": { "description": "وصف حد العناوين المحفوظة" }, - "savedFiltersLimitDescription": "الحد الأقصى لعدد المرشحات المحفوظة التي يمكن لهذا الدور إنشاؤها.", - "@savedFiltersLimitDescription": { + "savedHeadlinesFiltersLimitDescription": "الحد الأقصى لعدد مرشحات العناوين المحفوظة التي يمكن لهذا الدور إنشاؤها.", + "@savedHeadlinesFiltersLimitDescription": { "description": "وصف لحد المرشحات المحفوظة" }, "adFrequencyLabel": "تكرار الإعلان", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 04dcb37e..730a8675 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -1038,8 +1038,8 @@ "@savedHeadlinesLimitDescription": { "description": "Description for Saved Headlines Limit" }, - "savedFiltersLimitDescription": "Maximum number of saved filters this user role can create.", - "@savedFiltersLimitDescription": { + "savedHeadlinesFiltersLimitDescription": "Maximum number of saved headlines filters this user role can create.", + "@savedHeadlinesFiltersLimitDescription": { "description": "Description for Saved Filters Limit" }, "adFrequencyLabel": "Ad Frequency", From 784b3c311151f5967e68a91c3a73d16011243979 Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 11:28:16 +0100 Subject: [PATCH 12/27] refactor(app_configuration): convert UserPresetLimitsForm to StatefulWidget - Implement TextEditingController for each input field - Add logic to handle updated widget configuration - Replace TextFormFieldWithDescription with AppConfigIntField - Update import statements and remove unused imports --- .../widgets/user_preset_limits_form.dart | 167 +++++++++++++----- 1 file changed, 121 insertions(+), 46 deletions(-) diff --git a/lib/app_configuration/widgets/user_preset_limits_form.dart b/lib/app_configuration/widgets/user_preset_limits_form.dart index 9bbbb4d6..c46ee7fb 100644 --- a/lib/app_configuration/widgets/user_preset_limits_form.dart +++ b/lib/app_configuration/widgets/user_preset_limits_form.dart @@ -1,7 +1,7 @@ import 'package:core/core.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/app_config_form_fields.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart'; -import 'package:ui_kit/ui_kit.dart'; /// {@template user_preset_limits_form} /// A form for configuring user preset limits within the [RemoteConfig]. @@ -9,7 +9,7 @@ import 'package:ui_kit/ui_kit.dart'; /// This form provides fields to set the maximum number of saved filters /// for guest, authenticated, and premium users. /// {@endtemplate} -class UserPresetLimitsForm extends StatelessWidget { +class UserPresetLimitsForm extends StatefulWidget { /// {@macro user_preset_limits_form} const UserPresetLimitsForm({ required this.remoteConfig, @@ -23,68 +23,143 @@ class UserPresetLimitsForm extends StatelessWidget { /// Callback to notify parent of changes to the [RemoteConfig]. final ValueChanged onConfigChanged; + @override + State createState() => _UserPresetLimitsFormState(); +} + +class _UserPresetLimitsFormState extends State { + late final TextEditingController _guestController; + late final TextEditingController _standardController; + late final TextEditingController _premiumController; + + @override + void initState() { + super.initState(); + _initializeControllers(); + } + + @override + void didUpdateWidget(covariant UserPresetLimitsForm oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.remoteConfig.userPreferenceConfig != + oldWidget.remoteConfig.userPreferenceConfig) { + _updateControllers(); + } + } + + void _initializeControllers() { + final config = widget.remoteConfig.userPreferenceConfig; + _guestController = + TextEditingController( + text: config.guestSavedFiltersLimit.toString(), + ) + ..selection = TextSelection.collapsed( + offset: config.guestSavedFiltersLimit.toString().length, + ); + _standardController = + TextEditingController( + text: config.authenticatedSavedFiltersLimit.toString(), + ) + ..selection = TextSelection.collapsed( + offset: config.authenticatedSavedFiltersLimit.toString().length, + ); + _premiumController = + TextEditingController( + text: config.premiumSavedFiltersLimit.toString(), + ) + ..selection = TextSelection.collapsed( + offset: config.premiumSavedFiltersLimit.toString().length, + ); + } + + void _updateControllers() { + final config = widget.remoteConfig.userPreferenceConfig; + + final newGuestValue = config.guestSavedFiltersLimit.toString(); + if (_guestController.text != newGuestValue) { + _guestController.text = newGuestValue; + _guestController.selection = TextSelection.collapsed( + offset: newGuestValue.length, + ); + } + + final newStandardValue = config.authenticatedSavedFiltersLimit.toString(); + if (_standardController.text != newStandardValue) { + _standardController.text = newStandardValue; + _standardController.selection = TextSelection.collapsed( + offset: newStandardValue.length, + ); + } + + final newPremiumValue = config.premiumSavedFiltersLimit.toString(); + if (_premiumController.text != newPremiumValue) { + _premiumController.text = newPremiumValue; + _premiumController.selection = TextSelection.collapsed( + offset: newPremiumValue.length, + ); + } + } + + @override + void dispose() { + _guestController.dispose(); + _standardController.dispose(); + _premiumController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final l10n = AppLocalizationsX(context).l10n; - final userPreferenceConfig = remoteConfig.userPreferenceConfig; + final userPreferenceConfig = widget.remoteConfig.userPreferenceConfig; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - TextFormFieldWithDescription( + AppConfigIntField( label: l10n.guestUserRole, - description: l10n.savedFiltersLimitDescription, - initialValue: userPreferenceConfig.guestSavedFiltersLimit.toString(), - onChanged: (value) { - final newLimit = int.tryParse(value); - if (newLimit != null) { - onConfigChanged( - remoteConfig.copyWith( - userPreferenceConfig: userPreferenceConfig.copyWith( - guestSavedFiltersLimit: newLimit, - ), + description: l10n.savedHeadlinesFiltersLimitDescription, + value: userPreferenceConfig.guestSavedFiltersLimit, + onChanged: (newLimit) { + widget.onConfigChanged( + widget.remoteConfig.copyWith( + userPreferenceConfig: userPreferenceConfig.copyWith( + guestSavedFiltersLimit: newLimit, ), - ); - } + ), + ); }, + controller: _guestController, ), - const SizedBox(height: AppSpacing.lg), - TextFormFieldWithDescription( + AppConfigIntField( label: l10n.standardUserRole, - description: l10n.savedFiltersLimitDescription, - initialValue: userPreferenceConfig.authenticatedSavedFiltersLimit - .toString(), - onChanged: (value) { - final newLimit = int.tryParse(value); - if (newLimit != null) { - onConfigChanged( - remoteConfig.copyWith( - userPreferenceConfig: userPreferenceConfig.copyWith( - authenticatedSavedFiltersLimit: newLimit, - ), + description: l10n.savedHeadlinesFiltersLimitDescription, + value: userPreferenceConfig.authenticatedSavedFiltersLimit, + onChanged: (newLimit) { + widget.onConfigChanged( + widget.remoteConfig.copyWith( + userPreferenceConfig: userPreferenceConfig.copyWith( + authenticatedSavedFiltersLimit: newLimit, ), - ); - } + ), + ); }, + controller: _standardController, ), - const SizedBox(height: AppSpacing.lg), - TextFormFieldWithDescription( + AppConfigIntField( label: l10n.premiumUserRole, - description: l10n.savedFiltersLimitDescription, - initialValue: userPreferenceConfig.premiumSavedFiltersLimit - .toString(), - onChanged: (value) { - final newLimit = int.tryParse(value); - if (newLimit != null) { - onConfigChanged( - remoteConfig.copyWith( - userPreferenceConfig: userPreferenceConfig.copyWith( - premiumSavedFiltersLimit: newLimit, - ), + description: l10n.savedHeadlinesFiltersLimitDescription, + value: userPreferenceConfig.premiumSavedFiltersLimit, + onChanged: (newLimit) { + widget.onConfigChanged( + widget.remoteConfig.copyWith( + userPreferenceConfig: userPreferenceConfig.copyWith( + premiumSavedFiltersLimit: newLimit, ), - ); - } + ), + ); }, + controller: _premiumController, ), ], ); From 880737dfdbbe696bb0be1bc5f18cfb71b350d4f4 Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 11:41:57 +0100 Subject: [PATCH 13/27] feat(app_configuration): add user preset limits form and update expansion tiles - Add UserPresetLimitsForm widget to the feed configuration tab - Update existing expansion tiles indexing due to the new addition - Implement new expansion tile for user preset limits with appropriate localization and styling - Adjust spacing between expansion tiles --- .../view/tabs/feed_configuration_tab.dart | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/app_configuration/view/tabs/feed_configuration_tab.dart b/lib/app_configuration/view/tabs/feed_configuration_tab.dart index 83099120..fcfbab6b 100644 --- a/lib/app_configuration/view/tabs/feed_configuration_tab.dart +++ b/lib/app_configuration/view/tabs/feed_configuration_tab.dart @@ -1,6 +1,7 @@ import 'package:core/core.dart'; import 'package:flutter/material.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/feed_decorator_form.dart'; +import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/user_preset_limits_form.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/user_preference_limits_form.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/shared/extensions/feed_decorator_type_l10n.dart'; @@ -76,11 +77,39 @@ class _FeedConfigurationTabState extends State { }, ), const SizedBox(height: AppSpacing.lg), - // New Top-level ExpansionTile for Feed Decorators + // New Top-level ExpansionTile for User Preset Limits ValueListenableBuilder( valueListenable: _expandedTileIndex, builder: (context, expandedIndex, child) { const tileIndex = 1; + return ExpansionTile( + key: ValueKey('headlinesFilterPresetLimitsTile_$expandedIndex'), + title: Text(l10n.headlinesFilterPresetLimitsTitle), + childrenPadding: const EdgeInsetsDirectional.only( + start: AppSpacing.lg, + top: AppSpacing.md, + bottom: AppSpacing.md, + ), + expandedCrossAxisAlignment: CrossAxisAlignment.start, + onExpansionChanged: (isExpanded) { + _expandedTileIndex.value = isExpanded ? tileIndex : null; + }, + initiallyExpanded: expandedIndex == tileIndex, + children: [ + UserPresetLimitsForm( + remoteConfig: widget.remoteConfig, + onConfigChanged: widget.onConfigChanged, + ), + ], + ); + }, + ), + const SizedBox(height: AppSpacing.lg), + // New Top-level ExpansionTile for Feed Decorators + ValueListenableBuilder( + valueListenable: _expandedTileIndex, + builder: (context, expandedIndex, child) { + const tileIndex = 2; return ExpansionTile( key: ValueKey('feedDecoratorsTile_$expandedIndex'), title: Text(l10n.feedDecoratorsTitle), From f6e15933a86eb35c78cd3432734cab6588de9bcb Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 11:45:48 +0100 Subject: [PATCH 14/27] fix(l10n): remove duplicate strings and correct order - Remove duplicate "userPresetsTab" and "savedHeadlinesFiltersLimitDescription" entries - Correct the order of "savedHeadlinesFiltersLimitDescription" and "adFrequencyDescription" entries --- lib/l10n/arb/app_ar.arb | 12 ++++-------- lib/l10n/arb/app_en.arb | 4 ---- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/lib/l10n/arb/app_ar.arb b/lib/l10n/arb/app_ar.arb index fd30e0b5..b1159504 100644 --- a/lib/l10n/arb/app_ar.arb +++ b/lib/l10n/arb/app_ar.arb @@ -143,10 +143,6 @@ "@generalTab": { "description": "عنوان تبويب الإعدادات العامة" }, - "userPresetsTab": "الإعدادات المسبقة", - "@userPresetsTab": { - "description": "عنوان تبويب إعدادات الإعدادات المسبقة للمستخدم" - }, "userContentLimitsTitle": "حدود المحتوى والموجز للمستخدم", "@userContentLimitsTitle": { "description": "عنوان قسم حدود المحتوى والموجز للمستخدم" @@ -1038,10 +1034,6 @@ "@savedHeadlinesLimitDescription": { "description": "وصف حد العناوين المحفوظة" }, - "savedHeadlinesFiltersLimitDescription": "الحد الأقصى لعدد مرشحات العناوين المحفوظة التي يمكن لهذا الدور إنشاؤها.", - "@savedHeadlinesFiltersLimitDescription": { - "description": "وصف لحد المرشحات المحفوظة" - }, "adFrequencyLabel": "تكرار الإعلان", "@adFrequencyLabel": { "description": "تسمية تكرار الإعلان" @@ -1050,6 +1042,10 @@ "@adFrequencyDescription": { "description": "وصف تكرار الإعلان" }, + "savedHeadlinesFiltersLimitDescription": "الحد الأقصى لعدد مرشحات العناوين المحفوظة التي يمكن لهذا الدور إنشاؤها.", + "@savedHeadlinesFiltersLimitDescription": { + "description": "وصف لحد المرشحات المحفوظة" + }, "adPlacementIntervalLabel": "فترة وضع الإعلان", "@adPlacementIntervalLabel": { "description": "تسمية فترة وضع الإعلان" diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 730a8675..cad760d7 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -143,10 +143,6 @@ "@generalTab": { "description": "Tab title for General settings" }, - "userPresetsTab": "User Presets", - "@userPresetsTab": { - "description": "Tab title for User Presets settings" - }, "userContentLimitsTitle": "User Content & Feed Limits", "@userContentLimitsTitle": { "description": "Title for the User Content & Feed Limits section" From 3b9c25b14fc71b06b9a4f9d4b9f616c5873a734c Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 11:46:02 +0100 Subject: [PATCH 15/27] chore: misc --- lib/l10n/app_localizations.dart | 6 ------ lib/l10n/app_localizations_ar.dart | 5 +---- lib/l10n/app_localizations_en.dart | 3 --- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index e3e0ac03..4f137df8 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -290,12 +290,6 @@ abstract class AppLocalizations { /// **'General'** String get generalTab; - /// Tab title for User Presets settings - /// - /// In en, this message translates to: - /// **'User Presets'** - String get userPresetsTab; - /// Title for the User Content & Feed Limits section /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_ar.dart b/lib/l10n/app_localizations_ar.dart index 59cc7acd..d858952e 100644 --- a/lib/l10n/app_localizations_ar.dart +++ b/lib/l10n/app_localizations_ar.dart @@ -117,9 +117,6 @@ class AppLocalizationsAr extends AppLocalizations { @override String get generalTab => 'عام'; - @override - String get userPresetsTab => 'الإعدادات المسبقة'; - @override String get userContentLimitsTitle => 'حدود المحتوى والموجز للمستخدم'; @@ -841,7 +838,7 @@ class AppLocalizationsAr extends AppLocalizations { @override String get savedHeadlinesFiltersLimitDescription => - 'الحد الأقصى لعدد مرشحات العناوين المحفوظة التي يمكن لهذا الدور إنشاؤها.'; + 'الحد الأقصى لعدد مرشحات العناوين المحفوظة التي يمكن لهذا الدور إنشاؤها.'; @override String get adFrequencyLabel => 'تكرار الإعلان'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 0a73f179..0db76b45 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -116,9 +116,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get generalTab => 'General'; - @override - String get userPresetsTab => 'User Presets'; - @override String get userContentLimitsTitle => 'User Content & Feed Limits'; From e084fbd907c813ecfbe993593ca1b2471c895c55 Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 11:47:59 +0100 Subject: [PATCH 16/27] refactor(app_configuration): remove user presets configuration tab - Remove UserPresetsConfigurationTab widget - Remove user presets tab from AppConfigurationPage - Update tab controller length to 3 --- .../view/app_configuration_page.dart | 14 +----- .../tabs/user_presets_configuration_tab.dart | 47 ------------------- 2 files changed, 2 insertions(+), 59 deletions(-) delete mode 100644 lib/app_configuration/view/tabs/user_presets_configuration_tab.dart diff --git a/lib/app_configuration/view/app_configuration_page.dart b/lib/app_configuration/view/app_configuration_page.dart index 898b124e..158bd392 100644 --- a/lib/app_configuration/view/app_configuration_page.dart +++ b/lib/app_configuration/view/app_configuration_page.dart @@ -2,9 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/bloc/app_configuration_bloc.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/view/tabs/advertisements_configuration_tab.dart'; -import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/view/tabs/feed_configuration_tab.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/view/tabs/general_configuration_tab.dart'; -import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/view/tabs/user_presets_configuration_tab.dart'; +import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/view/tabs/feed_configuration_tab.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/shared/widgets/about_icon.dart'; import 'package:ui_kit/ui_kit.dart'; @@ -31,7 +30,7 @@ class _AppConfigurationPageState extends State @override void initState() { super.initState(); - _tabController = TabController(length: 4, vsync: this); + _tabController = TabController(length: 3, vsync: this); context.read().add(const AppConfigurationLoaded()); } @@ -68,7 +67,6 @@ class _AppConfigurationPageState extends State tabs: [ Tab(text: l10n.generalTab), Tab(text: l10n.feedTab), - Tab(text: l10n.userPresetsTab), Tab(text: l10n.advertisementsTab), ], ), @@ -150,14 +148,6 @@ class _AppConfigurationPageState extends State ); }, ), - UserPresetsConfigurationTab( - remoteConfig: remoteConfig, - onConfigChanged: (newConfig) { - context.read().add( - AppConfigurationFieldChanged(remoteConfig: newConfig), - ); - }, - ), AdvertisementsConfigurationTab( remoteConfig: remoteConfig, onConfigChanged: (newConfig) { diff --git a/lib/app_configuration/view/tabs/user_presets_configuration_tab.dart b/lib/app_configuration/view/tabs/user_presets_configuration_tab.dart deleted file mode 100644 index b37b8ab0..00000000 --- a/lib/app_configuration/view/tabs/user_presets_configuration_tab.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:core/core.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/user_preset_limits_form.dart'; -import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart'; -import 'package:ui_kit/ui_kit.dart'; - -/// {@template user_presets_configuration_tab} -/// A widget representing the "User Presets" tab in the App Configuration page. -/// -/// This tab allows configuration of user preset limits, such as saved filters. -/// {@endtemplate} -class UserPresetsConfigurationTab extends StatelessWidget { - /// {@macro user_presets_configuration_tab} - const UserPresetsConfigurationTab({ - required this.remoteConfig, - required this.onConfigChanged, - super.key, - }); - - /// The current [RemoteConfig] object. - final RemoteConfig remoteConfig; - - /// Callback to notify parent of changes to the [RemoteConfig]. - final ValueChanged onConfigChanged; - - @override - Widget build(BuildContext context) { - final l10n = AppLocalizationsX(context).l10n; - - return ListView( - padding: const EdgeInsets.all(AppSpacing.lg), - children: [ - ExpansionTile( - title: Text(l10n.userPresetsTab), - initiallyExpanded: true, - childrenPadding: const EdgeInsets.all(AppSpacing.lg), - children: [ - UserPresetLimitsForm( - remoteConfig: remoteConfig, - onConfigChanged: onConfigChanged, - ), - ], - ), - ], - ); - } -} From ad58d649c4bfc92badc6791f2024658567625675 Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 12:00:26 +0100 Subject: [PATCH 17/27] refactor(app_configuration): rename widget and update localization keys - Rename UserPresetLimitsForm to SavedHeadlinesFiltersLimitForm - Update widget documentation and comments - Change localization keys to be more specific to saved filters limits --- ...> saved_headlines_filters_limit_form.dart} | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) rename lib/app_configuration/widgets/{user_preset_limits_form.dart => saved_headlines_filters_limit_form.dart} (78%) diff --git a/lib/app_configuration/widgets/user_preset_limits_form.dart b/lib/app_configuration/widgets/saved_headlines_filters_limit_form.dart similarity index 78% rename from lib/app_configuration/widgets/user_preset_limits_form.dart rename to lib/app_configuration/widgets/saved_headlines_filters_limit_form.dart index c46ee7fb..6efbd66b 100644 --- a/lib/app_configuration/widgets/user_preset_limits_form.dart +++ b/lib/app_configuration/widgets/saved_headlines_filters_limit_form.dart @@ -3,15 +3,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/app_config_form_fields.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart'; -/// {@template user_preset_limits_form} -/// A form for configuring user preset limits within the [RemoteConfig]. +/// {@template saved_headlines_filters_limit_form} +/// A form for configuring saved headlines filter limits within the +/// [RemoteConfig]. /// /// This form provides fields to set the maximum number of saved filters /// for guest, authenticated, and premium users. /// {@endtemplate} -class UserPresetLimitsForm extends StatefulWidget { - /// {@macro user_preset_limits_form} - const UserPresetLimitsForm({ +class SavedHeadlinesFiltersLimitForm extends StatefulWidget { + /// {@macro saved_headlines_filters_limit_form} + const SavedHeadlinesFiltersLimitForm({ required this.remoteConfig, required this.onConfigChanged, super.key, @@ -24,10 +25,12 @@ class UserPresetLimitsForm extends StatefulWidget { final ValueChanged onConfigChanged; @override - State createState() => _UserPresetLimitsFormState(); + State createState() => + _SavedHeadlinesFiltersLimitFormState(); } -class _UserPresetLimitsFormState extends State { +class _SavedHeadlinesFiltersLimitFormState + extends State { late final TextEditingController _guestController; late final TextEditingController _standardController; late final TextEditingController _premiumController; @@ -39,7 +42,7 @@ class _UserPresetLimitsFormState extends State { } @override - void didUpdateWidget(covariant UserPresetLimitsForm oldWidget) { + void didUpdateWidget(covariant SavedHeadlinesFiltersLimitForm oldWidget) { super.didUpdateWidget(oldWidget); if (widget.remoteConfig.userPreferenceConfig != oldWidget.remoteConfig.userPreferenceConfig) { @@ -51,23 +54,20 @@ class _UserPresetLimitsFormState extends State { final config = widget.remoteConfig.userPreferenceConfig; _guestController = TextEditingController( - text: config.guestSavedFiltersLimit.toString(), - ) - ..selection = TextSelection.collapsed( + text: config.guestSavedFiltersLimit.toString(), + )..selection = TextSelection.collapsed( offset: config.guestSavedFiltersLimit.toString().length, ); _standardController = TextEditingController( - text: config.authenticatedSavedFiltersLimit.toString(), - ) - ..selection = TextSelection.collapsed( + text: config.authenticatedSavedFiltersLimit.toString(), + )..selection = TextSelection.collapsed( offset: config.authenticatedSavedFiltersLimit.toString().length, ); _premiumController = TextEditingController( - text: config.premiumSavedFiltersLimit.toString(), - ) - ..selection = TextSelection.collapsed( + text: config.premiumSavedFiltersLimit.toString(), + )..selection = TextSelection.collapsed( offset: config.premiumSavedFiltersLimit.toString().length, ); } @@ -117,8 +117,8 @@ class _UserPresetLimitsFormState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ AppConfigIntField( - label: l10n.guestUserRole, - description: l10n.savedHeadlinesFiltersLimitDescription, + label: l10n.guestSavedFiltersLimitLabel, + description: l10n.savedFiltersLimitDescription, value: userPreferenceConfig.guestSavedFiltersLimit, onChanged: (newLimit) { widget.onConfigChanged( @@ -132,8 +132,8 @@ class _UserPresetLimitsFormState extends State { controller: _guestController, ), AppConfigIntField( - label: l10n.standardUserRole, - description: l10n.savedHeadlinesFiltersLimitDescription, + label: l10n.standardUserSavedFiltersLimitLabel, + description: l10n.savedFiltersLimitDescription, value: userPreferenceConfig.authenticatedSavedFiltersLimit, onChanged: (newLimit) { widget.onConfigChanged( @@ -147,8 +147,8 @@ class _UserPresetLimitsFormState extends State { controller: _standardController, ), AppConfigIntField( - label: l10n.premiumUserRole, - description: l10n.savedHeadlinesFiltersLimitDescription, + label: l10n.premiumUserSavedFiltersLimitLabel, + description: l10n.savedFiltersLimitDescription, value: userPreferenceConfig.premiumSavedFiltersLimit, onChanged: (newLimit) { widget.onConfigChanged( From a745f84d1519a638102459e1ffcacd4d993369be Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 12:00:40 +0100 Subject: [PATCH 18/27] refactor(app_configuration): update saved headlines filter limits UI and naming - Rename 'UserPresetLimitsForm' to 'SavedHeadlinesFiltersLimitForm' - Update localization keys to use 'savedHeadlinesFilterLimits' instead of 'headlinesFilterPresetLimits' - Update ExpansionTile key and title to reflect the new naming --- .../view/tabs/feed_configuration_tab.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/app_configuration/view/tabs/feed_configuration_tab.dart b/lib/app_configuration/view/tabs/feed_configuration_tab.dart index fcfbab6b..44932989 100644 --- a/lib/app_configuration/view/tabs/feed_configuration_tab.dart +++ b/lib/app_configuration/view/tabs/feed_configuration_tab.dart @@ -1,7 +1,7 @@ import 'package:core/core.dart'; import 'package:flutter/material.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/feed_decorator_form.dart'; -import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/user_preset_limits_form.dart'; +import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/saved_headlines_filters_limit_form.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/user_preference_limits_form.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/shared/extensions/feed_decorator_type_l10n.dart'; @@ -83,8 +83,8 @@ class _FeedConfigurationTabState extends State { builder: (context, expandedIndex, child) { const tileIndex = 1; return ExpansionTile( - key: ValueKey('headlinesFilterPresetLimitsTile_$expandedIndex'), - title: Text(l10n.headlinesFilterPresetLimitsTitle), + key: ValueKey('savedHeadlinesFilterLimitsTile_$expandedIndex'), + title: Text(l10n.savedHeadlinesFilterLimitsTitle), childrenPadding: const EdgeInsetsDirectional.only( start: AppSpacing.lg, top: AppSpacing.md, @@ -96,7 +96,7 @@ class _FeedConfigurationTabState extends State { }, initiallyExpanded: expandedIndex == tileIndex, children: [ - UserPresetLimitsForm( + SavedHeadlinesFiltersLimitForm( remoteConfig: widget.remoteConfig, onConfigChanged: widget.onConfigChanged, ), From 60bb0314dc2139035c97c793a9ba7522b023a5a0 Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 12:01:42 +0100 Subject: [PATCH 19/27] refactor(l10n): remove unused arb entries and add new ones - Remove headlinesFilterPresetLimitsTitle and related entries - Remove savedHeadlinesFiltersLimitDescription - Add savedHeadlinesFilterLimitsTitle and related entries - Add guestSavedFiltersLimitLabel, standardUserSavedFiltersLimitLabel, and premiumUserSavedFiltersLimitLabel - Add savedFiltersLimitDescription --- lib/l10n/app_localizations.dart | 42 +++++++++++++++++++++--------- lib/l10n/app_localizations_ar.dart | 26 ++++++++++++------ lib/l10n/app_localizations_en.dart | 26 ++++++++++++------ lib/l10n/arb/app_ar.arb | 4 --- lib/l10n/arb/app_en.arb | 28 ++++++++++++++------ 5 files changed, 86 insertions(+), 40 deletions(-) diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 4f137df8..31a423fb 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -296,12 +296,6 @@ abstract class AppLocalizations { /// **'User Content & Feed Limits'** String get userContentLimitsTitle; - /// Title for the Headlines Filter Preset Limits section - /// - /// In en, this message translates to: - /// **'Headlines Filter Preset Limits'** - String get headlinesFilterPresetLimitsTitle; - /// Description for the User Content & Feed Limits section /// /// In en, this message translates to: @@ -1586,12 +1580,6 @@ abstract class AppLocalizations { /// **'Maximum number of headlines this user role can save.'** String get savedHeadlinesLimitDescription; - /// Description for Saved Filters Limit - /// - /// In en, this message translates to: - /// **'Maximum number of saved headlines filters this user role can create.'** - String get savedHeadlinesFiltersLimitDescription; - /// Label for Ad Frequency /// /// In en, this message translates to: @@ -1604,6 +1592,36 @@ abstract class AppLocalizations { /// **'How often an ad can appear for this user role (e.g., a value of 5 means an ad could be placed after every 5 news items).'** String get adFrequencyDescription; + /// Title for the Saved Headlines Filter Limits section + /// + /// In en, this message translates to: + /// **'Saved Headlines Filter Limits'** + String get savedHeadlinesFilterLimitsTitle; + + /// Label for Guest Saved Filters Limit + /// + /// In en, this message translates to: + /// **'Guest Saved Filters Limit'** + String get guestSavedFiltersLimitLabel; + + /// Label for Standard User Saved Filters Limit + /// + /// In en, this message translates to: + /// **'Standard User Saved Filters Limit'** + String get standardUserSavedFiltersLimitLabel; + + /// Label for Premium User Saved Filters Limit + /// + /// In en, this message translates to: + /// **'Premium User Saved Filters Limit'** + String get premiumUserSavedFiltersLimitLabel; + + /// Description for the limit on saved headlines filters + /// + /// In en, this message translates to: + /// **'Maximum number of saved headlines filters this user role can create.'** + String get savedFiltersLimitDescription; + /// Label for Ad Placement Interval /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_ar.dart b/lib/l10n/app_localizations_ar.dart index d858952e..7cfbe4fb 100644 --- a/lib/l10n/app_localizations_ar.dart +++ b/lib/l10n/app_localizations_ar.dart @@ -120,10 +120,6 @@ class AppLocalizationsAr extends AppLocalizations { @override String get userContentLimitsTitle => 'حدود المحتوى والموجز للمستخدم'; - @override - String get headlinesFilterPresetLimitsTitle => - 'حدود الإعدادات المسبقة لمرشحات الأخبار'; - @override String get userContentLimitsDescription => 'تعيين حدود للعناصر المتابعة والعناوين المحفوظة لكل فئة من فئات المستخدمين.'; @@ -836,10 +832,6 @@ class AppLocalizationsAr extends AppLocalizations { String get savedHeadlinesLimitDescription => 'الحد الأقصى لعدد العناوين الرئيسية التي يمكن لهذا الدور المستخدم حفظها.'; - @override - String get savedHeadlinesFiltersLimitDescription => - 'الحد الأقصى لعدد مرشحات العناوين المحفوظة التي يمكن لهذا الدور إنشاؤها.'; - @override String get adFrequencyLabel => 'تكرار الإعلان'; @@ -847,6 +839,24 @@ class AppLocalizationsAr extends AppLocalizations { String get adFrequencyDescription => 'عدد مرات ظهور الإعلان لهذا الدور المستخدم (على سبيل المثال، قيمة 5 تعني أنه يمكن وضع إعلان بعد كل 5 عناصر إخبارية).'; + @override + String get savedHeadlinesFilterLimitsTitle => 'Saved Headlines Filter Limits'; + + @override + String get guestSavedFiltersLimitLabel => 'Guest Saved Filters Limit'; + + @override + String get standardUserSavedFiltersLimitLabel => + 'Standard User Saved Filters Limit'; + + @override + String get premiumUserSavedFiltersLimitLabel => + 'Premium User Saved Filters Limit'; + + @override + String get savedFiltersLimitDescription => + 'Maximum number of saved headlines filters this user role can create.'; + @override String get adPlacementIntervalLabel => 'فترة وضع الإعلان'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 0db76b45..e51b7c97 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -119,10 +119,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get userContentLimitsTitle => 'User Content & Feed Limits'; - @override - String get headlinesFilterPresetLimitsTitle => - 'Headlines Filter Preset Limits'; - @override String get userContentLimitsDescription => 'Set limits on followed items and saved headlines for each user tier.'; @@ -834,10 +830,6 @@ class AppLocalizationsEn extends AppLocalizations { String get savedHeadlinesLimitDescription => 'Maximum number of headlines this user role can save.'; - @override - String get savedHeadlinesFiltersLimitDescription => - 'Maximum number of saved headlines filters this user role can create.'; - @override String get adFrequencyLabel => 'Ad Frequency'; @@ -845,6 +837,24 @@ class AppLocalizationsEn extends AppLocalizations { String get adFrequencyDescription => 'How often an ad can appear for this user role (e.g., a value of 5 means an ad could be placed after every 5 news items).'; + @override + String get savedHeadlinesFilterLimitsTitle => 'Saved Headlines Filter Limits'; + + @override + String get guestSavedFiltersLimitLabel => 'Guest Saved Filters Limit'; + + @override + String get standardUserSavedFiltersLimitLabel => + 'Standard User Saved Filters Limit'; + + @override + String get premiumUserSavedFiltersLimitLabel => + 'Premium User Saved Filters Limit'; + + @override + String get savedFiltersLimitDescription => + 'Maximum number of saved headlines filters this user role can create.'; + @override String get adPlacementIntervalLabel => 'Ad Placement Interval'; diff --git a/lib/l10n/arb/app_ar.arb b/lib/l10n/arb/app_ar.arb index b1159504..c0922fa3 100644 --- a/lib/l10n/arb/app_ar.arb +++ b/lib/l10n/arb/app_ar.arb @@ -147,10 +147,6 @@ "@userContentLimitsTitle": { "description": "عنوان قسم حدود المحتوى والموجز للمستخدم" }, - "headlinesFilterPresetLimitsTitle": "حدود الإعدادات المسبقة لمرشحات الأخبار", - "@headlinesFilterPresetLimitsTitle": { - "description": "عنوان قسم حدود الإعدادات المسبقة لمرشحات الأخبار" - }, "userContentLimitsDescription": "تعيين حدود للعناصر المتابعة والعناوين المحفوظة لكل فئة من فئات المستخدمين.", "@userContentLimitsDescription": { "description": "وصف قسم حدود المحتوى والموجز للمستخدم" diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index cad760d7..aab10d07 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -147,10 +147,6 @@ "@userContentLimitsTitle": { "description": "Title for the User Content & Feed Limits section" }, - "headlinesFilterPresetLimitsTitle": "Headlines Filter Preset Limits", - "@headlinesFilterPresetLimitsTitle": { - "description": "Title for the Headlines Filter Preset Limits section" - }, "userContentLimitsDescription": "Set limits on followed items and saved headlines for each user tier.", "@userContentLimitsDescription": { "description": "Description for the User Content & Feed Limits section" @@ -1034,10 +1030,6 @@ "@savedHeadlinesLimitDescription": { "description": "Description for Saved Headlines Limit" }, - "savedHeadlinesFiltersLimitDescription": "Maximum number of saved headlines filters this user role can create.", - "@savedHeadlinesFiltersLimitDescription": { - "description": "Description for Saved Filters Limit" - }, "adFrequencyLabel": "Ad Frequency", "@adFrequencyLabel": { "description": "Label for Ad Frequency" @@ -1046,6 +1038,26 @@ "@adFrequencyDescription": { "description": "Description for Ad Frequency" }, + "savedHeadlinesFilterLimitsTitle": "Saved Headlines Filter Limits", + "@savedHeadlinesFilterLimitsTitle": { + "description": "Title for the Saved Headlines Filter Limits section" + }, + "guestSavedFiltersLimitLabel": "Guest Saved Filters Limit", + "@guestSavedFiltersLimitLabel": { + "description": "Label for Guest Saved Filters Limit" + }, + "standardUserSavedFiltersLimitLabel": "Standard User Saved Filters Limit", + "@standardUserSavedFiltersLimitLabel": { + "description": "Label for Standard User Saved Filters Limit" + }, + "premiumUserSavedFiltersLimitLabel": "Premium User Saved Filters Limit", + "@premiumUserSavedFiltersLimitLabel": { + "description": "Label for Premium User Saved Filters Limit" + }, + "savedFiltersLimitDescription": "Maximum number of saved headlines filters this user role can create.", + "@savedFiltersLimitDescription": { + "description": "Description for the limit on saved headlines filters" + }, "adPlacementIntervalLabel": "Ad Placement Interval", "@adPlacementIntervalLabel": { "description": "Label for Ad Placement Interval" From 6a902e0dac98a324a82ae8e4ce043718121dd90e Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 12:06:54 +0100 Subject: [PATCH 20/27] feat(app_configuration): add descriptions to limits sections - Add description for User Content Limits section - Add description for Saved Headlines Filter Limits section - Update localizations to include new descriptions --- .../view/tabs/feed_configuration_tab.dart | 20 +++++++++++++++++++ lib/l10n/app_localizations.dart | 6 ++++++ lib/l10n/app_localizations_ar.dart | 4 ++++ lib/l10n/app_localizations_en.dart | 4 ++++ lib/l10n/arb/app_ar.arb | 4 ++-- lib/l10n/arb/app_en.arb | 4 ++++ 6 files changed, 40 insertions(+), 2 deletions(-) diff --git a/lib/app_configuration/view/tabs/feed_configuration_tab.dart b/lib/app_configuration/view/tabs/feed_configuration_tab.dart index 44932989..f9376dc0 100644 --- a/lib/app_configuration/view/tabs/feed_configuration_tab.dart +++ b/lib/app_configuration/view/tabs/feed_configuration_tab.dart @@ -68,6 +68,16 @@ class _FeedConfigurationTabState extends State { }, initiallyExpanded: expandedIndex == tileIndex, children: [ + Text( + l10n.userContentLimitsDescription, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.7), + ), + ), + const SizedBox(height: AppSpacing.lg), UserPreferenceLimitsForm( remoteConfig: widget.remoteConfig, onConfigChanged: widget.onConfigChanged, @@ -96,6 +106,16 @@ class _FeedConfigurationTabState extends State { }, initiallyExpanded: expandedIndex == tileIndex, children: [ + Text( + l10n.savedHeadlinesFilterLimitsDescription, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.7), + ), + ), + const SizedBox(height: AppSpacing.lg), SavedHeadlinesFiltersLimitForm( remoteConfig: widget.remoteConfig, onConfigChanged: widget.onConfigChanged, diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 31a423fb..782bde51 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -1592,6 +1592,12 @@ abstract class AppLocalizations { /// **'How often an ad can appear for this user role (e.g., a value of 5 means an ad could be placed after every 5 news items).'** String get adFrequencyDescription; + /// Description for the Saved Headlines Filter Limits section + /// + /// In en, this message translates to: + /// **'Set limits on the number of saved headlines filters for each user tier.'** + String get savedHeadlinesFilterLimitsDescription; + /// Title for the Saved Headlines Filter Limits section /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_ar.dart b/lib/l10n/app_localizations_ar.dart index 7cfbe4fb..f3070bf7 100644 --- a/lib/l10n/app_localizations_ar.dart +++ b/lib/l10n/app_localizations_ar.dart @@ -839,6 +839,10 @@ class AppLocalizationsAr extends AppLocalizations { String get adFrequencyDescription => 'عدد مرات ظهور الإعلان لهذا الدور المستخدم (على سبيل المثال، قيمة 5 تعني أنه يمكن وضع إعلان بعد كل 5 عناصر إخبارية).'; + @override + String get savedHeadlinesFilterLimitsDescription => + 'Set limits on the number of saved headlines filters for each user tier.'; + @override String get savedHeadlinesFilterLimitsTitle => 'Saved Headlines Filter Limits'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index e51b7c97..d7bbe2c6 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -837,6 +837,10 @@ class AppLocalizationsEn extends AppLocalizations { String get adFrequencyDescription => 'How often an ad can appear for this user role (e.g., a value of 5 means an ad could be placed after every 5 news items).'; + @override + String get savedHeadlinesFilterLimitsDescription => + 'Set limits on the number of saved headlines filters for each user tier.'; + @override String get savedHeadlinesFilterLimitsTitle => 'Saved Headlines Filter Limits'; diff --git a/lib/l10n/arb/app_ar.arb b/lib/l10n/arb/app_ar.arb index c0922fa3..8483d4f6 100644 --- a/lib/l10n/arb/app_ar.arb +++ b/lib/l10n/arb/app_ar.arb @@ -1038,8 +1038,8 @@ "@adFrequencyDescription": { "description": "وصف تكرار الإعلان" }, - "savedHeadlinesFiltersLimitDescription": "الحد الأقصى لعدد مرشحات العناوين المحفوظة التي يمكن لهذا الدور إنشاؤها.", - "@savedHeadlinesFiltersLimitDescription": { + "savedHeadlinesFilterLimitDescription": "الحد الأقصى لعدد مرشحات العناوين المحفوظة التي يمكن لهذا الدور إنشاؤها.", + "@savedHeadlinesFilterLimitDescription": { "description": "وصف لحد المرشحات المحفوظة" }, "adPlacementIntervalLabel": "فترة وضع الإعلان", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index aab10d07..f2733f62 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -1038,6 +1038,10 @@ "@adFrequencyDescription": { "description": "Description for Ad Frequency" }, + "savedHeadlinesFilterLimitsDescription": "Set limits on the number of saved headlines filters for each user tier.", + "@savedHeadlinesFilterLimitsDescription": { + "description": "Description for the Saved Headlines Filter Limits section" + }, "savedHeadlinesFilterLimitsTitle": "Saved Headlines Filter Limits", "@savedHeadlinesFilterLimitsTitle": { "description": "Title for the Saved Headlines Filter Limits section" From 7ddf14c024df66cb9e21b5ce2bce132ff376ad4b Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 12:12:15 +0100 Subject: [PATCH 21/27] refactor(l10n): remove unused localization strings and fix descriptions - Remove unused localization strings for saved filters limits - Rename and update descriptions for saved headlines filter limits - Update Arabic and English localization files accordingly - Adjust ARB files to reflect changes in localization strings --- lib/l10n/app_localizations.dart | 24 ------------------------ lib/l10n/app_localizations_ar.dart | 19 ++----------------- lib/l10n/app_localizations_en.dart | 15 --------------- lib/l10n/arb/app_ar.arb | 8 ++++++-- lib/l10n/arb/app_en.arb | 16 ---------------- 5 files changed, 8 insertions(+), 74 deletions(-) diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 782bde51..48b37d6a 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -1604,30 +1604,6 @@ abstract class AppLocalizations { /// **'Saved Headlines Filter Limits'** String get savedHeadlinesFilterLimitsTitle; - /// Label for Guest Saved Filters Limit - /// - /// In en, this message translates to: - /// **'Guest Saved Filters Limit'** - String get guestSavedFiltersLimitLabel; - - /// Label for Standard User Saved Filters Limit - /// - /// In en, this message translates to: - /// **'Standard User Saved Filters Limit'** - String get standardUserSavedFiltersLimitLabel; - - /// Label for Premium User Saved Filters Limit - /// - /// In en, this message translates to: - /// **'Premium User Saved Filters Limit'** - String get premiumUserSavedFiltersLimitLabel; - - /// Description for the limit on saved headlines filters - /// - /// In en, this message translates to: - /// **'Maximum number of saved headlines filters this user role can create.'** - String get savedFiltersLimitDescription; - /// Label for Ad Placement Interval /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_ar.dart b/lib/l10n/app_localizations_ar.dart index f3070bf7..b3172f36 100644 --- a/lib/l10n/app_localizations_ar.dart +++ b/lib/l10n/app_localizations_ar.dart @@ -841,25 +841,10 @@ class AppLocalizationsAr extends AppLocalizations { @override String get savedHeadlinesFilterLimitsDescription => - 'Set limits on the number of saved headlines filters for each user tier.'; + 'الحد الأقصى لعدد مرشحات العناوين المحفوظة التي يمكن لهذا الدور إنشاؤها.'; @override - String get savedHeadlinesFilterLimitsTitle => 'Saved Headlines Filter Limits'; - - @override - String get guestSavedFiltersLimitLabel => 'Guest Saved Filters Limit'; - - @override - String get standardUserSavedFiltersLimitLabel => - 'Standard User Saved Filters Limit'; - - @override - String get premiumUserSavedFiltersLimitLabel => - 'Premium User Saved Filters Limit'; - - @override - String get savedFiltersLimitDescription => - 'Maximum number of saved headlines filters this user role can create.'; + String get savedHeadlinesFilterLimitsTitle => 'حدود تصفيات العناوين المحفوظة'; @override String get adPlacementIntervalLabel => 'فترة وضع الإعلان'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index d7bbe2c6..8523c0ff 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -844,21 +844,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get savedHeadlinesFilterLimitsTitle => 'Saved Headlines Filter Limits'; - @override - String get guestSavedFiltersLimitLabel => 'Guest Saved Filters Limit'; - - @override - String get standardUserSavedFiltersLimitLabel => - 'Standard User Saved Filters Limit'; - - @override - String get premiumUserSavedFiltersLimitLabel => - 'Premium User Saved Filters Limit'; - - @override - String get savedFiltersLimitDescription => - 'Maximum number of saved headlines filters this user role can create.'; - @override String get adPlacementIntervalLabel => 'Ad Placement Interval'; diff --git a/lib/l10n/arb/app_ar.arb b/lib/l10n/arb/app_ar.arb index 8483d4f6..396e0576 100644 --- a/lib/l10n/arb/app_ar.arb +++ b/lib/l10n/arb/app_ar.arb @@ -1038,8 +1038,12 @@ "@adFrequencyDescription": { "description": "وصف تكرار الإعلان" }, - "savedHeadlinesFilterLimitDescription": "الحد الأقصى لعدد مرشحات العناوين المحفوظة التي يمكن لهذا الدور إنشاؤها.", - "@savedHeadlinesFilterLimitDescription": { + "savedHeadlinesFilterLimitsTitle": "حدود تصفيات العناوين المحفوظة", + "@savedHeadlinesFilterLimitsTitle": { + "description": "وصف لحد المرشحات المحفوظة" + }, + "savedHeadlinesFilterLimitsDescription": "الحد الأقصى لعدد مرشحات العناوين المحفوظة التي يمكن لهذا الدور إنشاؤها.", + "@savedHeadlinesFilterLimitsDescription": { "description": "وصف لحد المرشحات المحفوظة" }, "adPlacementIntervalLabel": "فترة وضع الإعلان", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index f2733f62..560b5542 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -1046,22 +1046,6 @@ "@savedHeadlinesFilterLimitsTitle": { "description": "Title for the Saved Headlines Filter Limits section" }, - "guestSavedFiltersLimitLabel": "Guest Saved Filters Limit", - "@guestSavedFiltersLimitLabel": { - "description": "Label for Guest Saved Filters Limit" - }, - "standardUserSavedFiltersLimitLabel": "Standard User Saved Filters Limit", - "@standardUserSavedFiltersLimitLabel": { - "description": "Label for Standard User Saved Filters Limit" - }, - "premiumUserSavedFiltersLimitLabel": "Premium User Saved Filters Limit", - "@premiumUserSavedFiltersLimitLabel": { - "description": "Label for Premium User Saved Filters Limit" - }, - "savedFiltersLimitDescription": "Maximum number of saved headlines filters this user role can create.", - "@savedFiltersLimitDescription": { - "description": "Description for the limit on saved headlines filters" - }, "adPlacementIntervalLabel": "Ad Placement Interval", "@adPlacementIntervalLabel": { "description": "Label for Ad Placement Interval" From 1b9408231e3decdd7b493250f07b23020ce0e595 Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 12:13:20 +0100 Subject: [PATCH 22/27] fix(localization): update saved headlines filter limit labels and descriptions - Replace guestSavedFiltersLimitLabel with savedHeadlinesFilterLimitsTitle - Replace standardUserSavedFiltersLimitLabel with savedHeadlinesFilterLimitsTitle - Replace premiumUserSavedFiltersLimitLabel with savedHeadlinesFilterLimitsTitle - Update description for all fields to use savedHeadlinesFilterLimitsTitle --- .../widgets/saved_headlines_filters_limit_form.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/app_configuration/widgets/saved_headlines_filters_limit_form.dart b/lib/app_configuration/widgets/saved_headlines_filters_limit_form.dart index 6efbd66b..57bd21c8 100644 --- a/lib/app_configuration/widgets/saved_headlines_filters_limit_form.dart +++ b/lib/app_configuration/widgets/saved_headlines_filters_limit_form.dart @@ -117,8 +117,8 @@ class _SavedHeadlinesFiltersLimitFormState crossAxisAlignment: CrossAxisAlignment.start, children: [ AppConfigIntField( - label: l10n.guestSavedFiltersLimitLabel, - description: l10n.savedFiltersLimitDescription, + label: l10n.savedHeadlinesFilterLimitsTitle, + description: l10n.savedHeadlinesFilterLimitsTitle, value: userPreferenceConfig.guestSavedFiltersLimit, onChanged: (newLimit) { widget.onConfigChanged( @@ -132,8 +132,8 @@ class _SavedHeadlinesFiltersLimitFormState controller: _guestController, ), AppConfigIntField( - label: l10n.standardUserSavedFiltersLimitLabel, - description: l10n.savedFiltersLimitDescription, + label: l10n.savedHeadlinesFilterLimitsTitle, + description: l10n.savedHeadlinesFilterLimitsTitle, value: userPreferenceConfig.authenticatedSavedFiltersLimit, onChanged: (newLimit) { widget.onConfigChanged( @@ -147,8 +147,8 @@ class _SavedHeadlinesFiltersLimitFormState controller: _standardController, ), AppConfigIntField( - label: l10n.premiumUserSavedFiltersLimitLabel, - description: l10n.savedFiltersLimitDescription, + label: l10n.savedHeadlinesFilterLimitsTitle, + description: l10n.savedHeadlinesFilterLimitsTitle, value: userPreferenceConfig.premiumSavedFiltersLimit, onChanged: (newLimit) { widget.onConfigChanged( From 7db311ae710f0e52e98bb44b8204549c133091da Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 12:13:49 +0100 Subject: [PATCH 23/27] style: format --- .../view/app_configuration_page.dart | 2 +- .../view/tabs/feed_configuration_tab.dart | 18 ++++++++---------- .../saved_headlines_filters_limit_form.dart | 15 +++++++++------ .../view/local_ads_management_page.dart | 1 - 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/app_configuration/view/app_configuration_page.dart b/lib/app_configuration/view/app_configuration_page.dart index 158bd392..20281e82 100644 --- a/lib/app_configuration/view/app_configuration_page.dart +++ b/lib/app_configuration/view/app_configuration_page.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/bloc/app_configuration_bloc.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/view/tabs/advertisements_configuration_tab.dart'; -import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/view/tabs/general_configuration_tab.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/view/tabs/feed_configuration_tab.dart'; +import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/view/tabs/general_configuration_tab.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/shared/widgets/about_icon.dart'; import 'package:ui_kit/ui_kit.dart'; diff --git a/lib/app_configuration/view/tabs/feed_configuration_tab.dart b/lib/app_configuration/view/tabs/feed_configuration_tab.dart index f9376dc0..cbff2078 100644 --- a/lib/app_configuration/view/tabs/feed_configuration_tab.dart +++ b/lib/app_configuration/view/tabs/feed_configuration_tab.dart @@ -71,11 +71,10 @@ class _FeedConfigurationTabState extends State { Text( l10n.userContentLimitsDescription, style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Theme.of(context) - .colorScheme - .onSurface - .withOpacity(0.7), - ), + color: Theme.of( + context, + ).colorScheme.onSurface.withOpacity(0.7), + ), ), const SizedBox(height: AppSpacing.lg), UserPreferenceLimitsForm( @@ -109,11 +108,10 @@ class _FeedConfigurationTabState extends State { Text( l10n.savedHeadlinesFilterLimitsDescription, style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Theme.of(context) - .colorScheme - .onSurface - .withOpacity(0.7), - ), + color: Theme.of( + context, + ).colorScheme.onSurface.withOpacity(0.7), + ), ), const SizedBox(height: AppSpacing.lg), SavedHeadlinesFiltersLimitForm( diff --git a/lib/app_configuration/widgets/saved_headlines_filters_limit_form.dart b/lib/app_configuration/widgets/saved_headlines_filters_limit_form.dart index 57bd21c8..bef747c3 100644 --- a/lib/app_configuration/widgets/saved_headlines_filters_limit_form.dart +++ b/lib/app_configuration/widgets/saved_headlines_filters_limit_form.dart @@ -54,20 +54,23 @@ class _SavedHeadlinesFiltersLimitFormState final config = widget.remoteConfig.userPreferenceConfig; _guestController = TextEditingController( - text: config.guestSavedFiltersLimit.toString(), - )..selection = TextSelection.collapsed( + text: config.guestSavedFiltersLimit.toString(), + ) + ..selection = TextSelection.collapsed( offset: config.guestSavedFiltersLimit.toString().length, ); _standardController = TextEditingController( - text: config.authenticatedSavedFiltersLimit.toString(), - )..selection = TextSelection.collapsed( + text: config.authenticatedSavedFiltersLimit.toString(), + ) + ..selection = TextSelection.collapsed( offset: config.authenticatedSavedFiltersLimit.toString().length, ); _premiumController = TextEditingController( - text: config.premiumSavedFiltersLimit.toString(), - )..selection = TextSelection.collapsed( + text: config.premiumSavedFiltersLimit.toString(), + ) + ..selection = TextSelection.collapsed( offset: config.premiumSavedFiltersLimit.toString().length, ); } diff --git a/lib/local_ads_management/view/local_ads_management_page.dart b/lib/local_ads_management/view/local_ads_management_page.dart index 7bf8761c..334a9e9a 100644 --- a/lib/local_ads_management/view/local_ads_management_page.dart +++ b/lib/local_ads_management/view/local_ads_management_page.dart @@ -8,7 +8,6 @@ import 'package:flutter_news_app_web_dashboard_full_source_code/local_ads_manage import 'package:flutter_news_app_web_dashboard_full_source_code/local_ads_management/view/native_ads_page.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/local_ads_management/view/video_ads_page.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/router/routes.dart'; -import 'package:flutter_news_app_web_dashboard_full_source_code/shared/extensions/extensions.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/shared/shared.dart'; import 'package:go_router/go_router.dart'; import 'package:ui_kit/ui_kit.dart'; From 2573bcb84f7305e742641398e43f95d75930a0aa Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 16:05:29 +0100 Subject: [PATCH 24/27] refactor(app_configuration): remove user content limits description text - Remove the Text widget displaying the user content limits description - Remove the SizedBox for spacing below the description text --- .../widgets/user_preference_limits_form.dart | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/app_configuration/widgets/user_preference_limits_form.dart b/lib/app_configuration/widgets/user_preference_limits_form.dart index 873ec8e7..6bfca60d 100644 --- a/lib/app_configuration/widgets/user_preference_limits_form.dart +++ b/lib/app_configuration/widgets/user_preference_limits_form.dart @@ -142,13 +142,6 @@ class _UserPreferenceLimitsFormState extends State return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - l10n.userContentLimitsDescription, - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), - ), - ), - const SizedBox(height: AppSpacing.lg), Align( alignment: AlignmentDirectional.centerStart, child: SizedBox( From 3272453fe9bb0db0b62e106c3eb04ef55926f988 Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 16:05:42 +0100 Subject: [PATCH 25/27] refactor(app_configuration): implement dynamic tabbed interface for saved headlines filters limit - Replace static form fields with a dynamic tabbed interface - Introduce TabController and map AppUserRole to tabs - Refactor TextEditingController implementation to use a map - Abstract logic for getting and updating saved filters limit by role - Improve code maintainability and extensibility for different user roles --- .../saved_headlines_filters_limit_form.dart | 210 +++++++++--------- 1 file changed, 111 insertions(+), 99 deletions(-) diff --git a/lib/app_configuration/widgets/saved_headlines_filters_limit_form.dart b/lib/app_configuration/widgets/saved_headlines_filters_limit_form.dart index bef747c3..cbdd24ae 100644 --- a/lib/app_configuration/widgets/saved_headlines_filters_limit_form.dart +++ b/lib/app_configuration/widgets/saved_headlines_filters_limit_form.dart @@ -2,6 +2,8 @@ import 'package:core/core.dart'; import 'package:flutter/material.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/app_config_form_fields.dart'; import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart'; +import 'package:flutter_news_app_web_dashboard_full_source_code/shared/extensions/app_user_role_l10n.dart'; +import 'package:ui_kit/ui_kit.dart'; /// {@template saved_headlines_filters_limit_form} /// A form for configuring saved headlines filter limits within the @@ -30,14 +32,18 @@ class SavedHeadlinesFiltersLimitForm extends StatefulWidget { } class _SavedHeadlinesFiltersLimitFormState - extends State { - late final TextEditingController _guestController; - late final TextEditingController _standardController; - late final TextEditingController _premiumController; + extends State + with SingleTickerProviderStateMixin { + late TabController _tabController; + late final Map _controllers; @override void initState() { super.initState(); + _tabController = TabController( + length: AppUserRole.values.length, + vsync: this, + ); _initializeControllers(); } @@ -46,125 +52,131 @@ class _SavedHeadlinesFiltersLimitFormState super.didUpdateWidget(oldWidget); if (widget.remoteConfig.userPreferenceConfig != oldWidget.remoteConfig.userPreferenceConfig) { - _updateControllers(); + _updateControllerValues(); } } void _initializeControllers() { - final config = widget.remoteConfig.userPreferenceConfig; - _guestController = - TextEditingController( - text: config.guestSavedFiltersLimit.toString(), - ) - ..selection = TextSelection.collapsed( - offset: config.guestSavedFiltersLimit.toString().length, - ); - _standardController = - TextEditingController( - text: config.authenticatedSavedFiltersLimit.toString(), - ) - ..selection = TextSelection.collapsed( - offset: config.authenticatedSavedFiltersLimit.toString().length, - ); - _premiumController = - TextEditingController( - text: config.premiumSavedFiltersLimit.toString(), - ) - ..selection = TextSelection.collapsed( - offset: config.premiumSavedFiltersLimit.toString().length, - ); + _controllers = { + for (final role in AppUserRole.values) + role: TextEditingController( + text: _getSavedFiltersLimit( + widget.remoteConfig.userPreferenceConfig, + role, + ).toString(), + )..selection = TextSelection.collapsed( + offset: _getSavedFiltersLimit( + widget.remoteConfig.userPreferenceConfig, + role, + ).toString().length, + ), + }; } - void _updateControllers() { - final config = widget.remoteConfig.userPreferenceConfig; - - final newGuestValue = config.guestSavedFiltersLimit.toString(); - if (_guestController.text != newGuestValue) { - _guestController.text = newGuestValue; - _guestController.selection = TextSelection.collapsed( - offset: newGuestValue.length, - ); - } - - final newStandardValue = config.authenticatedSavedFiltersLimit.toString(); - if (_standardController.text != newStandardValue) { - _standardController.text = newStandardValue; - _standardController.selection = TextSelection.collapsed( - offset: newStandardValue.length, - ); - } - - final newPremiumValue = config.premiumSavedFiltersLimit.toString(); - if (_premiumController.text != newPremiumValue) { - _premiumController.text = newPremiumValue; - _premiumController.selection = TextSelection.collapsed( - offset: newPremiumValue.length, - ); + void _updateControllerValues() { + for (final role in AppUserRole.values) { + final newLimit = _getSavedFiltersLimit( + widget.remoteConfig.userPreferenceConfig, + role, + ).toString(); + if (_controllers[role]?.text != newLimit) { + _controllers[role]?.text = newLimit; + _controllers[role]?.selection = TextSelection.collapsed( + offset: newLimit.length, + ); + } } } @override void dispose() { - _guestController.dispose(); - _standardController.dispose(); - _premiumController.dispose(); + _tabController.dispose(); + for (final controller in _controllers.values) { + controller.dispose(); + } super.dispose(); } @override Widget build(BuildContext context) { final l10n = AppLocalizationsX(context).l10n; - final userPreferenceConfig = widget.remoteConfig.userPreferenceConfig; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - AppConfigIntField( - label: l10n.savedHeadlinesFilterLimitsTitle, - description: l10n.savedHeadlinesFilterLimitsTitle, - value: userPreferenceConfig.guestSavedFiltersLimit, - onChanged: (newLimit) { - widget.onConfigChanged( - widget.remoteConfig.copyWith( - userPreferenceConfig: userPreferenceConfig.copyWith( - guestSavedFiltersLimit: newLimit, - ), - ), - ); - }, - controller: _guestController, + Align( + alignment: AlignmentDirectional.centerStart, + child: SizedBox( + height: kTextTabBarHeight, + child: TabBar( + controller: _tabController, + tabAlignment: TabAlignment.start, + isScrollable: true, + tabs: AppUserRole.values + .map((role) => Tab(text: role.l10n(context))) + .toList(), + ), + ), ), - AppConfigIntField( - label: l10n.savedHeadlinesFilterLimitsTitle, - description: l10n.savedHeadlinesFilterLimitsTitle, - value: userPreferenceConfig.authenticatedSavedFiltersLimit, - onChanged: (newLimit) { - widget.onConfigChanged( - widget.remoteConfig.copyWith( - userPreferenceConfig: userPreferenceConfig.copyWith( - authenticatedSavedFiltersLimit: newLimit, - ), - ), - ); - }, - controller: _standardController, - ), - AppConfigIntField( - label: l10n.savedHeadlinesFilterLimitsTitle, - description: l10n.savedHeadlinesFilterLimitsTitle, - value: userPreferenceConfig.premiumSavedFiltersLimit, - onChanged: (newLimit) { - widget.onConfigChanged( - widget.remoteConfig.copyWith( - userPreferenceConfig: userPreferenceConfig.copyWith( - premiumSavedFiltersLimit: newLimit, - ), - ), - ); - }, - controller: _premiumController, + const SizedBox(height: AppSpacing.lg), + SizedBox( + height: 120, + child: TabBarView( + controller: _tabController, + children: AppUserRole.values.map((role) { + final config = widget.remoteConfig.userPreferenceConfig; + return AppConfigIntField( + label: l10n.savedHeadlinesLimitLabel, + description: l10n.savedHeadlinesLimitDescription, + value: _getSavedFiltersLimit(config, role), + onChanged: (value) { + widget.onConfigChanged( + widget.remoteConfig.copyWith( + userPreferenceConfig: + _updateSavedFiltersLimit(config, value, role), + ), + ); + }, + controller: _controllers[role], + ); + }).toList(), + ), ), ], ); } + + /// Retrieves the saved filters limit for a given [AppUserRole]. + /// + /// This helper method abstracts the logic for accessing the correct limit + /// from the [UserPreferenceConfig] based on the provided [role]. + int _getSavedFiltersLimit(UserPreferenceConfig config, AppUserRole role) { + switch (role) { + case AppUserRole.guestUser: + return config.guestSavedFiltersLimit; + case AppUserRole.standardUser: + return config.authenticatedSavedFiltersLimit; + case AppUserRole.premiumUser: + return config.premiumSavedFiltersLimit; + } + } + + /// Updates the saved filters limit for a given [AppUserRole]. + /// + /// This helper method abstracts the logic for updating the correct limit + /// within the [UserPreferenceConfig] based on the provided [role] and [value]. + UserPreferenceConfig _updateSavedFiltersLimit( + UserPreferenceConfig config, + int value, + AppUserRole role, + ) { + switch (role) { + case AppUserRole.guestUser: + return config.copyWith(guestSavedFiltersLimit: value); + case AppUserRole.standardUser: + return config.copyWith(authenticatedSavedFiltersLimit: value); + case AppUserRole.premiumUser: + return config.copyWith(premiumSavedFiltersLimit: value); + } + } } From 70965cb2263adcdef46c310c7e916861ac02f54e Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 16:06:01 +0100 Subject: [PATCH 26/27] fix(localization): correct Arabic translation for 'savedHeadlinesFilterLimitsTitle' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update the Arabic translation from 'حدود تصفيات العناوين المحفوظة' to 'حدود مرشحات العناوين المحفوظة' - This change improves the accuracy of the translation, ensuring it correctly conveys the meaning of 'saved headlines filter limits' --- lib/l10n/app_localizations_ar.dart | 2 +- lib/l10n/arb/app_ar.arb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/l10n/app_localizations_ar.dart b/lib/l10n/app_localizations_ar.dart index b3172f36..1e778cbe 100644 --- a/lib/l10n/app_localizations_ar.dart +++ b/lib/l10n/app_localizations_ar.dart @@ -844,7 +844,7 @@ class AppLocalizationsAr extends AppLocalizations { 'الحد الأقصى لعدد مرشحات العناوين المحفوظة التي يمكن لهذا الدور إنشاؤها.'; @override - String get savedHeadlinesFilterLimitsTitle => 'حدود تصفيات العناوين المحفوظة'; + String get savedHeadlinesFilterLimitsTitle => 'حدود مرشحات العناوين المحفوظة'; @override String get adPlacementIntervalLabel => 'فترة وضع الإعلان'; diff --git a/lib/l10n/arb/app_ar.arb b/lib/l10n/arb/app_ar.arb index 396e0576..b3e75a9b 100644 --- a/lib/l10n/arb/app_ar.arb +++ b/lib/l10n/arb/app_ar.arb @@ -1038,7 +1038,7 @@ "@adFrequencyDescription": { "description": "وصف تكرار الإعلان" }, - "savedHeadlinesFilterLimitsTitle": "حدود تصفيات العناوين المحفوظة", + "savedHeadlinesFilterLimitsTitle": "حدود مرشحات العناوين المحفوظة", "@savedHeadlinesFilterLimitsTitle": { "description": "وصف لحد المرشحات المحفوظة" }, From 18aa3717456791143a51f51b7051ad07c478676c Mon Sep 17 00:00:00 2001 From: fulleni Date: 2025年10月13日 16:07:57 +0100 Subject: [PATCH 27/27] docs(changelog): remove duplicate 1.0.0 release notes - Removed redundant entry for version 1.0.0 in CHANGELOG.md - This change ensures the changelog contains unique and up-to-date information --- CHANGELOG.md | 1 - lib/local_ads_management/widgets/local_ad_action_buttons.dart | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2668ec18..c935ab69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,3 @@ ##1.0.0 -- **feat**: Added a "User Presets" tab to the App Configuration page to configure saved filter limits. - **BREAKING** feat!: migrated from date based versioning to semantic versioning. \ No newline at end of file diff --git a/lib/local_ads_management/widgets/local_ad_action_buttons.dart b/lib/local_ads_management/widgets/local_ad_action_buttons.dart index 83425b60..a9a721ab 100644 --- a/lib/local_ads_management/widgets/local_ad_action_buttons.dart +++ b/lib/local_ads_management/widgets/local_ad_action_buttons.dart @@ -180,6 +180,7 @@ class LocalAdActionButtons extends StatelessWidget { case 'copyId': // Copy the ad ID to the clipboard Clipboard.setData(ClipboardData(text: itemId)).then((_) { + // ignore: use_build_context_synchronously ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(l10n.idCopied),

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