Skip to content

Commit f92fec1

Browse files
committed
feat(app_configuration): add feed decorator form widget
- Implement FeedDecoratorForm StatefulWidget for configuring feed decorators - Add logic to handle different decorator types and user roles - Include functionality to update remote config based on form input - Create role-specific fields for customization
1 parent d9450b5 commit f92fec1

File tree

1 file changed

+255
-0
lines changed

1 file changed

+255
-0
lines changed
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
import 'package:core/core.dart';
2+
import 'package:flutter/material.dart';
3+
import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/app_config_form_fields.dart';
4+
import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/app_localizations.dart';
5+
import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart';
6+
import 'package:flutter_news_app_web_dashboard_full_source_code/shared/extensions/app_user_role_l10n.dart';
7+
import 'package:ui_kit/ui_kit.dart';
8+
9+
/// {@template feed_decorator_form}
10+
/// A form widget for configuring a single feed decorator's settings.
11+
/// {@endtemplate}
12+
class FeedDecoratorForm extends StatefulWidget {
13+
/// {@macro feed_decorator_form}
14+
const FeedDecoratorForm({
15+
required this.decoratorType,
16+
required this.remoteConfig,
17+
required this.onConfigChanged,
18+
super.key,
19+
});
20+
21+
/// The specific type of decorator this form is configuring.
22+
final FeedDecoratorType decoratorType;
23+
24+
/// The current [RemoteConfig] object.
25+
final RemoteConfig remoteConfig;
26+
27+
/// Callback to notify parent of changes to the [RemoteConfig].
28+
final ValueChanged<RemoteConfig> onConfigChanged;
29+
30+
@override
31+
State<FeedDecoratorForm> createState() => _FeedDecoratorFormState();
32+
}
33+
34+
class _FeedDecoratorFormState extends State<FeedDecoratorForm> {
35+
AppUserRole _selectedUserRole = AppUserRole.guestUser;
36+
late final TextEditingController _itemsToDisplayController;
37+
late final Map<AppUserRole, TextEditingController> _roleControllers;
38+
39+
@override
40+
void initState() {
41+
super.initState();
42+
_initializeControllers();
43+
}
44+
45+
@override
46+
void didUpdateWidget(covariant FeedDecoratorForm oldWidget) {
47+
super.didUpdateWidget(oldWidget);
48+
if (widget.remoteConfig.feedDecoratorConfig[widget.decoratorType] !=
49+
oldWidget.remoteConfig.feedDecoratorConfig[widget.decoratorType]) {
50+
_updateControllers();
51+
}
52+
}
53+
54+
void _initializeControllers() {
55+
final decoratorConfig =
56+
widget.remoteConfig.feedDecoratorConfig[widget.decoratorType]!;
57+
_itemsToDisplayController = TextEditingController(
58+
text: decoratorConfig.itemsToDisplay?.toString() ?? '',
59+
)..selection = TextSelection.collapsed(
60+
offset: decoratorConfig.itemsToDisplay?.toString().length ?? 0,
61+
);
62+
63+
_roleControllers = {
64+
for (final role in AppUserRole.values)
65+
role: TextEditingController(
66+
text: decoratorConfig.visibleTo[role]?.daysBetweenViews.toString() ??
67+
'',
68+
)..selection = TextSelection.collapsed(
69+
offset: decoratorConfig.visibleTo[role]?.daysBetweenViews
70+
.toString()
71+
.length ??
72+
0,
73+
),
74+
};
75+
}
76+
77+
void _updateControllers() {
78+
final decoratorConfig =
79+
widget.remoteConfig.feedDecoratorConfig[widget.decoratorType]!;
80+
_itemsToDisplayController.text =
81+
decoratorConfig.itemsToDisplay?.toString() ?? '';
82+
_itemsToDisplayController.selection = TextSelection.collapsed(
83+
offset: _itemsToDisplayController.text.length,
84+
);
85+
for (final role in AppUserRole.values) {
86+
_roleControllers[role]?.text =
87+
decoratorConfig.visibleTo[role]?.daysBetweenViews.toString() ?? '';
88+
_roleControllers[role]?.selection = TextSelection.collapsed(
89+
offset: _roleControllers[role]?.text.length ?? 0,
90+
);
91+
}
92+
}
93+
94+
@override
95+
void dispose() {
96+
_itemsToDisplayController.dispose();
97+
for (final controller in _roleControllers.values) {
98+
controller.dispose();
99+
}
100+
super.dispose();
101+
}
102+
103+
@override
104+
Widget build(BuildContext context) {
105+
final l10n = AppLocalizationsX(context).l10n;
106+
final decoratorConfig =
107+
widget.remoteConfig.feedDecoratorConfig[widget.decoratorType]!;
108+
109+
return Column(
110+
children: [
111+
SwitchListTile(
112+
title: Text(l10n.enabledLabel),
113+
value: decoratorConfig.enabled,
114+
onChanged: (value) {
115+
final newDecoratorConfig = decoratorConfig.copyWith(enabled: value);
116+
final newFeedDecoratorConfig =
117+
Map<FeedDecoratorType, FeedDecoratorConfig>.from(
118+
widget.remoteConfig.feedDecoratorConfig,
119+
)..[widget.decoratorType] = newDecoratorConfig;
120+
widget.onConfigChanged(
121+
widget.remoteConfig.copyWith(
122+
feedDecoratorConfig: newFeedDecoratorConfig,
123+
),
124+
);
125+
},
126+
),
127+
if (decoratorConfig.category == FeedDecoratorCategory.contentCollection)
128+
AppConfigIntField(
129+
label: l10n.itemsToDisplayLabel,
130+
description: l10n.itemsToDisplayDescription,
131+
value: decoratorConfig.itemsToDisplay ?? 0,
132+
onChanged: (value) {
133+
final newDecoratorConfig = decoratorConfig.copyWith(
134+
itemsToDisplay: value,
135+
);
136+
final newFeedDecoratorConfig =
137+
Map<FeedDecoratorType, FeedDecoratorConfig>.from(
138+
widget.remoteConfig.feedDecoratorConfig,
139+
)..[widget.decoratorType] = newDecoratorConfig;
140+
widget.onConfigChanged(
141+
widget.remoteConfig.copyWith(
142+
feedDecoratorConfig: newFeedDecoratorConfig,
143+
),
144+
);
145+
},
146+
controller: _itemsToDisplayController,
147+
),
148+
const SizedBox(height: AppSpacing.lg),
149+
Center(
150+
child: SegmentedButton<AppUserRole>(
151+
segments: AppUserRole.values
152+
.map(
153+
(role) => ButtonSegment<AppUserRole>(
154+
value: role,
155+
label: Text(role.l10n(context)),
156+
),
157+
)
158+
.toList(),
159+
selected: {_selectedUserRole},
160+
onSelectionChanged: (newSelection) {
161+
setState(() {
162+
_selectedUserRole = newSelection.first;
163+
});
164+
},
165+
),
166+
),
167+
const SizedBox(height: AppSpacing.lg),
168+
_buildRoleSpecificFields(context, l10n, _selectedUserRole, decoratorConfig),
169+
],
170+
);
171+
}
172+
173+
Widget _buildRoleSpecificFields(
174+
BuildContext context,
175+
AppLocalizations l10n,
176+
AppUserRole role,
177+
FeedDecoratorConfig decoratorConfig,
178+
) {
179+
final roleConfig = decoratorConfig.visibleTo[role];
180+
final isLinkAccountForStandardOrPremium =
181+
widget.decoratorType == FeedDecoratorType.linkAccount &&
182+
(role == AppUserRole.standardUser ||
183+
role == AppUserRole.premiumUser);
184+
185+
return Column(
186+
children: [
187+
CheckboxListTile(
188+
title: Text(l10n.visibleToRoleLabel(role.l10n(context))),
189+
value: roleConfig != null,
190+
onChanged: isLinkAccountForStandardOrPremium
191+
? null // Disable for standard and premium users for linkAccount
192+
: (value) {
193+
final newVisibleTo =
194+
Map<AppUserRole, FeedDecoratorRoleConfig>.from(
195+
decoratorConfig.visibleTo,
196+
);
197+
if (value ?? false) {
198+
newVisibleTo[role] = const FeedDecoratorRoleConfig(
199+
daysBetweenViews: 7,
200+
);
201+
} else {
202+
newVisibleTo.remove(role);
203+
}
204+
final newDecoratorConfig = decoratorConfig.copyWith(
205+
visibleTo: newVisibleTo,
206+
);
207+
final newFeedDecoratorConfig =
208+
Map<FeedDecoratorType, FeedDecoratorConfig>.from(
209+
widget.remoteConfig.feedDecoratorConfig,
210+
)..[widget.decoratorType] = newDecoratorConfig;
211+
widget.onConfigChanged(
212+
widget.remoteConfig.copyWith(
213+
feedDecoratorConfig: newFeedDecoratorConfig,
214+
),
215+
);
216+
},
217+
),
218+
if (roleConfig != null)
219+
Padding(
220+
padding: const EdgeInsets.symmetric(
221+
horizontal: AppSpacing.lg,
222+
vertical: AppSpacing.sm,
223+
),
224+
child: AppConfigIntField(
225+
label: l10n.daysBetweenViewsLabel,
226+
description: l10n.daysBetweenViewsDescription,
227+
value: roleConfig.daysBetweenViews,
228+
onChanged: (value) {
229+
final newRoleConfig = roleConfig.copyWith(
230+
daysBetweenViews: value,
231+
);
232+
final newVisibleTo =
233+
Map<AppUserRole, FeedDecoratorRoleConfig>.from(
234+
decoratorConfig.visibleTo,
235+
)..[role] = newRoleConfig;
236+
final newDecoratorConfig = decoratorConfig.copyWith(
237+
visibleTo: newVisibleTo,
238+
);
239+
final newFeedDecoratorConfig =
240+
Map<FeedDecoratorType, FeedDecoratorConfig>.from(
241+
widget.remoteConfig.feedDecoratorConfig,
242+
)..[widget.decoratorType] = newDecoratorConfig;
243+
widget.onConfigChanged(
244+
widget.remoteConfig.copyWith(
245+
feedDecoratorConfig: newFeedDecoratorConfig,
246+
),
247+
);
248+
},
249+
controller: _roleControllers[role],
250+
),
251+
),
252+
],
253+
);
254+
}
255+
}

0 commit comments

Comments
 (0)