From 7c0837d617d0fbd9f692a6ded7a9afe049485dd4 Mon Sep 17 00:00:00 2001 From: Zied Dahmani Date: Tue, 24 Sep 2024 14:56:31 +0100 Subject: [PATCH 1/4] refactor: remove s --- lib/core/extension/base_proposal_model_extension.dart | 1 + lib/ui/proposals/details/components/proposal_details_view.dart | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/core/extension/base_proposal_model_extension.dart b/lib/core/extension/base_proposal_model_extension.dart index fe8d5b64..de7248d6 100644 --- a/lib/core/extension/base_proposal_model_extension.dart +++ b/lib/core/extension/base_proposal_model_extension.dart @@ -2,6 +2,7 @@ import 'package:hypha_wallet/core/network/models/base_proposal_model.dart'; extension BaseProposalModelExtension on BaseProposalModel { String formatExpiration() { + // TODO(Zied-Saif): use another word instead of 'Expired' if (expiration == null) return 'Expired'; if (isExpired()) { diff --git a/lib/ui/proposals/details/components/proposal_details_view.dart b/lib/ui/proposals/details/components/proposal_details_view.dart index 660068f2..0fe96f80 100644 --- a/lib/ui/proposals/details/components/proposal_details_view.dart +++ b/lib/ui/proposals/details/components/proposal_details_view.dart @@ -141,7 +141,7 @@ class _ProposalDetailsViewState extends State { Padding( padding: const EdgeInsets.symmetric(vertical: 10), child: Text( - '${_proposalDetailsModel.cycleCount} Cycles', + '${_proposalDetailsModel.cycleCount} Cycle${_proposalDetailsModel.cycleCount == 1 ? '' : 's'}', style: context.hyphaTextTheme.reducedTitles, ), ), From d74922d12d5b08431e1b58984f03a666f982b644 Mon Sep 17 00:00:00 2001 From: Zied Dahmani Date: Tue, 24 Sep 2024 15:33:59 +0100 Subject: [PATCH 2/4] refactor: add extends InputUseCase --- lib/core/network/repository/proposal_repository.dart | 9 +++++---- .../list/interactor/get_proposals_use_case_input.dart | 9 +++++++++ lib/ui/proposals/list/interactor/proposals_bloc.dart | 3 ++- .../list/usecases/get_proposals_use_case.dart | 10 +++++----- 4 files changed, 21 insertions(+), 10 deletions(-) create mode 100644 lib/ui/proposals/list/interactor/get_proposals_use_case_input.dart diff --git a/lib/core/network/repository/proposal_repository.dart b/lib/core/network/repository/proposal_repository.dart index 54aa2801..4cf17612 100644 --- a/lib/core/network/repository/proposal_repository.dart +++ b/lib/core/network/repository/proposal_repository.dart @@ -10,6 +10,7 @@ import 'package:hypha_wallet/core/network/repository/profile_repository.dart'; import 'package:hypha_wallet/ui/architecture/result/result.dart'; import 'package:hypha_wallet/ui/profile/interactor/profile_data.dart'; import 'package:hypha_wallet/ui/proposals/filter/interactor/filter_status.dart'; +import 'package:hypha_wallet/ui/proposals/list/interactor/get_proposals_use_case_input.dart'; class ProposalRepository { final ProposalService _proposalService; @@ -17,9 +18,9 @@ class ProposalRepository { ProposalRepository(this._proposalService, this._profileService); - Future, HyphaError>> getProposals(UserProfileData user, List daos, FilterStatus filterStatus) async { - final List, HyphaError>>> futures = daos.map((DaoData dao) { - return filterStatus == FilterStatus.active ? _proposalService.getActiveProposals(user, dao.docId) : _proposalService.getPastProposals(user, dao.docId); + Future, HyphaError>> getProposals(UserProfileData user, GetProposalsUseCaseInput input) async { + final List, HyphaError>>> futures = input.daos.map((DaoData dao) { + return input.filterStatus == FilterStatus.active ? _proposalService.getActiveProposals(user, dao.docId) : _proposalService.getPastProposals(user, dao.docId); }).toList(); final List, HyphaError>> futureResults = await Future.wait(futures); @@ -38,7 +39,7 @@ class ProposalRepository { } try { - final List proposals = await _parseProposalsFromResponse(response, daos[i], filterStatus); + final List proposals = await _parseProposalsFromResponse(response, input.daos[i], input.filterStatus); allProposals.addAll(proposals); } catch (e, stackTrace) { LogHelper.e('Error parsing data into proposal model', error: e, stacktrace: stackTrace); diff --git a/lib/ui/proposals/list/interactor/get_proposals_use_case_input.dart b/lib/ui/proposals/list/interactor/get_proposals_use_case_input.dart new file mode 100644 index 00000000..aa1886f1 --- /dev/null +++ b/lib/ui/proposals/list/interactor/get_proposals_use_case_input.dart @@ -0,0 +1,9 @@ +import 'package:hypha_wallet/core/network/models/dao_data_model.dart'; +import 'package:hypha_wallet/ui/proposals/filter/interactor/filter_status.dart'; + +class GetProposalsUseCaseInput { + final List daos; + final FilterStatus filterStatus; + + GetProposalsUseCaseInput(this.daos, this.filterStatus); +} diff --git a/lib/ui/proposals/list/interactor/proposals_bloc.dart b/lib/ui/proposals/list/interactor/proposals_bloc.dart index ba9d7d76..78028fe3 100644 --- a/lib/ui/proposals/list/interactor/proposals_bloc.dart +++ b/lib/ui/proposals/list/interactor/proposals_bloc.dart @@ -9,6 +9,7 @@ import 'package:hypha_wallet/ui/architecture/result/result.dart'; import 'package:hypha_wallet/ui/profile/interactor/profile_data.dart'; import 'package:hypha_wallet/ui/profile/usecases/fetch_profile_use_case.dart'; import 'package:hypha_wallet/ui/proposals/filter/interactor/filter_status.dart'; +import 'package:hypha_wallet/ui/proposals/list/interactor/get_proposals_use_case_input.dart'; import 'package:hypha_wallet/ui/proposals/list/usecases/get_proposals_use_case.dart'; part 'proposals_bloc.freezed.dart'; @@ -49,7 +50,7 @@ class ProposalsBloc extends Bloc { } Future _fetchAndEmitProposals(Emitter emit, List daos, FilterStatus filterStatus) async { - final Result, HyphaError> proposalsResult = await _getProposalsUseCase.run(daos, filterStatus); + final Result, HyphaError> proposalsResult = await _getProposalsUseCase.run(GetProposalsUseCaseInput(daos, filterStatus)); if (proposalsResult.isValue) { emit(state.copyWith(pageState: PageState.success, proposals: proposalsResult.asValue!.value)); diff --git a/lib/ui/proposals/list/usecases/get_proposals_use_case.dart b/lib/ui/proposals/list/usecases/get_proposals_use_case.dart index 588c6963..67f7e40b 100644 --- a/lib/ui/proposals/list/usecases/get_proposals_use_case.dart +++ b/lib/ui/proposals/list/usecases/get_proposals_use_case.dart @@ -1,17 +1,17 @@ import 'package:hypha_wallet/core/error_handler/model/hypha_error.dart'; -import 'package:hypha_wallet/core/network/models/dao_data_model.dart'; import 'package:hypha_wallet/core/network/models/proposal_model.dart'; import 'package:hypha_wallet/core/network/repository/auth_repository.dart'; import 'package:hypha_wallet/core/network/repository/proposal_repository.dart'; +import 'package:hypha_wallet/ui/architecture/interactor/base_usecase.dart'; import 'package:hypha_wallet/ui/architecture/result/result.dart'; -import 'package:hypha_wallet/ui/proposals/filter/interactor/filter_status.dart'; +import 'package:hypha_wallet/ui/proposals/list/interactor/get_proposals_use_case_input.dart'; -// TODO(Zied): add 'extends' (check) -class GetProposalsUseCase { +class GetProposalsUseCase extends InputUseCase, HyphaError>, GetProposalsUseCaseInput> { final AuthRepository _authRepository; final ProposalRepository _proposalRepository; GetProposalsUseCase(this._authRepository, this._proposalRepository); - Future, HyphaError>> run(List daos, FilterStatus filterStatus) async => _proposalRepository.getProposals(_authRepository.authDataOrCrash.userProfileData, daos, filterStatus); + @override + Future, HyphaError>> run(GetProposalsUseCaseInput input) async => _proposalRepository.getProposals(_authRepository.authDataOrCrash.userProfileData, input); } From eb63b270258e8e2f07358713214e923b3d804a9e Mon Sep 17 00:00:00 2001 From: Zied Dahmani Date: Tue, 24 Sep 2024 22:25:01 +0100 Subject: [PATCH 3/4] feat: implement proposals history --- lib/core/di/bloc_module.dart | 10 +- lib/core/di/di_setup.dart | 2 + .../proposals/components/proposals_list.dart | 20 + .../components/proposals_history_view.dart | 35 ++ .../interactor/proposals_history_bloc.dart | 39 ++ .../proposals_history_bloc.freezed.dart | 396 ++++++++++++++++++ .../interactor/proposals_history_event.dart | 6 + .../interactor/proposals_history_state.dart | 9 + .../history/proposals_history_page.dart | 19 + .../list/components/proposals_view.dart | 15 +- .../list/interactor/proposals_bloc.dart | 1 + 11 files changed, 538 insertions(+), 14 deletions(-) create mode 100644 lib/ui/proposals/components/proposals_list.dart create mode 100644 lib/ui/proposals/history/components/proposals_history_view.dart create mode 100644 lib/ui/proposals/history/interactor/proposals_history_bloc.dart create mode 100644 lib/ui/proposals/history/interactor/proposals_history_bloc.freezed.dart create mode 100644 lib/ui/proposals/history/interactor/proposals_history_event.dart create mode 100644 lib/ui/proposals/history/interactor/proposals_history_state.dart create mode 100644 lib/ui/proposals/history/proposals_history_page.dart diff --git a/lib/core/di/bloc_module.dart b/lib/core/di/bloc_module.dart index f05f7ab5..3276585c 100644 --- a/lib/core/di/bloc_module.dart +++ b/lib/core/di/bloc_module.dart @@ -147,4 +147,12 @@ void _registerBlocsModule() { _getIt(), _getIt(), )); -} \ No newline at end of file + + _registerFactoryWithParams( + (dao, _) => ProposalsHistoryBloc( + _getIt(), + _getIt(), + dao + ), + ); +} diff --git a/lib/core/di/di_setup.dart b/lib/core/di/di_setup.dart index f10641d8..b4dbca63 100644 --- a/lib/core/di/di_setup.dart +++ b/lib/core/di/di_setup.dart @@ -30,6 +30,7 @@ import 'package:hypha_wallet/core/network/api/services/token_service.dart'; import 'package:hypha_wallet/core/network/api/services/transaction_history_service.dart'; import 'package:hypha_wallet/core/network/api/services/user_account_service.dart'; import 'package:hypha_wallet/core/network/ipfs/ipfs_manager.dart'; +import 'package:hypha_wallet/core/network/models/dao_data_model.dart'; import 'package:hypha_wallet/core/network/models/network.dart'; import 'package:hypha_wallet/core/network/models/user_profile_data.dart'; import 'package:hypha_wallet/core/network/networking_manager.dart'; @@ -76,6 +77,7 @@ import 'package:hypha_wallet/ui/proposals/details/usecases/get_proposal_details_ import 'package:hypha_wallet/ui/proposals/filter/interactor/filter_proposals_bloc.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/history/interactor/proposals_history_bloc.dart'; import 'package:hypha_wallet/ui/proposals/list/interactor/proposals_bloc.dart'; import 'package:hypha_wallet/ui/proposals/list/usecases/get_proposals_use_case.dart'; import 'package:hypha_wallet/ui/search_user/interactor/search_user_bloc.dart'; diff --git a/lib/ui/proposals/components/proposals_list.dart b/lib/ui/proposals/components/proposals_list.dart new file mode 100644 index 00000000..d85ed627 --- /dev/null +++ b/lib/ui/proposals/components/proposals_list.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +import 'package:hypha_wallet/core/network/models/proposal_model.dart'; +import 'package:hypha_wallet/ui/proposals/list/components/hypha_proposals_action_card.dart'; + +class ProposalsList extends StatelessWidget { + final List proposals; + + const ProposalsList(this.proposals, {super.key}); + + @override + Widget build(BuildContext context) { + return 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); + } +} diff --git a/lib/ui/proposals/history/components/proposals_history_view.dart b/lib/ui/proposals/history/components/proposals_history_view.dart new file mode 100644 index 00000000..c58a4038 --- /dev/null +++ b/lib/ui/proposals/history/components/proposals_history_view.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:get/get.dart'; +import 'package:hypha_wallet/design/hypha_colors.dart'; +import 'package:hypha_wallet/ui/proposals/components/proposals_list.dart'; +import 'package:hypha_wallet/ui/proposals/history/interactor/proposals_history_bloc.dart'; +import 'package:hypha_wallet/ui/shared/hypha_body_widget.dart'; + +class ProposalsHistoryView extends StatelessWidget { + const ProposalsHistoryView({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.isDarkMode ? HyphaColors.darkBlack : HyphaColors.offWhite, + appBar: AppBar( + title: const Text('Proposals History'), + ), + body: RefreshIndicator( + onRefresh: () async { + context.read().add(const ProposalsHistoryEvent.initial(refresh: true)); + }, + child: BlocBuilder( + builder: (context, state) { + return HyphaBodyWidget(pageState: state.pageState, success: (context) { + return Container( + padding: const EdgeInsets.all(20), + child: ProposalsList(state.proposals), + ); + }); + }), + ) + ); + } +} diff --git a/lib/ui/proposals/history/interactor/proposals_history_bloc.dart b/lib/ui/proposals/history/interactor/proposals_history_bloc.dart new file mode 100644 index 00000000..b0c85339 --- /dev/null +++ b/lib/ui/proposals/history/interactor/proposals_history_bloc.dart @@ -0,0 +1,39 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:hypha_wallet/core/error_handler/error_handler_manager.dart'; +import 'package:hypha_wallet/core/error_handler/model/hypha_error.dart'; +import 'package:hypha_wallet/core/network/models/dao_data_model.dart'; +import 'package:hypha_wallet/core/network/models/proposal_model.dart'; +import 'package:hypha_wallet/ui/architecture/interactor/page_states.dart'; +import 'package:hypha_wallet/ui/architecture/result/result.dart'; +import 'package:hypha_wallet/ui/proposals/filter/interactor/filter_status.dart'; +import 'package:hypha_wallet/ui/proposals/list/usecases/get_proposals_use_case.dart'; + +part 'proposals_history_bloc.freezed.dart'; +part 'proposals_history_event.dart'; +part 'proposals_history_state.dart'; + +class ProposalsHistoryBloc extends Bloc { + final GetProposalsUseCase _getProposalsUseCase; + final ErrorHandlerManager _errorHandlerManager; + final DaoData _dao; + + ProposalsHistoryBloc(this._getProposalsUseCase, this._errorHandlerManager, this._dao) : super(const ProposalsHistoryState()) { + on<_Initial>(_initial); + } + + Future _initial(_Initial event, Emitter emit) async { + if (!event.refresh) { + emit(state.copyWith(pageState: PageState.loading)); + } + + final Result, HyphaError> proposalsResult = await _getProposalsUseCase.run([_dao], FilterStatus.past); + + if (proposalsResult.isValue) { + emit(state.copyWith(pageState: PageState.success, proposals: proposalsResult.asValue!.value)); + } else { + await _errorHandlerManager.handlerError(proposalsResult.asError!.error); + emit(state.copyWith(pageState: PageState.failure)); + } + } +} diff --git a/lib/ui/proposals/history/interactor/proposals_history_bloc.freezed.dart b/lib/ui/proposals/history/interactor/proposals_history_bloc.freezed.dart new file mode 100644 index 00000000..f7f69edc --- /dev/null +++ b/lib/ui/proposals/history/interactor/proposals_history_bloc.freezed.dart @@ -0,0 +1,396 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'proposals_history_bloc.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +/// @nodoc +mixin _$ProposalsHistoryEvent { + bool get refresh => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult when({ + required TResult Function(bool refresh) initial, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(bool refresh)? initial, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(bool refresh)? initial, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(_Initial value) initial, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Initial value)? initial, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Initial value)? initial, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + + /// Create a copy of ProposalsHistoryEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $ProposalsHistoryEventCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ProposalsHistoryEventCopyWith<$Res> { + factory $ProposalsHistoryEventCopyWith(ProposalsHistoryEvent value, + $Res Function(ProposalsHistoryEvent) then) = + _$ProposalsHistoryEventCopyWithImpl<$Res, ProposalsHistoryEvent>; + @useResult + $Res call({bool refresh}); +} + +/// @nodoc +class _$ProposalsHistoryEventCopyWithImpl<$Res, + $Val extends ProposalsHistoryEvent> + implements $ProposalsHistoryEventCopyWith<$Res> { + _$ProposalsHistoryEventCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of ProposalsHistoryEvent + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? refresh = null, + }) { + return _then(_value.copyWith( + refresh: null == refresh + ? _value.refresh + : refresh // ignore: cast_nullable_to_non_nullable + as bool, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$InitialImplCopyWith<$Res> + implements $ProposalsHistoryEventCopyWith<$Res> { + factory _$$InitialImplCopyWith( + _$InitialImpl value, $Res Function(_$InitialImpl) then) = + __$$InitialImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({bool refresh}); +} + +/// @nodoc +class __$$InitialImplCopyWithImpl<$Res> + extends _$ProposalsHistoryEventCopyWithImpl<$Res, _$InitialImpl> + implements _$$InitialImplCopyWith<$Res> { + __$$InitialImplCopyWithImpl( + _$InitialImpl _value, $Res Function(_$InitialImpl) _then) + : super(_value, _then); + + /// Create a copy of ProposalsHistoryEvent + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? refresh = null, + }) { + return _then(_$InitialImpl( + refresh: null == refresh + ? _value.refresh + : refresh // ignore: cast_nullable_to_non_nullable + as bool, + )); + } +} + +/// @nodoc + +class _$InitialImpl implements _Initial { + const _$InitialImpl({this.refresh = false}); + + @override + @JsonKey() + final bool refresh; + + @override + String toString() { + return 'ProposalsHistoryEvent.initial(refresh: $refresh)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$InitialImpl && + (identical(other.refresh, refresh) || other.refresh == refresh)); + } + + @override + int get hashCode => Object.hash(runtimeType, refresh); + + /// Create a copy of ProposalsHistoryEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$InitialImplCopyWith<_$InitialImpl> get copyWith => + __$$InitialImplCopyWithImpl<_$InitialImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(bool refresh) initial, + }) { + return initial(refresh); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(bool refresh)? initial, + }) { + return initial?.call(refresh); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(bool refresh)? initial, + required TResult orElse(), + }) { + if (initial != null) { + return initial(refresh); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Initial value) initial, + }) { + return initial(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Initial value)? initial, + }) { + return initial?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Initial value)? initial, + required TResult orElse(), + }) { + if (initial != null) { + return initial(this); + } + return orElse(); + } +} + +abstract class _Initial implements ProposalsHistoryEvent { + const factory _Initial({final bool refresh}) = _$InitialImpl; + + @override + bool get refresh; + + /// Create a copy of ProposalsHistoryEvent + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$InitialImplCopyWith<_$InitialImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +mixin _$ProposalsHistoryState { + PageState get pageState => throw _privateConstructorUsedError; + List get proposals => throw _privateConstructorUsedError; + + /// Create a copy of ProposalsHistoryState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $ProposalsHistoryStateCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ProposalsHistoryStateCopyWith<$Res> { + factory $ProposalsHistoryStateCopyWith(ProposalsHistoryState value, + $Res Function(ProposalsHistoryState) then) = + _$ProposalsHistoryStateCopyWithImpl<$Res, ProposalsHistoryState>; + @useResult + $Res call({PageState pageState, List proposals}); +} + +/// @nodoc +class _$ProposalsHistoryStateCopyWithImpl<$Res, + $Val extends ProposalsHistoryState> + implements $ProposalsHistoryStateCopyWith<$Res> { + _$ProposalsHistoryStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of ProposalsHistoryState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? pageState = null, + Object? proposals = null, + }) { + return _then(_value.copyWith( + pageState: null == pageState + ? _value.pageState + : pageState // ignore: cast_nullable_to_non_nullable + as PageState, + proposals: null == proposals + ? _value.proposals + : proposals // ignore: cast_nullable_to_non_nullable + as List, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$ProposalsHistoryStateImplCopyWith<$Res> + implements $ProposalsHistoryStateCopyWith<$Res> { + factory _$$ProposalsHistoryStateImplCopyWith( + _$ProposalsHistoryStateImpl value, + $Res Function(_$ProposalsHistoryStateImpl) then) = + __$$ProposalsHistoryStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({PageState pageState, List proposals}); +} + +/// @nodoc +class __$$ProposalsHistoryStateImplCopyWithImpl<$Res> + extends _$ProposalsHistoryStateCopyWithImpl<$Res, + _$ProposalsHistoryStateImpl> + implements _$$ProposalsHistoryStateImplCopyWith<$Res> { + __$$ProposalsHistoryStateImplCopyWithImpl(_$ProposalsHistoryStateImpl _value, + $Res Function(_$ProposalsHistoryStateImpl) _then) + : super(_value, _then); + + /// Create a copy of ProposalsHistoryState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? pageState = null, + Object? proposals = null, + }) { + return _then(_$ProposalsHistoryStateImpl( + pageState: null == pageState + ? _value.pageState + : pageState // ignore: cast_nullable_to_non_nullable + as PageState, + proposals: null == proposals + ? _value._proposals + : proposals // ignore: cast_nullable_to_non_nullable + as List, + )); + } +} + +/// @nodoc + +class _$ProposalsHistoryStateImpl implements _ProposalsHistoryState { + const _$ProposalsHistoryStateImpl( + {this.pageState = PageState.initial, + final List proposals = const []}) + : _proposals = proposals; + + @override + @JsonKey() + final PageState pageState; + final List _proposals; + @override + @JsonKey() + List get proposals { + if (_proposals is EqualUnmodifiableListView) return _proposals; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_proposals); + } + + @override + String toString() { + return 'ProposalsHistoryState(pageState: $pageState, proposals: $proposals)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ProposalsHistoryStateImpl && + (identical(other.pageState, pageState) || + other.pageState == pageState) && + const DeepCollectionEquality() + .equals(other._proposals, _proposals)); + } + + @override + int get hashCode => Object.hash( + runtimeType, pageState, const DeepCollectionEquality().hash(_proposals)); + + /// Create a copy of ProposalsHistoryState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$ProposalsHistoryStateImplCopyWith<_$ProposalsHistoryStateImpl> + get copyWith => __$$ProposalsHistoryStateImplCopyWithImpl< + _$ProposalsHistoryStateImpl>(this, _$identity); +} + +abstract class _ProposalsHistoryState implements ProposalsHistoryState { + const factory _ProposalsHistoryState( + {final PageState pageState, + final List proposals}) = _$ProposalsHistoryStateImpl; + + @override + PageState get pageState; + @override + List get proposals; + + /// Create a copy of ProposalsHistoryState + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$ProposalsHistoryStateImplCopyWith<_$ProposalsHistoryStateImpl> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/lib/ui/proposals/history/interactor/proposals_history_event.dart b/lib/ui/proposals/history/interactor/proposals_history_event.dart new file mode 100644 index 00000000..bd9ea4a1 --- /dev/null +++ b/lib/ui/proposals/history/interactor/proposals_history_event.dart @@ -0,0 +1,6 @@ +part of 'proposals_history_bloc.dart'; + +@freezed +class ProposalsHistoryEvent with _$ProposalsHistoryEvent { + const factory ProposalsHistoryEvent.initial({@Default(false) bool refresh}) = _Initial; +} diff --git a/lib/ui/proposals/history/interactor/proposals_history_state.dart b/lib/ui/proposals/history/interactor/proposals_history_state.dart new file mode 100644 index 00000000..71180a10 --- /dev/null +++ b/lib/ui/proposals/history/interactor/proposals_history_state.dart @@ -0,0 +1,9 @@ +part of 'proposals_history_bloc.dart'; + +@freezed +class ProposalsHistoryState with _$ProposalsHistoryState { + const factory ProposalsHistoryState({ + @Default(PageState.initial) PageState pageState, + @Default([]) List proposals, + }) = _ProposalsHistoryState; +} diff --git a/lib/ui/proposals/history/proposals_history_page.dart b/lib/ui/proposals/history/proposals_history_page.dart new file mode 100644 index 00000000..b0cb4a2f --- /dev/null +++ b/lib/ui/proposals/history/proposals_history_page.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:get_it/get_it.dart'; +import 'package:hypha_wallet/core/network/models/dao_data_model.dart'; +import 'package:hypha_wallet/ui/proposals/history/components/proposals_history_view.dart'; +import 'package:hypha_wallet/ui/proposals/history/interactor/proposals_history_bloc.dart'; + +class ProposalsHistoryPage extends StatelessWidget { + final DaoData _dao; + const ProposalsHistoryPage(this._dao, {super.key}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => GetIt.I.get(param1: _dao)..add(const ProposalsHistoryEvent.initial()), + child: const ProposalsHistoryView(), + ); + } +} diff --git a/lib/ui/proposals/list/components/proposals_view.dart b/lib/ui/proposals/list/components/proposals_view.dart index 215fbc2b..3e186cc3 100644 --- a/lib/ui/proposals/list/components/proposals_view.dart +++ b/lib/ui/proposals/list/components/proposals_view.dart @@ -7,10 +7,9 @@ import 'package:hypha_wallet/design/hypha_colors.dart'; import 'package:hypha_wallet/design/themes/extensions/theme_extension_provider.dart'; import 'package:hypha_wallet/ui/blocs/authentication/authentication_bloc.dart'; import 'package:hypha_wallet/ui/profile/profile_page.dart'; +import 'package:hypha_wallet/ui/proposals/components/proposals_list.dart'; import 'package:hypha_wallet/ui/proposals/filter/filter_proposals_page.dart'; -import 'package:hypha_wallet/ui/proposals/filter/interactor/filter_proposals_bloc.dart'; import 'package:hypha_wallet/ui/proposals/filter/interactor/filter_status.dart'; -import 'package:hypha_wallet/ui/proposals/list/components/hypha_proposals_action_card.dart'; import 'package:hypha_wallet/ui/proposals/list/interactor/proposals_bloc.dart'; import 'package:hypha_wallet/ui/shared/hypha_body_widget.dart'; @@ -100,17 +99,7 @@ class ProposalsView extends StatelessWidget { 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)), + Expanded(child: ProposalsList(state.proposals)), ], ), ), diff --git a/lib/ui/proposals/list/interactor/proposals_bloc.dart b/lib/ui/proposals/list/interactor/proposals_bloc.dart index ba9d7d76..7ef16974 100644 --- a/lib/ui/proposals/list/interactor/proposals_bloc.dart +++ b/lib/ui/proposals/list/interactor/proposals_bloc.dart @@ -39,6 +39,7 @@ class ProposalsBloc extends Bloc { final Result profileResult = await _fetchProfileUseCase.run(); + // TODO(Zied): DAOs may be empty list due to crash or user has no DAO, so refactor if (profileResult.isValue && profileResult.asValue!.value.daos.isNotEmpty) { await _fetchAndEmitProposals(emit, profileResult.asValue!.value.daos, filterStatus); } else { From 97391eae3ddda65d3ada9f8c8650366df5d9a503 Mon Sep 17 00:00:00 2001 From: NIK Date: Fri, 27 Sep 2024 11:34:46 +0800 Subject: [PATCH 4/4] add accessors some whitespace fixes made by the IDE formatter --- lib/core/extension/string_extension.dart | 1 + .../models/proposal_details_model.dart | 62 ++++++++++--------- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/lib/core/extension/string_extension.dart b/lib/core/extension/string_extension.dart index 89684fd4..7d6b1ab5 100644 --- a/lib/core/extension/string_extension.dart +++ b/lib/core/extension/string_extension.dart @@ -4,6 +4,7 @@ extension NullableStringExtension on String? { extension StringExtension on String { // convert blockchain quantity to double, e.g. "1.0000 SEEDS" + // This parses a the value of type "Asset" and returns it as double double get quantityAsDouble { final List parts = split(' '); return double.parse(parts[0]); diff --git a/lib/core/network/models/proposal_details_model.dart b/lib/core/network/models/proposal_details_model.dart index e014297c..e06627a4 100644 --- a/lib/core/network/models/proposal_details_model.dart +++ b/lib/core/network/models/proposal_details_model.dart @@ -1,3 +1,4 @@ +import 'package:hypha_wallet/core/extension/string_extension.dart'; // Add this import import 'package:hypha_wallet/core/network/models/base_proposal_model.dart'; import 'package:hypha_wallet/core/network/models/dao_data_model.dart'; import 'package:hypha_wallet/core/network/models/vote_model.dart'; @@ -8,6 +9,13 @@ part 'proposal_details_model.g.dart'; @JsonSerializable() class ProposalDetailsModel extends BaseProposalModel { + double? get utilityAmountDouble => utilityAmount?.quantityAsDouble; + double? get voiceAmountDouble => voiceAmount?.quantityAsDouble; + double? get cashAmountDouble => cashAmount?.quantityAsDouble; + double? get utilityAmountPerPeriodDouble => utilityAmountPerPeriod?.quantityAsDouble; + double? get voiceAmountPerPeriodDouble => voiceAmountPerPeriod?.quantityAsDouble; + double? get cashAmountPerPeriodDouble => cashAmountPerPeriod?.quantityAsDouble; + @JsonKey(name: '__typename') final String type; @@ -44,37 +52,35 @@ class ProposalDetailsModel extends BaseProposalModel { @JsonKey(name: 'details_description_s') final String? description; - ProposalDetailsModel({ - required super.id, - required this.type, - required this.creationDate, - super.dao, - super.commitment, - super.title, - super.unity, - super.quorum, - super.expiration, - super.creator, - super.votes, - this.tokenMixPercentage, - this.cycleCount, - this.cycleStartDate, - this.utilityAmount, - this.voiceAmount, - this.cashAmount, - this.utilityAmountPerPeriod, - this.voiceAmountPerPeriod, - this.cashAmountPerPeriod, - this.description - }); + ProposalDetailsModel( + {required super.id, + required this.type, + required this.creationDate, + super.dao, + super.commitment, + super.title, + super.unity, + super.quorum, + super.expiration, + super.creator, + super.votes, + this.tokenMixPercentage, + this.cycleCount, + this.cycleStartDate, + this.utilityAmount, + this.voiceAmount, + this.cashAmount, + this.utilityAmountPerPeriod, + this.voiceAmountPerPeriod, + this.cashAmountPerPeriod, + this.description}); factory ProposalDetailsModel.fromJson(Map json) { - if(json['start'] is List){ - if((json['start'] as List).isNotEmpty){ + if (json['start'] is List) { + if ((json['start'] as List).isNotEmpty) { json['start'] = json['start'][0]['details_startTime_t']; - } - else{ - json['start']=null; + } else { + json['start'] = null; } } // TODO(Saif): check this