Skip to content

Fix countrries and languages uncomplete list in the con,tent managment #43

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
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c20e7c1
refactor(content_management): improve error handling in CreateSourceBloc
fulleni Jul 31, 2025
ca6e320
feat(content_management): add pagination fields to headline states
fulleni Jul 31, 2025
2612cb5
feat(content_management): add pagination events to headline blocs
fulleni Jul 31, 2025
b15d07c
feat(content_management): implement search and pagination for countries
fulleni Jul 31, 2025
38614f1
feat(content_management): add country search and pagination
fulleni Jul 31, 2025
204b536
feat(content_management): import bloc_concurrency for enhanced state …
fulleni Jul 31, 2025
4bb564b
feat(content_management): implement paginated logic in headline BLoCs
fulleni Jul 31, 2025
9aa277e
feat(shared): create searchable paginated dropdown form field
fulleni Jul 31, 2025
b784d40
feat(content_management): integrate searchable country dropdown
fulleni Jul 31, 2025
09ba20f
feat(content_management): add pagination fields to source states
fulleni Jul 31, 2025
517766b
feat(content_management): add pagination events to source blocs
fulleni Jul 31, 2025
76fd1af
feat(content_management): implement paginated logic in CreateSourceBloc
fulleni Jul 31, 2025
1a8818d
feat(content_management): implement paginated logic in EditSourceBloc
fulleni Jul 31, 2025
97069e7
feat(content_management): integrate searchable dropdowns in source forms
fulleni Jul 31, 2025
90db0c0
refactor(shared): remove obsolete dropdown widgets
fulleni Jul 31, 2025
6058e79
fix(content_management): correct debounce logic and complete handlers
fulleni Jul 31, 2025
e1381c4
refactor(widgets): remove unused widget exports
fulleni Jul 31, 2025
0936211
fix(content_management): improve country search and pagination
fulleni Jul 31, 2025
01df3d4
fix(content_management): improve pagination and filtering logic
fulleni Jul 31, 2025
3a0a193
fix(content_management): improve countries filter and pagination
fulleni Jul 31, 2025
5f5e4bb
refactor(content_management): improve type safety in edit source bloc
fulleni Jul 31, 2025
3bb633b
fix(content_management): resolve errors in EditSourceBloc
fulleni Jul 31, 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
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:bloc/bloc.dart';
import 'package:bloc_concurrency/bloc_concurrency.dart';
import 'package:core/core.dart';
import 'package:data_repository/data_repository.dart';
import 'package:equatable/equatable.dart';
Expand All @@ -8,6 +9,8 @@ import 'package:uuid/uuid.dart';
part 'create_headline_event.dart';
part 'create_headline_state.dart';

const _searchDebounceDuration = Duration(milliseconds: 300);

/// A BLoC to manage the state of creating a new headline.
class CreateHeadlineBloc
extends Bloc<CreateHeadlineEvent, CreateHeadlineState> {
Expand All @@ -32,6 +35,12 @@ class CreateHeadlineBloc
on<CreateHeadlineCountryChanged>(_onCountryChanged);
on<CreateHeadlineStatusChanged>(_onStatusChanged);
on<CreateHeadlineSubmitted>(_onSubmitted);
on<CreateHeadlineCountrySearchChanged>(
_onCountrySearchChanged,
transformer: restartable(),
);
Comment on lines +38 to +41

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For handling search input with a delay, bloc_concurrency provides a debounce transformer that is more idiomatic for this use case than restartable() combined with a manual Future.delayed. Using debounce will make the intent clearer and the event handler code cleaner.

    on<CreateHeadlineCountrySearchChanged>(
      _onCountrySearchChanged,
      transformer: debounce(_searchDebounceDuration),
    );

on<CreateHeadlineLoadMoreCountriesRequested>(
_onLoadMoreCountriesRequested);
}

final DataRepository<Headline> _headlinesRepository;
Expand All @@ -46,33 +55,30 @@ class CreateHeadlineBloc
) async {
emit(state.copyWith(status: CreateHeadlineStatus.loading));
try {
final [
sourcesResponse,
topicsResponse,
countriesResponse,
] = await Future.wait([
final [sourcesResponse, topicsResponse] = await Future.wait([
_sourcesRepository.readAll(
sort: [const SortOption('updatedAt', SortOrder.desc)],
),
_topicsRepository.readAll(
sort: [const SortOption('updatedAt', SortOrder.desc)],
),
_countriesRepository.readAll(
sort: [const SortOption('name', SortOrder.asc)],
),
]);

final sources = (sourcesResponse as PaginatedResponse<Source>).items;
final topics = (topicsResponse as PaginatedResponse<Topic>).items;
final countries =
(countriesResponse as PaginatedResponse<Country>).items;

final countriesResponse = await _countriesRepository.readAll(
sort: [const SortOption('name', SortOrder.asc)],
);

emit(
state.copyWith(
status: CreateHeadlineStatus.initial,
sources: sources,
topics: topics,
countries: countries,
countries: countriesResponse.items,
countriesCursor: countriesResponse.cursor,
countriesHasMore: countriesResponse.hasMore,
),
);
} on HttpException catch (e) {
Expand Down Expand Up @@ -189,4 +195,72 @@ class CreateHeadlineBloc
);
}
}

Future<void> _onCountrySearchChanged(
CreateHeadlineCountrySearchChanged event,
Emitter<CreateHeadlineState> emit,
) async {
await Future<void>.delayed(_searchDebounceDuration);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

With the debounce transformer suggested for the event handler, this manual delay is no longer necessary and should be removed.

emit(state.copyWith(countrySearchTerm: event.searchTerm));
try {
final countriesResponse = await _countriesRepository.readAll(
filter:
event.searchTerm.isNotEmpty ? {'name': event.searchTerm} : null,
sort: [const SortOption('name', SortOrder.asc)],
);

emit(
state.copyWith(
countries: countriesResponse.items,
countriesCursor: countriesResponse.cursor,
countriesHasMore: countriesResponse.hasMore,
),
);
} on HttpException catch (e) {
emit(state.copyWith(status: CreateHeadlineStatus.failure, exception: e));
} catch (e) {
emit(
state.copyWith(
status: CreateHeadlineStatus.failure,
exception: UnknownException('An unexpected error occurred: $e'),
),
);
}
}

Future<void> _onLoadMoreCountriesRequested(
CreateHeadlineLoadMoreCountriesRequested event,
Emitter<CreateHeadlineState> emit,
) async {
if (!state.countriesHasMore) return;

try {
final countriesResponse = await _countriesRepository.readAll(
pagination: state.countriesCursor != null
? PaginationOptions(cursor: state.countriesCursor)
: null,
filter: state.countrySearchTerm.isNotEmpty
? {'name': state.countrySearchTerm}
: null,
sort: [const SortOption('name', SortOrder.asc)],
);

emit(
state.copyWith(
countries: List.of(state.countries)..addAll(countriesResponse.items),
countriesCursor: countriesResponse.cursor,
countriesHasMore: countriesResponse.hasMore,
),
);
} on HttpException catch (e) {
emit(state.copyWith(status: CreateHeadlineStatus.failure, exception: e));
} catch (e) {
emit(
state.copyWith(
status: CreateHeadlineStatus.failure,
exception: UnknownException('An unexpected error occurred: $e'),
),
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,17 @@ final class CreateHeadlineStatusChanged extends CreateHeadlineEvent {
final class CreateHeadlineSubmitted extends CreateHeadlineEvent {
const CreateHeadlineSubmitted();
}

/// Event for when the country search term is changed.
final class CreateHeadlineCountrySearchChanged extends CreateHeadlineEvent {
const CreateHeadlineCountrySearchChanged(this.searchTerm);
final String searchTerm;
@override
List<Object?> get props => [searchTerm];
}

/// Event to request loading more countries.
final class CreateHeadlineLoadMoreCountriesRequested
extends CreateHeadlineEvent {
const CreateHeadlineLoadMoreCountriesRequested();
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ final class CreateHeadlineState extends Equatable {
this.sources = const [],
this.topics = const [],
this.countries = const [],
this.countriesHasMore = true,
this.countriesCursor,
this.countrySearchTerm = '',
this.contentStatus = ContentStatus.active,
this.exception,
this.createdHeadline,
Expand All @@ -48,6 +51,9 @@ final class CreateHeadlineState extends Equatable {
final List<Source> sources;
final List<Topic> topics;
final List<Country> countries;
final bool countriesHasMore;
final String? countriesCursor;
final String countrySearchTerm;
final ContentStatus contentStatus;
final HttpException? exception;
final Headline? createdHeadline;
Expand All @@ -74,6 +80,9 @@ final class CreateHeadlineState extends Equatable {
List<Source>? sources,
List<Topic>? topics,
List<Country>? countries,
bool? countriesHasMore,
String? countriesCursor,
String? countrySearchTerm,
ContentStatus? contentStatus,
HttpException? exception,
Headline? createdHeadline,
Expand All @@ -90,6 +99,9 @@ final class CreateHeadlineState extends Equatable {
sources: sources ?? this.sources,
topics: topics ?? this.topics,
countries: countries ?? this.countries,
countriesHasMore: countriesHasMore ?? this.countriesHasMore,
countriesCursor: countriesCursor ?? this.countriesCursor,
countrySearchTerm: countrySearchTerm ?? this.countrySearchTerm,
contentStatus: contentStatus ?? this.contentStatus,
exception: exception,
createdHeadline: createdHeadline ?? this.createdHeadline,
Expand All @@ -109,6 +121,9 @@ final class CreateHeadlineState extends Equatable {
sources,
topics,
countries,
countriesHasMore,
countriesCursor,
countrySearchTerm,
contentStatus,
exception,
createdHeadline,
Expand Down
Loading
Loading