From 5bf5e325ca3267b5ac155a01191545946de29718 Mon Sep 17 00:00:00 2001 From: Zied Dahmani Date: Thu, 26 Sep 2024 19:57:48 +0100 Subject: [PATCH] fix: filter proposals --- lib/core/di/bloc_module.dart | 1 - .../extension/proposals_filter_extension.dart | 7 ++ .../components/filter_proposals_view.dart | 3 +- .../filter/components/hypha_filter_card.dart | 6 +- .../interactor/filter_proposals_bloc.dart | 10 +-- ...ggregate_dao_proposal_counts_use_case.dart | 7 +- ...et_daos_from_proposal_counts_use_case.dart | 8 --- .../list/components/proposals_view.dart | 67 +++++++++-------- .../list/interactor/proposals_bloc.dart | 25 +++---- .../interactor/proposals_bloc.freezed.dart | 72 ++++--------------- .../list/interactor/proposals_event.dart | 1 - 11 files changed, 85 insertions(+), 122 deletions(-) create mode 100644 lib/core/extension/proposals_filter_extension.dart delete mode 100644 lib/ui/proposals/filter/usecases/get_daos_from_proposal_counts_use_case.dart diff --git a/lib/core/di/bloc_module.dart b/lib/core/di/bloc_module.dart index f05f7ab5..ec059eb7 100644 --- a/lib/core/di/bloc_module.dart +++ b/lib/core/di/bloc_module.dart @@ -144,7 +144,6 @@ void _registerBlocsModule() { _getIt(), _getIt(), _getIt(), - _getIt(), _getIt(), )); } \ No newline at end of file diff --git a/lib/core/extension/proposals_filter_extension.dart b/lib/core/extension/proposals_filter_extension.dart new file mode 100644 index 00000000..74b45b2b --- /dev/null +++ b/lib/core/extension/proposals_filter_extension.dart @@ -0,0 +1,7 @@ +import 'package:hypha_wallet/core/network/models/proposal_model.dart'; + +extension ProposalFilterExtension on List { + List filterByDao(List daoIds) { + return where((proposal) => daoIds.contains(proposal.dao?.docId)).toList(); + } +} diff --git a/lib/ui/proposals/filter/components/filter_proposals_view.dart b/lib/ui/proposals/filter/components/filter_proposals_view.dart index 0e868344..f9fdf372 100644 --- a/lib/ui/proposals/filter/components/filter_proposals_view.dart +++ b/lib/ui/proposals/filter/components/filter_proposals_view.dart @@ -74,7 +74,8 @@ class FilterProposalsView extends StatelessWidget { HyphaAppButton( title: 'SAVE FILTERS', onPressed: () { - filterProposalsBloc.add(FilterProposalsEvent.saveFilters(filterProposalsBloc.selectedDaoIndexNotifier.value == null ? state.daoProposalCounts: [state.daoProposalCounts[filterProposalsBloc.selectedDaoIndexNotifier.value!]], filterProposalsBloc.selectedStatusIndexNotifier.value == 0 ? FilterStatus.active : FilterStatus.past)); + final int? index = filterProposalsBloc.selectedDaoIndexNotifier.value; + filterProposalsBloc.add(FilterProposalsEvent.saveFilters(index == null ? state.daoProposalCounts : [state.daoProposalCounts[index]], filterProposalsBloc.selectedStatusIndexNotifier.value == 0 ? FilterStatus.active : FilterStatus.past)); }, ), const SizedBox( diff --git a/lib/ui/proposals/filter/components/hypha_filter_card.dart b/lib/ui/proposals/filter/components/hypha_filter_card.dart index 53d9429d..372373ef 100644 --- a/lib/ui/proposals/filter/components/hypha_filter_card.dart +++ b/lib/ui/proposals/filter/components/hypha_filter_card.dart @@ -1,11 +1,9 @@ import 'package:flutter/material.dart'; import 'package:hypha_wallet/core/network/models/dao_data_model.dart'; -import 'package:hypha_wallet/design/avatar_image/hypha_avatar_image.dart'; +import 'package:hypha_wallet/design/dao_image.dart'; import 'package:hypha_wallet/design/hypha_card.dart'; import 'package:hypha_wallet/design/hypha_colors.dart'; -import 'package:hypha_wallet/design/ipfs_image.dart'; import 'package:hypha_wallet/design/themes/extensions/theme_extension_provider.dart'; -import 'package:hypha_wallet/design/dao_image.dart'; class HyphaFilterCard extends StatelessWidget { final DaoData? dao; @@ -21,7 +19,7 @@ class HyphaFilterCard extends StatelessWidget { // TODO(Saif): fix the card height (filter by status) return GestureDetector( onTap: () { - valueNotifier.value = index; + valueNotifier.value = valueNotifier.value == index ? null : index; }, child: HyphaCard( child: Padding( diff --git a/lib/ui/proposals/filter/interactor/filter_proposals_bloc.dart b/lib/ui/proposals/filter/interactor/filter_proposals_bloc.dart index c58269d7..6f2324b1 100644 --- a/lib/ui/proposals/filter/interactor/filter_proposals_bloc.dart +++ b/lib/ui/proposals/filter/interactor/filter_proposals_bloc.dart @@ -11,7 +11,6 @@ import 'package:hypha_wallet/ui/profile/usecases/fetch_profile_use_case.dart'; import 'package:hypha_wallet/ui/proposals/filter/interactor/dao_proposal_count_entity.dart'; import 'package:hypha_wallet/ui/proposals/filter/interactor/filter_status.dart'; import 'package:hypha_wallet/ui/proposals/filter/usecases/aggregate_dao_proposal_counts_use_case.dart'; -import 'package:hypha_wallet/ui/proposals/filter/usecases/get_daos_from_proposal_counts_use_case.dart'; import 'package:hypha_wallet/ui/proposals/list/interactor/proposals_bloc.dart'; part 'page_command.dart'; @@ -23,14 +22,12 @@ class FilterProposalsBloc extends Bloc(_initial); @@ -40,11 +37,13 @@ class FilterProposalsBloc extends Bloc _selectedDaoIndexNotifier = ValueNotifier(null); + final ValueNotifier _selectedDaoIndexNotifier = ValueNotifier(0); final ValueNotifier _selectedStatusIndexNotifier = ValueNotifier(0); + List? _daoIds; ValueNotifier get selectedDaoIndexNotifier => _selectedDaoIndexNotifier; ValueNotifier get selectedStatusIndexNotifier => _selectedStatusIndexNotifier; + List? get daoIds => _daoIds; Future _initial(_Initial event, Emitter emit) async { emit(state.copyWith(pageState: PageState.loading)); @@ -76,7 +75,8 @@ class FilterProposalsBloc extends Bloc _saveFilters(_SaveFilters event, Emitter emit) async { - _proposalsBloc.add(ProposalsEvent.initial(daos: _getDaosFromProposalCountsUseCase.run(event.daoProposalCounts), filterStatus: event.filterStatus)); + _daoIds = event.daoProposalCounts.map((DaoProposalCountEntity daoProposalCount) => daoProposalCount.dao.docId).toList(); + _proposalsBloc.add(ProposalsEvent.initial(filterStatus: event.filterStatus)); emit(state.copyWith(command: const PageCommand.navigateToProposals())); } } diff --git a/lib/ui/proposals/filter/usecases/aggregate_dao_proposal_counts_use_case.dart b/lib/ui/proposals/filter/usecases/aggregate_dao_proposal_counts_use_case.dart index 61ad439b..7c35c325 100644 --- a/lib/ui/proposals/filter/usecases/aggregate_dao_proposal_counts_use_case.dart +++ b/lib/ui/proposals/filter/usecases/aggregate_dao_proposal_counts_use_case.dart @@ -20,8 +20,9 @@ class AggregateDaoProposalCountsUseCase { } } - return daoProposalCounts.entries - .map((entry) => DaoProposalCountEntity(entry.key, entry.value)) - .toList(); + final List> sortedEntries = daoProposalCounts.entries.toList() + ..sort((a, b) => a.key.settingsDaoTitle.compareTo(b.key.settingsDaoTitle)); + + return sortedEntries.map((entry) => DaoProposalCountEntity(entry.key, entry.value)).toList(); } } diff --git a/lib/ui/proposals/filter/usecases/get_daos_from_proposal_counts_use_case.dart b/lib/ui/proposals/filter/usecases/get_daos_from_proposal_counts_use_case.dart deleted file mode 100644 index ffd5fa31..00000000 --- a/lib/ui/proposals/filter/usecases/get_daos_from_proposal_counts_use_case.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:hypha_wallet/core/network/models/dao_data_model.dart'; -import 'package:hypha_wallet/ui/proposals/filter/interactor/dao_proposal_count_entity.dart'; - -class GetDaosFromProposalCountsUseCase { - List run(List daoProposalCounts) { - return daoProposalCounts.map((entity) => entity.dao).toList(); - } -} diff --git a/lib/ui/proposals/list/components/proposals_view.dart b/lib/ui/proposals/list/components/proposals_view.dart index 215fbc2b..44be8146 100644 --- a/lib/ui/proposals/list/components/proposals_view.dart +++ b/lib/ui/proposals/list/components/proposals_view.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:get/get.dart' as GetX; +import 'package:get_it/get_it.dart'; +import 'package:hypha_wallet/core/extension/proposals_filter_extension.dart'; +import 'package:hypha_wallet/core/network/models/proposal_model.dart'; import 'package:hypha_wallet/design/avatar_image/hypha_avatar_image.dart'; import 'package:hypha_wallet/design/background/hypha_page_background.dart'; import 'package:hypha_wallet/design/hypha_colors.dart'; @@ -84,36 +87,40 @@ class ProposalsView extends StatelessWidget { ), child: HyphaBodyWidget( pageState: state.pageState, - success: (context) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 22), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox( - height: 22, - ), - Text( - '${state.proposals.length} ${context.read().filterStatus.string} Proposal${state.proposals.length == 1 ? '' : 's'}', - style: context.hyphaTextTheme.ralMediumBody - .copyWith(color: HyphaColors.midGrey), - ), - const SizedBox( - height: 20, - ), - Expanded( - child: ListView.separated( - padding: const EdgeInsets.only(bottom: 22), - itemBuilder: (BuildContext context, - int index) => - HyphaProposalsActionCard(state.proposals[index]), - separatorBuilder: - (BuildContext context, int index) { - return const SizedBox(height: 16); - }, - itemCount: state.proposals.length)), - ], - ), - ), + success: (context) { + final List? daoIds = GetIt.I.get().daoIds; + final List proposals = daoIds != null ? state.proposals.filterByDao(daoIds) : state.proposals; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 22), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 22, + ), + Text( + '${proposals.length} ${context.read().filterStatus.string} Proposal${proposals.length == 1 ? '' : 's'}', + style: context.hyphaTextTheme.ralMediumBody + .copyWith(color: HyphaColors.midGrey), + ), + const SizedBox( + height: 20, + ), + Expanded( + child: ListView.separated( + padding: const EdgeInsets.only(bottom: 22), + itemBuilder: (BuildContext context, + int index) => + HyphaProposalsActionCard(proposals[index]), + separatorBuilder: + (BuildContext context, int index) { + return const SizedBox(height: 16); + }, + itemCount: proposals.length)), + ], + ), + ); + }, ), )), floatingActionButton: IconButton( diff --git a/lib/ui/proposals/list/interactor/proposals_bloc.dart b/lib/ui/proposals/list/interactor/proposals_bloc.dart index ba9d7d76..df94554c 100644 --- a/lib/ui/proposals/list/interactor/proposals_bloc.dart +++ b/lib/ui/proposals/list/interactor/proposals_bloc.dart @@ -24,6 +24,7 @@ class ProposalsBloc extends Bloc { on<_Initial>(_initial); } + List? _daos; FilterStatus filterStatus = FilterStatus.active; Future _initial(_Initial event, Emitter emit) async { @@ -32,19 +33,19 @@ class ProposalsBloc extends Bloc { filterStatus = event.filterStatus; } - if (event.daos != null) { - await _fetchAndEmitProposals(emit, event.daos!, filterStatus); - return; - } - - final Result profileResult = await _fetchProfileUseCase.run(); - - if (profileResult.isValue && profileResult.asValue!.value.daos.isNotEmpty) { - await _fetchAndEmitProposals(emit, profileResult.asValue!.value.daos, filterStatus); + if (_daos == null) { + final Result profileResult = await _fetchProfileUseCase.run(); + + if (profileResult.isValue && profileResult.asValue!.value.daos.isNotEmpty) { + _daos = profileResult.asValue!.value.daos; + await _fetchAndEmitProposals(emit, _daos!, filterStatus); + } else { + final HyphaError error = profileResult.isError ? profileResult.asError!.error : HyphaError.api('Failed to retrieve DAOs'); + await _errorHandlerManager.handlerError(error); + emit(state.copyWith(pageState: PageState.failure)); + } } else { - final HyphaError error = profileResult.isError ? profileResult.asError!.error : HyphaError.api('Failed to retrieve DAOs'); - await _errorHandlerManager.handlerError(error); - emit(state.copyWith(pageState: PageState.failure)); + await _fetchAndEmitProposals(emit, _daos!, filterStatus); } } diff --git a/lib/ui/proposals/list/interactor/proposals_bloc.freezed.dart b/lib/ui/proposals/list/interactor/proposals_bloc.freezed.dart index d58bf8b9..79658a00 100644 --- a/lib/ui/proposals/list/interactor/proposals_bloc.freezed.dart +++ b/lib/ui/proposals/list/interactor/proposals_bloc.freezed.dart @@ -17,27 +17,20 @@ final _privateConstructorUsedError = UnsupportedError( /// @nodoc mixin _$ProposalsEvent { bool get refresh => throw _privateConstructorUsedError; - List? get daos => throw _privateConstructorUsedError; FilterStatus get filterStatus => throw _privateConstructorUsedError; @optionalTypeArgs TResult when({ - required TResult Function( - bool refresh, List? daos, FilterStatus filterStatus) - initial, + required TResult Function(bool refresh, FilterStatus filterStatus) initial, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult? whenOrNull({ - TResult? Function( - bool refresh, List? daos, FilterStatus filterStatus)? - initial, + TResult? Function(bool refresh, FilterStatus filterStatus)? initial, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult maybeWhen({ - TResult Function( - bool refresh, List? daos, FilterStatus filterStatus)? - initial, + TResult Function(bool refresh, FilterStatus filterStatus)? initial, required TResult orElse(), }) => throw _privateConstructorUsedError; @@ -71,7 +64,7 @@ abstract class $ProposalsEventCopyWith<$Res> { ProposalsEvent value, $Res Function(ProposalsEvent) then) = _$ProposalsEventCopyWithImpl<$Res, ProposalsEvent>; @useResult - $Res call({bool refresh, List? daos, FilterStatus filterStatus}); + $Res call({bool refresh, FilterStatus filterStatus}); } /// @nodoc @@ -90,7 +83,6 @@ class _$ProposalsEventCopyWithImpl<$Res, $Val extends ProposalsEvent> @override $Res call({ Object? refresh = null, - Object? daos = freezed, Object? filterStatus = null, }) { return _then(_value.copyWith( @@ -98,10 +90,6 @@ class _$ProposalsEventCopyWithImpl<$Res, $Val extends ProposalsEvent> ? _value.refresh : refresh // ignore: cast_nullable_to_non_nullable as bool, - daos: freezed == daos - ? _value.daos - : daos // ignore: cast_nullable_to_non_nullable - as List?, filterStatus: null == filterStatus ? _value.filterStatus : filterStatus // ignore: cast_nullable_to_non_nullable @@ -118,7 +106,7 @@ abstract class _$$InitialImplCopyWith<$Res> __$$InitialImplCopyWithImpl<$Res>; @override @useResult - $Res call({bool refresh, List? daos, FilterStatus filterStatus}); + $Res call({bool refresh, FilterStatus filterStatus}); } /// @nodoc @@ -135,7 +123,6 @@ class __$$InitialImplCopyWithImpl<$Res> @override $Res call({ Object? refresh = null, - Object? daos = freezed, Object? filterStatus = null, }) { return _then(_$InitialImpl( @@ -143,10 +130,6 @@ class __$$InitialImplCopyWithImpl<$Res> ? _value.refresh : refresh // ignore: cast_nullable_to_non_nullable as bool, - daos: freezed == daos - ? _value._daos - : daos // ignore: cast_nullable_to_non_nullable - as List?, filterStatus: null == filterStatus ? _value.filterStatus : filterStatus // ignore: cast_nullable_to_non_nullable @@ -159,31 +142,18 @@ class __$$InitialImplCopyWithImpl<$Res> class _$InitialImpl implements _Initial { const _$InitialImpl( - {this.refresh = false, - final List? daos, - this.filterStatus = FilterStatus.active}) - : _daos = daos; + {this.refresh = false, this.filterStatus = FilterStatus.active}); @override @JsonKey() final bool refresh; - final List? _daos; - @override - List? get daos { - final value = _daos; - if (value == null) return null; - if (_daos is EqualUnmodifiableListView) return _daos; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(value); - } - @override @JsonKey() final FilterStatus filterStatus; @override String toString() { - return 'ProposalsEvent.initial(refresh: $refresh, daos: $daos, filterStatus: $filterStatus)'; + return 'ProposalsEvent.initial(refresh: $refresh, filterStatus: $filterStatus)'; } @override @@ -192,14 +162,12 @@ class _$InitialImpl implements _Initial { (other.runtimeType == runtimeType && other is _$InitialImpl && (identical(other.refresh, refresh) || other.refresh == refresh) && - const DeepCollectionEquality().equals(other._daos, _daos) && (identical(other.filterStatus, filterStatus) || other.filterStatus == filterStatus)); } @override - int get hashCode => Object.hash(runtimeType, refresh, - const DeepCollectionEquality().hash(_daos), filterStatus); + int get hashCode => Object.hash(runtimeType, refresh, filterStatus); /// Create a copy of ProposalsEvent /// with the given fields replaced by the non-null parameter values. @@ -212,33 +180,27 @@ class _$InitialImpl implements _Initial { @override @optionalTypeArgs TResult when({ - required TResult Function( - bool refresh, List? daos, FilterStatus filterStatus) - initial, + required TResult Function(bool refresh, FilterStatus filterStatus) initial, }) { - return initial(refresh, daos, filterStatus); + return initial(refresh, filterStatus); } @override @optionalTypeArgs TResult? whenOrNull({ - TResult? Function( - bool refresh, List? daos, FilterStatus filterStatus)? - initial, + TResult? Function(bool refresh, FilterStatus filterStatus)? initial, }) { - return initial?.call(refresh, daos, filterStatus); + return initial?.call(refresh, filterStatus); } @override @optionalTypeArgs TResult maybeWhen({ - TResult Function( - bool refresh, List? daos, FilterStatus filterStatus)? - initial, + TResult Function(bool refresh, FilterStatus filterStatus)? initial, required TResult orElse(), }) { if (initial != null) { - return initial(refresh, daos, filterStatus); + return initial(refresh, filterStatus); } return orElse(); } @@ -274,15 +236,11 @@ class _$InitialImpl implements _Initial { abstract class _Initial implements ProposalsEvent { const factory _Initial( - {final bool refresh, - final List? daos, - final FilterStatus filterStatus}) = _$InitialImpl; + {final bool refresh, final FilterStatus filterStatus}) = _$InitialImpl; @override bool get refresh; @override - List? get daos; - @override FilterStatus get filterStatus; /// Create a copy of ProposalsEvent diff --git a/lib/ui/proposals/list/interactor/proposals_event.dart b/lib/ui/proposals/list/interactor/proposals_event.dart index fa6ca754..05eb07ce 100644 --- a/lib/ui/proposals/list/interactor/proposals_event.dart +++ b/lib/ui/proposals/list/interactor/proposals_event.dart @@ -4,7 +4,6 @@ part of 'proposals_bloc.dart'; class ProposalsEvent with _$ProposalsEvent { const factory ProposalsEvent.initial({ @Default(false) bool refresh, - List? daos, @Default(FilterStatus.active) FilterStatus filterStatus, }) = _Initial; }