Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Feat custom filters useres limits configuration #100

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
fulleni merged 27 commits into main from feat-custom-filters-useres-limits-configuration
Oct 13, 2025
Merged
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
79e7ccf
build(deps): update git dependencies to use version tags
fulleni Oct 13, 2025
8477221
docs: add CHANGELOG.md file
fulleni Oct 13, 2025
b763f1b
chore(deps): remove empty line in pubspec.yaml
fulleni Oct 13, 2025
74b0aa7
feat(ui): add user preset limits form
fulleni Oct 13, 2025
6a67ad5
refactor(app-configuration): move user preset limits form to lib dire...
fulleni Oct 13, 2025
e753d38
feat(app_configuration): add user presets configuration tab
fulleni Oct 13, 2025
f5344d1
feat(app_configuration): add user presets configuration tab
fulleni Oct 13, 2025
5908712
chore: changelog
fulleni Oct 13, 2025
81f8091
feat(l10n): add user presets and filter limit translations
fulleni Oct 13, 2025
ff48e8d
fix(app_configuration): update preset tab label
fulleni Oct 13, 2025
2c90f0e
fix(l10n): correct arb key for saved headlines filters limit description
fulleni Oct 13, 2025
784b3c3
refactor(app_configuration): convert UserPresetLimitsForm to Stateful...
fulleni Oct 13, 2025
880737d
feat(app_configuration): add user preset limits form and update expan...
fulleni Oct 13, 2025
f6e1593
fix(l10n): remove duplicate strings and correct order
fulleni Oct 13, 2025
3b9c25b
chore: misc
fulleni Oct 13, 2025
e084fbd
refactor(app_configuration): remove user presets configuration tab
fulleni Oct 13, 2025
ad58d64
refactor(app_configuration): rename widget and update localization keys
fulleni Oct 13, 2025
a745f84
refactor(app_configuration): update saved headlines filter limits UI ...
fulleni Oct 13, 2025
60bb031
refactor(l10n): remove unused arb entries and add new ones
fulleni Oct 13, 2025
6a902e0
feat(app_configuration): add descriptions to limits sections
fulleni Oct 13, 2025
7ddf14c
refactor(l10n): remove unused localization strings and fix descriptions
fulleni Oct 13, 2025
1b94082
fix(localization): update saved headlines filter limit labels and des...
fulleni Oct 13, 2025
7db311a
style: format
fulleni Oct 13, 2025
2573bcb
refactor(app_configuration): remove user content limits description text
fulleni Oct 13, 2025
3272453
refactor(app_configuration): implement dynamic tabbed interface for s...
fulleni Oct 13, 2025
70965cb
fix(localization): correct Arabic translation for 'savedHeadlinesFilt...
fulleni Oct 13, 2025
18aa371
docs(changelog): remove duplicate 1.0.0 release notes
fulleni Oct 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor(app_configuration): implement dynamic tabbed interface for s...
...aved 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
  • Loading branch information
fulleni committed Oct 13, 2025
commit 3272453fe9bb0db0b62e106c3eb04ef55926f988
210 changes: 111 additions & 99 deletions lib/app_configuration/widgets/saved_headlines_filters_limit_form.dart
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -30,14 +32,18 @@ class SavedHeadlinesFiltersLimitForm extends StatefulWidget {
}

class _SavedHeadlinesFiltersLimitFormState
extends State<SavedHeadlinesFiltersLimitForm> {
late final TextEditingController _guestController;
late final TextEditingController _standardController;
late final TextEditingController _premiumController;
extends State<SavedHeadlinesFiltersLimitForm>
with SingleTickerProviderStateMixin {
late TabController _tabController;
late final Map<AppUserRole, TextEditingController> _controllers;

@override
void initState() {
super.initState();
_tabController = TabController(
length: AppUserRole.values.length,
vsync: this,
);
_initializeControllers();
}

Expand All @@ -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);
}
}
}

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /