This commit is contained in:
poka 2024-05-01 22:41:24 +02:00
parent 83a5d0959c
commit c7b4f3968b
10 changed files with 277 additions and 198 deletions

View File

@ -71,6 +71,7 @@ query ($address: String!) {
) {
edges {
node {
isActive
createdOn
issuer {
accountId
@ -89,6 +90,7 @@ query ($address: String!) {
) {
edges {
node {
isActive
createdOn
receiver {
accountId

View File

@ -14,6 +14,7 @@ import 'package:gecko/providers/home.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/providers/wallet_options.dart';
import 'package:gecko/providers/wallets_profiles.dart';
import 'package:gecko/widgets/certify/cert_state.dart';
import 'package:gecko/widgets/transaction_status.dart';
import 'package:pinenacl/ed25519.dart';
import 'package:polkawallet_sdk/api/apiKeyring.dart';
@ -212,8 +213,8 @@ class SubstrateSdk with ChangeNotifier {
Future<List<int>> getCertsCounter(String address) async {
final idtyIndex = await _getIdentityIndexOf(address);
if (idtyIndex == null) {
certsCounterCache.update(address, (_) => [0, 0], ifAbsent: () => [0, 0]);
return [0, 0];
certsCounterCache.update(address, (_) => [], ifAbsent: () => []);
return [];
}
final certsReceiver =
await _getStorage('certification.storageIdtyCertMeta($idtyIndex)') ??
@ -378,36 +379,37 @@ class SubstrateSdk with ChangeNotifier {
return totalAmount;
}
Future<Map<String, int>> certState(String from, String to) async {
Map<String, int> result = {};
Future<CertState> certState(String from, String to) async {
final toStatus = (await idtyStatus([to])).first;
final myWallets = MyWalletsProvider();
if (from != to && myWallets.getWalletDataByAddress(from)!.isMembre()) {
final removableOn = await getCertValidityPeriod(from, to);
final certMeta = await getCertMeta(from);
final int nextIssuableOn = certMeta['nextIssuableOn'] ?? 0;
final certRemovableDuration = (removableOn - blocNumber) * 6;
const int renewDelay = 2 * 30 * 24 * 3600; // 2 months
if (certRemovableDuration >= renewDelay) {
final certRenewDuration = certRemovableDuration - renewDelay;
result.putIfAbsent('certRenewable', () => certRenewDuration);
} else if (nextIssuableOn > blocNumber) {
final certDelayDuration = (nextIssuableOn - blocNumber) * 6;
result.putIfAbsent('certDelay', () => certDelayDuration);
} else if (toStatus == IdtyStatus.unconfirmed) {
result.putIfAbsent('toStatus', () => 1);
} else if (toStatus == IdtyStatus.none) {
result.putIfAbsent('toStatus', () => 2);
result.putIfAbsent('canCert', () => 0);
} else {
result.putIfAbsent('canCert', () => 0);
}
if (from == to || !myWallets.getWalletDataByAddress(from)!.isMembre()) {
return CertState(status: CertStatus.none);
}
return result;
final removableOn = await getCertValidityPeriod(from, to);
final certMeta = await getCertMeta(from);
final int nextIssuableOn = certMeta['nextIssuableOn'] ?? 0;
final certRemovableDuration = (removableOn - blocNumber) * 6;
const int renewDelay = 2 * 30 * 24 * 3600; // 2 months
if (toStatus == IdtyStatus.notMember) {
return CertState(status: CertStatus.none);
} else if (certRemovableDuration >= renewDelay) {
final certRenewDuration = certRemovableDuration - renewDelay;
return CertState(
status: CertStatus.canRenewIn,
duration: Duration(seconds: certRenewDuration));
} else if (nextIssuableOn > blocNumber) {
final certDelayDuration = (nextIssuableOn - blocNumber) * 6;
return CertState(
status: CertStatus.mustWaitBeforeCert,
duration: Duration(seconds: certDelayDuration));
} else if (toStatus == IdtyStatus.unconfirmed) {
return CertState(status: CertStatus.mustConfirmIdentity);
} else {
return CertState(status: CertStatus.canCert);
}
}
Future<Map> getCertMeta(String address) async {
@ -1057,7 +1059,10 @@ class SubstrateSdk with ChangeNotifier {
List txOptions = [];
String? rawParams;
final toCerts = await getCertsCounter(destAddress);
var toCerts = await getCertsCounter(destAddress);
if (toCerts.isEmpty) {
toCerts = [0, 0];
}
log.d(
"debug toCert: ${toCerts[0]} --- ${currencyParameters['minCertForMembership']!} --- $toIdtyStatus");

View File

@ -13,10 +13,9 @@ import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/providers/wallets_profiles.dart';
import 'package:gecko/screens/activity.dart';
import 'package:gecko/widgets/commons/common_elements.dart';
import 'package:gecko/widgets/certify/cert_state.dart';
import 'package:gecko/screens/myWallets/unlocking_wallet.dart';
import 'package:gecko/screens/qrcode_fullscreen.dart';
import 'package:gecko/screens/transaction_in_progress.dart';
import 'package:gecko/widgets/bottom_app_bar.dart';
import 'package:gecko/widgets/header_profile.dart';
import 'package:gecko/widgets/page_route_no_transition.dart';
@ -150,136 +149,15 @@ class WalletViewScreen extends StatelessWidget {
WalletData? defaultWallet = myWalletProvider.getDefaultWallet();
return FutureBuilder(
future: sub.certState(defaultWallet.address, address),
builder: (context, AsyncSnapshot<Map<String, int>> snapshot) {
if (snapshot.data == null) return const SizedBox.shrink();
final certStateData = snapshot.data!;
String duration = '';
if (certStateData['certDelay'] != null ||
certStateData['certRenewable'] != null) {
final Duration durationSeconds = Duration(
seconds: certStateData['certDelay'] ??
certStateData['certRenewable']!);
final seconds = durationSeconds.inSeconds;
final minutes = durationSeconds.inMinutes;
if (seconds <= 0) {
duration = 'seconds'.tr(args: ['0']);
} else if (seconds <= 60) {
duration = 'seconds'.tr(args: [seconds.toString()]);
} else if (seconds <= 3600) {
duration = 'minutes'.tr(args: [minutes.toString()]);
} else if (seconds <= 86400) {
final hours = durationSeconds.inHours;
final minutesLeft = minutes - hours * 60;
String showMinutes = '';
if (minutesLeft < 60) {}
showMinutes =
'minutes'.tr(args: [minutesLeft.toString()]);
duration =
'hours'.tr(args: [hours.toString(), showMinutes]);
} else if (seconds <= 2592000) {
final days = durationSeconds.inDays;
duration = 'days'.tr(args: [days.toString()]);
} else {
final months = (durationSeconds.inDays / 30).round();
duration = 'months'.tr(args: [months.toString()]);
}
}
final toStatus = certStateData['toStatus'];
builder: (context, AsyncSnapshot<CertState> snapshot) {
if (!snapshot.hasData) return const SizedBox.shrink();
final certState = snapshot.data!;
return Visibility(
visible: (snapshot.data != {}),
child: Column(children: <Widget>[
if (certStateData['canCert'] != null ||
duration == 'seconds'.tr(args: ['0']))
Column(children: <Widget>[
ScaledSizedBox(
height: buttonSize,
child: ClipOval(
child: Material(
color: const Color(0xffFFD58D),
child: InkWell(
key: keyCertify,
splashColor: orangeC,
child: const Padding(
padding: EdgeInsets.only(bottom: 0),
child: Image(
image: AssetImage(
'assets/gecko_certify.png')),
),
onTap: () async {
final result =
await confirmPopupCertification(
context,
'areYouSureYouWantToCertify1'
.tr(),
duniterIndexer
.walletNameIndexer[
address] ??
"noIdentity".tr(),
'areYouSureYouWantToCertify2'
.tr(),
getShortPubkey(address)) ??
false;
if (!result) return;
if (myWalletProvider.pinCode == '') {
await Navigator.push(
context,
MaterialPageRoute(
builder: (homeContext) {
return UnlockingWallet(
wallet: defaultWallet);
},
),
);
}
if (myWalletProvider.pinCode == '') {
return;
}
WalletsProfilesProvider
walletViewProvider = Provider.of<
WalletsProfilesProvider>(
context,
listen: false);
final acc = sub.getCurrentWallet();
final transactionId = await sub.certify(
acc.address!,
walletViewProvider.address,
myWalletProvider.pinCode);
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return TransactionInProgress(
transactionId: transactionId,
transType: 'cert');
}),
);
}),
),
),
),
ScaledSizedBox(height: 6),
Text(
toStatus == null
? "certify".tr()
: "createIdentity".tr(),
textAlign: TextAlign.center,
style: scaledTextStyle(
fontSize: buttonFontSize,
fontWeight: FontWeight.w500),
),
])
else if (toStatus == 1)
waitToCert('mustConfirmHisIdentity', duration)
else if (certStateData['certRenewable'] != null &&
duration != 'seconds'.tr(args: ['0']))
waitToCert('canRenewCertInX', duration)
else if (certStateData['certDelay'] != null)
waitToCert('mustWaitXBeforeCertify', duration)
]),
visible: certState.status != CertStatus.none,
child: CertStateWidget(
certState: certState,
address: address,
),
);
},
);
@ -377,30 +255,4 @@ class WalletViewScreen extends StatelessWidget {
]),
));
}
Widget waitToCert(String status, String duration) {
return Column(children: <Widget>[
ScaledSizedBox(
height: buttonSize,
child: Container(
foregroundDecoration: const BoxDecoration(
color: Colors.grey,
backgroundBlendMode: BlendMode.saturation,
),
child: const Opacity(
opacity: 0.5,
child: Image(image: AssetImage('assets/gecko_certify.png')),
),
),
),
Text(
status.tr(args: [duration]),
textAlign: TextAlign.center,
style: scaledTextStyle(
fontSize: buttonFontSize - 4,
fontWeight: FontWeight.w400,
color: Colors.grey[600]),
),
]);
}
}

View File

@ -22,7 +22,7 @@ class Certifications extends StatelessWidget {
FutureBuilder(
future: sub.getCertsCounter(address),
builder: (BuildContext context, AsyncSnapshot<List<int>?> certs) {
if ((certs.data != null && certs.data!.isEmpty) ||
if ((certs.data == null || certs.data!.isEmpty) ||
sub.certsCounterCache[address] == null) {
return const SizedBox.shrink();
}

View File

@ -0,0 +1,75 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:gecko/widgets/certify/certify_button.dart';
import 'package:gecko/widgets/certify/wait_to_cert.dart';
class CertStateWidget extends StatelessWidget {
const CertStateWidget(
{Key? key, required this.certState, required this.address})
: super(key: key);
final CertState certState;
final String address;
@override
Widget build(BuildContext context) {
switch (certState.status) {
case CertStatus.canCert:
return CertifyButton(address);
case CertStatus.mustConfirmIdentity:
return WaitToCertWidget(
messageKey: 'mustConfirmHisIdentity',
duration: formatDuration(certState.duration!));
case CertStatus.canRenewIn:
return WaitToCertWidget(
messageKey: 'canRenewCertInX',
duration: formatDuration(certState.duration!));
case CertStatus.mustWaitBeforeCert:
return WaitToCertWidget(
messageKey: 'mustWaitXBeforeCertify',
duration: formatDuration(certState.duration!));
default:
return const SizedBox.shrink();
}
}
String formatDuration(Duration duration) {
final seconds = duration.inSeconds;
final minutes = duration.inMinutes;
if (seconds <= 0) {
return 'seconds'.tr(args: ['0']);
} else if (seconds <= 60) {
return 'seconds'.tr(args: [seconds.toString()]);
} else if (seconds <= 3600) {
return 'minutes'.tr(args: [minutes.toString()]);
} else if (seconds <= 86400) {
final hours = duration.inHours;
final minutesLeft = minutes - hours * 60;
String showMinutes =
minutesLeft < 60 ? '' : 'minutes'.tr(args: [minutesLeft.toString()]);
return 'hours'.tr(args: [hours.toString(), showMinutes]);
} else if (seconds <= 2592000) {
final days = duration.inDays;
return 'days'.tr(args: [days.toString()]);
} else {
final months = (duration.inDays / 30).round();
return 'months'.tr(args: [months.toString()]);
}
}
}
enum CertStatus {
canCert,
mustConfirmIdentity,
canRenewIn,
mustWaitBeforeCert,
none,
}
class CertState {
final CertStatus status;
final Duration? duration;
CertState({required this.status, this.duration});
}

View File

@ -0,0 +1,102 @@
// ignore_for_file: use_build_context_synchronously
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:gecko/providers/duniter_indexer.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/providers/wallets_profiles.dart';
import 'package:gecko/screens/myWallets/unlocking_wallet.dart';
import 'package:gecko/screens/transaction_in_progress.dart';
import 'package:gecko/screens/wallet_view.dart' show buttonSize, buttonFontSize;
import 'package:gecko/widgets/commons/common_elements.dart';
import 'package:provider/provider.dart';
class CertifyButton extends StatelessWidget {
const CertifyButton(this.address, {Key? key}) : super(key: key);
final String address;
@override
Widget build(BuildContext context) {
final duniterIndexer = Provider.of<DuniterIndexer>(context, listen: false);
final sub = Provider.of<SubstrateSdk>(context, listen: false);
final myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
final defaultWallet = myWalletProvider.getDefaultWallet();
return Column(
children: <Widget>[
ScaledSizedBox(
height: buttonSize,
child: ClipOval(
child: Material(
color: const Color(0xffFFD58D),
child: InkWell(
key: keyCertify,
splashColor: orangeC,
onTap: () async {
final result = await confirmPopupCertification(
context,
'areYouSureYouWantToCertify1'.tr(),
duniterIndexer.walletNameIndexer[address] ??
"noIdentity".tr(),
'areYouSureYouWantToCertify2'.tr(),
getShortPubkey(address),
) ??
false;
if (!result) return;
if (myWalletProvider.pinCode == '') {
await Navigator.push(
context,
MaterialPageRoute(
builder: (homeContext) {
return UnlockingWallet(wallet: defaultWallet);
},
),
);
}
if (myWalletProvider.pinCode == '') {
return;
}
WalletsProfilesProvider walletViewProvider =
Provider.of<WalletsProfilesProvider>(context,
listen: false);
final acc = sub.getCurrentWallet();
final transactionId = await sub.certify(
acc.address!,
walletViewProvider.address,
myWalletProvider.pinCode,
);
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return TransactionInProgress(
transactionId: transactionId,
transType: 'cert',
);
}),
);
},
child: const Padding(
padding: EdgeInsets.only(bottom: 0),
child: Image(image: AssetImage('assets/gecko_certify.png')),
),
),
),
),
),
ScaledSizedBox(height: 6),
Text(
"certify".tr(),
textAlign: TextAlign.center,
style: scaledTextStyle(
fontSize: buttonFontSize, fontWeight: FontWeight.w500),
),
],
);
}
}

View File

@ -0,0 +1,40 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/screens/wallet_view.dart' show buttonSize, buttonFontSize;
class WaitToCertWidget extends StatelessWidget {
final String messageKey;
final String duration;
const WaitToCertWidget(
{Key? key, required this.messageKey, required this.duration})
: super(key: key);
@override
Widget build(BuildContext context) {
return Column(children: <Widget>[
ScaledSizedBox(
height: buttonSize,
child: Container(
foregroundDecoration: const BoxDecoration(
color: Colors.grey,
backgroundBlendMode: BlendMode.saturation,
),
child: const Opacity(
opacity: 0.5,
child: Image(image: AssetImage('assets/gecko_certify.png')),
),
),
),
Text(
messageKey.tr(args: [duration]),
textAlign: TextAlign.center,
style: scaledTextStyle(
fontSize: buttonFontSize - 4,
fontWeight: FontWeight.w400,
color: Colors.grey[600]),
),
]);
}
}

View File

@ -76,6 +76,9 @@ class CertsList extends StatelessWidget {
List listCerts = [];
for (final certNode in certsData) {
final cert = certNode['node'];
if (!cert['isActive']) {
continue;
}
final String issuerAddress = cert[certFrom]['accountId'];
final String issuerName = cert[certFrom]['name'];
final date = DateTime.parse('2024-02-04T21:20:54.001+00:00');

View File

@ -62,43 +62,43 @@ Future<bool?> confirmPopupCertification(BuildContext context, String question1,
return AlertDialog(
backgroundColor: backgroundColor,
content: ScaledSizedBox(
height: 230,
height: 220,
child: Column(
children: [
ScaledSizedBox(height: 15),
ScaledSizedBox(height: 10),
Text(
question1,
textAlign: TextAlign.center,
style:
scaledTextStyle(fontSize: 18, fontWeight: FontWeight.w400),
scaledTextStyle(fontSize: 16, fontWeight: FontWeight.w400),
),
ScaledSizedBox(height: 20),
ScaledSizedBox(height: 15),
Text(
username,
textAlign: TextAlign.center,
style:
scaledTextStyle(fontSize: 21, fontWeight: FontWeight.w500),
scaledTextStyle(fontSize: 19, fontWeight: FontWeight.w500),
),
ScaledSizedBox(height: 20),
ScaledSizedBox(height: 15),
Text(
question2,
textAlign: TextAlign.center,
style:
scaledTextStyle(fontSize: 18, fontWeight: FontWeight.w400),
scaledTextStyle(fontSize: 16, fontWeight: FontWeight.w400),
),
ScaledSizedBox(height: 20),
ScaledSizedBox(height: 15),
Text(
address,
textAlign: TextAlign.center,
style:
scaledTextStyle(fontSize: 18, fontWeight: FontWeight.w500),
scaledTextStyle(fontSize: 16, fontWeight: FontWeight.w500),
),
ScaledSizedBox(height: 20),
ScaledSizedBox(height: 15),
Text(
'?',
textAlign: TextAlign.center,
style:
scaledTextStyle(fontSize: 18, fontWeight: FontWeight.w400),
scaledTextStyle(fontSize: 16, fontWeight: FontWeight.w400),
),
],
),

View File

@ -2,7 +2,7 @@ name: gecko
description: Pay with G1.
publish_to: "none"
version: 0.1.7+76
version: 0.1.7+77
environment:
sdk: ">=2.12.0 <3.0.0"