Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
219 commits
Select commit Hold shift + click to select a range
4ab30cf
feat(dependencies): update core reference and add in_app_review depen…
fulleni Dec 3, 2025
d03a6bb
feat(l10n): add feedback and reporting translations
fulleni Dec 3, 2025
8343947
build(serialization): sync
fulleni Dec 3, 2025
b32cce8
feat(user_content): add native review service
fulleni Dec 3, 2025
9892440
feat(bootstrap): provide user content repositories and services
fulleni Dec 3, 2025
dab5079
feat(app): provide user content dependencies in App widget
fulleni Dec 3, 2025
1ee27bb
feat(app): add positive interaction tracking to AppBloc
fulleni Dec 3, 2025
92049da
feat(app): add positiveInteractionCount to AppState
fulleni Dec 3, 2025
29711f7
feat(app): define AppPositiveInteractionOcurred event
fulleni Dec 3, 2025
6a4af00
feat: ignore one_member_abstracts lint rule
fulleni Dec 3, 2025
2492cdf
fix(user_content): update app review service and data flow
fulleni Dec 3, 2025
536351a
feat(user_content): implement AppReviewService
fulleni Dec 3, 2025
2635cf0
feat(user_content): create RateAppBottomSheet
fulleni Dec 3, 2025
2019720
feat(user_content): create ProvideFeedbackBottomSheet
fulleni Dec 3, 2025
8c16639
feat(shared): add comment and report limits to ContentLimitationService
fulleni Dec 3, 2025
e8537f9
feat(demo): initialize user content data for demo mode
fulleni Dec 3, 2025
a23b22e
feat(demo): migrate user content data for demo users
fulleni Dec 3, 2025
20fa003
feat(user_content): create HeadlineActionsRow widget
fulleni Dec 3, 2025
3780780
refactor(feed): remove redundant more icon from HeadlineSourceRow
fulleni Dec 3, 2025
7a6df16
feat(feed): integrate HeadlineActionsRow into text-only tile
fulleni Dec 3, 2025
42138f3
feat(feed): integrate HeadlineActionsRow into image-start tile
fulleni Dec 3, 2025
b3e7176
feat(feed): integrate HeadlineActionsRow into image-top tile
fulleni Dec 3, 2025
751cd1f
refactor(feed): simplify HeadlineActionsBottomSheet for reporting
fulleni Dec 3, 2025
6bb3aae
feat(user_content): create ReportContentBottomSheet
fulleni Dec 3, 2025
1a295fc
feat(entity_details): add report action and positive interaction event
fulleni Dec 3, 2025
7293a66
feat(user_content): define EngagementBloc state
fulleni Dec 3, 2025
a0c9bce
feat(user_content): define EngagementBloc events
fulleni Dec 3, 2025
376debc
feat(user_content): implement EngagementBloc
fulleni Dec 3, 2025
ccdb4bf
feat(user_content): create ReactionSelector widget
fulleni Dec 3, 2025
b98346c
feat(user_content): create EngagementBottomSheet
fulleni Dec 3, 2025
0b89901
feat(feed): add logic to open engagement bottom sheet
fulleni Dec 3, 2025
03a7306
style: format
fulleni Dec 3, 2025
21dc96f
docs(README): add community and feedback systems description
fulleni Dec 3, 2025
b566fd0
docs(README): enhance Intelligent Review Funnel description
fulleni Dec 3, 2025
6ff89ca
feat(router): add route for engagement bottom sheet
fulleni Dec 3, 2025
52fb4cf
feat(router): implement engagement bottom sheet route
fulleni Dec 3, 2025
3f25d5a
feat(feed): define event to open engagement sheet
fulleni Dec 3, 2025
8e2e359
feat(feed): handle opening engagement bottom sheet
fulleni Dec 3, 2025
ad63f83
feat(feed): add navigation arguments to feed state
fulleni Dec 3, 2025
90ff157
feat(engagement): connect action buttons to BLoC
fulleni Dec 3, 2025
3f261e2
refactor(engagement): implement optimistic updates in EngagementBloc
fulleni Dec 3, 2025
6eb3da3
feat(engagement): implement comment input field and list UI
fulleni Dec 3, 2025
fd00bdb
feat(engagement): add icons to ReactionSelector
fulleni Dec 3, 2025
7fd4218
feat(engagement): implement quick reactions on headline tiles
fulleni Dec 3, 2025
3028177
feat(feed): implement navigation to engagement sheet
fulleni Dec 3, 2025
1ad5497
refactor(user_content): improve app review process and data recording
fulleni Dec 3, 2025
54a25ac
fix(feed): adjust headline actions row spacing and headline text layout
fulleni Dec 3, 2025
5a89435
refactor(dependencies): reorder and indent constructor parameters
fulleni Dec 3, 2025
f5178e0
feat(l10n): add translations for comment section
fulleni Dec 3, 2025
97359ed
refactor: remove unreachable code and update switch cases
fulleni Dec 3, 2025
70c44c5
refactor(engagement): improve code structure and consistency
fulleni Dec 3, 2025
3ce1d6f
refactor(reporting): update import path for localization
fulleni Dec 3, 2025
d5867b8
chore(deps): update core to latest commit
fulleni Dec 4, 2025
2cdd09f
fix(app-review): wrap feedback details update in ValueWrapper
fulleni Dec 4, 2025
f98a0a7
fix(engagement): wrap comment update in ValueWrapper
fulleni Dec 4, 2025
1b21efe
feat(l10n): add translation keys for content reporting
fulleni Dec 4, 2025
1e24209
build(l10n): sync
fulleni Dec 4, 2025
851d5fd
feat(DemoDataInitializerService): add support for Engagement, Report,…
fulleni Dec 4, 2025
a7ac660
refactor(user_content): Improve report content functionality and loca…
fulleni Dec 4, 2025
6028e2b
fix(l10n): correct arb file typo
fulleni Dec 4, 2025
b651250
feat(l10n): add 'Misinformation' report reason and fix formatting
fulleni Dec 4, 2025
a934434
build(l10n): sync
fulleni Dec 4, 2025
a928a3c
refactor(user_content): improve code structure and add localization
fulleni Dec 4, 2025
aea3406
feat(l10n): add localization strings for content limits and management
fulleni Dec 5, 2025
439ad6d
build(l10n): sync
fulleni Dec 5, 2025
7dc8bc4
build(dependencies): update core package reference
fulleni Dec 5, 2025
d85fc17
refactor(shared): update country client import path
fulleni Dec 5, 2025
eee3b86
feat(content limitation): implement environment-specific content limi…
fulleni Dec 5, 2025
6f68623
refactor(app): Restore in-memory app review interaction counting
fulleni Dec 5, 2025
31b6a13
refactor(review): Decouple AppReviewService from state persistence
fulleni Dec 5, 2025
c1a57ae
refactor(core)!: Abstract ContentLimitationService for env-specific l…
fulleni Dec 5, 2025
f4fb0e2
feat(core): Implement no-op ProductionContentLimitationService
fulleni Dec 5, 2025
ca69de6
feat(demo): Implement in-memory DemoContentLimitationService
fulleni Dec 5, 2025
fe569bd
feat(ui): Make ContentLimitationBottomSheet configurable
fulleni Dec 5, 2025
1a8d6c9
fix(feed_core): update headline tile spacing
fulleni Dec 5, 2025
8d3ce4b
feat(headlines-feed): implement content limitation handling in save f…
fulleni Dec 5, 2025
492a9dc
feat(engagement): add content limitation checks
fulleni Dec 5, 2025
915ec4a
feat(engagement): add limitationStatus to EngagementState
fulleni Dec 5, 2025
abf0eec
feat(engagement): conditionally render comment input field based on c…
fulleni Dec 5, 2025
ef2e7ab
feat(feed_decorators): implement content limitation for follow actions
fulleni Dec 5, 2025
c8273ce
feat(shared): expand headline actions bottom sheet
fulleni Dec 5, 2025
ca74c8a
feat(headline_source_row): adjust text opacity for improved readability
fulleni Dec 5, 2025
3e4e4c4
refactor(user_content): update headline actions and engagement flow
fulleni Dec 5, 2025
89aa047
feat(user_content): inject ContentLimitationService into HeadlineActi…
fulleni Dec 5, 2025
7597ea4
refactor(account): improve country follow/unfollow functionality
fulleni Dec 5, 2025
65181ae
refactor(account): add _FollowButton widget and move logic inside it
fulleni Dec 5, 2025
3788e2b
feat(account): add follow button with limitation handling
fulleni Dec 5, 2025
5ca94fa
fix(reporting): improve report submission process
fulleni Dec 5, 2025
badf7a3
refactor(router): remove unused engagement route and import
fulleni Dec 5, 2025
dec4c60
fix(entity_details): improve follow button interaction and UI
fulleni Dec 5, 2025
eeddfc1
fix(headlines-feed): improve code structure and readability
fulleni Dec 5, 2025
83dc382
refactor(discover): improve source following action handling
fulleni Dec 5, 2025
50b0f7d
refactor(headlines-feed): improve filter saving limitation handling
fulleni Dec 5, 2025
e6f3e0a
fix: misc
fulleni Dec 5, 2025
ed2988e
refactor(content_limitation): overhauling service structure and logic
fulleni Dec 5, 2025
8fe24cb
feat(engagement): add content limitation service and fix reaction type
fulleni Dec 5, 2025
da4e84e
feat(account): add follow button to add topic to follow page
fulleni Dec 5, 2025
cba0e07
fix(user_content): handle null user engagement and update reaction ha…
fulleni Dec 5, 2025
0f5995f
refactor: improve null safety in headline_actions_row.dart
fulleni Dec 5, 2025
409f4d1
refactor(account): enhance content limitation handling for country fo…
fulleni Dec 5, 2025
f71914f
refactor(account): enhance content limitation handling in follow page
fulleni Dec 5, 2025
52dfe94
refactor(account): enhance content limitation bottom sheet logic
fulleni Dec 5, 2025
8e3aca3
feat(discover): enhance content limitation handling
fulleni Dec 5, 2025
ecc1b37
refactor(entity_details): enhance content limitation handling
fulleni Dec 5, 2025
d2bffe8
refactor(headlines-feed): improve save filter dialog functionality
fulleni Dec 5, 2025
9a5c748
refactor(headline_actions): enhance content limitation handling
fulleni Dec 5, 2025
eb98a07
refactor(reporting): improve report submission handling and UI
fulleni Dec 5, 2025
3d823e2
style: format .
fulleni Dec 5, 2025
3077f75
ux(HeadlineActions): improve user experience after bottom sheet actions
fulleni Dec 6, 2025
78c311a
feat(engagement): simplify comment posting conditions
fulleni Dec 6, 2025
923b6f2
feat(user_content): wrap report content bottom sheet in SafeArea
fulleni Dec 6, 2025
47899c7
fix(user-content): update icon for skeptical reaction
fulleni Dec 6, 2025
7dae83b
feat(app_review): add environment-aware initial prompt cooldown
fulleni Dec 6, 2025
61873ff
fix(demo): handle conflicts when migrating or initializing demo data
fulleni Dec 6, 2025
ce86d70
feat(engagement): enhance multi-entity support and optimize UI synchr…
fulleni Dec 6, 2025
9b389ba
refactor(user_content): rename and simplify comments bottom sheet
fulleni Dec 6, 2025
3a12ae2
refactor(user_content): rename and re design inline reaction selector
fulleni Dec 6, 2025
4c3e7d0
refactor(user_content): simplify headline actions row
fulleni Dec 6, 2025
42aa7bf
refactor(engagement): remove AppEngagementChanged events
fulleni Dec 6, 2025
66710d6
feat(router): add engagement repository to router dependencies
fulleni Dec 6, 2025
fa390a1
feat(headlines-feed): sync feed items with engagement changes
fulleni Dec 6, 2025
e82677f
feat(l10n): add Arabic and English translations for comments feature
fulleni Dec 6, 2025
d1a817c
build(l10n): sync
fulleni Dec 6, 2025
86dbfd8
feat(headline): add actions bottom sheet
fulleni Dec 6, 2025
5dc86cf
refactor(engagement): improve comments button and modal
fulleni Dec 6, 2025
e3452a1
feat(InlineReactionSelector): add customizable color for unselected r…
fulleni Dec 6, 2025
aca4ebf
refactor(engagement): improve code structure and readability
fulleni Dec 8, 2025
5a640e6
feat(l10n): add localizations for guest user limit warnings
fulleni Dec 9, 2025
eed5186
feat(service): implement ContentLimitationService with caching and pr…
fulleni Dec 9, 2025
ebd5ba8
build(l10n): sync
fulleni Dec 9, 2025
b9e62d9
feat(service): implement ContentLimitationService initialization
fulleni Dec 9, 2025
5223fcd
feat(app): inject ContentLimitationService into AppInitializationPage
fulleni Dec 9, 2025
d567c0a
refactor(app): move ContentLimitationService initialization to bootstrap
fulleni Dec 9, 2025
09ca616
refactor(shared): update ContentLimitationService initialization
fulleni Dec 9, 2025
dfedf03
refactor(account): improve async handling and naming in follow button
fulleni Dec 9, 2025
b674813
refactor(account): improve context usage and upgrade flow
fulleni Dec 9, 2025
6af6341
fix(account): handle follow action limitation and improve UI flow
fulleni Dec 9, 2025
c475fae
refactor(account): improve follow button async logic and upgrade flow
fulleni Dec 9, 2025
2e5750c
refactor(headlines-feed): improve limitation handling in save filter …
fulleni Dec 9, 2025
979fe17
fix(engagement): handle content limitations correctly
fulleni Dec 9, 2025
ff69562
refactor(app): update ContentLimitationService initialization comment
fulleni Dec 9, 2025
977d055
fix(headlines-feed): update content limitation modal for filter saving
fulleni Dec 9, 2025
ebf0934
style(TODO): update username reference in add
fulleni Dec 9, 2025
3e0de4a
style: Update TODO comment with username
fulleni Dec 9, 2025
9a69ce9
style: Update TODO comment with username
fulleni Dec 9, 2025
6dde7cd
fix(discover): handle source follow action correctly
fulleni Dec 9, 2025
c24819f
refactor(entity_details): improve limitation handling and modal content
fulleni Dec 9, 2025
2dd48be
refactor: update TODO assignee in save_filter_dialog.dart
fulleni Dec 9, 2025
2b0dd1d
feat(content limitation): handle bookmark and report limitations
fulleni Dec 9, 2025
f0f7bcb
refactor(reporting): improve report submission flow and limit handling
fulleni Dec 9, 2025
6cc209e
fix(discover): handle content limitation status correctly
fulleni Dec 9, 2025
3066a8e
fix(feed_decorators): await limitationService.checkAction to ensure p…
fulleni Dec 9, 2025
d0028e4
fix(headlines-feed): await checkAction() to ensure filter creation
fulleni Dec 9, 2025
c4b0223
style: format
fulleni Dec 9, 2025
2dece8e
feat(l10n): add Arabic translations for comments feature
fulleni Dec 11, 2025
6982f91
build(l10n): sync
fulleni Dec 11, 2025
4bb7f82
feat(engagement): implement comment update functionality
fulleni Dec 11, 2025
2d8c47d
refactor(headline-actions): improve bottom sheet handling and localiz…
fulleni Dec 11, 2025
621a8b4
feat(engagement): enhance comments bottom sheet functionality
fulleni Dec 11, 2025
d15af6c
refactor(user_content): enhance headline actions row functionality
fulleni Dec 11, 2025
b7337f4
refactor(engagement): remove userId parameter from readAll calls
fulleni Dec 11, 2025
d3cda98
refactor(engagement): optimize comments feature in headline actions row
fulleni Dec 11, 2025
1ec4933
refactor(user_content): remove EngagementBloc from CommentsBottomSheet
fulleni Dec 11, 2025
fff9693
refactor(headlines-feed): remove unused engagement repository
fulleni Dec 11, 2025
9fbcd7e
): remove unused engagement repository
fulleni Dec 11, 2025
bfc633c
fix(user-content): optimize comment posting and update logic
fulleni Dec 11, 2025
27557e4
feat(comments): enhance comment interaction and functionality
fulleni Dec 11, 2025
88c9928
refactor(headlines-feed): integrate engagement management into Headli…
fulleni Dec 11, 2025
e17bf55
refactor(engagement): remove deprecated EngagementBloc
fulleni Dec 11, 2025
cca80b6
style(headlines-feed): format Bloc and State classes
fulleni Dec 11, 2025
eee8529
feat(CachedFeed): add engagementsMap to store entity engagements
fulleni Dec 11, 2025
e4f04fd
feat(headlines-feed): add comments navigation from headlines feed
fulleni Dec 11, 2025
0765b2b
refactor(user_content): rewrite comments bottom sheet
fulleni Dec 11, 2025
5279127
refactor(engagement): simplify HeadlineActionsRow and remove Engageme…
fulleni Dec 11, 2025
a50c348
feat(feed_core): add engagement state to headline actions row
fulleni Dec 11, 2025
97db86a
feat(router): add content limitation service to app router
fulleni Dec 11, 2025
f923e4c
feat(headlines-feed): implement comment updating functionality
fulleni Dec 11, 2025
0a6820e
feat(headlines-feed): implement content limitation handling
fulleni Dec 11, 2025
b62e1d3
refactor(headlines-feed): remove duplicate content limitation bottom …
fulleni Dec 11, 2025
6b054b1
feat(shared): add content limitation bottom sheet helper
fulleni Dec 11, 2025
c7059eb
feat(app): implement bookmark toggling and content reporting
fulleni Dec 11, 2025
49853c6
refactor(headline_actions): move bookmark toggle and report content t…
fulleni Dec 11, 2025
1ac9de6
refactor(reporting): simplify report submission process
fulleni Dec 11, 2025
b0a228d
feat(App): add content limitation and report services
fulleni Dec 11, 2025
327b442
style: format
fulleni Dec 11, 2025
f8d3b92
feat(l10n): add hint text for existing comments
fulleni Dec 11, 2025
b454281
build(l10n): sync
fulleni Dec 11, 2025
5cc4cdb
refactor(comments): improve comment input field and engagement handling
fulleni Dec 11, 2025
9f96218
refactor(logger): adjust log levels in demo services
fulleni Dec 11, 2025
f7abef1
refactor(engagement): improve code structure and readability
fulleni Dec 11, 2025
1470909
refactor(headlines-feed): simplify navigation arguments handling
fulleni Dec 11, 2025
54c4dff
fix: style
fulleni Dec 11, 2025
b135b61
refactor(headlines-feed): remove engagementsMap from CachedFeed and u…
fulleni Dec 11, 2025
c10bcfe
fix(app): update positive interaction count after eligibility check
fulleni Dec 11, 2025
eb8fa43
refactor(DemoDataMigrationService): migrate and delete engagements in…
fulleni Dec 11, 2025
9c37ae9
refactor(user_content): improve code structure in comments bottom sheet
fulleni Dec 12, 2025
301a1c1
refactor(router): wrap SavedHeadlinesPage in BlocProvider for depende…
fulleni Dec 12, 2025
8050e87
feat(l10n): add commenterName string for missing user objects
fulleni Dec 12, 2025
74bf191
build(l10n): sync
fulleni Dec 12, 2025
32a0ce1
feat(user_content): display user email first letter in comments avatar
fulleni Dec 12, 2025
3dc8459
chore: update core git ref in pubspec.yaml
fulleni Dec 12, 2025
fc7ba06
refactor(demo): remove unused data repositories and fixtures
fulleni Dec 12, 2025
ae0563c
feat(app): implement cache clearing for feed and ads on user change a…
fulleni Dec 12, 2025
4cd691d
refactor(data_migration): remove engagement migration
fulleni Dec 12, 2025
a480f42
style(l10n): update rating prompt button texts
fulleni Dec 12, 2025
6e98e59
build(l10n): build
fulleni Dec 12, 2025
a77605d
refactor(comments): simplify commenter name display
fulleni Dec 12, 2025
3faa2ff
fix(ui): adjust report content bottom sheet layout
fulleni Dec 12, 2025
a07a194
refactor(widgets): adjust layout and spacing of HeadlineTileImageStart
fulleni Dec 12, 2025
374f0d7
feat(reporting): add conditional headline reporting
fulleni Dec 12, 2025
4c83a49
feat(entity-details): conditionally show report option for sources
fulleni Dec 12, 2025
762e021
refactor(app-review): improve app review feature disablement logic
fulleni Dec 12, 2025
0f5246e
fix(headline_actions): update headline reporting enabled check
fulleni Dec 12, 2025
77a6c60
fix(entity-details): update condition for source reporting enablement
fulleni Dec 12, 2025
162d4ae
refactor(l10n): remove unused limit title translations
fulleni Dec 12, 2025
83a6ca4
build(l10n): sync
fulleni Dec 12, 2025
bb90381
refactor(account): remove duplicate limitation bottom sheet code
fulleni Dec 12, 2025
655d75b
refactor(feed_decorators): use dedicated function for content limitat…
fulleni Dec 12, 2025
d44e43d
refactor(content_limitation): improve content limitation handling
fulleni Dec 12, 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
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ A robust, backend-driven notification system keeps users informed and brings the
- **Integrated Notification Center:** Includes a full-featured in-app notification center where users can view their history. Foreground notifications are handled gracefully, appearing as an unread indicator that leads the user to this central hub, avoiding intrusive system alerts during active use.
> **Your Advantage:** You get a highly flexible and scalable notification system that avoids vendor lock-in and is ready to re-engage users from day one.

---

### 💬 Community & Feedback Systems
A complete suite of tools to build a vibrant user community and gather valuable feedback directly within the app.
- **Configurable Headline Engagement:** Enable immediate user interaction directly on each headline within the feed. The entire engagement system is controlled via remote configuration, allowing you to dynamically adjust the depth of user interaction—from simple reactions to full comment threads—without an app update.
- **Intelligent Review Funnel:** A sophisticated, multi-layered system that strategically prompts users for an app review. Its behavior is entirely driven by remote configuration, including cooldown periods and positive interaction thresholds. It first gauges user sentiment with a private, in-app prompt: positive responses trigger the native OS review dialog, while negative responses open a private feedback form, ensuring you only ask happy users for public reviews and capture valuable insights from others.
- **Moderated Content Reporting:** Empower your community to maintain content quality with a built-in reporting system. Users can easily report headlines, sources, or individual comments through a guided process. All reports are submitted to the backend and are designed to be managed and actioned from the companion web dashboard.
> **Your Advantage:** Deploy a full-featured community and feedback system from day one. Skip the complexity of building engagement UI, state management for reactions, and the nuanced logic of a best-practice app review funnel.

</details>

<details>
Expand Down
1 change: 1 addition & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ analyzer:
document_ignores: ignore
flutter_style_todos: ignore
lines_longer_than_80_chars: ignore
one_member_abstracts: ignore
prefer_asserts_with_message: ignore
use_build_context_synchronously: ignore
use_if_null_to_convert_nulls_to_bools: ignore
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,113 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_news_app_mobile_client_full_source_code/account/bloc/available_countries_bloc.dart';
import 'package:flutter_news_app_mobile_client_full_source_code/app/bloc/app_bloc.dart';
import 'package:flutter_news_app_mobile_client_full_source_code/l10n/app_localizations.dart';
import 'package:flutter_news_app_mobile_client_full_source_code/l10n/l10n.dart';
import 'package:flutter_news_app_mobile_client_full_source_code/shared/services/content_limitation_service.dart';
import 'package:flutter_news_app_mobile_client_full_source_code/shared/widgets/content_limitation_bottom_sheet.dart';
import 'package:ui_kit/ui_kit.dart';

class _FollowButton extends StatefulWidget {
const _FollowButton({required this.country, required this.isFollowed});

final Country country;
final bool isFollowed;

@override
State<_FollowButton> createState() => _FollowButtonState();
}

class _FollowButtonState extends State<_FollowButton> {
bool _isLoading = false;

Future<void> _onFollowToggled() async {
setState(() => _isLoading = true);

final l10n = AppLocalizations.of(context);
final appBloc = context.read<AppBloc>();
final userContentPreferences = appBloc.state.userContentPreferences;

if (userContentPreferences == null) {
setState(() => _isLoading = false);
return;
}

final updatedFollowedCountries = List<Country>.from(
userContentPreferences.followedCountries,
);

try {
if (widget.isFollowed) {
updatedFollowedCountries.removeWhere((c) => c.id == widget.country.id);
} else {
final limitationService = context.read<ContentLimitationService>();
final status = await limitationService.checkAction(
ContentAction.followCountry,
);

if (status != LimitationStatus.allowed) {
if (mounted) {
showContentLimitationBottomSheet(
context: context,
status: status,
action: ContentAction.followCountry,
);
}
return;
}
updatedFollowedCountries.add(widget.country);
}

final updatedPreferences = userContentPreferences.copyWith(
followedCountries: updatedFollowedCountries,
);

appBloc.add(
AppUserContentPreferencesChanged(preferences: updatedPreferences),
);
} on ForbiddenException catch (e) {
if (mounted) {
await showModalBottomSheet<void>(
context: context,
builder: (_) => ContentLimitationBottomSheet(
title: l10n.limitReachedTitle,
body: e.message,
buttonText: l10n.gotItButton,
),
);
}
} finally {
if (mounted) {
setState(() => _isLoading = false);
}
}
}

@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context);
final colorScheme = Theme.of(context).colorScheme;

if (_isLoading) {
return const SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(strokeWidth: 2),
);
}

return IconButton(
icon: widget.isFollowed
? Icon(Icons.check_circle, color: colorScheme.primary)
: const Icon(Icons.add_circle_outline),
tooltip: widget.isFollowed
? l10n.unfollowCountryTooltip(widget.country.name)
: l10n.followCountryTooltip(widget.country.name),
onPressed: _onFollowToggled,
);
}
}

/// {@template add_country_to_follow_page}
/// A page that allows users to browse and select countries to follow.
/// {@endtemplate}
Expand Down Expand Up @@ -138,76 +240,9 @@ class AddCountryToFollowPage extends StatelessWidget {
),
),
title: Text(country.name, style: textTheme.titleMedium),
trailing: IconButton(
icon: isFollowed
? Icon(
Icons.check_circle,
color: colorScheme.primary,
)
: Icon(
Icons.add_circle_outline,
color: colorScheme.onSurfaceVariant,
),
tooltip: isFollowed
? l10n.unfollowCountryTooltip(country.name)
: l10n.followCountryTooltip(country.name),
onPressed: () {
// Ensure user preferences are available before
// proceeding.
if (userContentPreferences == null) return;

// Create a mutable copy of the followed countries list.
final updatedFollowedCountries = List<Country>.from(
followedCountries,
);

// If the user is unfollowing, always allow it.
if (isFollowed) {
updatedFollowedCountries.removeWhere(
(c) => c.id == country.id,
);
final updatedPreferences = userContentPreferences
.copyWith(
followedCountries: updatedFollowedCountries,
);

context.read<AppBloc>().add(
AppUserContentPreferencesChanged(
preferences: updatedPreferences,
),
);
} else {
// If the user is following, check the limit first.
final limitationService = context
.read<ContentLimitationService>();
final status = limitationService.checkAction(
ContentAction.followCountry,
);

if (status == LimitationStatus.allowed) {
updatedFollowedCountries.add(country);
final updatedPreferences =
userContentPreferences.copyWith(
followedCountries:
updatedFollowedCountries,
);

context.read<AppBloc>().add(
AppUserContentPreferencesChanged(
preferences: updatedPreferences,
),
);
} else {
// If the limit is reached, show the bottom sheet.
showModalBottomSheet<void>(
context: context,
builder: (_) => ContentLimitationBottomSheet(
status: status,
),
);
}
}
},
trailing: _FollowButton(
country: country,
isFollowed: isFollowed,
),
contentPadding: const EdgeInsets.symmetric(
horizontal: AppSpacing.paddingMedium,
Expand Down
Loading
Loading