Skip to content

Commit d44e43d

Browse files
committed
refactor(content_limitation): improve content limitation handling
- Extract content limitation bottom sheet logic to a separate function - Update content limitation messages and actions - Rename some ContentAction values for clarity - Adjust content limitation checks in various parts of the app
1 parent 655d75b commit d44e43d

File tree

12 files changed

+52
-311
lines changed

12 files changed

+52
-311
lines changed

lib/app/bloc/app_bloc.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
1010
import 'package:flutter_news_app_mobile_client_full_source_code/ads/services/inline_ad_cache_service.dart';
1111
import 'package:flutter_news_app_mobile_client_full_source_code/app/models/app_life_cycle_status.dart';
1212
import 'package:flutter_news_app_mobile_client_full_source_code/app/models/initialization_result.dart';
13-
import 'package:flutter_news_app_mobile_client_full_source_code/headlines-feed/services/feed_cache_service.dart';
1413
import 'package:flutter_news_app_mobile_client_full_source_code/app/services/app_initializer.dart';
14+
import 'package:flutter_news_app_mobile_client_full_source_code/headlines-feed/services/feed_cache_service.dart';
1515
import 'package:flutter_news_app_mobile_client_full_source_code/notifications/services/push_notification_service.dart';
1616
import 'package:flutter_news_app_mobile_client_full_source_code/shared/extensions/extensions.dart';
1717
import 'package:flutter_news_app_mobile_client_full_source_code/shared/services/content_limitation_service.dart';

lib/app/services/demo_data_initializer_service.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class DemoDataInitializerService {
2121
DemoDataInitializerService({
2222
required DataRepository<AppSettings> appSettingsRepository,
2323
required DataRepository<UserContentPreferences>
24-
userContentPreferencesRepository,
24+
userContentPreferencesRepository,
2525
required DataRepository<InAppNotification> inAppNotificationRepository,
2626
required this.appSettingsFixturesData,
2727
required this.userContentPreferencesFixturesData,
@@ -33,7 +33,7 @@ class DemoDataInitializerService {
3333

3434
final DataRepository<AppSettings> _appSettingsRepository;
3535
final DataRepository<UserContentPreferences>
36-
_userContentPreferencesRepository;
36+
_userContentPreferencesRepository;
3737
final DataRepository<InAppNotification> _inAppNotificationRepository;
3838
final Logger _logger;
3939

@@ -62,7 +62,7 @@ class DemoDataInitializerService {
6262
/// This prevents "READ FAILED" errors when the application attempts to
6363
/// access these user-specific data points for a newly signed-in anonymous
6464
/// user in the demo environment.
65-
Future<void> initializeUserSpecificData(User user) async {
65+
Future<void> initializeUserSpecificData(User user) async {
6666
_logger.info('Initializing user-specific data for user ID: ${user.id}');
6767
await Future.wait([
6868
_ensureAppSettingsExist(user.id),

lib/discover/view/source_list_page.dart

Lines changed: 1 addition & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
44
import 'package:flutter_bloc/flutter_bloc.dart';
55
import 'package:flutter_news_app_mobile_client_full_source_code/app/bloc/app_bloc.dart';
66
import 'package:flutter_news_app_mobile_client_full_source_code/discover/bloc/source_list_bloc.dart';
7-
import 'package:flutter_news_app_mobile_client_full_source_code/l10n/app_localizations.dart';
87
import 'package:flutter_news_app_mobile_client_full_source_code/l10n/l10n.dart';
98
import 'package:flutter_news_app_mobile_client_full_source_code/router/routes.dart';
109
import 'package:flutter_news_app_mobile_client_full_source_code/shared/extensions/source_type_l10n_extensions.dart';
@@ -264,25 +263,11 @@ class _SourceListTile extends StatelessWidget {
264263
}
265264
} else {
266265
if (!context.mounted) return;
267-
final userRole = context.read<AppBloc>().state.user?.appRole;
268-
final content = _getBottomSheetContent(
266+
showContentLimitationBottomSheet(
269267
context: context,
270-
l10n: l10n,
271268
status: status,
272-
userRole: userRole,
273269
action: ContentAction.followSource,
274270
);
275-
276-
// If the limit is reached, show the informative bottom sheet.
277-
await showModalBottomSheet<void>(
278-
context: context,
279-
builder: (_) => ContentLimitationBottomSheet(
280-
title: content.title,
281-
body: content.body,
282-
buttonText: content.buttonText,
283-
onButtonPressed: content.onPressed,
284-
),
285-
);
286271
}
287272
}
288273
},
@@ -298,47 +283,3 @@ class _SourceListTile extends StatelessWidget {
298283
);
299284
}
300285
}
301-
302-
/// Determines the content for the [ContentLimitationBottomSheet] based on
303-
/// the user's role and the limitation status.
304-
({String title, String body, String buttonText, VoidCallback? onPressed})
305-
_getBottomSheetContent({
306-
required BuildContext context,
307-
required AppLocalizations l10n,
308-
required LimitationStatus status,
309-
required AppUserRole? userRole,
310-
required ContentAction action,
311-
}) {
312-
switch (status) {
313-
case LimitationStatus.anonymousLimitReached:
314-
return (
315-
title: l10n.anonymousLimitTitle,
316-
body: l10n.anonymousLimitBody,
317-
buttonText: l10n.anonymousLimitButton,
318-
onPressed: () {
319-
Navigator.of(context).pop();
320-
context.pushNamed(Routes.accountLinkingName);
321-
},
322-
);
323-
case LimitationStatus.standardUserLimitReached:
324-
// TODO(fulleni): Implement upgrade flow.
325-
return (
326-
title: l10n.standardLimitTitle,
327-
body: l10n.standardLimitBody,
328-
buttonText: l10n.standardLimitButton,
329-
onPressed: () => Navigator.of(context).pop(),
330-
);
331-
case LimitationStatus.premiumUserLimitReached:
332-
return (
333-
title: l10n.premiumLimitTitle,
334-
body: l10n.limitReachedBodyFollow,
335-
buttonText: l10n.premiumLimitButton,
336-
onPressed: () {
337-
Navigator.of(context).pop();
338-
context.goNamed(Routes.manageFollowedItemsName);
339-
},
340-
);
341-
case LimitationStatus.allowed:
342-
return (title: '', body: '', buttonText: '', onPressed: null);
343-
}
344-
}

lib/entity_details/view/entity_details_page.dart

Lines changed: 1 addition & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@ import 'package:flutter_news_app_mobile_client_full_source_code/app/bloc/app_blo
1010
import 'package:flutter_news_app_mobile_client_full_source_code/entity_details/bloc/entity_details_bloc.dart';
1111
import 'package:flutter_news_app_mobile_client_full_source_code/l10n/app_localizations.dart';
1212
import 'package:flutter_news_app_mobile_client_full_source_code/l10n/l10n.dart';
13-
import 'package:flutter_news_app_mobile_client_full_source_code/router/routes.dart';
1413
import 'package:flutter_news_app_mobile_client_full_source_code/shared/services/content_limitation_service.dart';
1514
import 'package:flutter_news_app_mobile_client_full_source_code/shared/widgets/content_limitation_bottom_sheet.dart';
1615
import 'package:flutter_news_app_mobile_client_full_source_code/shared/widgets/feed_core/feed_core.dart';
1716
import 'package:flutter_news_app_mobile_client_full_source_code/user_content/reporting/view/report_content_bottom_sheet.dart';
18-
import 'package:go_router/go_router.dart';
1917
import 'package:ui_kit/ui_kit.dart';
2018

2119
class EntityDetailsPageArguments {
@@ -214,28 +212,11 @@ class _EntityDetailsViewState extends State<EntityDetailsView> {
214212

215213
if (status != LimitationStatus.allowed) {
216214
if (!mounted) return;
217-
final userRole = context
218-
.read<AppBloc>()
219-
.state
220-
.user
221-
?.appRole;
222-
final content = _getBottomSheetContent(
215+
showContentLimitationBottomSheet(
223216
context: context,
224-
l10n: l10n,
225217
status: status,
226-
userRole: userRole,
227218
action: action,
228219
);
229-
230-
await showModalBottomSheet<void>(
231-
context: context,
232-
builder: (_) => ContentLimitationBottomSheet(
233-
title: content.title,
234-
body: content.body,
235-
buttonText: content.buttonText,
236-
onButtonPressed: content.onPressed,
237-
),
238-
);
239220
return;
240221
}
241222

@@ -475,47 +456,3 @@ class _EntityDetailsViewState extends State<EntityDetailsView> {
475456
);
476457
}
477458
}
478-
479-
/// Determines the content for the [ContentLimitationBottomSheet] based on
480-
/// the user's role and the limitation status.
481-
({String title, String body, String buttonText, VoidCallback? onPressed})
482-
_getBottomSheetContent({
483-
required BuildContext context,
484-
required AppLocalizations l10n,
485-
required LimitationStatus status,
486-
required AppUserRole? userRole,
487-
required ContentAction action,
488-
}) {
489-
switch (status) {
490-
case LimitationStatus.anonymousLimitReached:
491-
return (
492-
title: l10n.anonymousLimitTitle,
493-
body: l10n.anonymousLimitBody,
494-
buttonText: l10n.anonymousLimitButton,
495-
onPressed: () {
496-
Navigator.of(context).pop();
497-
context.pushNamed(Routes.accountLinkingName);
498-
},
499-
);
500-
case LimitationStatus.standardUserLimitReached:
501-
// TODO(fulleni): Implement upgrade flow.
502-
return (
503-
title: l10n.standardLimitTitle,
504-
body: l10n.standardLimitBody,
505-
buttonText: l10n.standardLimitButton,
506-
onPressed: () => Navigator.of(context).pop(),
507-
);
508-
case LimitationStatus.premiumUserLimitReached:
509-
return (
510-
title: l10n.premiumLimitTitle,
511-
body: l10n.limitReachedBodyFollow,
512-
buttonText: l10n.premiumLimitButton,
513-
onPressed: () {
514-
Navigator.of(context).pop();
515-
context.goNamed(Routes.manageFollowedItemsName);
516-
},
517-
);
518-
case LimitationStatus.allowed:
519-
return (title: '', body: '', buttonText: '', onPressed: null);
520-
}
521-
}

lib/headlines-feed/models/cached_feed.dart

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,7 @@ class CachedFeed extends Equatable {
4848
}
4949

5050
@override
51-
List<Object?> get props => [
52-
feedItems,
53-
hasMore,
54-
cursor,
55-
lastRefreshedAt,
56-
];
51+
List<Object?> get props => [feedItems, hasMore, cursor, lastRefreshedAt];
5752

5853
@override
5954
String toString() {

lib/headlines-feed/view/headlines_filter_page.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ Future<void> _createAndApplyFilter(BuildContext context) async {
159159
final l10n = AppLocalizations.of(context);
160160
final contentLimitationService = context.read<ContentLimitationService>();
161161
final limitationStatus = await contentLimitationService.checkAction(
162-
ContentAction.saveHeadlineFilter,
162+
ContentAction.saveFilter,
163163
);
164164

165165
// If the user has reached their limit, show the limitation bottom sheet

lib/headlines-feed/widgets/save_filter_dialog.dart

Lines changed: 5 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@ import 'package:flutter_news_app_mobile_client_full_source_code/app/bloc/app_blo
55
import 'package:flutter_news_app_mobile_client_full_source_code/l10n/app_localizations.dart';
66
import 'package:flutter_news_app_mobile_client_full_source_code/l10n/l10n.dart';
77
import 'package:flutter_news_app_mobile_client_full_source_code/notifications/services/push_notification_service.dart';
8-
import 'package:flutter_news_app_mobile_client_full_source_code/router/routes.dart';
98
import 'package:flutter_news_app_mobile_client_full_source_code/shared/services/content_limitation_service.dart';
109
import 'package:flutter_news_app_mobile_client_full_source_code/shared/widgets/content_limitation_bottom_sheet.dart';
11-
import 'package:go_router/go_router.dart';
1210
import 'package:ui_kit/ui_kit.dart';
1311

1412
/// {@template save_filter_dialog}
@@ -77,7 +75,7 @@ class _SaveFilterDialogState extends State<SaveFilterDialog> {
7775
final contentLimitationService = context.read<ContentLimitationService>();
7876

7977
final canPinStatus = await contentLimitationService.checkAction(
80-
ContentAction.pinHeadlineFilter,
78+
ContentAction.pinFilter,
8179
);
8280
if (mounted) {
8381
setState(() {
@@ -91,7 +89,7 @@ class _SaveFilterDialogState extends State<SaveFilterDialog> {
9189
final isAlreadySubscribed =
9290
widget.filterToEdit?.deliveryTypes.contains(type) ?? false;
9391
final limitationStatus = await contentLimitationService.checkAction(
94-
ContentAction.subscribeToHeadlineFilterNotifications,
92+
ContentAction.subscribeToSavedFilterNotifications,
9593
deliveryType: type,
9694
);
9795
if (mounted) {
@@ -162,28 +160,15 @@ class _SaveFilterDialogState extends State<SaveFilterDialog> {
162160
try {
163161
final limitationService = context.read<ContentLimitationService>();
164162
final status = await limitationService.checkAction(
165-
ContentAction.saveHeadlineFilter,
163+
ContentAction.saveFilter,
166164
);
167165

168166
if (status != LimitationStatus.allowed && widget.filterToEdit == null) {
169167
if (mounted) {
170-
final userRole = context.read<AppBloc>().state.user?.appRole;
171-
final content = _getBottomSheetContent(
168+
showContentLimitationBottomSheet(
172169
context: context,
173-
l10n: l10n,
174170
status: status,
175-
userRole: userRole,
176-
action: ContentAction.saveHeadlineFilter,
177-
);
178-
179-
await showModalBottomSheet<void>(
180-
context: context,
181-
builder: (_) => ContentLimitationBottomSheet(
182-
title: content.title,
183-
body: content.body,
184-
buttonText: content.buttonText,
185-
onButtonPressed: content.onPressed,
186-
),
171+
action: ContentAction.saveFilter,
187172
);
188173
}
189174
return;
@@ -345,59 +330,6 @@ class _SaveFilterDialogState extends State<SaveFilterDialog> {
345330
}
346331
}
347332

348-
/// Determines the content for the [ContentLimitationBottomSheet] based on
349-
/// the user's role and the limitation status.
350-
({String title, String body, String buttonText, VoidCallback? onPressed})
351-
_getBottomSheetContent({
352-
required BuildContext context,
353-
required AppLocalizations l10n,
354-
required LimitationStatus status,
355-
required AppUserRole? userRole,
356-
required ContentAction action,
357-
}) {
358-
switch (status) {
359-
case LimitationStatus.anonymousLimitReached:
360-
return (
361-
title: l10n.anonymousLimitTitle,
362-
body: l10n.anonymousLimitBody,
363-
buttonText: l10n.anonymousLimitButton,
364-
onPressed: () {
365-
Navigator.of(context).pop();
366-
context.pushNamed(Routes.accountLinkingName);
367-
},
368-
);
369-
case LimitationStatus.standardUserLimitReached:
370-
// TODO(fulleni): Implement upgrade flow.
371-
return (
372-
title: l10n.standardLimitTitle,
373-
body: l10n.standardLimitBody,
374-
buttonText: l10n.standardLimitButton,
375-
onPressed: () {
376-
Navigator.of(context).pop();
377-
},
378-
);
379-
case LimitationStatus.premiumUserLimitReached:
380-
final body = switch (action) {
381-
ContentAction.saveHeadlineFilter => l10n.limitReachedBodySaveFilters,
382-
ContentAction.pinHeadlineFilter => l10n.limitReachedBodyPinFilters,
383-
ContentAction.subscribeToHeadlineFilterNotifications =>
384-
l10n.limitReachedBodySubscribeToNotifications,
385-
_ => l10n.premiumLimitBody,
386-
};
387-
return (
388-
title: l10n.premiumLimitTitle,
389-
body: body,
390-
buttonText: l10n.premiumLimitButton,
391-
onPressed: () {
392-
Navigator.of(context).pop();
393-
context.goNamed(Routes.savedHeadlineFiltersName);
394-
},
395-
);
396-
case LimitationStatus.allowed:
397-
return (title: '', body: '', buttonText: '', onPressed: null);
398-
}
399-
}
400-
401333
/// An extension to provide localized strings for [PushNotificationSubscriptionDeliveryType].
402334
extension on PushNotificationSubscriptionDeliveryType {
403335
String toL10n(AppLocalizations l10n) {

0 commit comments

Comments
 (0)