Skip to content

Commit db82d71

Browse files
committed
feat(account): add country to follow page
- Implemented country selection - Fetches countries from repository - Shows loading/error states
1 parent b1e600a commit db82d71

File tree

1 file changed

+143
-0
lines changed

1 file changed

+143
-0
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_bloc/flutter_bloc.dart';
3+
import 'package:ht_main/account/bloc/account_bloc.dart';
4+
// TODO(cline): May need a specific BLoC for fetching all countries if not using an existing one.
5+
// For now, we'll assume direct repository access or a placeholder.
6+
import 'package:ht_main/l10n/l10n.dart';
7+
import 'package:ht_main/shared/constants/app_spacing.dart';
8+
import 'package:ht_main/shared/widgets/widgets.dart';
9+
import 'package:ht_shared/ht_shared.dart';
10+
import 'package:ht_data_repository/ht_data_repository.dart';
11+
12+
/// {@template add_country_to_follow_page}
13+
/// A page that allows users to browse and select countries to follow.
14+
/// {@endtemplate}
15+
class AddCountryToFollowPage extends StatefulWidget {
16+
/// {@macro add_country_to_follow_page}
17+
const AddCountryToFollowPage({super.key});
18+
19+
@override
20+
State<AddCountryToFollowPage> createState() => _AddCountryToFollowPageState();
21+
}
22+
23+
class _AddCountryToFollowPageState extends State<AddCountryToFollowPage> {
24+
List<Country> _allCountries = [];
25+
bool _isLoading = true;
26+
String? _errorMessage;
27+
28+
@override
29+
void initState() {
30+
super.initState();
31+
_fetchCountries();
32+
}
33+
34+
Future<void> _fetchCountries() async {
35+
setState(() {
36+
_isLoading = true;
37+
_errorMessage = null;
38+
});
39+
try {
40+
// Directly use the repository to fetch all countries
41+
// This assumes the Country repository's readAll method without userId
42+
// fetches all available countries.
43+
final countryRepository = context.read<HtDataRepository<Country>>();
44+
final paginatedResponse = await countryRepository.readAll();
45+
setState(() {
46+
_allCountries = paginatedResponse.items;
47+
_isLoading = false;
48+
});
49+
} on HtHttpException catch (e) {
50+
setState(() {
51+
_isLoading = false;
52+
_errorMessage = e.message;
53+
});
54+
} catch (e) {
55+
setState(() {
56+
_isLoading = false;
57+
_errorMessage = context.l10n.unknownError;
58+
});
59+
}
60+
}
61+
62+
@override
63+
Widget build(BuildContext context) {
64+
final l10n = context.l10n;
65+
66+
return Scaffold(
67+
appBar: AppBar(
68+
title: Text(l10n.addCountriesPageTitle), // New l10n key
69+
),
70+
body: Builder(
71+
builder: (context) {
72+
if (_isLoading) {
73+
return const Center(child: CircularProgressIndicator());
74+
}
75+
if (_errorMessage != null) {
76+
return FailureStateWidget(
77+
message: _errorMessage!,
78+
onRetry: _fetchCountries,
79+
);
80+
}
81+
if (_allCountries.isEmpty) {
82+
return FailureStateWidget(
83+
message: l10n.countryFilterEmptyHeadline, // Re-use
84+
);
85+
}
86+
87+
return BlocBuilder<AccountBloc, AccountState>(
88+
builder: (context, accountState) {
89+
final followedCountries =
90+
accountState.preferences?.followedCountries ?? [];
91+
92+
return ListView.builder(
93+
padding: const EdgeInsets.all(AppSpacing.md),
94+
itemCount: _allCountries.length,
95+
itemBuilder: (context, index) {
96+
final country = _allCountries[index];
97+
final isFollowed =
98+
followedCountries.any((fc) => fc.id == country.id);
99+
100+
return Card(
101+
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
102+
child: ListTile(
103+
leading: country.flagUrl.isNotEmpty &&
104+
Uri.tryParse(country.flagUrl)?.isAbsolute == true
105+
? SizedBox(
106+
width: 36,
107+
height: 24,
108+
child: Image.network(
109+
country.flagUrl,
110+
fit: BoxFit.cover,
111+
errorBuilder: (context, error, stackTrace) =>
112+
const Icon(Icons.public_outlined),
113+
),
114+
)
115+
: const Icon(Icons.public_outlined),
116+
title: Text(country.name),
117+
trailing: IconButton(
118+
icon: isFollowed
119+
? Icon(
120+
Icons.check_circle,
121+
color: Theme.of(context).colorScheme.primary,
122+
)
123+
: const Icon(Icons.add_circle_outline),
124+
tooltip: isFollowed
125+
? l10n.unfollowCountryTooltip(country.name)
126+
: l10n.followCountryTooltip(country.name), // New
127+
onPressed: () {
128+
context.read<AccountBloc>().add(
129+
AccountFollowCountryToggled(country: country),
130+
);
131+
},
132+
),
133+
),
134+
);
135+
},
136+
);
137+
},
138+
);
139+
},
140+
),
141+
);
142+
}
143+
}

0 commit comments

Comments
 (0)