Skip to content

Fix account content preferences #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
May 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ analyzer:
errors:
avoid_catches_without_on_clauses: ignore
avoid_print: ignore
avoid_redundant_argument_values: ignore
document_ignores: ignore
flutter_style_todos: ignore
lines_longer_than_80_chars: ignore
Expand Down
22 changes: 18 additions & 4 deletions lib/account/bloc/account_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,27 +69,36 @@ class AccountBloc extends Bloc<AccountEvent, AccountState> {
AccountLoadContentPreferencesRequested event,
Emitter<AccountState> emit,
) async {
emit(state.copyWith(status: AccountStatus.loading));
emit(state.copyWith(status: AccountStatus.loading)); // Indicate loading
try {
final preferences = await _userContentPreferencesRepository.read(
id: event.userId,
userId: event.userId, // Preferences are user-scoped
userId: event.userId,
);
emit(
state.copyWith(status: AccountStatus.success, preferences: preferences),
);
} on HtHttpException catch (e) {
} on NotFoundException { // Specifically handle NotFound
emit(
state.copyWith(
status: AccountStatus.success, // It's a success, just no data
preferences: UserContentPreferences(id: event.userId), // Provide default/empty
),
);
} on HtHttpException catch (e) { // Handle other HTTP errors
emit(
state.copyWith(
status: AccountStatus.failure,
errorMessage: 'Failed to load preferences: ${e.message}',
preferences: UserContentPreferences(id: event.userId), // Provide default
),
);
} catch (e) {
} catch (e) { // Catch-all for other unexpected errors
emit(
state.copyWith(
status: AccountStatus.failure,
errorMessage: 'An unexpected error occurred: $e',
preferences: UserContentPreferences(id: event.userId), // Provide default
),
);
}
Expand All @@ -108,22 +117,27 @@ class AccountBloc extends Bloc<AccountEvent, AccountState> {
);
return;
}
print('[AccountBloc._persistPreferences] Attempting to persist preferences for user ${state.user!.id}');
print('[AccountBloc._persistPreferences] Preferences to save: ${preferences.toJson()}');
try {
await _userContentPreferencesRepository.update(
id: state.user!.id, // ID of the preferences object is the user's ID
item: preferences,
userId: state.user!.id,
);
print('[AccountBloc._persistPreferences] Successfully persisted preferences for user ${state.user!.id}');
// Optimistic update already done, emit success if needed for UI feedback
// emit(state.copyWith(status: AccountStatus.success));
} on HtHttpException catch (e) {
print('[AccountBloc._persistPreferences] HtHttpException while persisting: ${e.message}');
emit(
state.copyWith(
status: AccountStatus.failure,
errorMessage: 'Failed to save preferences: ${e.message}',
),
);
} catch (e) {
print('[AccountBloc._persistPreferences] Unknown error while persisting: $e');
emit(
state.copyWith(
status: AccountStatus.failure,
Expand Down
56 changes: 56 additions & 0 deletions lib/account/bloc/available_countries_bloc.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:ht_data_repository/ht_data_repository.dart';
import 'package:ht_shared/ht_shared.dart' show Country, HtHttpException;

part 'available_countries_event.dart';
part 'available_countries_state.dart';

class AvailableCountriesBloc
extends Bloc<AvailableCountriesEvent, AvailableCountriesState> {
AvailableCountriesBloc({
required HtDataRepository<Country> countriesRepository,
}) : _countriesRepository = countriesRepository,
super(const AvailableCountriesState()) {
on<FetchAvailableCountries>(_onFetchAvailableCountries);
}

final HtDataRepository<Country> _countriesRepository;

Future<void> _onFetchAvailableCountries(
FetchAvailableCountries event,
Emitter<AvailableCountriesState> emit,
) async {
if (state.status == AvailableCountriesStatus.loading ||
state.status == AvailableCountriesStatus.success) {
return;
}
emit(state.copyWith(status: AvailableCountriesStatus.loading));
try {
final response = await _countriesRepository.readAll();
emit(
state.copyWith(
status: AvailableCountriesStatus.success,
availableCountries: response.items,
clearError: true,
),
);
} on HtHttpException catch (e) {
emit(
state.copyWith(
status: AvailableCountriesStatus.failure,
error: e.message,
),
);
} catch (e) {
emit(
state.copyWith(
status: AvailableCountriesStatus.failure,
error: 'An unexpected error occurred while fetching countries.',
),
);
}
}
}
12 changes: 12 additions & 0 deletions lib/account/bloc/available_countries_event.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
part of 'available_countries_bloc.dart';

abstract class AvailableCountriesEvent extends Equatable {
const AvailableCountriesEvent();

@override
List<Object> get props => [];
}

class FetchAvailableCountries extends AvailableCountriesEvent {
const FetchAvailableCountries();
}
47 changes: 47 additions & 0 deletions lib/account/bloc/available_countries_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
part of 'available_countries_bloc.dart';

enum AvailableCountriesStatus { initial, loading, success, failure }

class AvailableCountriesState extends Equatable {
const AvailableCountriesState({
this.status = AvailableCountriesStatus.initial,
this.availableCountries = const [],
this.error,
// Properties for pagination if added later
// this.hasMore = true,
// this.cursor,
});

final AvailableCountriesStatus status;
final List<Country> availableCountries;
final String? error;
// final bool hasMore;
// final String? cursor;

AvailableCountriesState copyWith({
AvailableCountriesStatus? status,
List<Country>? availableCountries,
String? error,
bool clearError = false,
// bool? hasMore,
// String? cursor,
// bool clearCursor = false,
}) {
return AvailableCountriesState(
status: status ?? this.status,
availableCountries: availableCountries ?? this.availableCountries,
error: clearError ? null : error ?? this.error,
// hasMore: hasMore ?? this.hasMore,
// cursor: clearCursor ? null : (cursor ?? this.cursor),
);
}

@override
List<Object?> get props => [
status,
availableCountries,
error,
// hasMore, // Add if pagination is implemented
// cursor, // Add if pagination is implemented
];
}
66 changes: 66 additions & 0 deletions lib/account/bloc/available_sources_bloc.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:ht_data_repository/ht_data_repository.dart';
import 'package:ht_shared/ht_shared.dart' show HtHttpException, Source;

part 'available_sources_event.dart';
part 'available_sources_state.dart';

class AvailableSourcesBloc
extends Bloc<AvailableSourcesEvent, AvailableSourcesState> {
AvailableSourcesBloc({
required HtDataRepository<Source> sourcesRepository,
}) : _sourcesRepository = sourcesRepository,
super(const AvailableSourcesState()) {
on<FetchAvailableSources>(_onFetchAvailableSources);
}

final HtDataRepository<Source> _sourcesRepository;
// Consider adding a limit if the number of sources can be very large.
// static const _sourcesLimit = 50;

Future<void> _onFetchAvailableSources(
FetchAvailableSources event,
Emitter<AvailableSourcesState> emit,
) async {
if (state.status == AvailableSourcesStatus.loading ||
state.status == AvailableSourcesStatus.success) {
// Avoid re-fetching if already loading or loaded,
// unless a refresh mechanism is added.
return;
}
emit(state.copyWith(status: AvailableSourcesStatus.loading));
try {
// Assuming readAll without parameters fetches all items.
// Add pagination if necessary for very large datasets.
final response = await _sourcesRepository.readAll(
// limit: _sourcesLimit, // Uncomment if pagination is needed
);
emit(
state.copyWith(
status: AvailableSourcesStatus.success,
availableSources: response.items,
// hasMore: response.hasMore, // Uncomment if pagination is needed
// cursor: response.cursor, // Uncomment if pagination is needed
clearError: true,
),
);
} on HtHttpException catch (e) {
emit(
state.copyWith(
status: AvailableSourcesStatus.failure,
error: e.message,
),
);
} catch (e) {
emit(
state.copyWith(
status: AvailableSourcesStatus.failure,
error: 'An unexpected error occurred while fetching sources.',
),
);
}
}
}
12 changes: 12 additions & 0 deletions lib/account/bloc/available_sources_event.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
part of 'available_sources_bloc.dart';

abstract class AvailableSourcesEvent extends Equatable {
const AvailableSourcesEvent();

@override
List<Object> get props => [];
}

class FetchAvailableSources extends AvailableSourcesEvent {
const FetchAvailableSources();
}
47 changes: 47 additions & 0 deletions lib/account/bloc/available_sources_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
part of 'available_sources_bloc.dart';

enum AvailableSourcesStatus { initial, loading, success, failure }

class AvailableSourcesState extends Equatable {
const AvailableSourcesState({
this.status = AvailableSourcesStatus.initial,
this.availableSources = const [],
this.error,
// Properties for pagination if added later
// this.hasMore = true,
// this.cursor,
});

final AvailableSourcesStatus status;
final List<Source> availableSources;
final String? error;
// final bool hasMore;
// final String? cursor;

AvailableSourcesState copyWith({
AvailableSourcesStatus? status,
List<Source>? availableSources,
String? error,
bool clearError = false,
// bool? hasMore,
// String? cursor,
// bool clearCursor = false,
}) {
return AvailableSourcesState(
status: status ?? this.status,
availableSources: availableSources ?? this.availableSources,
error: clearError ? null : error ?? this.error,
// hasMore: hasMore ?? this.hasMore,
// cursor: clearCursor ? null : (cursor ?? this.cursor),
);
}

@override
List<Object?> get props => [
status,
availableSources,
error,
// hasMore, // Add if pagination is implemented
// cursor, // Add if pagination is implemented
];
}
2 changes: 1 addition & 1 deletion lib/account/view/account_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class AccountPage extends StatelessWidget {
title: Text(l10n.accountContentPreferencesTile),
trailing: const Icon(Icons.chevron_right),
onTap: () {
context.goNamed(Routes.accountContentPreferencesName);
context.goNamed(Routes.manageFollowedItemsName); // Updated route
},
),
const Divider(), // Divider after Content Preferences
Expand Down
Loading
Loading