Skip to content

Commit a7070ab

Browse files
authored
Merge pull request #23 from headlines-toolkit/fix_account_content_preferences
Fix account content preferences
2 parents ff6fdf9 + 37935b3 commit a7070ab

21 files changed

+1382
-313
lines changed

analysis_options.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ analyzer:
22
errors:
33
avoid_catches_without_on_clauses: ignore
44
avoid_print: ignore
5+
avoid_redundant_argument_values: ignore
56
document_ignores: ignore
67
flutter_style_todos: ignore
78
lines_longer_than_80_chars: ignore

lib/account/bloc/account_bloc.dart

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,27 +69,36 @@ class AccountBloc extends Bloc<AccountEvent, AccountState> {
6969
AccountLoadContentPreferencesRequested event,
7070
Emitter<AccountState> emit,
7171
) async {
72-
emit(state.copyWith(status: AccountStatus.loading));
72+
emit(state.copyWith(status: AccountStatus.loading)); // Indicate loading
7373
try {
7474
final preferences = await _userContentPreferencesRepository.read(
7575
id: event.userId,
76-
userId: event.userId, // Preferences are user-scoped
76+
userId: event.userId,
7777
);
7878
emit(
7979
state.copyWith(status: AccountStatus.success, preferences: preferences),
8080
);
81-
} on HtHttpException catch (e) {
81+
} on NotFoundException { // Specifically handle NotFound
82+
emit(
83+
state.copyWith(
84+
status: AccountStatus.success, // It's a success, just no data
85+
preferences: UserContentPreferences(id: event.userId), // Provide default/empty
86+
),
87+
);
88+
} on HtHttpException catch (e) { // Handle other HTTP errors
8289
emit(
8390
state.copyWith(
8491
status: AccountStatus.failure,
8592
errorMessage: 'Failed to load preferences: ${e.message}',
93+
preferences: UserContentPreferences(id: event.userId), // Provide default
8694
),
8795
);
88-
} catch (e) {
96+
} catch (e) { // Catch-all for other unexpected errors
8997
emit(
9098
state.copyWith(
9199
status: AccountStatus.failure,
92100
errorMessage: 'An unexpected error occurred: $e',
101+
preferences: UserContentPreferences(id: event.userId), // Provide default
93102
),
94103
);
95104
}
@@ -108,22 +117,27 @@ class AccountBloc extends Bloc<AccountEvent, AccountState> {
108117
);
109118
return;
110119
}
120+
print('[AccountBloc._persistPreferences] Attempting to persist preferences for user ${state.user!.id}');
121+
print('[AccountBloc._persistPreferences] Preferences to save: ${preferences.toJson()}');
111122
try {
112123
await _userContentPreferencesRepository.update(
113124
id: state.user!.id, // ID of the preferences object is the user's ID
114125
item: preferences,
115126
userId: state.user!.id,
116127
);
128+
print('[AccountBloc._persistPreferences] Successfully persisted preferences for user ${state.user!.id}');
117129
// Optimistic update already done, emit success if needed for UI feedback
118130
// emit(state.copyWith(status: AccountStatus.success));
119131
} on HtHttpException catch (e) {
132+
print('[AccountBloc._persistPreferences] HtHttpException while persisting: ${e.message}');
120133
emit(
121134
state.copyWith(
122135
status: AccountStatus.failure,
123136
errorMessage: 'Failed to save preferences: ${e.message}',
124137
),
125138
);
126139
} catch (e) {
140+
print('[AccountBloc._persistPreferences] Unknown error while persisting: $e');
127141
emit(
128142
state.copyWith(
129143
status: AccountStatus.failure,
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import 'dart:async';
2+
3+
import 'package:bloc/bloc.dart';
4+
import 'package:equatable/equatable.dart';
5+
import 'package:ht_data_repository/ht_data_repository.dart';
6+
import 'package:ht_shared/ht_shared.dart' show Country, HtHttpException;
7+
8+
part 'available_countries_event.dart';
9+
part 'available_countries_state.dart';
10+
11+
class AvailableCountriesBloc
12+
extends Bloc<AvailableCountriesEvent, AvailableCountriesState> {
13+
AvailableCountriesBloc({
14+
required HtDataRepository<Country> countriesRepository,
15+
}) : _countriesRepository = countriesRepository,
16+
super(const AvailableCountriesState()) {
17+
on<FetchAvailableCountries>(_onFetchAvailableCountries);
18+
}
19+
20+
final HtDataRepository<Country> _countriesRepository;
21+
22+
Future<void> _onFetchAvailableCountries(
23+
FetchAvailableCountries event,
24+
Emitter<AvailableCountriesState> emit,
25+
) async {
26+
if (state.status == AvailableCountriesStatus.loading ||
27+
state.status == AvailableCountriesStatus.success) {
28+
return;
29+
}
30+
emit(state.copyWith(status: AvailableCountriesStatus.loading));
31+
try {
32+
final response = await _countriesRepository.readAll();
33+
emit(
34+
state.copyWith(
35+
status: AvailableCountriesStatus.success,
36+
availableCountries: response.items,
37+
clearError: true,
38+
),
39+
);
40+
} on HtHttpException catch (e) {
41+
emit(
42+
state.copyWith(
43+
status: AvailableCountriesStatus.failure,
44+
error: e.message,
45+
),
46+
);
47+
} catch (e) {
48+
emit(
49+
state.copyWith(
50+
status: AvailableCountriesStatus.failure,
51+
error: 'An unexpected error occurred while fetching countries.',
52+
),
53+
);
54+
}
55+
}
56+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
part of 'available_countries_bloc.dart';
2+
3+
abstract class AvailableCountriesEvent extends Equatable {
4+
const AvailableCountriesEvent();
5+
6+
@override
7+
List<Object> get props => [];
8+
}
9+
10+
class FetchAvailableCountries extends AvailableCountriesEvent {
11+
const FetchAvailableCountries();
12+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
part of 'available_countries_bloc.dart';
2+
3+
enum AvailableCountriesStatus { initial, loading, success, failure }
4+
5+
class AvailableCountriesState extends Equatable {
6+
const AvailableCountriesState({
7+
this.status = AvailableCountriesStatus.initial,
8+
this.availableCountries = const [],
9+
this.error,
10+
// Properties for pagination if added later
11+
// this.hasMore = true,
12+
// this.cursor,
13+
});
14+
15+
final AvailableCountriesStatus status;
16+
final List<Country> availableCountries;
17+
final String? error;
18+
// final bool hasMore;
19+
// final String? cursor;
20+
21+
AvailableCountriesState copyWith({
22+
AvailableCountriesStatus? status,
23+
List<Country>? availableCountries,
24+
String? error,
25+
bool clearError = false,
26+
// bool? hasMore,
27+
// String? cursor,
28+
// bool clearCursor = false,
29+
}) {
30+
return AvailableCountriesState(
31+
status: status ?? this.status,
32+
availableCountries: availableCountries ?? this.availableCountries,
33+
error: clearError ? null : error ?? this.error,
34+
// hasMore: hasMore ?? this.hasMore,
35+
// cursor: clearCursor ? null : (cursor ?? this.cursor),
36+
);
37+
}
38+
39+
@override
40+
List<Object?> get props => [
41+
status,
42+
availableCountries,
43+
error,
44+
// hasMore, // Add if pagination is implemented
45+
// cursor, // Add if pagination is implemented
46+
];
47+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import 'dart:async';
2+
3+
import 'package:bloc/bloc.dart';
4+
import 'package:equatable/equatable.dart';
5+
import 'package:ht_data_repository/ht_data_repository.dart';
6+
import 'package:ht_shared/ht_shared.dart' show HtHttpException, Source;
7+
8+
part 'available_sources_event.dart';
9+
part 'available_sources_state.dart';
10+
11+
class AvailableSourcesBloc
12+
extends Bloc<AvailableSourcesEvent, AvailableSourcesState> {
13+
AvailableSourcesBloc({
14+
required HtDataRepository<Source> sourcesRepository,
15+
}) : _sourcesRepository = sourcesRepository,
16+
super(const AvailableSourcesState()) {
17+
on<FetchAvailableSources>(_onFetchAvailableSources);
18+
}
19+
20+
final HtDataRepository<Source> _sourcesRepository;
21+
// Consider adding a limit if the number of sources can be very large.
22+
// static const _sourcesLimit = 50;
23+
24+
Future<void> _onFetchAvailableSources(
25+
FetchAvailableSources event,
26+
Emitter<AvailableSourcesState> emit,
27+
) async {
28+
if (state.status == AvailableSourcesStatus.loading ||
29+
state.status == AvailableSourcesStatus.success) {
30+
// Avoid re-fetching if already loading or loaded,
31+
// unless a refresh mechanism is added.
32+
return;
33+
}
34+
emit(state.copyWith(status: AvailableSourcesStatus.loading));
35+
try {
36+
// Assuming readAll without parameters fetches all items.
37+
// Add pagination if necessary for very large datasets.
38+
final response = await _sourcesRepository.readAll(
39+
// limit: _sourcesLimit, // Uncomment if pagination is needed
40+
);
41+
emit(
42+
state.copyWith(
43+
status: AvailableSourcesStatus.success,
44+
availableSources: response.items,
45+
// hasMore: response.hasMore, // Uncomment if pagination is needed
46+
// cursor: response.cursor, // Uncomment if pagination is needed
47+
clearError: true,
48+
),
49+
);
50+
} on HtHttpException catch (e) {
51+
emit(
52+
state.copyWith(
53+
status: AvailableSourcesStatus.failure,
54+
error: e.message,
55+
),
56+
);
57+
} catch (e) {
58+
emit(
59+
state.copyWith(
60+
status: AvailableSourcesStatus.failure,
61+
error: 'An unexpected error occurred while fetching sources.',
62+
),
63+
);
64+
}
65+
}
66+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
part of 'available_sources_bloc.dart';
2+
3+
abstract class AvailableSourcesEvent extends Equatable {
4+
const AvailableSourcesEvent();
5+
6+
@override
7+
List<Object> get props => [];
8+
}
9+
10+
class FetchAvailableSources extends AvailableSourcesEvent {
11+
const FetchAvailableSources();
12+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
part of 'available_sources_bloc.dart';
2+
3+
enum AvailableSourcesStatus { initial, loading, success, failure }
4+
5+
class AvailableSourcesState extends Equatable {
6+
const AvailableSourcesState({
7+
this.status = AvailableSourcesStatus.initial,
8+
this.availableSources = const [],
9+
this.error,
10+
// Properties for pagination if added later
11+
// this.hasMore = true,
12+
// this.cursor,
13+
});
14+
15+
final AvailableSourcesStatus status;
16+
final List<Source> availableSources;
17+
final String? error;
18+
// final bool hasMore;
19+
// final String? cursor;
20+
21+
AvailableSourcesState copyWith({
22+
AvailableSourcesStatus? status,
23+
List<Source>? availableSources,
24+
String? error,
25+
bool clearError = false,
26+
// bool? hasMore,
27+
// String? cursor,
28+
// bool clearCursor = false,
29+
}) {
30+
return AvailableSourcesState(
31+
status: status ?? this.status,
32+
availableSources: availableSources ?? this.availableSources,
33+
error: clearError ? null : error ?? this.error,
34+
// hasMore: hasMore ?? this.hasMore,
35+
// cursor: clearCursor ? null : (cursor ?? this.cursor),
36+
);
37+
}
38+
39+
@override
40+
List<Object?> get props => [
41+
status,
42+
availableSources,
43+
error,
44+
// hasMore, // Add if pagination is implemented
45+
// cursor, // Add if pagination is implemented
46+
];
47+
}

lib/account/view/account_page.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class AccountPage extends StatelessWidget {
4444
title: Text(l10n.accountContentPreferencesTile),
4545
trailing: const Icon(Icons.chevron_right),
4646
onTap: () {
47-
context.goNamed(Routes.accountContentPreferencesName);
47+
context.goNamed(Routes.manageFollowedItemsName); // Updated route
4848
},
4949
),
5050
const Divider(), // Divider after Content Preferences

0 commit comments

Comments
 (0)