Skip to content
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

[democracy] submitButton becomes close after proposalLifetime/confirmingPhase passed #1760

Merged
merged 10 commits into from
Feb 16, 2025
2 changes: 1 addition & 1 deletion app/lib/common/components/submit_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class _SubmitButtonStateSmall extends State<SubmitButtonSmall> {
return ElevatedButton(
onPressed: (!_submitting && widget.onPressed != null) ? _onPressed : null,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 14),
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 4),
backgroundColor: context.colorScheme.primary,
foregroundColor: Colors.white,
textStyle: context.titleSmall,
Expand Down
5 changes: 5 additions & 0 deletions app/lib/l10n/arb/app_de.arb
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@
"democracyVotedNotificationTitle": "Abgestimmt",
"democracySubmitProposalNotificationBody": "Du hast einen Vorschlag eingereicht, über den nun abgestimmt werden kann.",
"democracySubmitProposalNotificationTitle": "Vorschlag eingereicht",
"democracyUpdatedProposalStateNotificationBody": "Du hast diesen Vorschlag aktualisiert",
"democracyUpdatedProposalStateNotificationTitle": "Vorschlag aktualisiert",
"detail": "Detail",
"detailsEnter": "Gib deine Details ein.",
"developer": "Entwickler-Modus",
Expand Down Expand Up @@ -240,6 +242,9 @@
"proposalFieldErrorPositiveNumberRange": "Muss eine positive Zahl sein",
"proposalFieldErrorEnterInactivityTimeout": "Inaktivitätszeitlimit eingeben",
"proposalFieldErrorPositiveIntegerRange": "Muss eine positive ganze Zahl sein",
"proposalClose": "Schliessen",
"proposalUpdateState": "Aktualisieren",
"proposalUpdateExplanation": "Dies wird den Status des Vorschlags aktualisieren. Wenn er zu alt ist und nicht genügend Aye-Stimmen hat, wird er abgelehnt. Wenn er lange genug bestätigt wurde, wird er angenommen.",
"proposalOnlyBootstrappersOrReputablesCanSubmit": "Nur Bootstrappers oder Reputables können einen Vorschlag einreichen.",
"proposalSubmit": "Vorschlag einreichen",
"proposalSuperseded": "Verdrängt",
Expand Down
5 changes: 5 additions & 0 deletions app/lib/l10n/arb/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@
"democracyDiscussion": "Discuss proposals in the Forum!",
"democracyVotedNotificationBody": "You have voted for this proposal.",
"democracyVotedNotificationTitle": "Voted",
"democracyUpdatedProposalStateNotificationBody": "You have updated this proposal",
"democracyUpdatedProposalStateNotificationTitle": "Proposal Updated",
"democracySubmitProposalNotificationBody": "You made a proposal, which people can vote on now.",
"democracySubmitProposalNotificationTitle": "Proposal submitted",
"detail": "Detail",
Expand Down Expand Up @@ -241,6 +243,9 @@
"proposalFieldErrorEnterInactivityTimeout": "Enter inactivity timeout",
"proposalFieldErrorPositiveIntegerRange": "Must be a positive integer",
"proposalOnlyBootstrappersOrReputablesCanSubmit": "Only bootstrappers or reputables can submit a proposal.",
"proposalClose": "Close",
"proposalUpdateState": "Update",
"proposalUpdateExplanation": "This will update the proposal state. If it is too old and does not have enough Aye votes, it will be rejected. If it has been confirming long enough, it will pass.",
"proposalSubmit": "Submit Proposal",
"proposalSuperseded": "Superseded",
"proposalRejected": "Rejected",
Expand Down
5 changes: 5 additions & 0 deletions app/lib/l10n/arb/app_fr.arb
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@
"democracyDiscussion": "Discute de suggestions dans le forum!",
"democracyVotedNotificationBody": "Tu as voté pour cette proposition.",
"democracyVotedNotificationTitle": "Voté",
"democracyUpdatedProposalStateNotificationBody": "Vous avez mis à jour cette proposition",
"democracyUpdatedProposalStateNotificationTitle": "Proposition mise à jour",
"democracySubmitProposalNotificationBody": "Vous avez fait une proposition, sur laquelle les gens peuvent voter maintenant.",
"democracySubmitProposalNotificationTitle": "Proposition soumise",
"detail": "Détail",
Expand Down Expand Up @@ -241,6 +243,9 @@
"proposalFieldErrorEnterInactivityTimeout": "Entrez le délai d'inactivité",
"proposalFieldErrorPositiveIntegerRange": "Doit être un entier positif",
"proposalOnlyBootstrappersOrReputablesCanSubmit": "Seuls les bootstrappers ou les Reputables peuvent soumettre une proposition.",
"proposalClose": "Fermer",
"proposalUpdateState": "Mettre à jour",
"proposalUpdateExplanation": "Cela mettra à jour l'état de la proposition. Si elle est trop ancienne et n'a pas suffisamment de votes Aye, elle sera rejetée. Si elle a été confirmée assez longtemps, elle sera acceptée.",
"proposalSubmit": "Soumettre une proposition",
"proposalSuperseded": "Epargné",
"proposalRejected": "Refusé",
Expand Down
5 changes: 5 additions & 0 deletions app/lib/l10n/arb/app_ru.arb
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@
"democracyVotedNotificationTitle": "проголосовали",
"democracySubmitProposalNotificationBody": "Вы внесли предложение, за которое люди могут проголосовать прямо сейчас.",
"democracySubmitProposalNotificationTitle": "Предложение отправлено",
"democracyUpdatedProposalStateNotificationBody": "Вы обновили это предложение",
"democracyUpdatedProposalStateNotificationTitle": "Предложение обновлено",
"detail": "Детали",
"detailsEnter": "Введите свои данные",
"developer": "Режим разработчика",
Expand Down Expand Up @@ -241,6 +243,9 @@
"proposalFieldErrorEnterInactivityTimeout": "Введите тайм-аут неактивности",
"proposalFieldErrorPositiveIntegerRange": "Должно быть положительное целое число",
"proposalOnlyBootstrappersOrReputablesCanSubmit": "Только бутстраперы или уважаемые участники могут подать предложение.",
"proposalClose": "Закрыть",
"proposalUpdateState": "Обновить",
"proposalUpdateExplanation": "Это обновит статус предложения. Если оно слишком старое и не имеет достаточного количества голосов 'За', оно будет отклонено. Если оно подтверждается достаточно долго, оно будет принято.",
"proposalSubmit": "Подать предложение",
"proposalRejected": "Отменено",
"proposalSuperseded": "заменен",
Expand Down
5 changes: 5 additions & 0 deletions app/lib/l10n/arb/app_sw.arb
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@
"democracyVotedNotificationTitle": "Umepiga kura",
"democracySubmitProposalNotificationBody": "Ulitoa pendekezo, ambalo watu wanaweza kulipigia kura sasa.",
"democracySubmitProposalNotificationTitle": "Pendekezo limewasilishwa",
"democracyUpdatedProposalStateNotificationBody": "Umesasisha pendekezo hili",
"democracyUpdatedProposalStateNotificationTitle": "Pendekezo limesasishwa",
"detail": "Taarifa",
"detailsEnter": "ingiza taarifa zako.",
"developer": "Developer mode",
Expand Down Expand Up @@ -241,6 +243,9 @@
"proposalFieldErrorEnterInactivityTimeout": "Weka muda wa kutokufanya kazi",
"proposalFieldErrorPositiveIntegerRange": "Lazima iwe namba kamili chanya",
"proposalOnlyBootstrappersOrReputablesCanSubmit": "Ni bootstrappers au waheshimika pekee wanaoweza kuwasilisha pendekezo.",
"proposalClose": "Funga",
"proposalUpdateState": "Sasisha",
"proposalUpdateExplanation": "Hii itasasisha hali ya pendekezo. Ikiwa ni la zamani sana na halina kura za kutosha za 'Ndio', litakataliwa. Ikiwa limekuwa likithibitishwa kwa muda wa kutosha, litakubaliwa.",
"proposalSubmit": "Wasilisha Pendekezo",
"proposalSuperseded": "Iliyopita",
"proposalRejected": "Imekaataliwa",
Expand Down
16 changes: 16 additions & 0 deletions app/lib/page-encointer/democracy/helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,23 @@ extension ProposalExt on Proposal {
return state.runtimeType == SupersededBy || state.runtimeType == Rejected;
}

/// Returns true if the proposal started after now - `duration`.
bool isMoreRecentThan(Duration duration) {
return DateTime.now().subtract(duration).isBefore(DateTime.fromMillisecondsSinceEpoch(start.toInt()));
}

/// Returns true if the proposal started before now - `duration`.
bool isOlderThan(Duration duration) {
return !isMoreRecentThan(duration);
}

/// Returns true if the proposal has been in `Confirming` for longer than `duration`.
///
/// Returns null if the proposal is not in confirming state at all.
bool? isConfirmingLongerThan(Duration duration) {
if (state.runtimeType != Confirming) return null;

final confirmingSince = (state as Confirming).since;
return DateTime.now().subtract(duration).isAfter(DateTime.fromMillisecondsSinceEpoch(confirmingSince.toInt()));
}
}
78 changes: 56 additions & 22 deletions app/lib/page-encointer/democracy/widgets/proposal_tile.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:encointer_wallet/l10n/l10.dart';
import 'package:encointer_wallet/modules/modules.dart';
import 'package:encointer_wallet/page-encointer/democracy/widgets/update_proposal_button.dart';
import 'package:encointer_wallet/service/service.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
Expand Down Expand Up @@ -80,17 +81,13 @@ class _ProposalTileState extends State<ProposalTile> {
ListTile(
contentPadding: const EdgeInsets.symmetric(),
leading: Text(widget.proposalId.toString(), style: titleSmall),
subtitle: SizedBox(
// ensure constant height even for missing texts without turnout.
height: 60,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('${l10n.proposalTurnout}: $turnout / $electorateSize'),
if (turnout != 0) Text(l10n.proposalApprovalThreshold((threshold * 100).toStringAsFixed(2))),
if (turnout != 0) passingOrFailingText(context, proposal, tally, widget.params),
],
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('${l10n.proposalTurnout}: $turnout / $electorateSize'),
if (turnout != 0) Text(l10n.proposalApprovalThreshold((threshold * 100).toStringAsFixed(2))),
if (turnout != 0) passingOrFailingText(context, proposal, tally, widget.params),
],
),
trailing: voteButtonOrProposalStatus(context),
),
Expand Down Expand Up @@ -175,18 +172,55 @@ class _ProposalTileState extends State<ProposalTile> {
case Approved:
return Text(l10n.proposalApproved, style: const TextStyle(color: Colors.green));
case Ongoing:
final proposalLifetime = Duration(milliseconds: widget.params.proposalLifetime.toInt());
if (proposal.isOlderThan(proposalLifetime)) {
// Proposal lifetime has passed; proposal has expired.
return SizedBox(
height: 50,
width: 60,
child: UpdateProposalButton(
proposalId: widget.proposalId,
onPressed: _updateState,
),
);
} else {
return SizedBox(
height: 50,
width: 60,
child: VoteButton(
proposal: proposal,
proposalId: widget.proposalId,
purposeId: widget.purposeId,
democracyParams: widget.params,
onPressed: _updateState,
),
);
}
case Confirming:
return SizedBox(
height: 50,
width: 60,
child: VoteButton(
proposal: proposal,
proposalId: widget.proposalId,
purposeId: widget.purposeId,
democracyParams: widget.params,
onPressed: _updateState,
),
);
final confirmDuration = Duration(milliseconds: widget.params.confirmationPeriod.toInt());
if (proposal.isConfirmingLongerThan(confirmDuration)!) {
// confirmation time has passed
return SizedBox(
height: 50,
width: 60,
child: UpdateProposalButton(
proposalId: widget.proposalId,
onPressed: _updateState,
),
);
} else {
return SizedBox(
height: 50,
width: 60,
child: VoteButton(
proposal: proposal,
proposalId: widget.proposalId,
purposeId: widget.purposeId,
democracyParams: widget.params,
onPressed: _updateState,
),
);
}
default:
// should never happen.
return const Text('Unknown Proposal State');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import 'package:encointer_wallet/common/components/submit_button_cupertino.dart';
import 'package:encointer_wallet/service/service.dart';
import 'package:encointer_wallet/utils/alerts/app_alert.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'package:encointer_wallet/common/components/submit_button.dart';
import 'package:encointer_wallet/l10n/l10.dart';
import 'package:encointer_wallet/service/tx/lib/tx.dart';
import 'package:encointer_wallet/store/app.dart';

class UpdateProposalButton extends StatefulWidget {
const UpdateProposalButton({
super.key,
required this.proposalId,
required this.onPressed,
});

final BigInt proposalId;
final void Function() onPressed;

@override
State<UpdateProposalButton> createState() => _UpdateProposalButtonState();
}

class _UpdateProposalButtonState extends State<UpdateProposalButton> {
@override
void initState() {
super.initState();
}

@override
Widget build(BuildContext context) {
final l10n = context.l10n;
final store = context.read<AppStore>();

return SubmitButtonSmall(
onPressed: (context) async {
await _showSubmitUpdateProposalStateDialog(store, widget.proposalId);
widget.onPressed();
},
child: Text(l10n.proposalClose),
);
}

Future<void> _showSubmitUpdateProposalStateDialog(AppStore store, BigInt proposalId) {
final l10n = context.l10n;

return AppAlert.showDialog(
context,
title: Text('${l10n.proposal} $proposalId'),
content: Padding(
padding: const EdgeInsets.only(top: 10),
child: Text(l10n.proposalUpdateExplanation),
),
actions: <Widget>[
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
SubmitButtonCupertino(
onPressed: (BuildContext context) async {
await _submitUpdateProposalState(store);
Navigator.of(context).pop();
},
child: Text(l10n.proposalUpdateState, style: const TextStyle(color: Colors.green)),
),
CupertinoButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(l10n.cancel),
),
],
),
],
),
],
);
}

Future<void> _submitUpdateProposalState(AppStore store) async {
await submitDemocracyUpdateProposalState(
context,
store,
webApi,
store.account.getKeyringAccount(store.account.currentAccountPubKey!),
widget.proposalId,
txPaymentAsset: store.encointer.getTxPaymentAsset(store.encointer.chosenCid),
);

setState(() {});
}
}
22 changes: 22 additions & 0 deletions app/lib/service/substrate_api/encointer/encointer_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'dart:math';
import 'package:convert/convert.dart' show hex;

import 'package:encointer_wallet/config/consts.dart';
import 'package:encointer_wallet/config/networks/networks.dart';
import 'package:encointer_wallet/mocks/mock_bazaar_data.dart';
import 'package:encointer_wallet/models/bazaar/account_business_tuple.dart';
import 'package:encointer_wallet/models/bazaar/business_identifier.dart';
Expand Down Expand Up @@ -807,6 +808,27 @@ class EncointerApi {
}

DemocracyParams democracyParams() {
return switch (store.settings.currentNetwork) {
Network.encointerKusama => encointerKusamaParams(),
Network.encointerRococo => encointerKusamaParams(),
Network.gesell => encointerSoloParams(),
Network.gesellDev => encointerSoloParams(),
};
}

DemocracyParams encointerSoloParams() {
final minTurnout = BigInt.one;
final confirmationPeriod = BigInt.from(300000);
final proposalLifetime = BigInt.from(1200000);

return DemocracyParams(
minTurnout: minTurnout,
confirmationPeriod: confirmationPeriod,
proposalLifetime: proposalLifetime,
);
}

DemocracyParams encointerKusamaParams() {
final minTurnout = encointerKusama.constant.encointerDemocracy.minTurnout;
final confirmationPeriod = encointerKusama.constant.encointerDemocracy.confirmationPeriod;
final proposalLifetime = encointerKusama.constant.encointerDemocracy.proposalLifetime;
Expand Down
3 changes: 3 additions & 0 deletions app/lib/service/tx/lib/src/submit_to_inner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Future<void> submitTxInner(
);

if (report.isExtrinsicFailed) {
Log.e('[TX] Extrinsic Failed: ${report.dispatchError!.toJson()}');
_onTxError(store);
onError?.call(report.dispatchError!);
final message = getLocalizedTxErrorMessage(l10n, report.dispatchError!);
Expand Down Expand Up @@ -86,6 +87,8 @@ void _showErrorDialog(BuildContext context, ErrorNotificationMsg message) {
final l10n = context.l10n;
final languageCode = Localizations.localeOf(context).languageCode;

print('Should show error dialog');

AppAlert.showDialog<void>(
context,
title: Text(message.title),
Expand Down
Loading
Loading