Skip to content
Snippets Groups Projects
Commit ac3be881 authored by poka's avatar poka
Browse files

add renewal button on wallet option screen

parent b0cae2b5
No related branches found
No related tags found
No related merge requests found
Pipeline #39047 waiting for manual action
......@@ -246,5 +246,6 @@
"membershipExpiresOn": "Membership expires on {}, (renewable {} days before)",
"renewingMembership": "Renewing membership",
"membershipRenewalConfirmed": "Your membership renewal request has been registered and will be effective within a few minutes",
"membershipRenewalPending": "Membership renewal in progress..."
"membershipRenewalPending": "Membership renewal in progress...",
"membershipRenewalPeriodNotRespected": "You must wait for the renewal period before you can renew your membership"
}
......@@ -247,5 +247,6 @@
"membershipExpiresOn": "Adhésion expira el {}, (renovable {} días antes)",
"renewingMembership": "Renovando adhésion",
"membershipRenewalConfirmed": "Su solicitud de renovación de membresía ha sido registrada y será efectiva en unos minutos",
"membershipRenewalPending": "Renovación de membresía en curso..."
"membershipRenewalPending": "Renovación de membresía en curso...",
"membershipRenewalPeriodNotRespected": "Debe esperar el período de renovación antes de poder renovar su membresía"
}
......@@ -246,5 +246,6 @@
"membershipExpiresOn": "Adhésion expire le {} (renouvelable {} jours avant)",
"renewingMembership": "Renouvellement d'adhésion",
"membershipRenewalConfirmed": "Votre demande de renouvellement d'adhésion a bien été prise en compte et sera effective d'ici quelques minutes",
"membershipRenewalPending": "Renouvellement d'adhésion en cours..."
"membershipRenewalPending": "Renouvellement d'adhésion en cours...",
"membershipRenewalPeriodNotRespected": "Vous devez attendre la période de renouvellement avant de pouvoir renouveler votre adhésion"
}
// ignore_for_file: use_build_context_synchronously
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/screens/transaction_in_progress.dart';
import 'package:gecko/utils.dart';
import 'package:gecko/widgets/commons/common_elements.dart';
import 'package:provider/provider.dart';
import 'package:gecko/models/membership_status.dart';
class MembershipRenewal {
static RenewalInfo calculateRenewalInfo(MembershipStatus status, int renewalPeriodBlocks) {
if (status.expireDate == null) {
return RenewalInfo(canRenew: false);
}
final now = DateTime.now();
final renewalPeriodInSeconds = renewalPeriodBlocks * 6;
final renewalDate = status.expireDate!.subtract(Duration(seconds: renewalPeriodInSeconds));
final isExpired = status.expireDate!.isBefore(now);
final canRenew = now.isAfter(renewalDate) && !status.hasPendingRenewal;
return RenewalInfo(
expireDate: status.expireDate,
renewalDate: renewalDate,
isExpired: isExpired,
canRenew: canRenew,
hasPendingRenewal: status.hasPendingRenewal,
);
}
static Future<void> executeRenewal(BuildContext context, String address) async {
final answer = await confirmPopup(context, 'areYouSureYouWantToRenewMembership'.tr()) ?? false;
if (!answer) return;
final myWalletProvider = Provider.of<MyWalletsProvider>(context, listen: false);
if (!await myWalletProvider.askPinCode()) return;
final sub = Provider.of<SubstrateSdk>(context, listen: false);
final transactionId = await sub.renewMembership(address, myWalletProvider.pinCode);
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return TransactionInProgress(
transactionId: transactionId,
transType: 'renewMembership',
fromAddress: getShortPubkey(address),
toAddress: getShortPubkey(address),
);
}),
);
}
static Widget buildExpirationText(RenewalInfo info, {double? width}) {
if (info.expireDate == null) return const SizedBox.shrink();
final text = info.hasPendingRenewal
? 'membershipRenewalPending'.tr()
: info.isExpired
? 'membershipExpiredOn'.tr(args: [DateFormat('dd/MM/yyyy').format(info.expireDate!)])
: info.canRenew
? 'membershipExpiresOnSimple'.tr(args: [DateFormat('dd/MM/yyyy').format(info.expireDate!)])
: 'membershipExpiresOn'
.tr(args: [DateFormat('dd/MM/yyyy').format(info.expireDate!), ((info.expireDate!.difference(info.renewalDate!)).inDays).toString()]);
final textWidget = Text(
text,
style: scaledTextStyle(
fontSize: width != null ? 15 : 12,
color: Colors.grey[500],
fontStyle: width != null ? FontStyle.italic : null,
),
);
return width != null ? SizedBox(width: scaleSize(width), child: textWidget) : SizedBox(width: scaleSize(250), child: textWidget);
}
}
class RenewalInfo {
final DateTime? expireDate;
final DateTime? renewalDate;
final bool isExpired;
final bool canRenew;
final bool hasPendingRenewal;
RenewalInfo({
this.expireDate,
this.renewalDate,
this.isExpired = false,
this.canRenew = false,
this.hasPendingRenewal = false,
});
}
......@@ -14,6 +14,7 @@ import 'package:gecko/screens/transaction_in_progress.dart';
import 'package:gecko/widgets/commons/top_appbar.dart';
import 'package:provider/provider.dart';
import 'package:gecko/models/membership_status.dart';
import 'package:gecko/models/membership_renewal.dart';
class ManageMembership extends StatelessWidget {
const ManageMembership({super.key, required this.address});
......@@ -141,52 +142,25 @@ class ManageMembership extends StatelessWidget {
Widget renewMembership(BuildContext context, MembershipStatus status) {
final sub = Provider.of<SubstrateSdk>(context, listen: false);
if (status.expireDate == null) return const SizedBox.shrink();
final now = DateTime.now();
final renewalPeriodInSeconds = (sub.currencyParameters['membershipRenewalPeriod']!) * 6;
final renewalDate = status.expireDate!.subtract(Duration(seconds: renewalPeriodInSeconds));
final renewalPeriodInDays = (renewalPeriodInSeconds / 86400).truncate();
final info = MembershipRenewal.calculateRenewalInfo(
status,
sub.currencyParameters['membershipRenewalPeriod']!,
);
final isExpired = status.expireDate!.isBefore(now);
final canRenew = now.isAfter(renewalDate) && !status.hasPendingRenewal;
if (info.expireDate == null) return const SizedBox.shrink();
return ScaledSizedBox(
height: 75,
child: InkWell(
key: keyRenewMembership,
onTap: canRenew
? () async {
final answer = await confirmPopup(context, 'areYouSureYouWantToRenewMembership'.tr()) ?? false;
if (!answer) return;
final myWalletProvider = Provider.of<MyWalletsProvider>(context, listen: false);
if (!await myWalletProvider.askPinCode()) return;
final transactionId = await sub.renewMembership(address, myWalletProvider.pinCode);
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return TransactionInProgress(
transactionId: transactionId,
transType: 'renewMembership',
fromAddress: getShortPubkey(address),
toAddress: getShortPubkey(address),
);
}),
);
}
: null,
onTap: info.canRenew ? () => MembershipRenewal.executeRenewal(context, address) : null,
child: Row(
children: <Widget>[
ScaledSizedBox(width: 20),
Image.asset(
'assets/medal.png',
height: scaleSize(28),
color: canRenew ? null : Colors.grey[500],
color: info.canRenew ? null : Colors.grey[500],
),
ScaledSizedBox(width: 16),
Column(
......@@ -197,25 +171,10 @@ class ManageMembership extends StatelessWidget {
'renewMembership'.tr(),
style: scaledTextStyle(
fontSize: 17,
color: canRenew ? null : Colors.grey[500],
),
),
SizedBox(
width: scaleSize(250),
child: Text(
status.hasPendingRenewal
? 'membershipRenewalPending'.tr()
: isExpired
? 'membershipExpiredOn'.tr(args: [DateFormat('dd/MM/yyyy').format(status.expireDate!)])
: canRenew
? 'membershipExpiresOnSimple'.tr(args: [DateFormat('dd/MM/yyyy').format(status.expireDate!)])
: 'membershipExpiresOn'.tr(args: [DateFormat('dd/MM/yyyy').format(status.expireDate!), renewalPeriodInDays.toString()]),
style: scaledTextStyle(
fontSize: 12,
color: Colors.grey[500],
),
color: info.canRenew ? null : Colors.grey[500],
),
),
MembershipRenewal.buildExpirationText(info),
],
),
],
......
......@@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/svg.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/membership_status.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:gecko/providers/duniter_indexer.dart';
......@@ -23,7 +24,6 @@ import 'package:gecko/screens/qrcode_fullscreen.dart';
import 'package:gecko/utils.dart';
import 'package:gecko/widgets/balance.dart';
import 'package:gecko/widgets/bottom_app_bar.dart';
import 'package:gecko/widgets/buttons/manage_membership_button.dart';
import 'package:gecko/widgets/certifications.dart';
import 'package:gecko/widgets/commons/offline_info.dart';
import 'package:gecko/widgets/idty_status.dart';
......@@ -31,6 +31,8 @@ import 'package:gecko/widgets/name_by_address.dart';
import 'package:gecko/widgets/page_route_no_transition.dart';
import 'package:provider/provider.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:gecko/widgets/buttons/manage_membership_button.dart';
import 'package:gecko/models/membership_renewal.dart';
class WalletOptions extends StatelessWidget {
const WalletOptions({Key? keyMyWallets, required this.wallet}) : super(key: keyMyWallets);
......@@ -184,6 +186,7 @@ class WalletOptions extends StatelessWidget {
walletProvider.isDefaultWallet = walletOptions.address.text == defaultWallet.address;
return Column(children: [
confirmIdentityButton(walletProvider),
if (wallet.isMembre()) renewMembershipButton(walletProvider),
pubkeyWidget(walletProvider, context),
ScaledSizedBox(height: 11),
activityWidget(context, historyProvider, walletProvider),
......@@ -194,7 +197,7 @@ class WalletOptions extends StatelessWidget {
],
Column(children: [
if (!walletProvider.isDefaultWallet && !wallet.isMembre()) deleteWallet(context, walletProvider, currentChest),
if (wallet.isMembre()) const ManageMembershipButton()
const ManageMembershipButton(),
]),
if (isAlone) aloneWalletOptions()
]);
......@@ -267,8 +270,8 @@ class WalletOptions extends StatelessWidget {
if (snapshot.data!.first == IdtyStatus.unconfirmed) {
return Column(children: [
ScaledSizedBox(
width: 310,
height: 55,
width: 290,
height: 50,
child: ElevatedButton(
key: keyConfirmIdentity,
style: ElevatedButton.styleFrom(
......@@ -464,6 +467,48 @@ class WalletOptions extends StatelessWidget {
);
});
}
Widget renewMembershipButton(WalletOptionsProvider walletProvider) {
return Consumer<SubstrateSdk>(builder: (context, sub, _) {
return FutureBuilder<MembershipStatus>(
future: sub.getMembershipStatus(walletProvider.address.text),
builder: (context, snapshot) {
if (!snapshot.hasData || snapshot.hasError) {
return const SizedBox.shrink();
}
final info = MembershipRenewal.calculateRenewalInfo(
snapshot.data!,
sub.currencyParameters['membershipRenewalPeriod']!,
);
if (!info.canRenew) return const SizedBox.shrink();
return Column(children: [
ScaledSizedBox(
width: 290,
height: 50,
child: ElevatedButton(
key: keyRenewMembership,
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: orangeC,
),
onPressed: () => MembershipRenewal.executeRenewal(context, walletProvider.address.text),
child: Text(
'renewMembership'.tr(),
style: scaledTextStyle(fontSize: 17),
),
),
),
ScaledSizedBox(height: 7),
MembershipRenewal.buildExpirationText(info, width: 250),
ScaledSizedBox(height: 40),
]);
},
);
});
}
}
Widget aloneWalletOptions() {
......
......@@ -95,7 +95,9 @@ class _TransactionInProgressState extends State<TransactionInProgress> {
resultText = 'extrinsicValidated'.tr(args: [actionMap[widget.transType] ?? 'strangeTransaction'.tr()]);
}
} else if (txContent!.status == TransactionStatus.failed) {
resultText = errorTransactionMap[txContent!.error] ?? txContent!.error!;
final errorParts = txContent!.error?.split('Exception: ');
final error = errorParts != null && errorParts.length > 1 ? errorParts[1] : txContent!.error;
resultText = errorTransactionMap[error] ?? error!;
} else {
resultText = statusStatusMap[txContent!.status] ?? 'Unknown status: ${txContent!.status}';
}
......@@ -106,10 +108,13 @@ class _TransactionInProgressState extends State<TransactionInProgress> {
TransactionStatusIcon(txContent!.status),
ScaledSizedBox(height: 7),
if (txContent!.status != TransactionStatus.none)
Text(
SizedBox(
width: scaleSize(300),
child: Text(
resultText,
textAlign: TextAlign.center,
style: scaledTextStyle(fontSize: 16),
),
)
],
);
......
import 'package:easy_localization/easy_localization.dart';
enum TransactionStatus {
sending,
propagation,
validating,
failed,
success,
timeout,
finalized,
none
}
enum TransactionStatus { sending, propagation, validating, failed, success, timeout, finalized, none }
Map<String, String> actionMap = {
'pay': 'transaction'.tr(),
......@@ -26,8 +17,7 @@ Map<TransactionStatus, String> statusStatusMap = {
TransactionStatus.propagation: 'propagating'.tr(),
TransactionStatus.validating: 'validating'.tr(),
TransactionStatus.success: 'extrinsicValidated'.tr(args: [actionMap['pay']!]),
TransactionStatus.finalized:
'extrinsicFinalized'.tr(args: [actionMap['pay']!]),
TransactionStatus.finalized: 'extrinsicFinalized'.tr(args: [actionMap['pay']!]),
TransactionStatus.timeout: 'execTimeoutOver'.tr(),
};
......@@ -37,7 +27,7 @@ Map<String, String> errorTransactionMap = {
'cert.CannotCertifySelf': 'canNotCertifySelf'.tr(),
'identity.IdtyNameAlreadyExist': 'nameAlreadyExist'.tr(),
'balances.KeepAlive': '2GDtoKeepAlive'.tr(),
'1010: Invalid Transaction: Inability to pay some fees , e.g. account balance too low':
'youHaveToFeedThisAccountBeforeUsing'.tr(),
'1010: Invalid Transaction: Inability to pay some fees , e.g. account balance too low': 'youHaveToFeedThisAccountBeforeUsing'.tr(),
'Token.FundsUnavailable': 'fundsUnavailable'.tr(),
'wot.MembershipRenewalPeriodNotRespected': 'membershipRenewalPeriodNotRespected'.tr(),
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment