Skip to content

Commit d99501d

Browse files
authored
[democracy] submitButton becomes close after proposalLifetime/confirmingPhase passed (#1760)
* [democracy] get network dependent democracy constants * [democracy] remove constant height widget * [democracy] add button that will close the proposal if the proposal has lived longer than its lifetime or has been in confirming long enough * fmt * localize update button * add tx notification for updating the proposal state * log extrinsic error * add minor horizontal padding to submitButtonSmall * fmt * fix comment
1 parent 42b6a06 commit d99501d

File tree

13 files changed

+249
-23
lines changed

13 files changed

+249
-23
lines changed

app/lib/common/components/submit_button.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class _SubmitButtonStateSmall extends State<SubmitButtonSmall> {
7474
return ElevatedButton(
7575
onPressed: (!_submitting && widget.onPressed != null) ? _onPressed : null,
7676
style: ElevatedButton.styleFrom(
77-
padding: const EdgeInsets.symmetric(vertical: 14),
77+
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 4),
7878
backgroundColor: context.colorScheme.primary,
7979
foregroundColor: Colors.white,
8080
textStyle: context.titleSmall,

app/lib/l10n/arb/app_de.arb

+5
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@
9393
"democracyVotedNotificationTitle": "Abgestimmt",
9494
"democracySubmitProposalNotificationBody": "Du hast einen Vorschlag eingereicht, über den nun abgestimmt werden kann.",
9595
"democracySubmitProposalNotificationTitle": "Vorschlag eingereicht",
96+
"democracyUpdatedProposalStateNotificationBody": "Du hast diesen Vorschlag aktualisiert",
97+
"democracyUpdatedProposalStateNotificationTitle": "Vorschlag aktualisiert",
9698
"detail": "Detail",
9799
"detailsEnter": "Gib deine Details ein.",
98100
"developer": "Entwickler-Modus",
@@ -240,6 +242,9 @@
240242
"proposalFieldErrorPositiveNumberRange": "Muss eine positive Zahl sein",
241243
"proposalFieldErrorEnterInactivityTimeout": "Inaktivitätszeitlimit eingeben",
242244
"proposalFieldErrorPositiveIntegerRange": "Muss eine positive ganze Zahl sein",
245+
"proposalClose": "Schliessen",
246+
"proposalUpdateState": "Aktualisieren",
247+
"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.",
243248
"proposalOnlyBootstrappersOrReputablesCanSubmit": "Nur Bootstrappers oder Reputables können einen Vorschlag einreichen.",
244249
"proposalSubmit": "Vorschlag einreichen",
245250
"proposalSuperseded": "Verdrängt",

app/lib/l10n/arb/app_en.arb

+5
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@
9191
"democracyDiscussion": "Discuss proposals in the Forum!",
9292
"democracyVotedNotificationBody": "You have voted for this proposal.",
9393
"democracyVotedNotificationTitle": "Voted",
94+
"democracyUpdatedProposalStateNotificationBody": "You have updated this proposal",
95+
"democracyUpdatedProposalStateNotificationTitle": "Proposal Updated",
9496
"democracySubmitProposalNotificationBody": "You made a proposal, which people can vote on now.",
9597
"democracySubmitProposalNotificationTitle": "Proposal submitted",
9698
"detail": "Detail",
@@ -241,6 +243,9 @@
241243
"proposalFieldErrorEnterInactivityTimeout": "Enter inactivity timeout",
242244
"proposalFieldErrorPositiveIntegerRange": "Must be a positive integer",
243245
"proposalOnlyBootstrappersOrReputablesCanSubmit": "Only bootstrappers or reputables can submit a proposal.",
246+
"proposalClose": "Close",
247+
"proposalUpdateState": "Update",
248+
"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.",
244249
"proposalSubmit": "Submit Proposal",
245250
"proposalSuperseded": "Superseded",
246251
"proposalRejected": "Rejected",

app/lib/l10n/arb/app_fr.arb

+5
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@
9191
"democracyDiscussion": "Discute de suggestions dans le forum!",
9292
"democracyVotedNotificationBody": "Tu as voté pour cette proposition.",
9393
"democracyVotedNotificationTitle": "Voté",
94+
"democracyUpdatedProposalStateNotificationBody": "Vous avez mis à jour cette proposition",
95+
"democracyUpdatedProposalStateNotificationTitle": "Proposition mise à jour",
9496
"democracySubmitProposalNotificationBody": "Vous avez fait une proposition, sur laquelle les gens peuvent voter maintenant.",
9597
"democracySubmitProposalNotificationTitle": "Proposition soumise",
9698
"detail": "Détail",
@@ -241,6 +243,9 @@
241243
"proposalFieldErrorEnterInactivityTimeout": "Entrez le délai d'inactivité",
242244
"proposalFieldErrorPositiveIntegerRange": "Doit être un entier positif",
243245
"proposalOnlyBootstrappersOrReputablesCanSubmit": "Seuls les bootstrappers ou les Reputables peuvent soumettre une proposition.",
246+
"proposalClose": "Fermer",
247+
"proposalUpdateState": "Mettre à jour",
248+
"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.",
244249
"proposalSubmit": "Soumettre une proposition",
245250
"proposalSuperseded": "Epargné",
246251
"proposalRejected": "Refusé",

app/lib/l10n/arb/app_ru.arb

+5
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@
9393
"democracyVotedNotificationTitle": "проголосовали",
9494
"democracySubmitProposalNotificationBody": "Вы внесли предложение, за которое люди могут проголосовать прямо сейчас.",
9595
"democracySubmitProposalNotificationTitle": "Предложение отправлено",
96+
"democracyUpdatedProposalStateNotificationBody": "Вы обновили это предложение",
97+
"democracyUpdatedProposalStateNotificationTitle": "Предложение обновлено",
9698
"detail": "Детали",
9799
"detailsEnter": "Введите свои данные",
98100
"developer": "Режим разработчика",
@@ -241,6 +243,9 @@
241243
"proposalFieldErrorEnterInactivityTimeout": "Введите тайм-аут неактивности",
242244
"proposalFieldErrorPositiveIntegerRange": "Должно быть положительное целое число",
243245
"proposalOnlyBootstrappersOrReputablesCanSubmit": "Только бутстраперы или уважаемые участники могут подать предложение.",
246+
"proposalClose": "Закрыть",
247+
"proposalUpdateState": "Обновить",
248+
"proposalUpdateExplanation": "Это обновит статус предложения. Если оно слишком старое и не имеет достаточного количества голосов 'За', оно будет отклонено. Если оно подтверждается достаточно долго, оно будет принято.",
244249
"proposalSubmit": "Подать предложение",
245250
"proposalRejected": "Отменено",
246251
"proposalSuperseded": "заменен",

app/lib/l10n/arb/app_sw.arb

+5
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@
9393
"democracyVotedNotificationTitle": "Umepiga kura",
9494
"democracySubmitProposalNotificationBody": "Ulitoa pendekezo, ambalo watu wanaweza kulipigia kura sasa.",
9595
"democracySubmitProposalNotificationTitle": "Pendekezo limewasilishwa",
96+
"democracyUpdatedProposalStateNotificationBody": "Umesasisha pendekezo hili",
97+
"democracyUpdatedProposalStateNotificationTitle": "Pendekezo limesasishwa",
9698
"detail": "Taarifa",
9799
"detailsEnter": "ingiza taarifa zako.",
98100
"developer": "Developer mode",
@@ -241,6 +243,9 @@
241243
"proposalFieldErrorEnterInactivityTimeout": "Weka muda wa kutokufanya kazi",
242244
"proposalFieldErrorPositiveIntegerRange": "Lazima iwe namba kamili chanya",
243245
"proposalOnlyBootstrappersOrReputablesCanSubmit": "Ni bootstrappers au waheshimika pekee wanaoweza kuwasilisha pendekezo.",
246+
"proposalClose": "Funga",
247+
"proposalUpdateState": "Sasisha",
248+
"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.",
244249
"proposalSubmit": "Wasilisha Pendekezo",
245250
"proposalSuperseded": "Iliyopita",
246251
"proposalRejected": "Imekaataliwa",

app/lib/page-encointer/democracy/helpers.dart

+16
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,23 @@ extension ProposalExt on Proposal {
234234
return state.runtimeType == SupersededBy || state.runtimeType == Rejected;
235235
}
236236

237+
/// Returns true if the proposal started after now - `duration`.
237238
bool isMoreRecentThan(Duration duration) {
238239
return DateTime.now().subtract(duration).isBefore(DateTime.fromMillisecondsSinceEpoch(start.toInt()));
239240
}
241+
242+
/// Returns true if the proposal started before now - `duration`.
243+
bool isOlderThan(Duration duration) {
244+
return !isMoreRecentThan(duration);
245+
}
246+
247+
/// Returns true if the proposal has been in `Confirming` for longer than `duration`.
248+
///
249+
/// Returns null if the proposal is not in confirming state at all.
250+
bool? isConfirmingLongerThan(Duration duration) {
251+
if (state.runtimeType != Confirming) return null;
252+
253+
final confirmingSince = (state as Confirming).since;
254+
return DateTime.now().subtract(duration).isAfter(DateTime.fromMillisecondsSinceEpoch(confirmingSince.toInt()));
255+
}
240256
}

app/lib/page-encointer/democracy/widgets/proposal_tile.dart

+56-22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:encointer_wallet/l10n/l10.dart';
22
import 'package:encointer_wallet/modules/modules.dart';
3+
import 'package:encointer_wallet/page-encointer/democracy/widgets/update_proposal_button.dart';
34
import 'package:encointer_wallet/service/service.dart';
45
import 'package:flutter/material.dart';
56
import 'package:intl/intl.dart';
@@ -80,17 +81,13 @@ class _ProposalTileState extends State<ProposalTile> {
8081
ListTile(
8182
contentPadding: const EdgeInsets.symmetric(),
8283
leading: Text(widget.proposalId.toString(), style: titleSmall),
83-
subtitle: SizedBox(
84-
// ensure constant height even for missing texts without turnout.
85-
height: 60,
86-
child: Column(
87-
crossAxisAlignment: CrossAxisAlignment.start,
88-
children: [
89-
Text('${l10n.proposalTurnout}: $turnout / $electorateSize'),
90-
if (turnout != 0) Text(l10n.proposalApprovalThreshold((threshold * 100).toStringAsFixed(2))),
91-
if (turnout != 0) passingOrFailingText(context, proposal, tally, widget.params),
92-
],
93-
),
84+
subtitle: Column(
85+
crossAxisAlignment: CrossAxisAlignment.start,
86+
children: [
87+
Text('${l10n.proposalTurnout}: $turnout / $electorateSize'),
88+
if (turnout != 0) Text(l10n.proposalApprovalThreshold((threshold * 100).toStringAsFixed(2))),
89+
if (turnout != 0) passingOrFailingText(context, proposal, tally, widget.params),
90+
],
9491
),
9592
trailing: voteButtonOrProposalStatus(context),
9693
),
@@ -175,18 +172,55 @@ class _ProposalTileState extends State<ProposalTile> {
175172
case Approved:
176173
return Text(l10n.proposalApproved, style: const TextStyle(color: Colors.green));
177174
case Ongoing:
175+
final proposalLifetime = Duration(milliseconds: widget.params.proposalLifetime.toInt());
176+
if (proposal.isOlderThan(proposalLifetime)) {
177+
// Proposal lifetime has passed; proposal has expired.
178+
return SizedBox(
179+
height: 50,
180+
width: 60,
181+
child: UpdateProposalButton(
182+
proposalId: widget.proposalId,
183+
onPressed: _updateState,
184+
),
185+
);
186+
} else {
187+
return SizedBox(
188+
height: 50,
189+
width: 60,
190+
child: VoteButton(
191+
proposal: proposal,
192+
proposalId: widget.proposalId,
193+
purposeId: widget.purposeId,
194+
democracyParams: widget.params,
195+
onPressed: _updateState,
196+
),
197+
);
198+
}
178199
case Confirming:
179-
return SizedBox(
180-
height: 50,
181-
width: 60,
182-
child: VoteButton(
183-
proposal: proposal,
184-
proposalId: widget.proposalId,
185-
purposeId: widget.purposeId,
186-
democracyParams: widget.params,
187-
onPressed: _updateState,
188-
),
189-
);
200+
final confirmDuration = Duration(milliseconds: widget.params.confirmationPeriod.toInt());
201+
if (proposal.isConfirmingLongerThan(confirmDuration)!) {
202+
// confirmation time has passed
203+
return SizedBox(
204+
height: 50,
205+
width: 60,
206+
child: UpdateProposalButton(
207+
proposalId: widget.proposalId,
208+
onPressed: _updateState,
209+
),
210+
);
211+
} else {
212+
return SizedBox(
213+
height: 50,
214+
width: 60,
215+
child: VoteButton(
216+
proposal: proposal,
217+
proposalId: widget.proposalId,
218+
purposeId: widget.purposeId,
219+
democracyParams: widget.params,
220+
onPressed: _updateState,
221+
),
222+
);
223+
}
190224
default:
191225
// should never happen.
192226
return const Text('Unknown Proposal State');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import 'package:encointer_wallet/common/components/submit_button_cupertino.dart';
2+
import 'package:encointer_wallet/service/service.dart';
3+
import 'package:encointer_wallet/utils/alerts/app_alert.dart';
4+
import 'package:flutter/cupertino.dart';
5+
import 'package:flutter/material.dart';
6+
import 'package:provider/provider.dart';
7+
8+
import 'package:encointer_wallet/common/components/submit_button.dart';
9+
import 'package:encointer_wallet/l10n/l10.dart';
10+
import 'package:encointer_wallet/service/tx/lib/tx.dart';
11+
import 'package:encointer_wallet/store/app.dart';
12+
13+
class UpdateProposalButton extends StatefulWidget {
14+
const UpdateProposalButton({
15+
super.key,
16+
required this.proposalId,
17+
required this.onPressed,
18+
});
19+
20+
final BigInt proposalId;
21+
final void Function() onPressed;
22+
23+
@override
24+
State<UpdateProposalButton> createState() => _UpdateProposalButtonState();
25+
}
26+
27+
class _UpdateProposalButtonState extends State<UpdateProposalButton> {
28+
@override
29+
void initState() {
30+
super.initState();
31+
}
32+
33+
@override
34+
Widget build(BuildContext context) {
35+
final l10n = context.l10n;
36+
final store = context.read<AppStore>();
37+
38+
return SubmitButtonSmall(
39+
onPressed: (context) async {
40+
await _showSubmitUpdateProposalStateDialog(store, widget.proposalId);
41+
widget.onPressed();
42+
},
43+
child: Text(l10n.proposalClose),
44+
);
45+
}
46+
47+
Future<void> _showSubmitUpdateProposalStateDialog(AppStore store, BigInt proposalId) {
48+
final l10n = context.l10n;
49+
50+
return AppAlert.showDialog(
51+
context,
52+
title: Text('${l10n.proposal} $proposalId'),
53+
content: Padding(
54+
padding: const EdgeInsets.only(top: 10),
55+
child: Text(l10n.proposalUpdateExplanation),
56+
),
57+
actions: <Widget>[
58+
Column(
59+
children: [
60+
Row(
61+
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
62+
children: [
63+
SubmitButtonCupertino(
64+
onPressed: (BuildContext context) async {
65+
await _submitUpdateProposalState(store);
66+
Navigator.of(context).pop();
67+
},
68+
child: Text(l10n.proposalUpdateState, style: const TextStyle(color: Colors.green)),
69+
),
70+
CupertinoButton(
71+
onPressed: () => Navigator.of(context).pop(),
72+
child: Text(l10n.cancel),
73+
),
74+
],
75+
),
76+
],
77+
),
78+
],
79+
);
80+
}
81+
82+
Future<void> _submitUpdateProposalState(AppStore store) async {
83+
await submitDemocracyUpdateProposalState(
84+
context,
85+
store,
86+
webApi,
87+
store.account.getKeyringAccount(store.account.currentAccountPubKey!),
88+
widget.proposalId,
89+
txPaymentAsset: store.encointer.getTxPaymentAsset(store.encointer.chosenCid),
90+
);
91+
92+
setState(() {});
93+
}
94+
}

app/lib/service/substrate_api/encointer/encointer_api.dart

+22
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'dart:math';
44
import 'package:convert/convert.dart' show hex;
55

66
import 'package:encointer_wallet/config/consts.dart';
7+
import 'package:encointer_wallet/config/networks/networks.dart';
78
import 'package:encointer_wallet/mocks/mock_bazaar_data.dart';
89
import 'package:encointer_wallet/models/bazaar/account_business_tuple.dart';
910
import 'package:encointer_wallet/models/bazaar/business_identifier.dart';
@@ -807,6 +808,27 @@ class EncointerApi {
807808
}
808809

809810
DemocracyParams democracyParams() {
811+
return switch (store.settings.currentNetwork) {
812+
Network.encointerKusama => encointerKusamaParams(),
813+
Network.encointerRococo => encointerKusamaParams(),
814+
Network.gesell => encointerSoloParams(),
815+
Network.gesellDev => encointerSoloParams(),
816+
};
817+
}
818+
819+
DemocracyParams encointerSoloParams() {
820+
final minTurnout = BigInt.one;
821+
final confirmationPeriod = BigInt.from(300000);
822+
final proposalLifetime = BigInt.from(1200000);
823+
824+
return DemocracyParams(
825+
minTurnout: minTurnout,
826+
confirmationPeriod: confirmationPeriod,
827+
proposalLifetime: proposalLifetime,
828+
);
829+
}
830+
831+
DemocracyParams encointerKusamaParams() {
810832
final minTurnout = encointerKusama.constant.encointerDemocracy.minTurnout;
811833
final confirmationPeriod = encointerKusama.constant.encointerDemocracy.confirmationPeriod;
812834
final proposalLifetime = encointerKusama.constant.encointerDemocracy.proposalLifetime;

app/lib/service/tx/lib/src/submit_to_inner.dart

+3
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ Future<void> submitTxInner(
4848
);
4949

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

90+
print('Should show error dialog');
91+
8992
AppAlert.showDialog<void>(
9093
context,
9194
title: Text(message.title),

0 commit comments

Comments
 (0)