From b0cae2b5cee9f25f7967c508b4dce0ea7d016e1c Mon Sep 17 00:00:00 2001
From: poka <poka@p2p.legal>
Date: Sun, 1 Dec 2024 18:24:52 +0100
Subject: [PATCH] improve membership renewal info

---
 assets/translations/en.json                  |  6 ++-
 assets/translations/es.json                  |  6 ++-
 assets/translations/fr.json                  |  6 ++-
 lib/models/membership_status.dart            |  9 ++++
 lib/providers/substrate_sdk.dart             | 47 ++++++++++++++++----
 lib/screens/myWallets/manage_membership.dart | 41 ++++++++++-------
 pubspec.yaml                                 |  2 +-
 7 files changed, 87 insertions(+), 30 deletions(-)
 create mode 100644 lib/models/membership_status.dart

diff --git a/assets/translations/en.json b/assets/translations/en.json
index e78ef29e..bd04327a 100644
--- a/assets/translations/en.json
+++ b/assets/translations/en.json
@@ -242,7 +242,9 @@
     "renewMembership": "Renew my membership",
     "areYouSureYouWantToRenewMembership": "Are you sure you want to renew your membership ?",
     "membershipExpiredOn": "Membership expired on {}",
-    "membershipExpiresOn": "Membership expires on {}",
+    "membershipExpiresOnSimple": "Membership expires on {}",
+    "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"
+    "membershipRenewalConfirmed": "Your membership renewal request has been registered and will be effective within a few minutes",
+    "membershipRenewalPending": "Membership renewal in progress..."
 }
diff --git a/assets/translations/es.json b/assets/translations/es.json
index b92a7973..a1e9fcce 100644
--- a/assets/translations/es.json
+++ b/assets/translations/es.json
@@ -243,7 +243,9 @@
     "renewMembership": "Renovar mi adhésion",
     "areYouSureYouWantToRenewMembership": "¿Estás seguro de que quieres renovar tu adhésion?",
     "membershipExpiredOn": "Adhésion expirée el {}",
-    "membershipExpiresOn": "Adhésion expira el {}",
+    "membershipExpiresOnSimple": "Adhésion expira el {}",
+    "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"
+    "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..."
 }
diff --git a/assets/translations/fr.json b/assets/translations/fr.json
index 0e48261e..fa833e88 100644
--- a/assets/translations/fr.json
+++ b/assets/translations/fr.json
@@ -242,7 +242,9 @@
     "renewMembership": "Renouveler mon adhésion",
     "areYouSureYouWantToRenewMembership": "Êtes-vous sûr de vouloir renouveler votre adhésion ?",
     "membershipExpiredOn": "Adhésion expirée le {}",
-    "membershipExpiresOn": "Adhésion expire le {}",
+    "membershipExpiresOnSimple": "Adhésion expire le {}",
+    "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"
+    "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..."
 }
diff --git a/lib/models/membership_status.dart b/lib/models/membership_status.dart
new file mode 100644
index 00000000..4413943e
--- /dev/null
+++ b/lib/models/membership_status.dart
@@ -0,0 +1,9 @@
+class MembershipStatus {
+  final DateTime? expireDate;
+  final bool hasPendingRenewal;
+
+  const MembershipStatus({
+    required this.expireDate,
+    required this.hasPendingRenewal,
+  });
+}
diff --git a/lib/providers/substrate_sdk.dart b/lib/providers/substrate_sdk.dart
index 3cce4033..86eb366c 100644
--- a/lib/providers/substrate_sdk.dart
+++ b/lib/providers/substrate_sdk.dart
@@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:gecko/globals.dart';
 import 'package:gecko/models/chest_data.dart';
+import 'package:gecko/models/membership_status.dart';
 import 'package:gecko/models/migrate_wallet_checks.dart';
 import 'package:gecko/models/transaction_content.dart';
 import 'package:gecko/models/wallet_data.dart';
@@ -139,10 +140,13 @@ class SubstrateSdk with ChangeNotifier {
     }
   }
 
-  Future<int> _getStorageConst(String call) async {
-    final result = (await sdk.webView!.evalJavascript('api.consts.$call', wrapPromise: false) ?? [null])[0];
+  Future<List<int>> _getStorageConst(List<String> calls) async {
+    final result = await sdk.webView!.evalJavascript(
+      'Object.values(Object.fromEntries([${calls.map((call) => '["$call", api.consts.$call[0]]').join(',')}]))',
+      wrapPromise: false,
+    );
 
-    return checkInt(result) ?? 0;
+    return (result as List).map((dynamic value) => checkInt(value) ?? 0).toList();
   }
 
   int? checkInt(dynamic value) {
@@ -486,14 +490,19 @@ class SubstrateSdk with ChangeNotifier {
       'certPeriod': 'certification.certPeriod.words',
       'certMaxByIssuer': 'certification.maxByIssuer.words',
       'certValidityPeriod': 'certification.validityPeriod.words',
+      'membershipRenewalPeriod': 'membership.membershipRenewalPeriod.words',
     };
 
-    for (final param in currencyParametersNames.keys) {
-      try {
-        currencyParameters[param] = await _getStorageConst(currencyParametersNames[param]!);
-      } catch (e) {
-        log.e('error while getting param $param :: $e');
+    try {
+      final values = await _getStorageConst(currencyParametersNames.values.toList());
+
+      int i = 0;
+      for (final param in currencyParametersNames.keys) {
+        currencyParameters[param] = values[i];
+        i++;
       }
+    } catch (e) {
+      log.e('error while getting currency parameters: $e');
     }
     log.i('currencyParameters: $currencyParameters');
   }
@@ -1249,4 +1258,26 @@ newKeySig: $newKeySigType""");
     _executeCall(transactionContent, txInfo, [], password);
     return transactionId;
   }
+
+  Future<MembershipStatus> getMembershipStatus(String address) async {
+    final idtyIndex = await _getIdentityIndexOf(address);
+    if (idtyIndex == null) return MembershipStatus(expireDate: null, hasPendingRenewal: false);
+
+    // Vérifier si une évaluation est en cours
+    final hasPendingRenewal = await _getStorage('distance.pendingEvaluationRequest($idtyIndex)') != null;
+
+    final expireOnMap = await _getStorage('membership.membership($idtyIndex)') ?? {};
+    final expireOn = expireOnMap['expireOn'] as int;
+
+    // Calculate time difference from current block (6 seconds per block)
+    final blockDifference = expireOn - blocNumber;
+
+    // Returns expiration date by adding (or subtracting if expired) time from now
+    final expireDate = DateTime.now().add(Duration(seconds: blockDifference * 6));
+
+    return MembershipStatus(
+      expireDate: expireDate,
+      hasPendingRenewal: hasPendingRenewal,
+    );
+  }
 }
diff --git a/lib/screens/myWallets/manage_membership.dart b/lib/screens/myWallets/manage_membership.dart
index a4559d95..3732395d 100644
--- a/lib/screens/myWallets/manage_membership.dart
+++ b/lib/screens/myWallets/manage_membership.dart
@@ -13,6 +13,7 @@ import 'package:gecko/screens/myWallets/migrate_identity.dart';
 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';
 
 class ManageMembership extends StatelessWidget {
   const ManageMembership({super.key, required this.address});
@@ -28,11 +29,11 @@ class ManageMembership extends StatelessWidget {
       body: SafeArea(
         child: Column(children: <Widget>[
           ScaledSizedBox(height: 20),
-          FutureBuilder<DateTime?>(
-            future: sub.membershipExpireIn(address),
+          FutureBuilder<MembershipStatus>(
+            future: sub.getMembershipStatus(address),
             builder: (context, snapshot) {
               if (snapshot.hasData) {
-                return renewMembership(context, snapshot.data);
+                return renewMembership(context, snapshot.data!);
               }
               return const SizedBox.shrink();
             },
@@ -138,14 +139,17 @@ class ManageMembership extends StatelessWidget {
     );
   }
 
-  Widget renewMembership(BuildContext context, DateTime? expireIn) {
+  Widget renewMembership(BuildContext context, MembershipStatus status) {
     final sub = Provider.of<SubstrateSdk>(context, listen: false);
-    if (expireIn == null) return const SizedBox.shrink();
+    if (status.expireDate == null) return const SizedBox.shrink();
 
     final now = DateTime.now();
-    final twoMonthsFromNow = now.add(const Duration(days: 60));
-    final isExpired = expireIn.isBefore(now);
-    final canRenew = expireIn.isBefore(twoMonthsFromNow);
+    final renewalPeriodInSeconds = (sub.currencyParameters['membershipRenewalPeriod']!) * 6;
+    final renewalDate = status.expireDate!.subtract(Duration(seconds: renewalPeriodInSeconds));
+    final renewalPeriodInDays = (renewalPeriodInSeconds / 86400).truncate();
+
+    final isExpired = status.expireDate!.isBefore(now);
+    final canRenew = now.isAfter(renewalDate) && !status.hasPendingRenewal;
 
     return ScaledSizedBox(
       height: 75,
@@ -196,13 +200,20 @@ class ManageMembership extends StatelessWidget {
                     color: canRenew ? null : Colors.grey[500],
                   ),
                 ),
-                Text(
-                  isExpired
-                      ? 'membershipExpiredOn'.tr(args: [DateFormat('dd/MM/yyyy').format(expireIn)])
-                      : 'membershipExpiresOn'.tr(args: [DateFormat('dd/MM/yyyy').format(expireIn)]),
-                  style: scaledTextStyle(
-                    fontSize: 12,
-                    color: 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],
+                    ),
                   ),
                 ),
               ],
diff --git a/pubspec.yaml b/pubspec.yaml
index fa73307b..0cf17bcb 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -2,7 +2,7 @@ name: gecko
 description: Pay with G1.
 publish_to: "none"
 
-version: 0.1.13+85
+version: 0.1.14+86
 
 environment:
   sdk: ^3.5.3
-- 
GitLab