Skip to content

Commit

Permalink
Merge branch 'main' into feat/add-proposals-history-section
Browse files Browse the repository at this point in the history
# Conflicts:
#	lib/ui/proposals/list/components/proposals_view.dart
  • Loading branch information
nbetsaif committed Sep 30, 2024
2 parents 3cbc659 + 3377dc1 commit 4f5c227
Show file tree
Hide file tree
Showing 30 changed files with 1,143 additions and 453 deletions.
11 changes: 9 additions & 2 deletions lib/core/di/bloc_module.dart
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,14 @@ void _registerBlocsModule() {
_getIt<ProposalsBloc>(),
_getIt<FetchProfileUseCase>(),
_getIt<AggregateDaoProposalCountsUseCase>(),
_getIt<GetDaosFromProposalCountsUseCase>(),
_getIt<ErrorHandlerManager>(),
));
}

_registerFactoryWithParams<ProposalsHistoryBloc, DaoData, void>(
(dao, _) => ProposalsHistoryBloc(
_getIt<GetProposalsUseCase>(),
_getIt<ErrorHandlerManager>(),
dao
),
);
}
3 changes: 2 additions & 1 deletion lib/core/di/di_setup.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -75,7 +76,7 @@ import 'package:hypha_wallet/ui/profile/usecases/set_name_use_case.dart';
import 'package:hypha_wallet/ui/proposals/details/usecases/get_proposal_details_use_case.dart';
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';
Expand Down
4 changes: 1 addition & 3 deletions lib/core/di/usecases_module.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,4 @@ void _registerUseCasesModule() {
_registerFactory(() => GetProposalDetailsUseCase(_getIt<AuthRepository>(), _getIt<ProposalRepository>()));

_registerFactory(() => AggregateDaoProposalCountsUseCase());

_registerFactory(() => GetDaosFromProposalCountsUseCase());
}
}
1 change: 1 addition & 0 deletions lib/core/extension/base_proposal_model_extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
58 changes: 44 additions & 14 deletions lib/core/extension/proposal_details_model_extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ import 'package:hypha_wallet/core/network/models/proposal_details_model.dart';
import 'package:intl/intl.dart';

extension ProposalDetailsModelExtension on ProposalDetailsModel {
double tokenMixToPercent() => tokenMixPercentage == null ? 0 : tokenMixPercentage! * .01;
double tokenMixToPercent() =>
tokenMixPercentage == null ? 0 : tokenMixPercentage! * .01;

String formatCycleStartDate() => cycleStartDate != null ? DateFormat('EEEE, MMMM yyyy').format(cycleStartDate!) : '';
String formatCycleStartDate() => cycleStartDate != null
? DateFormat('EEEE, MMMM yyyy').format(cycleStartDate!)
: '';

String cycleEndDate() => DateFormat('EEEE, MMMM yyyy').format(cycleStartDate!.add(Duration(days: cycleCount! * 7)));
String cycleEndDate() => DateFormat('EEEE, MMMM yyyy')
.format(cycleStartDate!.add(Duration(days: cycleCount! * 7)));

String? tokenTitle(int index) {
String input;
Expand Down Expand Up @@ -39,27 +43,53 @@ extension ProposalDetailsModelExtension on ProposalDetailsModel {
return null;
}

// TODO(Saif): adjust this function
String? tokenValue(int index, bool isOneCycleRewardsShown) {
String input;

double? tokenValue(int index, bool isOneCycleRewardsShown) {
double? tokenAmount;
double? tokenAmountPerPeriod;
final int cycleDurationSec = 2629800;
final double periodsOnCycle = cycleDurationSec / periodDurationSec!;
switch (index) {
case 0:
input = utilityAmount ?? utilityAmountPerPeriod!;
tokenAmount = utilityAmountDouble;
tokenAmountPerPeriod = utilityAmountPerPeriodDouble;
break;
case 1:
input = voiceAmount ?? voiceAmountPerPeriod!;
tokenAmount = voiceAmountDouble;
tokenAmountPerPeriod = voiceAmountPerPeriodDouble;
break;
case 2:
input = cashAmount ?? cashAmountPerPeriod!;
tokenAmount = cashAmountDouble;
tokenAmountPerPeriod = cashAmountPerPeriodDouble;

break;
default:
return null;
}
if (tokenAmount != null) {
if (isOneCycleRewardsShown) return tokenAmount;
return tokenAmount / periodsOnCycle;
}
if (tokenAmountPerPeriod != null) {
if (isOneCycleRewardsShown) {
return tokenAmountPerPeriod * periodsOnCycle;
}
return tokenAmountPerPeriod;
}
return tokenAmount;
}
}

final RegExp regExp = RegExp(r'(\S+)\s');
final match = regExp.firstMatch(input);

return match?.group(1);
extension TokenTypeExtension on TokenType {
String get name {
switch (this) {
case TokenType.utility:
return 'Utility Token';
case TokenType.voice:
return 'Voice Token';
case TokenType.cash:
return 'Cash Token';
default:
return '';
}
}
}
7 changes: 7 additions & 0 deletions lib/core/extension/proposals_filter_extension.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import 'package:hypha_wallet/core/network/models/proposal_model.dart';

extension ProposalFilterExtension on List<ProposalModel> {
List<ProposalModel> filterByDao(List<int> daoIds) {
return where((proposal) => daoIds.contains(proposal.dao?.docId)).toList();
}
}
1 change: 1 addition & 0 deletions lib/core/extension/string_extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> parts = split(' ');
return double.parse(parts[0]);
Expand Down
3 changes: 2 additions & 1 deletion lib/core/network/api/services/proposal_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ class ProposalService {
Future<Result<Map<String, dynamic>, HyphaError>> getProposalDetails(
String proposalId, UserProfileData user) async {
final String query =
'{"query":"query proposalDetails(\$docId: String!) { getDocument(docId: \$docId) {__typename docId creator createdDate ... on Votable {pass: voteAggregate(filter: { vote_vote_s: { regexp: \\"/.*pass*./\\" } }) {count} fail: voteAggregate(filter: { vote_vote_s: { regexp: \\"/.*fail*./\\" } }) {count} vote { ... on Vote { vote_voter_n vote_vote_s } } } ... on Edit {details_title_s details_description_s details_periodCount_i details_deferredPercX100_i ballot_expiration_t dao {settings {settings_daoTitle_s}} details_ballotAlignment_i details_ballotQuorum_i} ... on Queststart {details_title_s details_description_s details_periodCount_i details_deferredPercX100_i start {details_startTime_t} ballot_expiration_t dao {settings {settings_daoTitle_s}} details_ballotAlignment_i details_ballotQuorum_i details_pegAmount_a details_voiceAmount_a details_rewardAmount_a} ... on Questcomple {details_title_s details_description_s ballot_expiration_t dao {settings {settings_daoTitle_s}} details_pegAmount_a details_voiceAmount_a details_rewardAmount_a} ... on Policy {details_title_s details_description_s details_periodCount_i details_deferredPercX100_i ballot_expiration_t dao {settings {settings_daoTitle_s}} details_ballotAlignment_i details_ballotQuorum_i details_pegAmount_a details_voiceAmount_a details_rewardAmount_a} ... on Payout {details_title_s details_description_s details_periodCount_i details_deferredPercX100_i start {details_startTime_t} ballot_expiration_t dao {settings {settings_daoTitle_s}} details_ballotAlignment_i details_ballotQuorum_i details_pegAmount_a details_voiceAmount_a details_rewardAmount_a} ... on Badge {details_title_s details_description_s details_periodCount_i ballot_expiration_t dao {settings {settings_daoTitle_s}} details_ballotAlignment_i details_ballotQuorum_i} ... on Poll {details_title_s details_description_s ballot_expiration_t dao {settings {settings_daoTitle_s}} details_ballotAlignment_i details_ballotQuorum_i} ... on Budget {details_title_s details_description_s details_deferredPercX100_i ballot_expiration_t dao {settings {settings_daoTitle_s}} details_ballotAlignment_i details_ballotQuorum_i details_pegAmount_a details_voiceAmount_a details_rewardAmount_a} ... on Assignment {details_title_s details_description_s details_periodCount_i details_deferredPercX100_i start {details_startTime_t} details_rewardSalaryPerPeriod_a details_voiceSalaryPerPeriod_a details_pegSalaryPerPeriod_a ballot_expiration_t dao {settings {settings_daoTitle_s}} details_timeShareX100_i details_ballotAlignment_i details_ballotQuorum_i} } }", "variables":{"docId":"$proposalId"}}';
'{"query":"query proposalDetails(\$docId: String!) { getDocument(docId: \$docId) {__typename docId creator createdDate ... on Votable {pass: voteAggregate(filter: { vote_vote_s: { regexp: \\"/.*pass*./\\" } }) {count} fail: voteAggregate(filter: { vote_vote_s: { regexp: \\"/.*fail*./\\" } }) {count} vote { ... on Vote { vote_voter_n vote_vote_s } } } ... on Edit {details_title_s details_description_s details_periodCount_i details_deferredPercX100_i ballot_expiration_t dao {settings {settings_daoTitle_s settings_periodDurationSec_i}} details_ballotAlignment_i details_ballotQuorum_i} ... on Queststart {details_title_s details_description_s details_periodCount_i details_deferredPercX100_i start {details_startTime_t} ballot_expiration_t dao {settings {settings_daoTitle_s settings_periodDurationSec_i}} details_ballotAlignment_i details_ballotQuorum_i details_pegAmount_a details_voiceAmount_a details_rewardAmount_a} ... on Questcomple {details_title_s details_description_s ballot_expiration_t dao {settings {settings_daoTitle_s settings_periodDurationSec_i}} details_pegAmount_a details_voiceAmount_a details_rewardAmount_a} ... on Policy {details_title_s details_description_s details_periodCount_i details_deferredPercX100_i ballot_expiration_t dao {settings {settings_daoTitle_s settings_periodDurationSec_i}} details_ballotAlignment_i details_ballotQuorum_i details_pegAmount_a details_voiceAmount_a details_rewardAmount_a} ... on Payout {details_title_s details_description_s details_periodCount_i details_deferredPercX100_i start {details_startTime_t} ballot_expiration_t dao {settings {settings_daoTitle_s settings_periodDurationSec_i}} details_ballotAlignment_i details_ballotQuorum_i details_pegAmount_a details_voiceAmount_a details_rewardAmount_a} ... on Badge {details_title_s details_description_s details_periodCount_i ballot_expiration_t dao {settings {settings_daoTitle_s settings_periodDurationSec_i}} details_ballotAlignment_i details_ballotQuorum_i} ... on Poll {details_title_s details_description_s ballot_expiration_t dao {settings {settings_daoTitle_s settings_periodDurationSec_i}} details_ballotAlignment_i details_ballotQuorum_i} ... on Budget {details_title_s details_description_s details_deferredPercX100_i ballot_expiration_t dao {settings {settings_daoTitle_s settings_periodDurationSec_i}} details_ballotAlignment_i details_ballotQuorum_i details_pegAmount_a details_voiceAmount_a details_rewardAmount_a} ... on Assignment {details_title_s details_description_s details_periodCount_i details_deferredPercX100_i start {details_startTime_t} details_rewardSalaryPerPeriod_a details_voiceSalaryPerPeriod_a details_pegSalaryPerPeriod_a ballot_expiration_t dao {settings {settings_daoTitle_s settings_periodDurationSec_i}} details_timeShareX100_i details_ballotAlignment_i details_ballotQuorum_i} } }", "variables":{"docId":"$proposalId"}}';
return _graphQLService.graphQLQuery(network: user.network, query: query);
}


}
105 changes: 70 additions & 35 deletions lib/core/network/models/proposal_details_model.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -6,11 +7,35 @@ import 'package:json_annotation/json_annotation.dart';

part 'proposal_details_model.g.dart';

enum TokenType {
utility,
voice,
cash,
}

@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;

@JsonKey(name: 'settings_periodDurationSec_i')
final int? periodDurationSec;

@JsonKey(name: 'createdDate')
final DateTime creationDate;

Expand Down Expand Up @@ -44,50 +69,60 @@ 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,
this.periodDurationSec});

factory ProposalDetailsModel.fromJson(Map<String, dynamic> json) {
if(json['start'] is List){
if((json['start'] as List).isNotEmpty){
// Handle the 'start' field if it's a list
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;
}
if (json['dao'] is List) {
final daoList = json['dao'];
if (daoList.isNotEmpty &&
daoList[0] is Map &&
daoList[0]['settings'] is List) {
final settingsList = daoList[0]['settings'] as List;
if (settingsList.isNotEmpty && settingsList[0] is Map) {
json['settings_periodDurationSec_i'] =
settingsList[0]['settings_periodDurationSec_i'];
}
}
}
// TODO(Saif): check this
json['dao'] = null;
json['creator'] = null;
/*if(json['dao'] != null) {
json['dao'] = json['dao'][0]['settings'][0]['settings_daoTitle_s'];
}*/
return _$ProposalDetailsModelFromJson(json);
}

List<VoteModel> fetchVotersByStatus(VoteStatus voteStatus) => votes
?.where((vote) => vote.voteStatus == voteStatus)
.map((vote) => vote)
.toList() ?? [];
List<VoteModel> fetchVotersByStatus(VoteStatus voteStatus) =>
votes
?.where((vote) => vote.voteStatus == voteStatus)
.map((vote) => vote)
.toList() ??
[];
}
3 changes: 3 additions & 0 deletions lib/core/network/models/proposal_details_model.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions lib/core/network/repository/proposal_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ 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;
final ProfileService _profileService;

ProposalRepository(this._proposalService, this._profileService);

Future<Result<List<ProposalModel>, HyphaError>> getProposals(UserProfileData user, List<DaoData> daos, FilterStatus filterStatus) async {
final List<Future<Result<Map<String, dynamic>, HyphaError>>> futures = daos.map((DaoData dao) {
return filterStatus == FilterStatus.active ? _proposalService.getActiveProposals(user, dao.docId) : _proposalService.getPastProposals(user, dao.docId);
Future<Result<List<ProposalModel>, HyphaError>> getProposals(UserProfileData user, GetProposalsUseCaseInput input) async {
final List<Future<Result<Map<String, dynamic>, 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<Result<Map<String, dynamic>, HyphaError>> futureResults = await Future.wait(futures);
Expand All @@ -38,7 +39,7 @@ class ProposalRepository {
}

try {
final List<ProposalModel> proposals = await _parseProposalsFromResponse(response, daos[i], filterStatus);
final List<ProposalModel> 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);
Expand Down
20 changes: 20 additions & 0 deletions lib/ui/proposals/components/proposals_list.dart
Original file line number Diff line number Diff line change
@@ -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<ProposalModel> 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);
}
}
Loading

0 comments on commit 4f5c227

Please sign in to comment.