Compare commits

..

1 Commits

Author SHA1 Message Date
poka 68c8a61939 add gdev.p2p.legal endpoint 2022-11-27 03:30:27 +01:00
172 changed files with 7789 additions and 11537 deletions

1
.env Normal file
View File

@ -0,0 +1 @@
ip_address=127.0.0.1

7
.gitignore vendored
View File

@ -47,6 +47,9 @@ android/key.properties
# Rust things
/target
# Linux builds
linux/
# Custom
scripts/private/
AppDir/
@ -56,5 +59,5 @@ android/app/build.gradle
integration_test/duniter/data/chains/
# Ignore PC deps
scripts/pushGecko
android-old
macos/
windows/

View File

@ -7,7 +7,7 @@ stages:
.env:
image: axiomteam/gecko-ci:v0.0.11
tags:
- docker
- redshift
format:
extends: .env
@ -28,10 +28,9 @@ build_and_test:
- if: $CI_COMMIT_TAG || $CI_MERGE_REQUEST_ID
- when: manual
stage: build_and_test
tags:
- redshift
script:
- flutter clean
- rm -rf /root/.pub-cache
- flutter pub get
- flutter analyze
releases:test:

View File

@ -4,7 +4,7 @@
# This file should be version controlled.
version:
revision: c07f7888888435fd9df505aa2efc38d3cf65681b
revision: 85684f9300908116a78138ea4c6036c35c9a1236
channel: stable
project_type: app
@ -13,26 +13,11 @@ project_type: app
migration:
platforms:
- platform: root
create_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
base_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
create_revision: 85684f9300908116a78138ea4c6036c35c9a1236
base_revision: 85684f9300908116a78138ea4c6036c35c9a1236
- platform: android
create_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
base_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
- platform: ios
create_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
base_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
- platform: linux
create_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
base_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
- platform: macos
create_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
base_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
- platform: web
create_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
base_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
- platform: windows
create_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
base_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
create_revision: 85684f9300908116a78138ea4c6036c35c9a1236
base_revision: 85684f9300908116a78138ea4c6036c35c9a1236
# User provided section

View File

@ -1,13 +1,12 @@
# Ğecko
Ğecko is mobile client for Duniter v2s blockchain (Ḡ1v2): https://duniter.org/blog/duniter-substrate<br>
It use polkawallet_sdk package to interact with Duniter: https://github.com/polkawallet-io/sdk
Ğecko is a transaction client owned by [Axiom-Team association] and written in Dart. It is fast and secure thanks to native code compilation. It is not intended to manage member accounts, but rather simple wallets.
This application is maintained by [Axiom-Team association](https://axiom-team.fr/).
The development is quite early, you can participate in the discussion [on the Duniter forum](https://forum.duniter.org/t/gecko-nouveau-client-de-paiements-1-sur-mobile-en-cours-de-developpement-dart-flutter/7857) (mostly FR)
You can download the last version of the app [here](https://forum.duniter.org/t/gecko-gdev-last-build/9367/last).<br>
You can ask questions about Ḡecko developpement in our [Duniter forum](https://forum.duniter.org/t/gecko-talks-user-support/9372/last).
[Axiom-Team association]: https://axiom-team.fr/
## Getting Started
<div align="center">
@ -18,78 +17,73 @@ You can ask questions about Ḡecko developpement in our [Duniter forum](https:/
## Develop
To contribute to the code, we advise you to install the following development environment:
To contribute to the code, we advise you to install the following development environment.
1. [Android Studio](https://developer.android.com/studio/install)
2. [Flutter](https://docs.flutter.dev/get-started/install)
3. [VSCode](https://code.visualstudio.com/docs/setup/linux) or [VSCodium](https://vscodium.com/)
1. Android Studio
- Android VM
- Android NDK
1. Flutter SDK
1. VSCode/Codium Flutter extension
This will take about 12GB on your drive and 20 min of your time (with a good connection). Don't
hesitate to ask on the forum for a peer-coding session if you are stuck.
At the end, `flutter doctor` command should be OK for what you need.
This will take about 12GB on your drive and 30 min of your time (with a good connection). Don't hesitate to ask on the forum for a peer-coding session if you are stuck.
### Android Studio
Android Studio will let you set up an Android VM and install tools you need.
- Install [Android Studio](https://developer.android.com/studio/) using your favorite installation
method.
- Install [Android Studio](https://developer.android.com/studio/) using your favorite installation method.
- At startup, do not open a project but click "configure" at the bottom of the "Welcome" menu
- In "SDK Manager"
- SDK Platforms tab
- select Android 11 (R) API level 30 (default) or higher
- SDK Platforms Ttab
- note your SDK folder location (later used for Rust environment variables)
- select Android 11 (R) API level 30 (default)
- SDK Tools
- select NDK (native development kit used to compile Rust to native target)
- In "AVD Manager"
- create a virtual machine
- create a virtual machine (ours is Pixel 4 32bits machine)
- launch it in the emulator
If you reach this point without trouble, you're good to go for the next step.
### iOS (Xcode on Mac)
TODO: documentation
### Flutter SDK
Flutter is a powerfull SDK to develop Android
apps. [Install it](https://flutter.dev/docs/get-started/install/linux) with your favorite
installation method.
Flutter is a powerfull SDK to develop Android apps. [Install it](https://flutter.dev/docs/get-started/install/linux) with your favorite installation method.
### VSCode
We are using VSCode and therefore document the process for this IDE. Of course you're free to use
whatever you want.
Clone the ğecko repo and open a dart file (e.g. `lib/main.dart`). VSCode will suggest you to insall
relevant extensions.
We are using VSCode and therefore document the process for this IDE. Of course you're free to use whatever you want.
Clone the ğecko repo and open a dart file (e.g. `lib/main.dart`). VSCode will suggest you to insall relevant extensions.
### Launch the app in debug mode
### Build the app
Start a VM, then open a dart file (e.g. `lib/main.dart`), type the `F5` key to build the code. The app should open
automatically in your VM which is running.
In a dart file (e.g. `lib/main.dart`), type the `F5` key to build the code. The app should open automatically in your VM which is running.
### Build APK
### Build your app for Desktop
You will need to generate PlayStore key or disable signing APK before continue.
Then, check this script and launch it:
#### Linux
Install dependancies:
`sudo apt-get install clang cmake ninja-build pkg-config libgtk-3-dev`
Then build debug for linux:
`flutter run -d linux`
If you get this error:
```
./scripts/build-apk.sh
flutter /usr/share/cmake-3.16/Modules/FindPkgConfig.cmake:643 (_pkg_check_modules_internal)
```
### Integration tests
Please try:
Open an android or iOS emulator, then launch this script:
`sudo apt install liblzma-dev`
```
./integration_test/launch_test.sh
```
then
It will start the [default test scenario](https://git.duniter.org/clients/gecko/-/blob/master/integration_test/scenarios/gecko_complete.dart).
`flutter clean && flutter run -d linux`
You can start another scenario defined [here](https://git.duniter.org/clients/gecko/-/tree/master/integration_test/scenarios) specifying the name of the file without its extension, for example to run migrate_cesium_identity.dart test:
## Roadmap
```
./integration_test/launch_test.sh migrate_cesium_identity
```
### A problem ?
Please open an issue here: https://git.duniter.org/clients/gecko/-/boards
-> https://pad.p2p.legal/gecko-roadmap-2022?view

View File

@ -1,9 +1,3 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
@ -12,6 +6,11 @@ if (localPropertiesFile.exists()) {
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
@ -22,6 +21,10 @@ if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
@ -29,30 +32,21 @@ if (keystorePropertiesFile.exists()) {
}
android {
namespace "gecko.axiomteam.fr"
compileSdk flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
compileSdkVersion 33
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "gecko.axiomteam.fr"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
minSdkVersion 19
targetSdkVersion 33
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
multiDexEnabled true
@ -71,7 +65,7 @@ android {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.release
signingConfig signingConfigs.release //poka: comment this to build unsigned release, or set to signingConfigs.debug to sign with debug keys
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
@ -82,9 +76,11 @@ android {
}
}
flutter {
source '../..'
}
dependencies {}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.work:work-runtime-ktx:2.7.0'
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,7 +1,20 @@
buildscript {
ext.kotlin_version = '1.7.10'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
jcenter()
}
}
@ -13,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@ -1,5 +1,7 @@
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip

View File

@ -1,26 +1,11 @@
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}
settings.ext.flutterSdkPath = flutterSdkPath()
include ':app'
includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
}
include ":app"
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@ -23,7 +23,7 @@
"toUnlockEnterPassword": "To unlock your safe, enter your secret code, away from prying lizards:",
"rememberPassword": "Keep this code in memory for 15 minutes",
"myRootWallet": "My root wallet",
"currentWallet": "My current wallet",
"currentWallet": "My current chest",
"wallet": "Wallet",
"displayMnemonic": "Display my mnemonic sentence",
"changePassword": "Change my password",
@ -71,8 +71,7 @@
"areYouSureToDeleteWallet": "Are you sure you want to delete the chest \"{}\"?",
"areYouSureForgetAllChests": "Are you sure you want to forget all your chests?",
"areYouSureToForgetWallet": "Are you sure you wan to forget the wallet \"{}\"?",
"areYouSureYouWantToCertify1": "Are you sure you want to certify the identity :",
"areYouSureYouWantToCertify2": "having the address :",
"areYouSureYouWantToCertify": "Are you sure you want to certify the address:\n\n{}",
"yes": "Yes",
"no": "No",
"keepYourMnemonicSecret": "Try to keep this phrase a secret, as it allows anyone who knows it to access all your wallets.",
@ -103,9 +102,7 @@
"identityCreated": "Identity created",
"identityConfirmed": "Identity confirmed",
"identityExpired": "Identity expired",
"identityRevoked": "Identity revoked",
"confirmYourIdentity": "Confirm your identity",
"noResult": "No results",
"noDuniterNodeAvailableTryLater": "No Duniter node available, please try again later",
"youAreConnectedToNode": "You are connected to node",
"accountActivity": "Account activity",
@ -153,38 +150,35 @@
"canRenewCertInX": "You can renew\nthis certification\nin {}",
"executeATransfer": "Execute a transfer",
"executeTheTransfer": "Execute the transfer",
"doATransfer": "Execute a\ntransfer",
"doATransfer" : "Execute a\ntransfer",
"seconds": "{} seconds",
"minutes": "{} minutes",
"hours": "{} hours {}",
"days": "{} days",
"months": "{} months",
"certify": "Certify this\nidentity",
"from": "From:{}",
"to": "To:{}",
"certify": "Certify",
"from": "From:",
"to": "To:",
"amount": "Amount:",
"choiceOfSourceWallet": "Choose a source wallet",
"extrinsicInProgress": "{} in progress",
"extrinsicValidated": "{} validated !",
"extrinsicFinalized": "{} finalized !",
"fromMinus": "from",
"toMinus": "to",
"deleteThisWallet": "Delete this wallet",
"cancel": "Cancel",
"inBlockchainResult": "In {} blockchain",
"search": "Search an identity\nor an address",
"currencyNode": "node",
"search": "Search",
"currencyNode": "{} node :",
"contactsManagementWithNbr": "My contacts ({})",
"contactsManagement": "My contacts",
"noContacts": "You don't have any contact",
"addContact": "Add\nto contacts",
"removeContact": "Remove\nthis contact",
"scanRootDerivationInProgress": "Scan root address",
"derivationsScanProgress": "Scan {} firsts addresses",
"importDerivationsInProgress": "Import address {}/{}",
"derivationsScanProgress": "Scan address {}/{}",
"youAreOffline": "You are offline...",
"importG1v1": "Import old G1v1 account",
"migrateToThisWallet": "Migrate to this wallet:",
"selectDestWallet": "Select a target wallet:",
"youMustWaitBeforeCashoutThisAccount": "You have to wait a few moment before migrate this account",
"thisAccountIsEmpty": "This account is empty",
"youCannotMigrateIdentityToExistingIdentity": "You cannot migrate an identity\nto an account that already has an identity",
@ -201,40 +195,5 @@
"youCannotRevokeThisIdentity": "You cannot revoke this identity while\nit is member of the blacksmiths web",
"showUdAmounts": "Show amounts in UD",
"ud": "{}UD",
"chooseATargetWallet": "Choose a target wallet",
"thisMnemonicHasBeenCopiedToClipboard": "This mnemonic has been copied to clipboard",
"smithCantMigrateIdentity": "You can't migrate this identity while you're member of smith web",
"received": "Received",
"sent": "Sent",
"createIdentity": "Create a new\nidentity",
"memberAccountOf": "Account of {}",
"pasteAddress": "Paste address from\nclipboard",
"historyStart": "Beginning of history",
"blockchainStart": "Beginning of the ĞDev",
"networkSettings": "Network Settings",
"displaySettings": "Display Settings",
"indexer": "Indexer",
"anAutoNodeChoosed": "A secure and valid node will be automatically used from a random list.",
"rootWallet": "Root Wallet",
"blockN": "block N°{}",
"thisIsNotAGoodCode": "This is not a good code",
"youHaveToBeConnectedToValidateChest": "You have to be connected\nto validate your chest",
"thisIdentityAlreadyExist": "This identity already exists",
"removedFromcontacts": "Removed from contacts",
"addedToContacts": "Added to contacts",
"certificationsOf": "Certifications of {}",
"explainDraggableWallet": "Drag and drop a tile onto another to make a transaction, by holding down the tile.",
"skip": "Skip",
"fees": "fees: {} {}",
"feesExplanation": "To ensure network security, an execution fee is charged on each transaction.",
"feesExplanationDetails": "These fees are transferred to the common treasury account.",
"gotit": "Got it",
"moreInfo": "More information",
"keepThisPaperSafe": "Keep this sheet safe from prying lizards.\nIt will allow you to restore all your wallets at any time.",
"fundsUnavailable": "Insufficient funds",
"addressNotBelongToMnemonic": "The address you provided does not belong to this recovery sentence",
"enterYourNewMnemonic": "Enter your new recovery sentence",
"enterYourNewAddress": "Enter your new address {}",
"youCanMigrateThisIdentity": "You can migrate this identity !",
"identityMigrated": "Identity migrated"
"chooseATargetWallet": "Choose a target wallet"
}

View File

@ -1,92 +1,90 @@
{
"searchWallet": "Buscar\nmonedero",
"manageWallets": "Gestionar\nmonederos",
"searchWallet": "Buscar\nbilletera",
"manageWallets": "Gestionar\nbilleteras",
"scanQRCode": "Escanear un\ncódigo QR",
"wellConnectedToNode": "Estás bien conectado al nodo\n{}",
"wellConnectedToNode": "Estas bien conectada al nodo\n{}",
"networkLost": "Se ha perdido la red...",
"noDuniterEndointAvailable": "No hay servidor disponible...",
"connectionPending": "Conexión pendiente...",
"noLizard": "no hay lagarto ;-)",
"loading": "Cargando...",
"forgot_password.png": "forgot_password_es.png",
"warningForgotPassword": "En una cadena de bloques o blockchain, no hay procedimiento de recuperación mediante correo electrónico. Sólo tu frase de recuperación puede permitirte recuperar tus Ğ1 en cualquier momento.",
"forgot_password.png": "forgot_password_en.png",
"warningForgotPassword": "In a blockchain, there is no email recovery procedure. Only your recovery phrase can allow you to recover your Ğ1 at any time.",
"fastAppDescription": "La aplicación de pago {}\nmás rápida que un reptil de Vietnam",
"createWallet": "Crear un monedero",
"restoreWallet": "Restaurar mis monederos",
"createWallet": "Crear una billetera",
"restoreWallet": "Restaurar mis billeteras",
"parameters": "Parámetros",
"chooseAnotherMnemonic": "Elige otra frase de restauración",
"iNotedMyMnemonic": "He anotado mi frase de restauración",
"printMyMnemonic": "Imprimir mi frase de restauración",
"manageChest": "Configurar este cofre",
"changeChest": "Cambiar de cofre",
"GeckoChest": "Cofre de Ğecko",
"toUnlockEnterPassword": "Para desbloquear tu cofre, introduce tu contraseña, lejos de lagartijas curiosas:",
"rememberPassword": "Mantener en memoria mi contraseña durante 15 minutos",
"myRootWallet": "Mi monedero principal",
"currentWallet": "Mi monedero actual",
"wallet": "monedero",
"displayMnemonic": "Mostrar mi frase de restauración",
"chooseAnotherMnemonic": "Choose an other\nmnemonic sentence",
"iNotedMyMnemonic": "He escrito mi frase",
"printMyMnemonic": "Print my mnemonic sentence",
"manageChest": "Configure this chest",
"changeChest": "Change chest",
"geckoChest": "Ğecko chest",
"toUnlockEnterPassword": "To unlock your safe, enter your secret code, away from prying lizards:",
"rememberPassword": "Keep this code in memory for 15 minutes",
"myRootWallet": "Mi billetera principal",
"currentWallet": "My current chest",
"wallet": "Billetera",
"displayMnemonic": "Display my mnemonic sentence",
"changePassword": "Cambiar mi contraseña",
"createDerivation": "Crear una nueva derivación",
"createCustomDerivation": "Crear una nueva derivación personalizada",
"deleteChest": "Borrar este cofre",
"openThisChest": "Abrir este cofre",
"createChest": "Crear un nuevo cofre",
"importChest": "Importar un cofre",
"selectMyChest": "Seleccionar mi cofre",
"accessMyChest": "Acceder a mi cofre",
"manageMembership": "Administrar mi afiliación",
"chooseThisWallet": "Elegir este monedero",
"thisWalletIsDefault": "Monedero definido por defecto",
"defineWalletAsDefault": "Definir este monedero como por defecto",
"displayActivity": "Visualizar actividad",
"displayNActivity": "Visualizar\nactividad",
"createDerivation": "Create a new derivation",
"createCustomDerivation": "Create a new custom derivation",
"deleteChest": "Delete this chest",
"openThisChest": "Open this chest",
"createChest": "Create a new chest",
"importChest": "Import a chest",
"selectMyChest": "Select my chest",
"accessMyChest": "Access my chest",
"manageMembership": "Manage my membership",
"chooseThisWallet": "Elegir esta billetera",
"thisWalletIsDefault": "This wallet is the default one",
"defineWalletAsDefault": "Define this as the default one",
"displayActivity": "Display activity",
"displayNActivity": "Display\nactivity",
"memberValidated": "Miembro validado!",
"copyAddress": "Copiar\nla dirección",
"copyAddress": "Copiar\ndirección",
"copy": "Copiar",
"thisAddressHasBeenCopiedToClipboard": "Esta dirección se ha copiado al portapapeles",
"chooseWalletName": "Elige un nuevo nombre\npara tu monedero:",
"choosePassword": "Elige una contraseña aleatoria:",
"chooseDerivation": "Elige una derivación:",
"thisAddressHasBeenCopiedToClipboard": "Esta dirección se ha copiado al cortapapeles",
"chooseWalletName": "Choose a new name\nfor your wallet:",
"choosePassword": "Choose a random password:",
"chooseDerivation": "Choose a derivation:",
"validate": "Validar",
"confirm": "Confirmar",
"confirmPayment": "Confirmar pago",
"GeckoGenerateYourWalletFromMnemonic": "Ğecko construye tu monedero a partir de una **frase de restauración**. Es como unos planos que permiten restaurar tu monedero.",
"keepThisMnemonicSecure": "Guarda esta frase con cuidado, porque sin ella Ğecko no podrá restaurar tus monederos el día que cambies de dispositivo.",
"GeckoGeneratedYourMnemonicKeepItSecret": "¡Ğecko generó tu frase de restauración con éxito! Mantenla en secreto, porque cualquiera que la sepa puede acceder a todos tus monederos.",
"newWallet": "Nuevo monedero",
"itsTimeToUseAPenAndPaper": "Es el momento de coger un **papel y un bolígrafo** para anotar tu frase de restauración.",
"yourMnemonic": "tu frase de restauración",
"gecko_also_can_forget.png": "gecko_also_can_forget_es.png",
"didYouNoteMnemonicToBeSureTypeWord": "¿Anotaste bien tu frase de restauración? Para asegurarnos, escriba la **palabra #{}** de tu frase de restauración en el cuadro de abajo:",
"GeckoWillGenerateAPassword": "Ahora Ğecko te generará una contraseña corta que te permitirá acceder rápidamente a tus monederos, sin tener que escribir cada vez tu frase de recuperación.",
"myPassword": "Mi contraseña",
"thisPasswordProtectsYourWalletsInASecureChest": "Esta contraseña protege tus monederos en un cofre **del que sólo tú tienes acceso**, para que tus monederos no puedan ser utilizados por otros.",
"hereIsThePasswordKeepIt": "¡Y aquí está tu contraseña!\n\nMemorízala o anótala, porque Ğecko te la pedirá **cada vez** que quieras hacer un pago en este dispositivo.",
"geckoGenerateYourWalletFromMnemonic": "Ğecko builds your wallet from a **restoration sentence**. It is a bit like the blueprint that builds your wallet.",
"keepThisMnemonicSecure": "Keep this sentence carefully, because without it Ğecko will not be able to rebuild your wallets the day you change your phone.",
"geckoGeneratedYourMnemonicKeepItSecret": "Ğecko generated your mnemonic successfully! Keep it secret, because anyone who knows it can access all your wallets.",
"newWallet": "New Wallet",
"itsTimeToUseAPenAndPaper": "It's time to take a **pen and paper** in order to write down your mnemonic.",
"yourMnemonic": "Your mnemonic",
"gecko_also_can_forget.png": "gecko_also_can_forget_en.png",
"didYouNoteMnemonicToBeSureTypeWord": "Did you write down your menmonic?\n\n To be sure, please type the **{}th word** of your restoration phrase in the field below:",
"geckoWillGenerateAPassword": "Gecko will now generate for you a short password that will allow you to quickly access your wallets, without having to type your recovery sentence every time.",
"myPassword": "My password",
"thisPasswordProtectsYourWalletsInASecureChest": "This secret code protects your wallets in a safe **which only you have the code for**, so that your wallets cannot be used by others.",
"hereIsThePasswordKeepIt": "And here is your password!\n\nMemorize it or write it down, because you will be asked **every time** you want to make a payment on this device.",
"chooseAnotherPassword": "Elige otra contraseña",
"iNotedMyPassword": "He anotado mi contraseña",
"GeckoWillCheckPassword": "Ğecko comprobará contigo si has recordado tu contraseña. Escribe tu contraseña en el cuadro de abajo para comprobar que lo has apuntado correctamente.",
"yourChestAndWalletWereCreatedSuccessfully": "¡Genial!\n\nTu cofre y tu primer monedero han sido creados con éxito.\n\n¡Felicidades!",
"allGood": "¡Todo bien!",
"areYouSureToDeleteWallet": "¿Seguro de que quieres eliminar el cofre \"{}\"?",
"areYouSureForgetAllChests": "¿Seguro de que quieres olvidar todos tus cofres?",
"areYouSureToForgetWallet": "¿Seguro de que quieres olvidar el monedero \"{}\"?",
"areYouSureYouWantToCertify": "¿Seguro de que quiere certificar a la dirección\n\n{}?",
"areYouSureYouWantToCertify1": "¿Seguro de que quiere certificar a la identidad :",
"areYouSureYouWantToCertify2": " a la dirección :",
"yes": "Sí",
"iNotedMyPassword": "I noted my password",
"geckoWillCheckPassword": "Gecko will check with you if you have remembered your secret code.\n\n Type your secret code in the field below to check that you have written it down correctly.",
"yourChestAndWalletWereCreatedSuccessfully": "Super!\n\nYour chest and your first portfolio have been created with great success.\n\nCongratulations!",
"allGood": "That's all good!",
"areYouSureToDeleteWallet": "Are you sure you want to delete the chest \"{}\"?",
"areYouSureForgetAllChests": "Are you sure you want to forget all your chests?",
"areYouSureToForgetWallet": "Are you sure you wan to forget the wallet \"{}\"?",
"areYouSureYouWantToCertify": "Are you sure you want to certify the address:\n\n{}",
"yes": "Si",
"no": "No",
"keepYourMnemonicSecret": "Intenta mantener esta frase de restauración en secreto, ya que permite a cualquiera que la conozca acceder a todas tus monederos.",
"iGeneratedYourMnemonicKeepItSecret": "¡He generado tu frase de restauración!\nIntenta mantenerla en secreto, ya que permite a cualquiera que la conozca acceder a todos tus monederos.",
"myMnemonic": "Mi frase de restauración",
"close": "Cerrar",
"toRestoreEnterMnemonic": "Para restaurar tus monederos Ğecko, introduce las 12 palabras que componen tu frase de restauración en los cuadros de abajo:",
"pasteFromClipboard": "Pegar del\nportapapeles",
"restoreAChest": "Restaurar un cofre",
"restoreThisChest": "Restaurar este cofre",
"keepYourMnemonicSecret": "Try to keep this phrase a secret, as it allows anyone who knows it to access all your wallets.",
"iGeneratedYourMnemonicKeepItSecret": "I've generated your restoration phrase!\n Try to keep it a secret, as it allows anyone who knows it to access all your portfolios.",
"myMnemonic": "My mnemonic",
"close": "Close",
"toRestoreEnterMnemonic": "To restore your Gecko wallets, enter in the fields below the 12 words that constitute your restoration phrase:",
"pasteFromClipboard": "Paste from\nclipboard",
"restoreAChest": "Restore a chest",
"restoreThisChest": "Restore this chest",
"continue": "Continuar",
"itsTheGoodWord": "¡Es la palabra correcta!",
"nthMnemonicWord": "palabra de tu mnemotecnia",
"itsTheGoodWord": "It's the good word!",
"nthMnemonicWord": "word of your mnemonic",
"1th": "Primera",
"2th": "Segunda",
"3th": "Tercera",
@ -100,19 +98,17 @@
"11th": "Undécima",
"12th": "Duodécima",
"yourPasswordLengthIsX": "La longitud de tu contraseña es {}",
"noIdentity": "Ninguna identidad",
"noIdentity": "No identity",
"identityCreated": "Identidad creada",
"identityConfirmed": "Identidad confirmada",
"identityExpired": "Identidad caducada",
"identityRevoked": "Identidad revocada",
"identityExpired": "Identitdad caducada",
"confirmYourIdentity": "Confirma tu identidad",
"noResult": "Ningún resultado",
"noDuniterNodeAvailableTryLater": "No hay ningún nodo Duniter disponible, por favor, inténtalo más tarde",
"youAreConnectedToNode": "Estás conectado al nodo",
"noDuniterNodeAvailableTryLater": "No Duniter node available, please try again later",
"youAreConnectedToNode": "You are connected to node",
"accountActivity": "Actividad de la cuenta",
"noNetworkNoHistory": "El estado de la red no permite\nmostrar el historial de la cuenta",
"noDataToDisplay": "No hay datos que mostrar.",
"noTransactionToDisplay": "No hay transacciónes a mostrar",
"noNetworkNoHistory": "Network state does not allow\nto display account history",
"noDataToDisplay": "No data to be displayed.",
"noTransactionToDisplay": "No transaction to display",
"month1": "Enero",
"month2": "Febrero",
"month3": "Marzo",
@ -128,114 +124,76 @@
"today": "Hoy",
"yesterday": "Ayer",
"thisWeek": "Esta semana",
"chestNotCompatibleMustReinstallGecko": "La versión de tus cofres no es compatible con esta versión de Ğecko.\nTodos tus cofres serán olvidados, debes importarlos de nuevo.",
"notConnectedToInternet": "No estás conectado a Internet",
"researchResults": "Resultados de tu búsqueda",
"resultsFor": "Resultados para ",
"forgetAllMyChests": "Olvida todos mis cofres",
"transaction": "Transacción",
"certification": "Certificación",
"identityConfirm": "Confirmación de la identidad",
"revokeAdhesion": "Revocación de adhesión",
"strangeTransaction": "Transacción extraña",
"chestNotCompatibleMustReinstallGecko": "The version of your safes is no longer compatible with this version of Ğecko.\nAll your safes will be forgotten, you must import them again.",
"notConnectedToInternet": "No estas conectado a internet",
"researchResults": "Results of your research",
"resultsFor": "Results for ",
"forgetAllMyChests": "Forget all my chests",
"transaction": "Transaccion",
"certification": "Certificacion",
"identityConfirm": "Identity confirmation",
"revokeAdhesion": "Adhesion revocation",
"strangeTransaction": "Strange transaction",
"sending": "Enviando...",
"propagating": "Propagando...",
"validating": "Validando...",
"anErrorOccured": "Ocurrió un error",
"24hbetweenCerts": "Hay que esperar 24 horas entre certificaciones",
"canNotCertifySelf": "No puedes certificarte a ti mismo",
"nameAlreadyExist": "Este nombre ya está ocupado",
"2GDtoKeepAlive": "Tienes que mantener al menos 2ĞD para mantener tu cuenta viva",
"youHaveToFeedThisAccountBeforeUsing": "Tienes que nutrir el balance\nde esta cuenta antes de usarla.",
"execTimeoutOver": "Se ha agotado el tiempo de ejecución",
"seeAWallet": "Ver un monedero",
"anErrorOccurred": "Ocurrió un error",
"24hbetweenCerts": "You have to wait 24h between certs",
"canNotCertifySelf": "You can not certify yourself",
"nameAlreadyExist": "This name is already taken",
"2GDtoKeepAlive": "You have to keep at least 2ĞD to keep your account alive",
"youHaveToFeedThisAccountBeforeUsing": "You have to feed this account\nbefore using it.",
"execTimeoutOver": "Execution timeout is over",
"seeAWallet": "Ver una billetera",
"mustWaitXBeforeCertify": "Tienes que esperar\n{} antes de\nvolver a certificar",
"mustConfirmHisIdentity": "Esta persona debe confirmar\nsu identidad antes de poder ser certificada",
"canRenewCertInX": "Podrás renovar\nla certificación\nen {}",
"executeATransfer": "Ejecutar una transacción",
"executeTheTransfer": "Ejecutar la transacción",
"doATransfer": "Hacer una\ntransacción",
"mustConfirmHisIdentity": "This person must confirm\nhis identity before can be\ncertified",
"canRenewCertInX": "You can renew\nthis certification\nin {}",
"executeATransfer": "Execute a transfer",
"executeTheTransfer": "Execute the transfer",
"doATransfer" : "Execute a\ntransfer",
"seconds": "{} segundos",
"minutes": "{} minutos",
"hours": "{} horas {}",
"days": "{} dias",
"months": "{} meses",
"certify": "Certificar esta\nidentidad",
"from": "De:{}",
"to": "A:{}",
"certify": "Certify",
"from": "De:",
"to": "A:",
"amount": "Importe:",
"choiceOfSourceWallet": "Elige un monedero de origen",
"choiceOfSourceWallet": "Choose a source wallet",
"extrinsicInProgress": "{} en progreso",
"extrinsicValidated": "¡ {} validado !",
"extrinsicFinalized": "{} finalized !",
"extrinsicValidated": "{} validado !",
"fromMinus": "de",
"toMinus": "a",
"deleteThisWallet": "Borrar este monedero",
"deleteThisWallet": "Delete this wallet",
"cancel": "Cancelar",
"inBlockchainResult": "En la blockchain {}",
"search": "Buscar una identidad\no una dirección",
"currencyNode": "Nodo",
"inBlockchainResult": "In {} blockchain",
"search": "Buscar",
"currencyNode": "{} nodo :",
"contactsManagementWithNbr": "Mis contactos ({})",
"contactsManagement": "Mis contactos",
"noContacts": "No tienes ningún contacto",
"addContact": "Añadir\na contactos",
"removeContact": "Eliminar\neste contacto",
"scanRootDerivationInProgress": "Scan root address",
"derivationsScanProgress": "Scan {} firsts addresses",
"importDerivationsInProgress": "Import address {}/{}",
"youAreOffline": "Estás desconectado...",
"importG1v1": "Importar la cuenta antigua de G1v1",
"migrateToThisWallet": "Migrate to this wallet:",
"youMustWaitBeforeCashoutThisAccount": "Tienes que esperar unos minutos antes de migrar esta cuenta",
"thisAccountIsEmpty": "Esta cuenta está vacía",
"youCannotMigrateIdentityToExistingIdentity": "No se puede migrar una identidad\na una cuenta que ya tiene una identidad",
"importOldAccount": "Importar antigua cuenta",
"enterCesiumId": "Ingresa tu frase secreta de Cesium",
"enterCesiumPassword": "Ingresa tu contraseña de Cesium",
"migrateAccount": "Migrar cuenta",
"migrateIdentity": "Migrar identidad",
"identityMigration": "Migración de la identidad",
"areYouSureMigrateIdentity": "¿Estás seguro de que quieres migrar permanentemente la identidad **{}** con saldo de **{}**?",
"someoneCreatedYourIdentity": "¡ Alguien ha creado tu {} identidad !",
"noContacts": "You don't have any contact",
"addContact": "Add\nto contacts",
"removeContact": "Remove\nthis contact",
"derivationsScanProgress": "Scan address {}/{}",
"youAreOffline": "You are offline...",
"importG1v1": "Import old G1v1 account",
"selectDestWallet": "Select a target wallet:",
"youMustWaitBeforeCashoutThisAccount": "You have to wait a few moment before migrate this account",
"thisAccountIsEmpty": "This account is empty",
"youCannotMigrateIdentityToExistingIdentity": "You cannot migrate an identity\nto an account that already has an identity",
"importOldAccount": "Import your old account",
"enterCesiumId": "Ingrese su ID de Cesium",
"enterCesiumPassword": "Ingrese su contraseña de Cesium",
"migrateAccount": "Migrate account",
"migrateIdentity": "Migrate identity",
"identityMigration": "Identity migration",
"areYouSureMigrateIdentity": "Are you sure you want to permanently migrate identity **{}** with balance of **{}** ?",
"someoneCreatedYourIdentity": "Someone created your {} identity !",
"confirmMyIdentity": "Confirmar mi identidad",
"revokeMyIdentity": "Revocar mi identidad",
"youCannotRevokeThisIdentity": "No puedes revocar esta identidad mientras\nseas miembro de la red de forjadores",
"showUdAmounts": "Mostrar importes en DU",
"ud": "{}DU",
"chooseATargetWallet": "Elige un monedero de destino",
"thisMnemonicHasBeenCopiedToClipboard": "This mnemonic has been copied to clipboard",
"smithCantMigrateIdentity": "You can't migrate this identity while you're member of smith web",
"received": "Received",
"sent": "Sent",
"createIdentity": "Create a new\nidentity",
"memberAccountOf": "Account of {}",
"pasteAddress": "Paste address from\nclipboard",
"historyStart": "Beginning of history",
"blockchainStart": "Comienzo de la ĞDev",
"networkSettings": "Parametros de red",
"displaySettings": "Parametros interficie",
"indexer": "Indexer",
"anAutoNodeChoosed": "Se usará automáticamente un nodo seguro y valido desde una lista aleatoria.",
"rootWallet": "Monedero raíz",
"blockN": "bloque N°{}",
"thisIsNotAGoodCode": "Este codígo no es valido",
"youHaveToBeConnectedToValidateChest": "Tienes que tener conneción\npara validar tu cofre",
"thisIdentityAlreadyExist": "Esta identidad ya existe",
"removedFromcontacts": "Removed from contacts",
"addedToContacts": "Added to contacts",
"certificationsOf": "Certifications of {}",
"explainDraggableWallet": "Drag and drop a tile onto another to make a transaction, by holding down the tile.",
"skip": "Skip",
"fees": "fees: {} {}",
"feesExplanation": "To ensure network security, an execution fee is charged on each transaction.",
"feesExplanationDetails": "These fees are transferred to the common treasury account.",
"gotit": "Got it",
"moreInfo": "More information",
"keepThisPaperSafe": "Keep this sheet safe from prying lizards.\nIt will allow you to restore all your wallets at any time.",
"fundsUnavailable": "Insufficient funds",
"addressNotBelongToMnemonic": "The address you provided does not belong to this recovery sentence",
"enterYourNewMnemonic": "Enter your new recovery sentence",
"enterYourNewAddress": "Enter your new address {}",
"youCanMigrateThisIdentity": "You can migrate this identity !",
"identityMigrated": "Identity migrated"
}
"youCannotRevokeThisIdentity": "You cannot revoke this identity while\nit is member of the blacksmiths web",
"showUdAmounts": "Show amounts in UD",
"ud": "{}UD",
"chooseATargetWallet": "Elija una billetera de destino"
}

View File

@ -2,7 +2,7 @@
"searchWallet": "Rechercher un\nportefeuille",
"manageWallets": "Gérer mes\nportefeuilles",
"scanQRCode": "Scanner un\nQR code",
"wellConnectedToNode": "Vous êtes bien connecté au noeud\n{}",
"wellConnectedToNode": "Vous êtes bien connecté aux noeud\n{}",
"networkLost": "Le réseau a été perdu...",
"noDuniterEndointAvailable": "Aucun serveur disponible...",
"connectionPending": "Connexion en cours...",
@ -66,13 +66,12 @@
"chooseAnotherPassword": "Choisir un autre code secret",
"iNotedMyPassword": "J'ai noté mon code secret",
"geckoWillCheckPassword": "Gecko va vérifier avec vous si vous avez bien mémorisé votre code secret.\n\nTapez votre code secret dans le champ ci-dessous pour vérifier que vous lavez bien noté.",
"yourChestAndWalletWereCreatedSuccessfully": "Top !\n\nVotre coffre et votre premier portefeuille ont été créés avec un immense succès.\n\nFélicitations !",
"yourChestAndWalletWereCreatedSuccessfully": "Top !\n\nVotre coffre votre premier portefeuille ont été créés avec un immense succès.\n\nFélicitations !",
"allGood": "Cest tout bon !",
"areYouSureToDeleteWallet": "Êtes-vous sûr de vouloir supprimer le coffre \"{}\" ?",
"areYouSureForgetAllChests": "Êtes-vous sûr de vouloir oublier tous vos coffres ?",
"areYouSureToForgetWallet": "Êtes-vous sûr de vouloir oublier le portefeuille \"{}\" ?",
"areYouSureYouWantToCertify1": "Êtes-vous certain de vouloir certifier l'identité :",
"areYouSureYouWantToCertify2": "ayant pour adresse :",
"areYouSureYouWantToCertify": "Êtes-vous certain de vouloir certifier l'adresse:\n\n{}",
"yes": "Oui",
"no": "Non",
"keepYourMnemonicSecret": "Tâchez de garder cette phrase bien secrète, car elle permet à quiconque la connaît daccéder à tous vos portefeuilles.",
@ -103,7 +102,6 @@
"identityCreated": "Identité créée",
"identityConfirmed": "Identité confirmée",
"identityExpired": "Identité expirée",
"identityRevoked": "Identité révoqué",
"confirmYourIdentity": "Confirmez votre identité",
"noResult": "Aucun résultat",
"noDuniterNodeAvailableTryLater": "Aucun noeud Duniter disponible, veuillez réessayer ultérieurement",
@ -159,33 +157,30 @@
"hours": "{} heures {}",
"days": "{} jours",
"months": "{} mois",
"certify": "Certifier cette\nidentité",
"from": "Depuis:{}",
"to": "Vers:{}",
"certify": "Certifier",
"from": "Depuis:",
"to": "Vers:",
"amount": "Montant:",
"choiceOfSourceWallet": "Choix du portefeuille source",
"extrinsicInProgress": "{} en cours",
"extrinsicValidated": "{} validée !",
"extrinsicFinalized": "{} finalisé !",
"extrinsicValidated": "{} validé !",
"fromMinus": "de",
"toMinus": "vers",
"deleteThisWallet": "Supprimer ce portefeuille",
"cancel": "Annuler",
"inBlockchainResult": "Dans la blockchain {}",
"search": "Rechercher une identité\nou une adresse",
"currencyNode": "Noeud",
"search": "Rechercher",
"currencyNode": "Noeud {} :",
"contactsManagementWithNbr": "Mes contacts ({})",
"contactsManagement": "Mes contacts",
"noContacts": "Vous n'avez aucun contact",
"addContact": "Ajouter\naux contacts",
"removeContact": "Supprimer\nce contact",
"scanRootDerivationInProgress": "Scan de l'adresse racine",
"derivationsScanProgress": "Scan des {} premières adresses",
"importDerivationsInProgress": "Importation de l'adresse {}/{}",
"derivationsScanProgress": "Scan de l'adresse {}/{}",
"youAreOffline": "Vous êtes hors ligne...",
"importG1v1": "Importer un ancien compte G1v1",
"migrateToThisWallet": "Migrer vers ce portefeuille:",
"youMustWaitBeforeCashoutThisAccount": "Vous devez attendre quelques minutes\navant de pouvoir migrer ce compte",
"selectDestWallet": "Sélectionnez un portefeuille cible:",
"youMustWaitBeforeCashoutThisAccount": "Vous devez attendre quelques minutes avant de pouvoir migrer ce compte",
"thisAccountIsEmpty": "Ce compte est vide",
"youCannotMigrateIdentityToExistingIdentity": "Vous ne pouvez pas migrer une identité\nvers un compte disposant déjà d'une identité",
"importOldAccount": "Importer son ancien compte",
@ -201,40 +196,5 @@
"youCannotRevokeThisIdentity": "Vous ne pouvez pas révoquer cette identité tant\nqu'elle fait partie de la toile forgerons",
"showUdAmounts": "Afficher les montants en DU",
"ud": "{}DU",
"chooseATargetWallet": "Choisissez un portefeuille cible",
"thisMnemonicHasBeenCopiedToClipboard": "Cette phrase secrète viens d'être copié dans votre presse-papier.",
"smithCantMigrateIdentity": "Vous ne pouvez pas migrer cette identité\ntant que vous êtes dans la toile forgerons",
"received": "Reçus",
"sent": "Envoyés",
"createIdentity": "Créer sa nouvelle\nidentité",
"memberAccountOf": "Compte de {}",
"pasteAddress": "Coller l'adresse depuis\nle presse-papier",
"historyStart": "Début de l'historique",
"blockchainStart": "Début de la ĞDev",
"networkSettings": "Connectivité réseau",
"displaySettings": "Affichage",
"indexer": "Indexer",
"anAutoNodeChoosed": "Un noeud sûr et valide sera choisi automatiquement parmis une liste aléatoire.",
"rootWallet": "Portefeuille racine",
"blockN": "bloc N°{}",
"thisIsNotAGoodCode": "Ce n'est pas le bon code",
"youHaveToBeConnectedToValidateChest": "Vous devez vous connecter à internet\npour valider votre coffre",
"thisIdentityAlreadyExist": "Cette identité existe déjà",
"removedFromcontacts": "Retiré des contact",
"addedToContacts": "Ajouté au contacts",
"certificationsOf": "Certifications de {}",
"explainDraggableWallet": "Faites glisser et déposez une tuile sur une autre pour effectuer une transaction, avec un appui long.",
"skip": "Passer",
"fees": "frais: {} {}",
"feesExplanation": "Pour garantir la sécurité du réseau, des frais d'exécution sont prélevés sur chaque transaction.",
"feesExplanationDetails": "Ces frais sont transférés vers le compte de trésorerie commune.",
"gotit": "J'ai compris",
"moreInfo": "Plus d'info",
"keepThisPaperSafe": "Gardez cette feuille précieusement, à labri des lézards indiscrets.\nElle vous permettra de restaurer tous vos portefeuilles à tout moment.",
"fundsUnavailable": "Fonds insuffisants",
"addressNotBelongToMnemonic": "L'adresse que vous avez fournit n'appartient pas à cette phrase de restauration",
"enterYourNewMnemonic": "Entrez votre nouvelle phrase de restauration",
"enterYourNewAddress": "Entrez votre nouvelle adresse {}",
"youCanMigrateThisIdentity": "Vous pouvez migrer vers cette identité !",
"identityMigrated": "Identité migré"
"chooseATargetWallet": "Choisissez un portefeuille cible"
}

View File

@ -1,220 +0,0 @@
{
"searchWallet": "Cerca\nportafoglio",
"manageWallets": "Gestisci\nportafogli",
"scanQRCode": "Scannerizza un\ncodice QR",
"wellConnectedToNode": "Sei ben connesso al nodo\n{}",
"networkLost": "Rete persa...",
"noDuniterEndointAvailable": "Server non disponibile...",
"connectionPending": "Connessione in corso...",
"noLizard": "non ci sono lucertole ;-)",
"loading": "Caricando...",
"forgot_password.png": "forgot_password_es.png",
"warningForgotPassword": "In una catena di blocchi o blockchain, non ci sono procedure per recupero tramite email. Solo la tua frase di recupero puó permetterti di recuperare i tuoi Ğ1 in qualunque momento.",
"fastAppDescription": "L'applicazione di pagamento {}\npiú rapida che un rettile del Vietnam",
"createWallet": "Crea un portafoglio",
"restoreWallet": "Restaura miei portafogli",
"parameters": "Parametri",
"chooseAnotherMnemonic": "Scegli un'altra frase di recupero",
"iNotedMyMnemonic": "Ho annotato la mia frase di recupero",
"printMyMnemonic": "Stampa la mia frase di recupero",
"manageChest": "Configura questo scrigno",
"changeChest": "Cambia scrigno",
"GeckoChest": "Scrigno di Ğecko",
"toUnlockEnterPassword": "Per sbloccare il tuo scrigno, introduci la password, lontano da lucertole curiose:",
"rememberPassword": "Mantieni in memoria la mia password per 15 minuti",
"myRootWallet": "Il mio portafoglio principale",
"currentWallet": "Il mio scrigno attuale",
"wallet": "portafoglio",
"displayMnemonic": "Mostra la mia frase di recupero",
"changePassword": "Cambia la mia password",
"createDerivation": "Crea una nuova derivazione",
"createCustomDerivation": "Crea una nuova derivazione personalizzata",
"deleteChest": "Elimina questo scrigno",
"openThisChest": "Apri questo scrigno",
"createChest": "Crea un nuovo scrigno",
"importChest": "Importa uno scrigno",
"selectMyChest": "Seleziona scrigno",
"accessMyChest": "Accedi a scrigno",
"manageMembership": "Amministra affiliazione",
"chooseThisWallet": "Scegli questo portafoglio",
"thisWalletIsDefault": "portafoglio predefinito",
"defineWalletAsDefault": "Imposta come predefinito",
"displayActivity": "Visualizza attivitá",
"displayNActivity": "Visualizza\nattivitá",
"memberValidated": "Membro confermato!",
"copyAddress": "Copia\nl'indirizzo",
"copy": "Copia",
"thisAddressHasBeenCopiedToClipboard": "Questo indirizzo é stato copiato negli appunti",
"chooseWalletName": "Scegli un nuovo nome\nper il tuo portafoglio:",
"choosePassword": "Scegli una password casuale:",
"chooseDerivation": "Scegli una derivazione:",
"validate": "Valida",
"confirm": "Conferma",
"confirmPayment": "Conferma pagamento",
"GeckoGenerateYourWalletFromMnemonic": "Ğecko costruisce il tuo portafoglio da una **frase di recupero**. É come una mappa che permette ricostruire il tuo portafoglio.",
"keepThisMnemonicSecure": "Sii attento nel salvare e conservare questa frase, perché senza di essa Ğecko non potrá ricostruire il tuo portafoglio il giorno che cambierai dispositivo.",
"GeckoGeneratedYourMnemonicKeepItSecret": "Ğecko ha generato la tua frase di recupero! Mantienila segreta, perché chiunque che la sappia potrá accedere al tuo portafoglio.",
"newWallet": "Nuovo portafoglio",
"itsTimeToUseAPenAndPaper": "É il momento di prendere **carta e penna** per annotare la frase di recupero.",
"yourMnemonic": "la tua frase di recupero",
"gecko_also_can_forget.png": "gecko_also_can_forget_es.png",
"didYouNoteMnemonicToBeSureTypeWord": "Ti sei segnato bene la frase di recupero? Per assicurarcene, scrivi la **parola {}** della tua frase di recupero:",
"GeckoWillGenerateAPassword": "Ora Ğecko ti creerá una password corta che ti permetterá accedere rapidamente ai tuoi portafogli, senza dover ogni volta inserire la frase di recupero.",
"myPassword": "La mia password",
"thisPasswordProtectsYourWalletsInASecureChest": "Questa password protegge i tuo portafogli e uno scrigno **di cui solo tu hai accesso**, cosicché i tuoi portafogli non possano essere usati da altri.",
"hereIsThePasswordKeepIt": "Ecco la tua password!\n\nMemorizzala e annotala, perché Ğecko te la chiederá **ogni volta** che si voglia effettuare una pagamento da questo dispositivo.",
"chooseAnotherPassword": "Scegli un'altra password",
"iNotedMyPassword": "Ho annotato la password",
"GeckoWillCheckPassword": "Ğecko controllará insieme a te se ti ricordi la password. Scrivi la password per controllare che l'hai appuntata correttamente.",
"yourChestAndWalletWereCreatedSuccessfully": "Fantastico!\n\nIl tuo scrigno e il primo portafoglio sono stati creati con successo.\n\nCongratulazioni!",
"allGood": "Tutto bene!",
"areYouSureToDeleteWallet": "Sicuro che vuoi eliminare lo scrigno \"{}\"?",
"areYouSureForgetAllChests": "Sicuro che vuoi dimenticare tutti i tuoi scrigni?",
"areYouSureToForgetWallet": "Sicuro che vuoi dimenticare il tuo portafoglio \"{}\"?",
"areYouSureYouWantToCertify": "Sicuro che vuoi certificare l'indirizzo\n\n{}?",
"areYouSureYouWantToCertify1": "Sicuro che vuoi certificare l'identitá :",
"areYouSureYouWantToCertify2": " all'indirizzo :",
"yes": "Sí",
"no": "No",
"keepYourMnemonicSecret": "Cerca di mantenere questa frase di recupero segreta, essa permette a chiunque la conosca di accedere a tutti i tuoi portafogli.",
"iGeneratedYourMnemonicKeepItSecret": "La tua frase di recupero é stata creata!\nCerca di mantenere questa frase di recupero segreta, essa permette a chiunque la conosca di accedere a tutti i tuoi portafogli.",
"myMnemonic": "la mia frase di recupero",
"close": "Chiudi",
"toRestoreEnterMnemonic": "Per recuperare i tuoi portafogli Ğecko, introduci le 12 parole che compongono la tua frase di recupero:",
"pasteFromClipboard": "Incolla dagli\nappunti",
"restoreAChest": "Recupera uno scrigno",
"restoreThisChest": "Recupera questo scrigno",
"continue": "Continua",
"itsTheGoodWord": "É la parola corretta!",
"nthMnemonicWord": "parola della tua mnemotecnia",
"1th": "Prima",
"2th": "Seconda",
"3th": "Terza",
"4th": "Quarta",
"5th": "Quinta",
"6th": "Sesta",
"7th": "Settima",
"8th": "Ottava",
"9th": "Nona",
"10th": "Decima",
"11th": "Undicesima",
"12th": "Dodicesima",
"yourPasswordLengthIsX": "La lunghezza della tua password é {}",
"noIdentity": "Nessuna identitá",
"identityCreated": "Identitá creata",
"identityConfirmed": "Identitá confermata",
"identityExpired": "Identitá scaduta",
"confirmYourIdentity": "Conferma identitá",
"noResult": "Nessun risultato",
"noDuniterNodeAvailableTryLater": "Non ci sono nodi Duniter disponibili, per favore, riprova piú tardi",
"youAreConnectedToNode": "Sei connesso al nodo",
"accountActivity": "Attivitá del conto",
"noNetworkNoHistory": "Lo stato della rete non lo permette\nmostra lo storico del conto",
"noDataToDisplay": "Non ci sono dati da mostrare.",
"noTransactionToDisplay": "Non ci sono transazioni da mostrare",
"month1": "Gennaio",
"month2": "Febbraio",
"month3": "Marzo",
"month4": "Aprile",
"month5": "Maggio",
"month6": "Giugno",
"month7": "Luglio",
"month8": "Agosto",
"month9": "Settembre",
"month10": "Ottobre",
"month11": "Novembre",
"month12": "Dicembre",
"today": "Oggi",
"yesterday": "Ieri",
"thisWeek": "Questa settimana",
"chestNotCompatibleMustReinstallGecko": "la versione dei tuoi scrigni non é compatibile con questa versione di Ğecko.\nTutti i tuoi scrigni saranno dimenticati, dovrai importarli di nuovo.",
"notConnectedToInternet": "Non connesso ad Internet",
"researchResults": "Risultati ricerca",
"resultsFor": "Risultati per ",
"forgetAllMyChests": "Dimentica tutti i miei scrigni",
"transaction": "Transazione",
"certification": "Certificazione",
"identityConfirm": "Conferma di identitá",
"revokeAdhesion": "Revocazione di adesione",
"strangeTransaction": "Transazione 'strana'",
"sending": "Inviando...",
"propagating": "Propagando...",
"validating": "Validando...",
"anErrorOccured": "Si é verificato un errore",
"24hbetweenCerts": "Bisogna aspettare 24 ore fra le certificazioni",
"canNotCertifySelf": "Non puoi certificare te stesso",
"nameAlreadyExist": "Nome giá in uso",
"2GDtoKeepAlive": "Devi mantenere almeno 2ĞD per mantenere il conto vivo",
"youHaveToFeedThisAccountBeforeUsing": "Devi incrementare il saldo\ndi questo conto prima di usarlo.",
"execTimeoutOver": "Tempo di esecuzione terminato",
"seeAWallet": "Vedi un portafoglio",
"mustWaitXBeforeCertify": "Devi aspettare\n{} prima di\ncertificare di nuovo",
"mustConfirmHisIdentity": "Questa persona deve confermare\nla sua identitá prima di essere certificata",
"canRenewCertInX": "Potrai rinnovare\nla certificazione\nin {}",
"executeATransfer": "Esegui una transazione",
"executeTheTransfer": "Esegui la transazione",
"doATransfer" : "Esegui una\ntransazione",
"seconds": "{} secondi",
"minutes": "{} minuti",
"hours": "{} ore {}",
"days": "{} giorni",
"months": "{} mesi",
"certify": "Certifica questa\nidentitá",
"from": "Da:",
"to": "A:",
"amount": "Importo:",
"choiceOfSourceWallet": "Scegli un portafoglio sorgente",
"extrinsicInProgress": "{} in corso",
"extrinsicValidated": "¡ {} validato !",
"fromMinus": "da",
"toMinus": "a",
"deleteThisWallet": "Elimina questo portafoglio",
"cancel": "Annulla",
"inBlockchainResult": "Nella blockchain {}",
"search": "Cerca una identitá\no un indirizzo",
"currencyNode": "Nodo",
"contactsManagementWithNbr": "I miei contatti ({})",
"contactsManagement": "I miei contatti",
"noContacts": "Non ci sono contatti salvati",
"addContact": "Aggiungi\nun contatto",
"removeContact": "Elimina\nquesto contatto",
"derivationsScanProgress": "Scannerizzando l'indirizzo {}/{}",
"youAreOffline": "Disconnesso...",
"importG1v1": "Importa il conto antico da G1v1, versione precedente",
"migrateToThisWallet": "Seleziona un portafoglio di destinazione:",
"youMustWaitBeforeCashoutThisAccount": "Devi aspettare qualche minuto prima di migrare questo conto",
"thisAccountIsEmpty": "Questo conto é vuoto",
"youCannotMigrateIdentityToExistingIdentity": "Non si puó migrare un'identitá\na un conto che ne ha giá una",
"importOldAccount": "Importa conto precedente",
"enterCesiumId": "Inserisci la tua frase segreta di Cesium",
"enterCesiumPassword": "Inserisci la tua password di Cesium",
"migrateAccount": "Migra conto",
"migrateIdentity": "Migra identitá",
"identityMigration": "Migrazione dell'identitá",
"areYouSureMigrateIdentity": "Sei sicuro che vuoi permanentemente migrare l'identitá **{}** con saldo di **{}**?",
"someoneCreatedYourIdentity": "Qualcuno ha creato la tua {} identitá !",
"confirmMyIdentity": "Conferma la mia identitá",
"revokeMyIdentity": "Revoca la mia identitá",
"youCannotRevokeThisIdentity": "Non puoi revocare questa identitá fintanto che\nappartieni alla rete di forgiatori",
"showUdAmounts": "Mostra importi in DU",
"ud": "{}DU",
"chooseATargetWallet": "Scegli un portafoglio di destinazione",
"thisMnemonicHasBeenCopiedToClipboard": "Questo mnemonic é statocopiato negli appunti",
"smithCantMigrateIdentity":"Non puoi migrare questa identitá fintanto che\nappartieni alla rete di forgiatori",
"received": "Ricevuto",
"sent": "Inviato",
"createIdentity": "Crea una nuova \nidentitá",
"memberAccountOf": "Conto di {}",
"pasteAddress": "Incolla l'indirizzo \ndagli appunti",
"historyStart" :"Inizio della storia",
"blockchainStart": "Inizio di ĞDev",
"networkSettings": "Impostazioni di rete",
"displaySettings": "Impostazioni interfaccia",
"indexer": "Indexer",
"anAutoNodeChoosed": "Un nodo sicuro e valido sará automaticamente scelto da una lista casuale.",
"rootWallet": "Portafoglio radice",
"blockN": "blocco N°{}",
"thisIsNotAGoodCode": "Questo codice non é valido",
"youHaveToBeConnectedToValidateChest": "Vous devez vous connecter à internet\npour valider votre coffre",
"thisIdentityAlreadyExist": "Identitá giá esistente"
}

View File

@ -1,7 +1,4 @@
[
"wss://gdev.p2p.legal/ws",
"wss://gdev.coinduf.eu/ws",
"wss://vit.fdn.org/ws",
"wss://gdev.cgeek.fr/ws",
"wss://gdev.pini.fr/ws"
"wss://gdev.librelois.fr/ws"
]

View File

@ -1,3 +1,6 @@
[
"gdev-squid.axiom-team.fr"
"https://gdev-indexer.p2p.legal",
"https://idx.gdev.cgeek.fr",
"https://duniter-indexer.coinduf.eu",
"http://192.168.1.72:8080"
]

View File

@ -1 +0,0 @@
extensions:

View File

@ -1,49 +0,0 @@
{
"ud": 1000,
"first_ud": null,
"first_ud_reeval": null,
"genesis_parameters": {
"genesis_certs_expire_on": 1000,
"genesis_certs_min_received": 2,
"genesis_memberships_expire_on": 1000,
"genesis_smith_certs_expire_on": 1000,
"genesis_smith_certs_min_received": 2,
"genesis_smith_memberships_expire_on": 100000
},
"parameters": {
"babe_epoch_duration": 30,
"cert_period": 15,
"cert_max_by_issuer": 10,
"cert_min_received_cert_to_issue_cert": 2,
"cert_validity_period": 1000,
"idty_confirm_period": 40,
"idty_creation_period": 50,
"membership_period": 1000,
"membership_renewal_period": 1000,
"pending_membership_period": 500,
"ud_creation_period": 10,
"ud_reeval_period": 50,
"smith_cert_period": 15,
"smith_cert_max_by_issuer": 8,
"smith_cert_min_received_cert_to_issue_cert": 2,
"smith_cert_validity_period": 1000,
"smith_membership_period": 1000,
"smith_inactivity_max_duration": 1000,
"smith_pending_membership_period": 500,
"smith_wot_first_cert_issuable_on": 4,
"smith_wot_min_cert_for_membership": 3,
"wot_first_cert_issuable_on": 0,
"wot_min_cert_for_create_idty_right": 3,
"wot_min_cert_for_membership": 3
},
"clique_smiths": [
{ "name": "test1" },
{ "name": "test2" },
{ "name": "test3" },
{ "name": "test4" },
{ "name": "Alice" }
],
"sudo_key": "5FeggKqw2AbnGZF9Y9WPM2QTgzENS3Hit94Ewgmzdg5a3LNa",
"treasury_funder_address": "5FeggKqw2AbnGZF9Y9WPM2QTgzENS3Hit94Ewgmzdg5a3LNa",
"technical_committee": ["test1", "test2", "test3"]
}

View File

@ -1,126 +0,0 @@
{
"initial_monetary_mass": 90100,
"current_block": {
"number": 0,
"medianTime": 1700000000
},
"identities": {
"test1": {
"index": 7,
"balance": 10000,
"revoked": false,
"membership_expire_on": 2700000000,
"membership_revokes_on": 2700000001,
"next_cert_issuable_on": 0,
"certs_received": {
"test2": 2700000000,
"test3": 2700000000,
"test4": 2700000000
},
"owner_pubkey": "BgC76sdA6zxPSAMW6sZ1e3NEntLrkLT8DY3z2MEmJJgK"
},
"test2": {
"index": 1,
"balance": 10000,
"revoked": false,
"membership_expire_on": 2700000000,
"membership_revokes_on": 2700000001,
"next_cert_issuable_on": 0,
"certs_received": {
"test1": 2700000000,
"test3": 2700000000,
"test4": 2700000000
},
"owner_pubkey": "6xNFhRFHKyx9iZ3ucc3AFf5cjsWw5jH3p6EnFXw3D8T6"
},
"test3": {
"index": 2,
"balance": 10000,
"revoked": false,
"membership_expire_on": 2700000000,
"membership_revokes_on": 2700000001,
"next_cert_issuable_on": 0,
"certs_received": {
"test1": 2700000000,
"test2": 2700000000,
"test4": 2700000000
},
"owner_pubkey": "BpSSPEVE1yze9wrfjkU4wfnFa7WgKNysHxe3H9iT9fvx"
},
"test4": {
"index": 3,
"balance": 10000,
"revoked": false,
"membership_expire_on": 2700000000,
"membership_revokes_on": 2700000001,
"next_cert_issuable_on": 0,
"certs_received": {
"test1": 2700000000,
"test2": 2700000000,
"test3": 2700000000
},
"owner_pubkey": "5LqbvutJtRTHvnforyndwPbkC4Kf5cJtdRQaDcHoMi8S"
},
"test5": {
"index": 4,
"balance": 10000,
"revoked": false,
"membership_expire_on": 2700000000,
"membership_revokes_on": 2700000001,
"next_cert_issuable_on": 0,
"certs_received": {
"test1": 2700000000,
"test2": 2700000000,
"test3": 2700000000
},
"owner_pubkey": "6FgzG8NwatTWHo7rM7sPP6P4Q95R2ZQNqYiHCs38RT21"
},
"test6": {
"index": 5,
"balance": 10000,
"revoked": false,
"membership_expire_on": 2700000000,
"membership_revokes_on": 2700000001,
"next_cert_issuable_on": 0,
"certs_received": {
"test1": 2700000000
},
"owner_pubkey": "FZ8URw5rPqpWegWnufpcBDkg6tMpc2JmNZVCuPA9g3nq"
},
"testCesium1": {
"index": 6,
"balance": 10000,
"revoked": false,
"membership_expire_on": 2700000000,
"membership_revokes_on": 2700000001,
"next_cert_issuable_on": 0,
"certs_received": {
"test1": 2700000000,
"test2": 2700000000,
"test3": 2700000000
},
"owner_pubkey": "DCovzCEnQm9GUWe6mr8u42JR1JAuoj3HbQUGdCkfTzSr"
},
"Alice": {
"index": 8,
"balance": 10000,
"revoked": false,
"membership_expire_on": 2700000000,
"membership_revokes_on": 2700000001,
"certs_received": {
"test1": 2700000000,
"test2": 2700000000,
"test3": 2700000000,
"test4": 2700000000,
"test5": 2700000000,
"test6": 2700000000,
"testCesium1": 2700000000
},
"owner_address": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
"next_cert_issuable_on": 0
}
},
"wallets": {
"BPKBoTrrLD1XWmpZdRsfDnDT1M6PBBvgzPxAKNdutVV2": 10000
}
}

View File

@ -0,0 +1,79 @@
{
"first_ud": 10000,
"first_ud_reeval": 50,
"genesis_parameters": {
"genesis_certs_expire_on": 10,
"genesis_certs_min_received": 3,
"genesis_memberships_expire_on": 1051200,
"genesis_smith_certs_expire_on": 2102400,
"genesis_smith_certs_min_received": 3,
"genesis_smith_memberships_expire_on": 1051200
},
"identities": {
"test1": {
"balance": 10000,
"certs": ["test2", "test3", "test4"],
"pubkey": "5FeggKqw2AbnGZF9Y9WPM2QTgzENS3Hit94Ewgmzdg5a3LNa"
},
"test2": {
"balance": 10000,
"certs": ["test1", "test3", "test4"],
"pubkey": "5E4i8vcNjnrDp21Sbnp32WHm2gz8YP3GGFwmdpfg5bHd8Whb"
},
"test3": {
"balance": 10000,
"certs": ["test1", "test2", "test4"],
"pubkey": "5FhTLzXLNBPmtXtDBFECmD7fvKmTtTQDtvBTfVr97tachA1p"
},
"test4": {
"balance": 10000,
"certs": ["test1", "test2", "test3"],
"pubkey": "5DXJ4CusmCg8S1yF6JGVn4fxgk5oFx42WctXqHZ17mykgje5"
},
"testCesium1": {
"balance": 10000,
"certs": ["test1", "test2", "test3"],
"pubkey": "5GAT6CJW8yVKwUuQc7sM5Kk9GZVTpbZYk9PfjNXtvnNgAJZ1"
}
},
"parameters": {
"babe_epoch_duration": 30,
"cert_period": 15,
"cert_max_by_issuer": 10,
"cert_min_received_cert_to_issue_cert": 2,
"cert_validity_period": 1000,
"idty_confirm_period": 40,
"idty_creation_period": 50,
"membership_period": 1000,
"pending_membership_period": 500,
"ud_creation_period": 10,
"ud_reeval_period": 50,
"smith_cert_period": 15,
"smith_cert_max_by_issuer": 8,
"smith_cert_min_received_cert_to_issue_cert": 2,
"smith_cert_validity_period": 1000,
"smith_membership_period": 1000,
"smith_pending_membership_period": 500,
"smiths_wot_first_cert_issuable_on": 4,
"smiths_wot_min_cert_for_membership": 3,
"wot_first_cert_issuable_on": 0,
"wot_min_cert_for_create_idty_right": 3,
"wot_min_cert_for_membership": 3
},
"smiths": {
"test1": {
"certs": ["test2", "test3", "test4"]
},
"test2": {
"certs": ["test1", "test3", "test4"]
},
"test3": {
"certs": ["test1", "test2", "test4"]
},
"test4": {
"certs": ["test1", "test2", "test3"]
}
},
"sudo_key": "5FeggKqw2AbnGZF9Y9WPM2QTgzENS3Hit94Ewgmzdg5a3LNa",
"technical_committee": ["test1", "test2", "test3"]
}

View File

@ -1 +0,0 @@
7bd0907a4984e1ab7899edce63b2e286e8f3daad5a8e2bff40cee61ca6b036cd

View File

@ -3,10 +3,8 @@ version: "3.5"
services:
duniter-v2s-gecko-tests:
container_name: duniter-v2s-gecko-tests
# image: duniter/duniter-v2s:debug-sha-4d5e08be
image: duniter/duniter-v2s:debug-sha-44b09061
command: --alice --force-authoring --reserved-only --no-mdns
#--sealing=manual
image: duniter/duniter-v2s:debug-latest
command: --sealing=manual
ports:
- "127.0.0.1:9615:9615"
- "127.0.0.1:9933:9933"
@ -14,8 +12,7 @@ services:
- "30333:30333"
environment:
DUNITER_INSTANCE_NAME: "gecko_tests"
DUNITER_CHAIN_NAME: "gdev_dev"
DUNITER_GENESIS_CONFIG: "/var/lib/duniter/gecko_config.json"
DUNITER_GENESIS_DATA: "/var/lib/duniter/gecko_data.json"
DUNITER_CHAIN_NAME: "dev"
DUNITER_GENESIS_CONFIG: "/var/lib/duniter/gecko_tests.json"
volumes:
- ./data:/var/lib/duniter

View File

@ -5,9 +5,8 @@ option=$2
[[ ! $testName ]] && testName='gecko_complete'
# Get local IP and set .env
#ip_address=$(hostname -I 2>/dev/null || ipconfig 2>&1 | grep "IPv4 Address" | head -n1 | awk -F ':' '{ print $2}' | tr -d ' ') # old fashion style...
#ip_address="10.0.2.2"
#echo "ip_address=$ip_address" > .env
ip_address=$(hostname -I | awk '{print $1}')
echo "ip_address=$ip_address" > .env
[[ $option == 'human' ]] && echo "isHumanReading=true" >> .env
## Start local Duniter node
@ -21,10 +20,10 @@ cd ../..
flutter test integration_test/scenarios/$testName.dart && echo '0' > /tmp/geckoTestResult || echo '1' > /tmp/geckoTestResult
# Reset .env
# echo "ip_address=127.0.0.1" > .env # not used anymore, host IP is 10.0.2.2
echo "ip_address=127.0.0.1" > .env
# Stop Duniter
cd integration_test/duniter
docker compose down
exit 0

View File

@ -1,4 +1,4 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:integration_test/integration_test.dart';
@ -7,7 +7,7 @@ import '../utility/tests_utility.dart';
void main() async {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
// await dotenv.load();
await dotenv.load();
testWidgets('Certifications state', (testerLoc) async {
tester = testerLoc;
@ -25,9 +25,9 @@ void main() async {
await tapKey(keyConfirmSearch);
await waitFor(test5.shortAddress());
await tapKey(keySearchResult(test5.address));
await waitFor('createIdentity'.tr());
await waitFor('mustWaitXBeforeCertify'.tr().substring(0, 6), reverse: true);
await waitFor('canRenewCertInX'.tr().substring(0, 8), reverse: true);
await waitFor('Certifier');
await waitFor('Vous devez ', reverse: true);
await waitFor('Vous pourrez renouveler ', reverse: true);
// Background pay 25
await bkPay(

View File

@ -1,5 +1,4 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:integration_test/integration_test.dart';
@ -8,10 +7,9 @@ import '../utility/tests_utility.dart';
void main() async {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
// await dotenv.load();
await dotenv.load();
testWidgets('Gecko complete', (testerLoc) async {
FlutterError.onError = ignoreOverflowErrors;
// Share WidgetTester to test provider
tester = testerLoc;
@ -37,7 +35,7 @@ void main() async {
Future payTest2() async {
spawnBlock(until: 13);
await waitFor('searchWallet'.tr());
await waitFor('Rechercher');
await tapKey(keyOpenSearch);
final addressToSearch = await clipPaste();
final endAddress = addressToSearch.substring(addressToSearch.length - 6);
@ -52,11 +50,8 @@ Future payTest2() async {
await enterText(keyAmountField, '12.14');
await tapKey(keyConfirmPayment);
spawnBlock(duration: 500);
await tester.pump(const Duration(seconds: 2));
await waitFor('sending'.tr(),
reverse: true, timeout: const Duration(seconds: 20));
await waitFor('extrinsicValidated'.tr(args: ['transaction'.tr()]),
timeout: const Duration(seconds: 12));
await waitFor('validé !', timeout: const Duration(seconds: 1));
await tapKey(keyCloseTransactionScreen, duration: 0);
await waitFor('12.14');
spawnBlock(duration: 500);
@ -66,17 +61,12 @@ Future payTest2() async {
Future certifyTest5() async {
// Create identity with Test1 account
await pump(number: 8);
await tapKey(keyCertify);
await tapKey(keyConfirm);
spawnBlock(duration: 1000);
await pump(number: 3);
await waitFor('sending'.tr(),
reverse: true, timeout: const Duration(seconds: 20));
await waitFor('extrinsicValidated'.tr(args: ['certification'.tr()]),
timeout: const Duration(seconds: 6));
spawnBlock(duration: 500);
await waitFor('validé !', timeout: const Duration(seconds: 1));
await tapKey(keyCloseTransactionScreen);
await waitFor('identityCreated'.tr());
await waitFor('Identité créée');
// Confirm Identity Test5
await tapKey(keyAppBarChest, duration: 300);
@ -86,21 +76,19 @@ Future certifyTest5() async {
await tapKey(keyConfirmIdentity);
await enterText(keyEnterIdentityUsername, test5.name);
await tapKey(keyConfirm);
spawnBlock(duration: 1000);
await pump(number: 3);
await waitFor('sending'.tr(),
reverse: true, timeout: const Duration(seconds: 20));
spawnBlock(duration: 500);
await waitFor('validé !', timeout: const Duration(seconds: 1));
await tapKey(keyCloseTransactionScreen);
await waitFor('identityConfirmed'.tr());
await waitFor('Identité confirmée');
humanRead(2);
// Set wallet 2 as default wallet
await goBack();
await tapKey(keyOpenWallet(test2.address));
await tapKey(keySetDefaultWallet);
await waitFor('thisWalletIsDefault'.tr());
await waitFor('Ce portefeuille est celui par defaut');
// Search Wallet 5 again
await tapKey(keyAppBarHome);
await tapKey(keyAppBarSearch);
final addressToSearch = await clipPaste();
final endAddress = addressToSearch.substring(addressToSearch.length - 6);
expect(addressToSearch, test5.address);
@ -114,34 +102,30 @@ Future certifyTest5() async {
// Certify with test2 account
await tapKey(keyCertify);
await tapKey(keyConfirm);
spawnBlock(duration: 1000);
await pump(number: 3);
await waitFor('sending'.tr(),
reverse: true, settle: false, timeout: const Duration(seconds: 20));
spawnBlock(duration: 500);
await waitFor('validé !', timeout: const Duration(seconds: 1));
await tapKey(keyCloseTransactionScreen);
await waitFor('2');
// Change default wallet to test3
await tapKey(keyPay);
await tapKey(keyDropdownWallets);
await tapKey(keyChangeChest);
await tapKey(keySelectThisWallet(test3.address));
await tapKey(keyPopButton);
await tapKey(keyConfirm);
await sleep();
// Certify with test3 account
await tapKey(keyCertify);
await tapKey(keyConfirm);
spawnBlock(duration: 1000);
await pump(number: 3);
await waitFor('sending'.tr(),
reverse: true, settle: false, timeout: const Duration(seconds: 20));
spawnBlock(duration: 500);
await waitFor('validé !', timeout: const Duration(seconds: 1));
await tapKey(keyCloseTransactionScreen);
await waitFor('mustWaitXBeforeCertify'.tr().substring(0, 8));
await waitFor('Vous devez attendre');
// Check if test5 is member
await tapKey(keyAppBarChest, duration: 300);
await tapKey(keyOpenWallet(test5.address));
await waitFor('memberValidated'.tr());
await waitFor('Membre validé !');
// spawn 20 blocs and check if ud is creating
await spawnBlock(until: 20);

View File

@ -1,4 +1,4 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:integration_test/integration_test.dart';
@ -7,7 +7,7 @@ import '../utility/tests_utility.dart';
void main() async {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
// await dotenv.load();
await dotenv.load();
testWidgets('Identity revocation', (testerLoc) async {
tester = testerLoc;
@ -32,27 +32,36 @@ void main() async {
await bkConfirmIdentity(fromAddress: test5.address, name: test5.name);
await bkCertify(fromAddress: test2.address, destAddress: test5.address);
await bkCertify(fromAddress: test3.address, destAddress: test5.address);
await waitFor('memberValidated'.tr(), exactMatch: true);
await waitFor('Membre validé !', exactMatch: true);
// Revoke test5
await goBack();
await tapKey(keyOpenWallet(test5.address));
await tapKey(keyManageMembership, duration: 100);
await tapKey(keyManageMembership, duration: 1000);
await tapKey(keyRevokeIdty);
await tapKey(keyConfirm);
spawnBlock(duration: 1000);
await tester.pump(const Duration(seconds: 2));
await waitFor('sending'.tr(),
reverse: true, settle: false, timeout: const Duration(seconds: 20));
spawnBlock(duration: 2000);
await waitFor('validé !', timeout: const Duration(seconds: 4));
await tapKey(keyCloseTransactionScreen, duration: 0);
await waitFor('noIdentity'.tr(), exactMatch: true);
await waitFor('Aucune identité', exactMatch: true);
await sleep();
// Check test1 cannot be revoked
await goBack();
await tapKey(keyAddDerivation);
await tapKey(keyOpenWallet(test1.address), duration: 300);
await tapKey(keyManageMembership, duration: 300);
await waitFor('youCannotRevokeThisIdentity'.tr().substring(0, 15));
await tapKey(keyOpenWallet(test1.address), duration: 500);
await tapKey(keyManageMembership, duration: 1000);
await waitFor('Vous ne pouvez pas révoquer cette identité');
// // Try migrate test1 identity to test6 address
// await tapKey(keyMigrateIdentity);
// await tapKey(keySelectWallet);
// await tapKey(keySelectThisWallet(test6.address), selectLast: true);
// await spawnBlock(number: 100);
// await waitFor('Vous devez attendre', reverse: true);
// await waitForButtonEnabled(keyConfirm);
// await tapKey(keyConfirm, duration: 500);
// await spawnBlock(duration: 2000);
// await waitFor('validé !');
// await tapKey(keyCloseTransactionScreen, duration: 0);
// await sleep(5000);
}, timeout: testTimeout());
}

View File

@ -1,5 +1,5 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:integration_test/integration_test.dart';
@ -8,7 +8,7 @@ import '../utility/tests_utility.dart';
void main() async {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
// await dotenv.load();
await dotenv.load();
testWidgets('Migrate Cesium identity and balance', (testerLoc) async {
tester = testerLoc;
@ -19,15 +19,14 @@ void main() async {
await firstOpenChest();
// Go to test1 options and check if balance growup with UDs creations
await scrollUntil(keyAddDerivation);
await tapKey(keyAddDerivation);
await waitFor(' 6');
await waitFor('Portefeuille 6');
await scrollUntil(keyImportG1v1);
await tapKey(keyImportG1v1);
await enterText(keyCesiumId, 'test');
await enterText(keyCesiumPassword, 'test');
await waitFor('DCovzCEnQm9GUWe6mr8u42JR1JAuoj3HbQUGdCkfTzSr');
await waitFor(cesiumTest1.shortAddress());
await waitFor('100.0');
await waitFor('3', exactMatch: true);
@ -43,19 +42,15 @@ void main() async {
await tapKey(keySelectThisWallet(test6.address), selectLast: true);
await waitForButtonEnabled(keyConfirm);
await tapKey(keyConfirm);
spawnBlock(duration: 1000);
await tester.pump(const Duration(seconds: 2));
await tester.pump(const Duration(seconds: 1));
await waitFor('sending'.tr(),
reverse: true, settle: false, timeout: const Duration(seconds: 20));
spawnBlock(duration: 2000);
await waitFor('validé !');
await tapKey(keyCloseTransactionScreen, duration: 0);
await tapKey(keyOpenWallet(test6.address), duration: 300);
await pump(number: 4);
await waitFor('3', exactMatch: true);
await waitFor('memberValidated'.tr());
await waitFor('Membre validé !');
await waitFor('110.01', exactMatch: true);
await waitFor('99.98', exactMatch: true);
}, timeout: testTimeout());
}

View File

@ -1,3 +1,4 @@
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import '../utility/general_actions.dart';
@ -5,7 +6,7 @@ import '../utility/tests_utility.dart';
void main() async {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
// await dotenv.load();
await dotenv.load();
testWidgets('Onboarding and multi chest', (testerLoc) async {
tester = testerLoc;

View File

@ -1,3 +1,4 @@
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:integration_test/integration_test.dart';
@ -6,7 +7,7 @@ import '../utility/tests_utility.dart';
void main() async {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
// await dotenv.load();
await dotenv.load();
testWidgets('UDs creation state', (testerLoc) async {
tester = testerLoc;

View File

@ -1,5 +1,5 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/widgets_keys.dart';
@ -10,7 +10,8 @@ import 'tests_utility.dart';
// GENERAL ACTIONS
Future changeNode() async {
const ipAddress = '10.0.2.2';
final ipAddress = dotenv.env['ip_address'] ?? '127.0.0.1';
log.d('ip address: $ipAddress');
await tapKey(keyDrawerMenu);
await tapKey(keyParameters);
@ -24,7 +25,7 @@ Future changeNode() async {
}
Future deleteAllWallets() async {
if (await isPresent('searchWallet'.tr())) {
if (await isPresent('Rechercher')) {
await tapKey(keyDrawerMenu);
await tapKey(keyParameters);
@ -64,18 +65,12 @@ Future restoreChest() async {
// Enter password
await enterText(keyPinForm, 'AAAAA', 0);
// pump a few frame
await pump(duration: const Duration(milliseconds: 500), number: 10);
// Check if string "Accéder à mon coffre" is present in screen
await waitFor('accessMyChest'.tr(), settle: false);
await waitFor('Accéder à mon coffre');
// Go to wallets home
await tapKey(keyGoWalletsHome, duration: 0);
// Skip tutorial
await skipWalletDragTutorial();
// Check if string "ĞD" is present in screen
await waitFor('ĞD');
@ -89,7 +84,7 @@ Future restoreChest() async {
await tapKey(keyCopyAddress);
// Check if string "Cette adresse a été copié" is present in screen
await waitFor('thisAddressHasBeenCopiedToClipboard'.tr());
await waitFor('Cette adresse a été copié');
// Pop screen 2 time to go back home
await goBack();
@ -131,7 +126,7 @@ Future onboardingNewChest() async {
final askedWord = mnemonic[askedWordNumber - 1];
await enterText(keyInputWord, askedWord);
await waitFor('continue'.tr(), exactMatch: true);
await waitFor('Continuer', exactMatch: true);
await tapKey(keyGoNext);
await tapKey(keyGoNext);
await tapKey(keyGoNext);
@ -148,19 +143,20 @@ Future onboardingNewChest() async {
await enterText(keyPinForm, 'AAAAA', 0);
// Check if string "Accéder à mon coffre" is present in screen
await waitFor('accessMyChest'.tr());
await waitFor('Accéder à mon coffre');
// Go to wallets home
await tapKey(keyGoWalletsHome, duration: 0);
// Check if string "Mon portefeuille co" is present in screen
await waitFor('currentWallet'.tr());
await waitFor('Mon portefeuille co');
await waitFor('0.0', exactMatch: true);
// await waitFor('Scanner un');
}
Future addDerivation() async {
await tapKey(keyAddDerivation);
await waitFor(' 5');
await waitFor('Portefeuille 5');
}
Future firstOpenChest() async {
@ -169,15 +165,5 @@ Future firstOpenChest() async {
final isCached = await isIconPresent(Icons.check_box);
if (!isCached) await tapKey(keyCachePassword, duration: 0);
await enterText(keyPinForm, 'AAAAA', 0);
await skipWalletDragTutorial();
await waitFor(test1.name);
}
Future skipWalletDragTutorial() async {
await pump(duration: const Duration(milliseconds: 500), number: 6);
await pump(duration: const Duration(seconds: 2));
if (await isPresent('explainDraggableWallet'.tr().substring(0, 13),
timeout: const Duration(seconds: 5), settle: false)) {
await tapKey(keyDragAndDrop, duration: 0);
}
await waitFor('100.0', exactMatch: true);
}

View File

@ -1,6 +1,6 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/wallet_data.dart';
@ -11,7 +11,8 @@ import 'package:provider/provider.dart';
import 'dart:io' as io;
import 'package:gecko/main.dart' as app;
const isHumanReading = false;
final bool isHumanReading =
dotenv.env['isHumanReading'] == 'true' ? true : false;
Timeout testTimeout([int seconds = 120]) =>
Timeout(Duration(seconds: isHumanReading ? 600 : seconds));
final sub = Provider.of<SubstrateSdk>(homeContext, listen: false);
@ -51,15 +52,6 @@ Future sleep([int time = 1000]) async {
await Future.delayed(Duration(milliseconds: time));
}
Future pump(
{Duration duration = const Duration(milliseconds: 300),
int number = 1}) async {
for (int i = 0; i < number; i++) {
log.i("pump $i");
await tester.pump(duration = duration);
}
}
Future<String> clipPaste() async =>
(await Clipboard.getData('text/plain'))?.text ?? '';
@ -76,7 +68,7 @@ Future tapKey(Key buttonKey,
await tester.pumpAndSettle(Duration(milliseconds: duration));
}
final Finder finder = customFinder ?? find.byKey(buttonKey);
log.i('INTEGRATION TEST: Tap on ${finder.describeMatch(Plurality.zero)}}');
log.d('INTEGRATION TEST: Tap on ${finder.description}}');
await tester.tap(selectLast ? finder.last : finder);
humanRead();
}
@ -104,7 +96,7 @@ Future<void> waitForButtonEnabled(Key key,
bool reverse = false}) async {
final end = DateTime.now().add(timeout);
log.i('INTEGRATION TEST: Wait for $key to be enabled');
log.d('INTEGRATION TEST: Wait for $key to be enabled');
do {
if (DateTime.now().isAfter(end)) {
@ -119,9 +111,9 @@ Future<void> waitForButtonEnabled(Key key,
Future goBack() async {
final NavigatorState navigator = tester.state(find.byType(Navigator));
log.i('INTEGRATION TEST: Go back');
log.d('INTEGRATION TEST: Go back');
navigator.pop();
await tester.pumpAndSettle();
await tester.pump();
humanRead();
}
@ -129,33 +121,28 @@ Future enterText(Key fieldKey, String textIn, [int duration = 200]) async {
if (duration != 0) {
await tester.pumpAndSettle(Duration(milliseconds: duration));
}
log.i('INTEGRATION TEST: Enter text: $textIn');
log.d('INTEGRATION TEST: Enter text: $textIn');
await tester.enterText(find.byKey(fieldKey), textIn);
humanRead();
}
Future<void> waitFor(String text,
{Duration timeout = const Duration(seconds: 5),
final bool reverse = false,
final bool exactMatch = false,
final bool settle = true,
final int pumpDuration = 100}) async {
bool reverse = false,
bool exactMatch = false}) async {
final end = DateTime.now().add(timeout);
Finder finder = exactMatch ? find.text(text) : find.textContaining(text);
log.i('INTEGRATION TEST: Wait for: $text');
log.d('INTEGRATION TEST: Wait for: $text');
final searchType = reverse ? 'reversed text' : 'text';
final String searchType = reverse ? 'reversed text' : 'text';
do {
if (DateTime.now().isAfter(end)) {
throw Exception('Timed out waiting for $searchType : "$text"');
}
if (settle) {
await tester.pumpAndSettle(Duration(milliseconds: pumpDuration),
EnginePhase.sendSemanticsUpdate, timeout);
}
await tester.pumpAndSettle();
await Future.delayed(const Duration(milliseconds: 100));
} while (reverse ? finder.evaluate().isNotEmpty : finder.evaluate().isEmpty);
humanRead();
@ -163,9 +150,9 @@ Future<void> waitFor(String text,
// Test if text is visible on screen, return a boolean
Future<bool> isPresent(String text,
{Duration timeout = const Duration(seconds: 1), bool settle = true}) async {
{Duration timeout = const Duration(seconds: 1)}) async {
try {
await waitFor(text, timeout: timeout, settle: settle);
await waitFor(text, timeout: timeout);
humanRead();
return true;
} catch (exception) {
@ -179,8 +166,9 @@ Future<bool> isIconPresent(IconData icon,
{Duration timeout = const Duration(seconds: 1)}) async {
await tester.pumpAndSettle();
final finder = find.byIcon(icon);
log.d('tatatatatatata: ${finder.evaluate()}');
humanRead();
return finder.evaluate().isNotEmpty;
return finder.evaluate().isEmpty ? false : true;
}
Future spawnBlock({int number = 1, int duration = 200, int? until}) async {
@ -234,11 +222,11 @@ Future bkConfirmIdentity(
// Change node in background
Future bkSetNode([String? endpoint]) async {
if (endpoint == null) {
const ipAddress = '10.0.2.2';
final ipAddress = dotenv.env['ip_address'] ?? '127.0.0.1';
endpoint = 'ws://$ipAddress:9944';
}
configBox.put('customEndpoint', endpoint);
sub.connectNode();
sub.connectNode(homeContext);
}
// Restore chest in background
@ -270,14 +258,14 @@ Future<WalletData> _addImportAccount(
final address = await sub.importAccount(
mnemonic: mnemonic, derivePath: '//$derivation', password: 'AAAAA');
final myWallet = WalletData(
version: dataVersion,
chest: chest,
address: address,
number: number,
name: name,
derivation: derivation,
imageDefaultPath: '${number % 4}.png',
isOwned: true);
await walletBox.put(myWallet.address, myWallet);
imageDefaultPath: '${number % 4}.png');
await walletBox.add(myWallet);
return myWallet;
}
@ -286,8 +274,8 @@ Future<WalletData> _addImportAccount(
Future bkDeleteAllWallets() async {
final myWalletProvider =
Provider.of<MyWalletsProvider>(homeContext, listen: false);
final isWalletsPresents = await isPresent('scanQRCode'.tr(),
timeout: const Duration(milliseconds: 300));
final isWalletsPresents =
await isPresent('Scanner un', timeout: const Duration(milliseconds: 300));
if (isWalletsPresents) {
await walletBox.clear();
await chestBox.clear();
@ -313,14 +301,14 @@ Future bkFastStart([bool restoreChest = true]) async {
if (restoreChest) {
// Restore the test chest
await bkRestoreChest();
await waitFor("noLizard".tr());
await waitFor("y'a pas de lézard");
}
}
Future startWait() async {
app.main();
await waitFor('Test starting...', reverse: true);
await tester.pumpAndSettle(const Duration(seconds: 2));
await tester.pumpAndSettle(const Duration(milliseconds: 300));
await sleep(3000);
}
@ -329,32 +317,6 @@ String getWidgetText(Key key) {
return (word4Finder.evaluate().single.widget as Text).data!;
}
void ignoreOverflowErrors(
FlutterErrorDetails details, {
bool forceReport = false,
}) {
bool ifIsOverflowError = false;
bool isUnableToLoadAsset = false;
// Detect overflow error.
var exception = details.exception;
if (exception is FlutterError) {
ifIsOverflowError = !exception.diagnostics.any(
(e) => e.value.toString().startsWith("A RenderFlex overflowed by"),
);
isUnableToLoadAsset = !exception.diagnostics.any(
(e) => e.value.toString().startsWith("Unable to load asset"),
);
}
// Ignore if is overflow error.
if (ifIsOverflowError || isUnableToLoadAsset) {
debugPrint('Ignored Error');
} else {
FlutterError.dumpErrorToConsole(details, forceReport: forceReport);
}
}
class TestWallet {
String address;
String name;

View File

@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
<string>8.0</string>
</dict>
</plist>

View File

@ -1,140 +0,0 @@
PODS:
- barcode_scan2 (0.0.1):
- Flutter
- MTBBarcodeScanner
- SwiftProtobuf
- connectivity_plus (0.0.1):
- Flutter
- ReachabilitySwift
- Flutter (1.0.0)
- flutter_aes_ecb_pkcs5 (0.0.1):
- Flutter
- flutter_inappwebview (0.0.1):
- Flutter
- flutter_inappwebview/Core (= 0.0.1)
- OrderedSet (~> 5.0)
- flutter_inappwebview/Core (0.0.1):
- Flutter
- OrderedSet (~> 5.0)
- image_cropper (0.0.4):
- Flutter
- TOCropViewController (~> 2.6.1)
- image_picker_ios (0.0.1):
- Flutter
- integration_test (0.0.1):
- Flutter
- MTBBarcodeScanner (5.0.11)
- OrderedSet (5.0.0)
- package_info_plus (0.4.5):
- Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- permission_handler_apple (9.0.4):
- Flutter
- printing (1.0.0):
- Flutter
- ReachabilitySwift (5.0.0)
- Sentry/HybridSDK (7.31.5)
- sentry_flutter (0.0.1):
- Flutter
- FlutterMacOS
- Sentry/HybridSDK (= 7.31.5)
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- SwiftProtobuf (1.21.0)
- TOCropViewController (2.6.1)
- url_launcher_ios (0.0.1):
- Flutter
- webview_flutter_wkwebview (0.0.1):
- Flutter
DEPENDENCIES:
- barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- Flutter (from `Flutter`)
- flutter_aes_ecb_pkcs5 (from `.symlinks/plugins/flutter_aes_ecb_pkcs5/ios`)
- flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`)
- image_cropper (from `.symlinks/plugins/image_cropper/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- integration_test (from `.symlinks/plugins/integration_test/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- printing (from `.symlinks/plugins/printing/ios`)
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`)
SPEC REPOS:
trunk:
- MTBBarcodeScanner
- OrderedSet
- ReachabilitySwift
- Sentry
- SwiftProtobuf
- TOCropViewController
EXTERNAL SOURCES:
barcode_scan2:
:path: ".symlinks/plugins/barcode_scan2/ios"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
Flutter:
:path: Flutter
flutter_aes_ecb_pkcs5:
:path: ".symlinks/plugins/flutter_aes_ecb_pkcs5/ios"
flutter_inappwebview:
:path: ".symlinks/plugins/flutter_inappwebview/ios"
image_cropper:
:path: ".symlinks/plugins/image_cropper/ios"
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
integration_test:
:path: ".symlinks/plugins/integration_test/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/ios"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
printing:
:path: ".symlinks/plugins/printing/ios"
sentry_flutter:
:path: ".symlinks/plugins/sentry_flutter/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/ios"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
webview_flutter_wkwebview:
:path: ".symlinks/plugins/webview_flutter_wkwebview/ios"
SPEC CHECKSUMS:
barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0
connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_aes_ecb_pkcs5: fb682a7bb13f29cfbb33f88f7e1ed2211eacf5db
flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721
image_cropper: 60c2789d1f1a78c873235d4319ca0c34a69f2d98
image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb
integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852
permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce
printing: eafa00acb682c0ca029d4d98d0798f55a1e27102
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
Sentry: 4c9babff9034785067c896fd580b1f7de44da020
sentry_flutter: b10ae7a5ddcbc7f04648eeb2672b5747230172f1
shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca
SwiftProtobuf: afced68785854575756db965e9da52bbf3dc45e7
TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863
url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2
webview_flutter_wkwebview: b7e70ef1ddded7e69c796c7390ee74180182971f
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
COCOAPODS: 1.11.3

View File

@ -13,7 +13,6 @@
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
FE317B9470ED5615CA3CB087 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B6BDF717F540F951CBCF3B0 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@ -30,15 +29,12 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
0B6BDF717F540F951CBCF3B0 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
4C6E3D6B3826D9CF6FE74C71 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9715B8CB8C4BB6B1549A4B43 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@ -46,7 +42,6 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B7B5EAF367303889DDE6246F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -54,7 +49,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
FE317B9470ED5615CA3CB087 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -78,8 +72,6 @@
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
F45015BCA13948A291A8C8E9 /* Pods */,
ADA8E12261C2D5AFC64EEABC /* Frameworks */,
);
sourceTree = "<group>";
};
@ -106,25 +98,6 @@
path = Runner;
sourceTree = "<group>";
};
ADA8E12261C2D5AFC64EEABC /* Frameworks */ = {
isa = PBXGroup;
children = (
0B6BDF717F540F951CBCF3B0 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
F45015BCA13948A291A8C8E9 /* Pods */ = {
isa = PBXGroup;
children = (
B7B5EAF367303889DDE6246F /* Pods-Runner.debug.xcconfig */,
9715B8CB8C4BB6B1549A4B43 /* Pods-Runner.release.xcconfig */,
4C6E3D6B3826D9CF6FE74C71 /* Pods-Runner.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -132,14 +105,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
BF832FA7B2681D407FBCCD35 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
C7115DDDD9D931A7B2E38D5D /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -156,7 +127,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
@ -226,45 +197,6 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
BF832FA7B2681D407FBCCD35 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
C7115DDDD9D931A7B2E38D5D /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -340,7 +272,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -363,10 +295,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
@ -426,7 +355,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -475,7 +404,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -499,10 +428,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
@ -530,10 +456,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",

View File

@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "self:">
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -4,7 +4,4 @@
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@ -1,122 +1,122 @@
{
"images": [
"images" : [
{
"filename": "Icon-App-20x20@2x.png",
"idiom": "iphone",
"scale": "2x",
"size": "20x20"
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"filename": "Icon-App-20x20@3x.png",
"idiom": "iphone",
"scale": "3x",
"size": "20x20"
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"filename": "Icon-App-29x29@1x.png",
"idiom": "iphone",
"scale": "1x",
"size": "29x29"
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"filename": "Icon-App-29x29@2x.png",
"idiom": "iphone",
"scale": "2x",
"size": "29x29"
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"filename": "Icon-App-29x29@3x.png",
"idiom": "iphone",
"scale": "3x",
"size": "29x29"
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"filename": "Icon-App-40x40@2x.png",
"idiom": "iphone",
"scale": "2x",
"size": "40x40"
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"filename": "Icon-App-40x40@3x.png",
"idiom": "iphone",
"scale": "3x",
"size": "40x40"
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"filename": "Icon-App-60x60@2x.png",
"idiom": "iphone",
"scale": "2x",
"size": "60x60"
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"filename": "Icon-App-60x60@3x.png",
"idiom": "iphone",
"scale": "3x",
"size": "60x60"
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"filename": "Icon-App-20x20@1x.png",
"idiom": "ipad",
"scale": "1x",
"size": "20x20"
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"filename": "Icon-App-20x20@2x.png",
"idiom": "ipad",
"scale": "2x",
"size": "20x20"
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"filename": "Icon-App-29x29@1x.png",
"idiom": "ipad",
"scale": "1x",
"size": "29x29"
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"filename": "Icon-App-29x29@2x.png",
"idiom": "ipad",
"scale": "2x",
"size": "29x29"
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"filename": "Icon-App-40x40@1x.png",
"idiom": "ipad",
"scale": "1x",
"size": "40x40"
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"filename": "Icon-App-40x40@2x.png",
"idiom": "ipad",
"scale": "2x",
"size": "40x40"
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"filename": "Icon-App-76x76@1x.png",
"idiom": "ipad",
"scale": "1x",
"size": "76x76"
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"filename": "Icon-App-76x76@2x.png",
"idiom": "ipad",
"scale": "2x",
"size": "76x76"
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"filename": "Icon-App-83.5x83.5@2x.png",
"idiom": "ipad",
"scale": "2x",
"size": "83.5x83.5"
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"filename": "Icon-App-1024x1024@1x.png",
"idiom": "ios-marketing",
"scale": "1x",
"size": "1024x1024"
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info": {
"author": "icons_launcher",
"version": 1
"info" : {
"version" : 1,
"author" : "xcode"
}
}
}

View File

@ -47,7 +47,5 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
</dict>
</plist>

View File

@ -1,5 +1,4 @@
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:gecko/models/chest_data.dart';
import 'package:gecko/models/g1_wallets_list.dart';
@ -8,11 +7,10 @@ import 'package:hive_flutter/hive_flutter.dart';
import 'package:logger/logger.dart';
// Version of box data
const int dataVersion = 9;
const int dataVersion = 4;
late String appVersion;
const int pinLength = 5;
const int maxWalletsInSafe = 30;
const String appLang = 'english';
late Box<WalletData> walletBox;
@ -21,16 +19,15 @@ late Box configBox;
late Box<G1WalletsList> g1WalletsBox;
late Box<G1WalletsList> contactsBox;
// late Box keystoreBox;
late Directory avatarsDirectory;
late Directory avatarsCacheDirectory;
late bool isTall;
late Directory imageDirectory;
const cesiumPod = "https://g1.data.le-sou.org";
// String cesiumPod = "https://g1.data.presles.fr";
// String cesiumPod = "https://g1.data.le-sou.org";
String cesiumPod = "https://g1.data.presles.fr";
// String cesiumPod = "https://g1.data.e-is.pro";
const datapodEndpoint = 'gdev-datapod.p2p.legal';
// const datapodEndpoint = '10.0.2.2:8080';
// Responsive ratios
late bool isTall;
late double ratio;
// Contexts
late BuildContext homeContext;
@ -53,24 +50,3 @@ const debugPin = true;
String indexerEndpoint = '';
late double balanceRatio;
late int udValue;
// Indexer
late DateTime startBlockchainTime;
bool startBlockchainInitialized = false;
late int currentUdIndex;
final Map<int, String> monthsInYear = {
1: "month1".tr(),
2: "month2".tr(),
3: "month3".tr(),
4: "month4".tr(),
5: "month5".tr(),
6: "month6".tr(),
7: "month7".tr(),
8: "month8".tr(),
9: "month9".tr(),
10: "month10".tr(),
11: "month11".tr(),
12: "month12".tr()
};

View File

@ -16,20 +16,21 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/providers/cesium_plus.dart';
import 'package:gecko/models/chest_data.dart';
import 'package:gecko/models/g1_wallets_list.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/providers/chest_provider.dart';
import 'package:gecko/models/g1_wallets_list.dart';
import 'package:gecko/providers/duniter_indexer.dart';
import 'package:gecko/providers/generate_wallets.dart';
import 'package:gecko/providers/settings_provider.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/providers/v2s_datapod.dart';
import 'package:gecko/providers/wallets_profiles.dart';
import 'package:gecko/providers/home.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/providers/search.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/providers/wallet_options.dart';
import 'package:gecko/screens/home.dart';
import 'package:flutter/material.dart';
@ -50,67 +51,78 @@ Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized();
// if (kDebugMode) {
// await dotenv.load();
// }
if (kDebugMode) {
await dotenv.load();
}
final homeProvider = HomeProvider();
HomeProvider homeProvider = HomeProvider();
// DuniterIndexer _duniterIndexer = DuniterIndexer();
await initHiveForFlutter();
await homeProvider.initHive();
configBox = await Hive.openBox("configBox");
appVersion = await homeProvider.getAppVersion();
// Reset GraphQL cache
// final cache = HiveStore();
// cache.reset();
// Configure Hive and open boxes
Hive.registerAdapter(WalletDataAdapter());
Hive.registerAdapter(ChestDataAdapter());
Hive.registerAdapter(G1WalletsListAdapter());
Hive.registerAdapter(IdAdapter());
Hive.registerAdapter(IdtyStatusAdapter());
walletBox = await Hive.openBox<WalletData>("walletBox");
chestBox = await Hive.openBox<ChestData>("chestBox");
configBox = await Hive.openBox("configBox");
await Hive.deleteBoxFromDisk('g1WalletsBox');
g1WalletsBox = await Hive.openBox<G1WalletsList>("g1WalletsBox");
contactsBox = await Hive.openBox<G1WalletsList>("contactsBox");
await homeProvider.getValidEndpoints();
// await configBox.delete('isCacheChecked');
if (configBox.get('isCacheChecked') == null) {
configBox.put('isCacheChecked', false);
}
// log.d(await configBox.get('endpoint'));
HttpOverrides.global = MyHttpOverrides();
if (kReleaseMode && enableSentry) {
await SentryFlutter.init((options) {
options.dsn =
'https://c09587b46eaa42e8b9fda28d838ed180@o496840.ingest.sentry.io/5572110';
},
appRunner: () => SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp]).then((_) {
runApp(EasyLocalization(
supportedLocales: const [
Locale('en'),
Locale('fr'),
Locale('es'),
Locale('it')
],
path: 'assets/translations',
fallbackLocale: const Locale('en'),
child: const Gecko(),
));
}));
} else {
log.i('Debug mode enabled: No sentry alert');
// CatcherOptions debugOptions = CatcherOptions(DialogReportMode(), [
// SentryHandler(SentryClient(SentryOptions(
// dsn:
// "https://c09587b46eaa42e8b9fda28d838ed180@o496840.ingest.sentry.io/5572110")))
// ]);
// // CatcherOptions releaseOptions = CatcherOptions(NotificationReportMode(), [
// // EmailManualHandler(["poka@p2p.legal"])
// // ]);
// Catcher(rootWidget: Gecko(endPointGVA, _store), debugConfig: debugOptions);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
.then((_) {
runApp(EasyLocalization(
// test, force locale :: startLocale: Locale.fromSubtags(languageCode: 'it'),
supportedLocales: const [
Locale('en'),
Locale('fr'),
Locale('es'),
Locale('it')
],
await SentryFlutter.init(
(options) {
options.dsn =
'https://c09587b46eaa42e8b9fda28d838ed180@o496840.ingest.sentry.io/5572110';
},
appRunner: () => runApp(
EasyLocalization(
supportedLocales: const [Locale('en'), Locale('fr'), Locale('es')],
path: 'assets/translations',
fallbackLocale: const Locale('en'),
child: const Gecko(),
),
),
);
} else {
log.i('Debug mode enabled: No sentry alerte');
runApp(
EasyLocalization(
supportedLocales: const [Locale('en'), Locale('fr'), Locale('es')],
path: 'assets/translations',
fallbackLocale: const Locale('en'),
child: const Gecko(),
));
});
),
);
}
}
@ -119,6 +131,8 @@ class Gecko extends StatelessWidget {
@override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
// To configure multi_endpoints GraphQLProvider: https://stackoverflow.com/q/70656513/8301867
return MultiProvider(
@ -130,23 +144,26 @@ class Gecko extends StatelessWidget {
ChangeNotifierProvider(create: (_) => GenerateWalletsProvider()),
ChangeNotifierProvider(create: (_) => WalletOptionsProvider()),
ChangeNotifierProvider(create: (_) => SearchProvider()),
ChangeNotifierProvider(create: (_) => CesiumPlusProvider()),
ChangeNotifierProvider(create: (_) => SubstrateSdk()),
ChangeNotifierProvider(create: (_) => DuniterIndexer()),
ChangeNotifierProvider(create: (_) => SettingsProvider()),
ChangeNotifierProvider(create: (_) => V2sDatapodProvider())
ChangeNotifierProvider(create: (_) => SettingsProvider())
],
child: MaterialApp(
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
builder: (context, child) => ResponsiveBreakpoints.builder(
child: child!,
breakpoints: [
const Breakpoint(start: 0, end: 450, name: MOBILE),
const Breakpoint(start: 451, end: 800, name: TABLET),
const Breakpoint(start: 801, end: double.infinity, name: DESKTOP),
],
),
builder: (context, widget) => ResponsiveWrapper.builder(
BouncingScrollWrapper.builder(context, widget!),
maxWidth: 1200,
minWidth: 480,
defaultScale: true,
breakpoints: [
const ResponsiveBreakpoint.resize(480, name: MOBILE),
const ResponsiveBreakpoint.autoScale(800, name: TABLET),
const ResponsiveBreakpoint.resize(1000, name: DESKTOP),
],
background: Container(color: backgroundColor)),
title: 'Ğecko',
theme: ThemeData(
appBarTheme: const AppBarTheme(
@ -154,21 +171,18 @@ class Gecko extends StatelessWidget {
foregroundColor: Color(0xFF000000),
),
primaryColor: const Color(0xffFFD58D),
scaffoldBackgroundColor: backgroundColor,
canvasColor: backgroundColor,
dialogBackgroundColor: backgroundColor,
textTheme: const TextTheme(
bodyLarge: TextStyle(fontSize: 16),
bodyMedium: TextStyle(fontSize: 18),
bodyText1: TextStyle(fontSize: 16),
bodyText2: TextStyle(fontSize: 18),
).apply(
bodyColor: const Color(0xFF000000),
),
colorScheme:
ColorScheme.fromSwatch().copyWith(secondary: Colors.grey[850]),
),
home: const HomeScreen(),
initialRoute: "/",
routes: {
'/': (context) => const HomeScreen(),
'/mywallets': (context) => const WalletsHome(),
'/search': (context) => const SearchScreen(),
'/searchResult': (context) => const SearchResultScreen(),

View File

@ -1,5 +1,7 @@
import 'dart:io';
import 'package:hive_flutter/hive_flutter.dart';
part 'chest_data.g.dart';
@HiveType(typeId: 1)

View File

@ -1,3 +1,4 @@
import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
part 'g1_wallets_list.g.dart';
@ -14,18 +15,22 @@ class G1WalletsList {
Id? id;
@HiveField(3)
String? username;
Image? avatar;
@HiveField(4)
String? csName;
String? username;
@HiveField(5)
String? csName;
@HiveField(6)
bool? isMembre;
G1WalletsList({
required this.address,
this.balance,
this.id,
this.avatar,
this.username,
this.csName,
this.isMembre,

View File

@ -20,16 +20,17 @@ class G1WalletsListAdapter extends TypeAdapter<G1WalletsList> {
address: fields[0] as String,
balance: fields[1] as double?,
id: fields[2] as Id?,
username: fields[3] as String?,
csName: fields[4] as String?,
isMembre: fields[5] as bool?,
avatar: fields[3] as Image?,
username: fields[4] as String?,
csName: fields[5] as String?,
isMembre: fields[6] as bool?,
);
}
@override
void write(BinaryWriter writer, G1WalletsList obj) {
writer
..writeByte(6)
..writeByte(7)
..writeByte(0)
..write(obj.address)
..writeByte(1)
@ -37,10 +38,12 @@ class G1WalletsListAdapter extends TypeAdapter<G1WalletsList> {
..writeByte(2)
..write(obj.id)
..writeByte(3)
..write(obj.username)
..write(obj.avatar)
..writeByte(4)
..write(obj.csName)
..write(obj.username)
..writeByte(5)
..write(obj.csName)
..writeByte(6)
..write(obj.isMembre);
}

View File

@ -1,32 +0,0 @@
import 'package:gecko/models/wallet_data.dart';
class MigrateWalletChecks {
final Map balance;
final IdtyStatus idtyStatus;
final String validationStatus;
final bool canValidate;
const MigrateWalletChecks({
required this.balance,
required this.idtyStatus,
required this.validationStatus,
required this.canValidate,
});
const MigrateWalletChecks.defaultValues({
this.balance = const {'transferableBalance': 0},
this.idtyStatus = IdtyStatus.none,
this.validationStatus = '',
this.canValidate = false,
});
@override
String toString() {
return {
'balance': balance,
'idtyStatus': idtyStatus,
'validationStatus': validationStatus,
'canValidate': canValidate,
}.toString();
}
}

View File

@ -1,51 +0,0 @@
const String updateProfileQ = r'''
mutation ($address: String!, $hash: String!, $signature: String!, $title: String, $description: String, $avatarBase64: String, $geoloc: GeolocInput, $city: String, $socials: [SocialInput!]) {
updateProfile(address: $address, hash: $hash, signature: $signature, title: $title, description: $description, avatarBase64: $avatarBase64, geoloc: $geoloc, city: $city, socials: $socials) {
message
success
}
}
''';
const String deleteProfileQ = r'''
mutation ($address: String!, $hash: String!, $signature: String!) {
deleteProfile(address: $address, hash: $hash, signature: $signature) {
message
success
}
}
''';
const String migrateProfileQ = r'''
mutation ($addressOld: String!, $addressNew: String!, $hash: String!, $signature: String!) {
migrateProfile(addressOld: $addressOld, addressNew: $addressNew, hash: $hash, signature: $signature) {
message
success
}
}
''';
const String addTransactionCommentQ = r'''
mutation ($id: String!, $address: String!, $hash: String!, $signature: String!, $comment: String!) {
addTransaction(id: $id, address: $address, hash: $hash, signature: $signature, comment: $comment) {
message
success
}
}
''';
const String getAvatarQ = r'''
query ($address: String!) {
profiles_by_pk(address: $address) {
avatar64
}
}
''';
const String profileEditedAtQ = r'''
query ($address: String!) {
profiles_by_pk(address: $address) {
updated_at
}
}
''';

View File

@ -1,55 +1,45 @@
const String getNameByAddressQ = r'''
query ($address: String!) {
identityConnection(
where: { accountId: { _eq: $address } }
orderBy: { name: ASC }
) {
edges {
node {
name
accountId
}
account_by_pk(pubkey: $address) {
identity {
name
}
pubkey
}
}
''';
const String searchAddressByNameQ = r'''
query ($name: String!) {
identityConnection(
where: { name: { _ilike: $name } }
orderBy: { name: ASC }
) {
edges {
node {
name
accountId
}
}
search_identity(args: {name: $name}) {
pubkey
name
}
}
''';
const String getHistoryByAddressRelayQ = r'''
query ($address: String!, $first: Int!, $after: String) {
transferConnection(
after: $after
first: $first
orderBy: { timestamp: DESC }
where: { _or: [{ fromId: { _eq: $address } }, { toId: { _eq: $address } }] }
) {
const String getHistoryByAddressQ = r'''
query ($address: String!, $number: Int!, $cursor: String) {
transaction_connection(where:
{_or: [
{issuer_pubkey: {_eq: $address}},
{receiver_pubkey: {_eq: $address}}
]},
order_by: {created_at: desc},
first: $number,
after: $cursor) {
edges {
node {
amount
timestamp
fromId
from {
created_at
issuer_pubkey
receiver_pubkey
issuer {
identity {
name
}
}
toId
to {
receiver {
identity {
name
}
@ -59,87 +49,12 @@ query ($address: String!, $first: Int!, $after: String) {
pageInfo {
endCursor
hasNextPage
hasPreviousPage
startCursor
}
}
}
''';
const String getCertsReceived = r'''
query ($address: String!) {
certConnection(
where: {receiver: {accountId: {_eq: $address}}}
) {
edges {
node {
createdOn
issuer {
accountId
name
}
}
}
}
}
''';
const String getCertsSent = r'''
query ($address: String!) {
certConnection(
where: {issuer: {accountId: {_eq: $address}}}
) {
edges {
node {
createdOn
receiver {
accountId
name
}
}
}
}
}
''';
const String isIdtyExistQ = r'''
query ($name: String!) {
identityConnection(where: {name: {_eq: ""}}) {
edges {
node {
name
}
}
}
}
''';
const String getBlockchainStartQ = r'''
query {
blockConnection(first: 1) {
edges {
node {
height
timestamp
}
}
}
}
''';
const String subscribeHistoryIssuedQ = r'''
subscription ($address: String!) {
accountConnection(
where: {id: {_eq: $address}}
) {
edges {
node {
transfersIssued(limit: 1, orderBy: {timestamp: DESC}) {
toId
amount
timestamp
blockNumber
}
}
}
}
}
''';
// To parse indexer date format
// log.d(DateTime.parse("2022-06-13T16:51:24.001+00:00").toString());

View File

@ -1,43 +0,0 @@
import 'package:flutter/material.dart';
import 'package:gecko/globals.dart';
double scaleSize(double size) {
final scale = MediaQuery.of(homeContext).size.width / 375;
return size * scale;
}
TextStyle scaledTextStyle({
double fontSize = 16,
double? height,
FontStyle? fontStyle,
FontWeight? fontWeight,
Color? color,
List<Shadow>? shadows,
String? fontFamily,
double? letterSpacing,
}) {
return TextStyle(
fontSize: scaleSize(fontSize),
height: height,
fontWeight: fontWeight,
fontStyle: fontStyle,
color: color,
shadows: shadows,
fontFamily: fontFamily,
letterSpacing: letterSpacing,
);
}
class ScaledSizedBox extends SizedBox {
ScaledSizedBox({
Key? key,
double? width,
double? height,
Widget? child,
}) : super(
key: key,
width: width != null ? scaleSize(width) : null,
height: height != null ? scaleSize(height) : null,
child: child,
);
}

View File

@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
class StatefulWrapper extends StatefulWidget {
final Function onInit;
final Widget child;
const StatefulWrapper({Key? key, required this.onInit, required this.child})
: super(key: key);
@override
StatefulWrapperState createState() => StatefulWrapperState();
}
class StatefulWrapperState extends State<StatefulWrapper> {
@override
void initState() {
widget.onInit();
super.initState();
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}

View File

@ -1,19 +0,0 @@
import 'package:gecko/widgets/transaction_status.dart';
class TransactionContent {
final String transactionId;
TransactionStatus status;
final String from;
final String to;
final double amount;
String? error;
TransactionContent({
required this.transactionId,
required this.status,
required this.from,
required this.to,
required this.amount,
this.error,
});
}

View File

@ -1,66 +1,41 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:io';
import 'package:gecko/globals.dart';
import 'package:gecko/providers/v2s_datapod.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:provider/provider.dart';
import 'package:uuid/uuid.dart';
part 'wallet_data.g.dart';
@HiveType(typeId: 0)
class WalletData extends HiveObject {
@HiveField(0)
String address;
int? version;
@HiveField(1)
int? chest;
@HiveField(2)
int? number;
String? address;
@HiveField(3)
String? name;
int? number;
@HiveField(4)
int? derivation;
String? name;
@HiveField(5)
String? imageDefaultPath;
int? derivation;
@HiveField(6)
String? imageCustomPath;
String? imageDefaultPath;
@HiveField(7)
bool isOwned;
String? imageCustomPath;
@HiveField(8)
IdtyStatus identityStatus;
@HiveField(9)
double balance;
@HiveField(10)
List<int>? certs;
@HiveField(11)
DateTime? profileUpdatedTime;
WalletData({
required this.address,
this.chest,
this.number,
this.name,
this.derivation,
this.imageDefaultPath,
this.imageCustomPath,
this.profileUpdatedTime,
this.isOwned = false,
this.identityStatus = IdtyStatus.unknown,
this.balance = 0,
this.certs,
});
WalletData(
{this.version,
this.chest,
this.address,
this.number,
this.name,
this.derivation,
this.imageDefaultPath,
this.imageCustomPath});
// representation of WalletData when debugging
@override
@ -70,89 +45,18 @@ class WalletData extends HiveObject {
// creates the ':'-separated string from the WalletData
String inLine() {
return "$chest:$number:$name:$derivation:$imageDefaultPath:$imageCustomPath:$identityStatus";
}
bool hasIdentity() {
return identityStatus == IdtyStatus.unconfirmed ||
identityStatus == IdtyStatus.unvalidated ||
identityStatus == IdtyStatus.member;
}
bool isMembre() {
return identityStatus == IdtyStatus.member;
}
bool exist() {
return balance != 0;
}
Future<DateTime?> getUpdatedTime() async {
final datapod = Provider.of<V2sDatapodProvider>(homeContext, listen: false);
return await datapod.profileEditedAt(address);
}
Future<bool> shouldUpdateProfile() async {
final remoteUpdatedProfile = await getUpdatedTime();
late Duration difference;
if (profileUpdatedTime != null && remoteUpdatedProfile != null) {
difference = profileUpdatedTime!.difference(remoteUpdatedProfile);
} else if (remoteUpdatedProfile != null) {
return true;
} else {
difference = Duration.zero;
}
return difference.inSeconds.abs() >= 30;
}
/// This method get the remote avatar on v2s-datapod only if needed, and store it on disk
Future getDatapodAvatar() async {
if (!await shouldUpdateProfile()) return;
final datapod = Provider.of<V2sDatapodProvider>(homeContext, listen: false);
final avatarUuid = const Uuid().v4();
await datapod.getRemoteAvatar(address, uuid: avatarUuid);
final avatarPath = '${avatarsDirectory.path}/$address-$avatarUuid';
if (!await File(avatarPath).exists()) return;
profileUpdatedTime = await getUpdatedTime();
imageCustomPath = avatarPath;
walletBox.put(address, this);
datapod.reload();
}
bool hasCustomImage() {
return imageCustomPath != null;
return "$chest:$number:$name:$derivation:$imageDefaultPath";
}
// returns only the id part of the ':'-separated string
List<int?> id() {
return [chest, number];
}
}
@HiveType(typeId: 5)
enum IdtyStatus {
@HiveField(0)
none,
class NewWallet {
final String address;
final String password;
@HiveField(1)
unconfirmed,
@HiveField(2)
unvalidated,
@HiveField(3)
member,
@HiveField(4)
notMember,
@HiveField(5)
revoked,
@HiveField(6)
unknown
NewWallet._(this.address, this.password);
}

View File

@ -17,49 +17,37 @@ class WalletDataAdapter extends TypeAdapter<WalletData> {
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return WalletData(
address: fields[0] as String,
version: fields[0] as int?,
chest: fields[1] as int?,
number: fields[2] as int?,
name: fields[3] as String?,
derivation: fields[4] as int?,
imageDefaultPath: fields[5] as String?,
imageCustomPath: fields[6] as String?,
profileUpdatedTime: fields[11] as DateTime?,
isOwned: fields[7] as bool,
identityStatus: fields[8] as IdtyStatus,
balance: fields[9] as double,
certs: (fields[10] as List?)?.cast<int>(),
address: fields[2] as String?,
number: fields[3] as int?,
name: fields[4] as String?,
derivation: fields[5] as int?,
imageDefaultPath: fields[6] as String?,
imageCustomPath: fields[7] as String?,
);
}
@override
void write(BinaryWriter writer, WalletData obj) {
writer
..writeByte(12)
..writeByte(8)
..writeByte(0)
..write(obj.address)
..write(obj.version)
..writeByte(1)
..write(obj.chest)
..writeByte(2)
..write(obj.number)
..write(obj.address)
..writeByte(3)
..write(obj.name)
..write(obj.number)
..writeByte(4)
..write(obj.derivation)
..write(obj.name)
..writeByte(5)
..write(obj.imageDefaultPath)
..write(obj.derivation)
..writeByte(6)
..write(obj.imageCustomPath)
..write(obj.imageDefaultPath)
..writeByte(7)
..write(obj.isOwned)
..writeByte(8)
..write(obj.identityStatus)
..writeByte(9)
..write(obj.balance)
..writeByte(10)
..write(obj.certs)
..writeByte(11)
..write(obj.profileUpdatedTime);
..write(obj.imageCustomPath);
}
@override
@ -72,67 +60,3 @@ class WalletDataAdapter extends TypeAdapter<WalletData> {
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class IdtyStatusAdapter extends TypeAdapter<IdtyStatus> {
@override
final int typeId = 5;
@override
IdtyStatus read(BinaryReader reader) {
switch (reader.readByte()) {
case 0:
return IdtyStatus.none;
case 1:
return IdtyStatus.unconfirmed;
case 2:
return IdtyStatus.unvalidated;
case 3:
return IdtyStatus.member;
case 4:
return IdtyStatus.notMember;
case 5:
return IdtyStatus.revoked;
case 6:
return IdtyStatus.unknown;
default:
return IdtyStatus.none;
}
}
@override
void write(BinaryWriter writer, IdtyStatus obj) {
switch (obj) {
case IdtyStatus.none:
writer.writeByte(0);
break;
case IdtyStatus.unconfirmed:
writer.writeByte(1);
break;
case IdtyStatus.unvalidated:
writer.writeByte(2);
break;
case IdtyStatus.member:
writer.writeByte(3);
break;
case IdtyStatus.notMember:
writer.writeByte(4);
break;
case IdtyStatus.revoked:
writer.writeByte(5);
break;
case IdtyStatus.unknown:
writer.writeByte(6);
break;
}
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is IdtyStatusAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@ -5,13 +5,12 @@ const keyInfoPopup = Key('keyInfoPopup');
const keyGoNext = Key('keyGoNext');
const keyCancel = Key('keyCancel');
const keyConfirm = Key('keyConfirm');
const keyAppBarHome = Key('keyAppBarSearch');
const keyAppBarSearch = Key('keyAppBarSearch');
const keyAppBarQrcode = Key('keyAppBarQrcode');
const keyAppBarChest = Key('keyAppBarChest');
// Home
const keyParameters = Key('keyParameters');
const keyDebugScreen = Key('keyDebugScreen');
const keyContacts = Key('keyContacts');
const keyDrawerMenu = Key('keyDrawerMenu');
const keyOpenWalletsHomme = Key('keyOpenWalletsHomme');
@ -24,11 +23,9 @@ const keyImportG1v1 = Key('keyImportG1v1');
const keyChangeChest = Key('keyChangeChest');
const keyListWallets = Key('keyListWallets');
const keyAddDerivation = Key('keyAddDerivation');
final keyDragAndDrop = GlobalKey(debugLabel: 'keyDragAndDrop');
// Wallet options
const keyCopyAddress = Key('keyCopyAddress');
const keyCopyPubkey = Key('keyCopyPubkey');
const keyOpenActivity = Key('keyOpenActivity');
const keyManageMembership = Key('keyManageMembership');
const keySetDefaultWallet = Key('keySetDefaultWallet');
@ -64,9 +61,6 @@ const keyCloseTransactionScreen = Key('keyCloseTransactionScreen');
const keyListTransactions = Key('keyListTransactions');
const keyActivityScreen = Key('keyActivityScreen');
// Certification view
const keyCertsReceived = Key('keyCertsReceived');
// Unlock wallet
const keyUnlockWallet = Key('keyUnlockWallet');
const keyPinForm = Key('keyPinForm');
@ -99,8 +93,6 @@ const keyCesiumPassword = Key('keyCesiumPassword');
const keySelectWallet = Key('keySelectWallet');
const keyCesiumIdVisible = Key('keyCesiumIdVisible');
const keyDropdownWallets = Key('keyDropdownKey');
// Items keys
Key keyTransaction(int keyId) => Key('keyTransaction$keyId');
Key keyMnemonicWord(String word) => Key('keyMnemonicWord$word');

View File

@ -0,0 +1,158 @@
import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:gecko/globals.dart';
import 'package:path_provider/path_provider.dart';
// import 'package:http/http.dart' as http;
class CesiumPlusProvider with ChangeNotifier {
TextEditingController cesiumName = TextEditingController();
Image defaultAvatar(double size) =>
Image.asset(('assets/icon_user.png'), height: size);
CancelToken avatarCancelToken = CancelToken();
Future<List> _buildQuery(pubkey) async {
var queryGetAvatar = json.encode({
"query": {
"bool": {
"should": [
{
"match": {
'_id': {"query": pubkey, "boost": 2}
}
},
{
"prefix": {'_id': pubkey}
}
]
}
},
"highlight": {
"fields": {"title": {}, "tags": {}}
},
"from": 0,
"size": 100,
"_source": [
"title",
"avatar",
"avatar._content_type",
"description",
"city",
"address",
"socials.url",
"creationTime",
"membersCount",
"type"
],
"indices_boost": {"user": 100, "page": 1, "group": 0.01}
});
String requestUrl = "/user,page,group/profile,record/_search";
String podRequest = cesiumPod + requestUrl;
Map<String, String> headers = {
'Content-type': 'application/json',
'Accept': 'application/json',
};
return [podRequest, queryGetAvatar, headers];
}
Future<String> getName(String? pubkey) async {
String? name;
if (g1WalletsBox.get(pubkey)?.csName != null) {
return g1WalletsBox.get(pubkey)!.csName!;
}
List queryOptions = await _buildQuery(pubkey);
var dio = Dio();
late Response response;
try {
response = await dio.post(
queryOptions[0],
data: queryOptions[1],
options: Options(
headers: queryOptions[2],
sendTimeout: 3000,
receiveTimeout: 5000,
),
);
// response = await http.post((Uri.parse(queryOptions[0])),
// body: queryOptions[1], headers: queryOptions[2]);
} catch (e) {
log.e(e);
}
if (response.data['hits']['hits'].toString() == '[]') {
return '';
}
final bool nameExist =
response.data['hits']['hits'][0]['_source'].containsKey("title");
if (!nameExist) {
return '';
}
name = response.data['hits']['hits'][0]['_source']['title'];
name ??= '';
g1WalletsBox.get(pubkey)!.csName = name;
return name;
}
Future<Image?> getAvatar(String? pubkey, double size) async {
if (g1WalletsBox.get(pubkey)?.avatar != null) {
return g1WalletsBox.get(pubkey)!.avatar;
}
var dio = Dio();
// log.d(_pubkey);
List queryOptions = await _buildQuery(pubkey);
late Response response;
try {
response = await dio
.post(queryOptions[0],
data: queryOptions[1],
options: Options(
headers: queryOptions[2],
sendTimeout: 4000,
receiveTimeout: 15000,
),
cancelToken: avatarCancelToken)
.timeout(
const Duration(seconds: 15),
);
// response = await http.post((Uri.parse(queryOptions[0])),
// body: queryOptions[1], headers: queryOptions[2]);
} catch (e) {
log.e(e);
}
if (response.data['hits']['hits'].toString() == '[]' ||
!response.data['hits']['hits'][0]['_source'].containsKey("avatar")) {
return defaultAvatar(size);
}
final avatar =
response.data['hits']['hits'][0]['_source']['avatar']['_content'];
var avatarFile =
File('${(await getTemporaryDirectory()).path}/avatar_$pubkey.png');
await avatarFile.writeAsBytes(base64.decode(avatar));
final finalAvatar = Image.file(
avatarFile,
height: size,
fit: BoxFit.fitWidth,
);
g1WalletsBox.get(pubkey)!.avatar = finalAvatar;
return finalAvatar;
}
}

View File

@ -3,7 +3,6 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/chest_data.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:gecko/providers/my_wallets.dart';
@ -17,11 +16,11 @@ class ChestProvider with ChangeNotifier {
Future deleteChest(context, ChestData chest) async {
final bool? answer = await (_confirmDeletingChest(context, chest.name));
final sub = Provider.of<SubstrateSdk>(context, listen: false);
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
if (answer ?? false) {
await sub.deleteAccounts(getChestWallets(chest));
await chestBox.delete(chest.key);
final myWalletProvider =
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
myWalletProvider.pinCode = '';
@ -43,9 +42,10 @@ class ChestProvider with ChangeNotifier {
List<String> getChestWallets(ChestData chest) {
List<String> toDelete = [];
log.d(chest.key);
walletBox.toMap().forEach((key, WalletData value) {
if (value.chest == chest.key) {
toDelete.add(value.address);
toDelete.add(value.address!);
}
});
return toDelete;
@ -54,27 +54,19 @@ class ChestProvider with ChangeNotifier {
Future<bool?> _confirmDeletingChest(context, String? walletName) async {
return showDialog<bool>(
context: context,
barrierDismissible: true,
barrierDismissible: true, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: Text(
'areYouSureToDeleteWallet'.tr(args: [walletName!]),
style: scaledTextStyle(fontSize: 17),
),
title: Text('areYouSureToDeleteWallet'.tr(args: [walletName!])),
actions: <Widget>[
TextButton(
child: Text("no".tr(),
style:
scaledTextStyle(fontSize: 17, color: Colors.blueAccent),
key: keyCancel),
child: Text("no".tr(), key: keyCancel),
onPressed: () {
Navigator.pop(context, false);
},
),
TextButton(
child: Text("yes".tr(),
style: scaledTextStyle(fontSize: 17, color: Colors.red),
key: keyConfirm),
child: Text("yes".tr(), key: keyConfirm),
onPressed: () {
Navigator.pop(context, true);
},

View File

@ -5,19 +5,28 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/g1_wallets_list.dart';
import 'package:gecko/models/queries_indexer.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:gecko/providers/cesium_plus.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/providers/wallet_options.dart';
import 'package:gecko/providers/wallets_profiles.dart';
import 'package:gecko/screens/wallet_view.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:provider/provider.dart';
import 'package:truncate/truncate.dart';
class DuniterIndexer with ChangeNotifier {
Map<String, String?> walletNameIndexer = {};
String? fetchMoreCursor;
Map? pageInfo;
int nPage = 1;
int nRepositories = 20;
List? transBC;
List listIndexerEndpoints = [];
bool isLoadingIndexer = false;
Future<QueryResult<Object?>?> Function()? refetch;
late GraphQLClient indexerClient;
void reload() {
notifyListeners();
@ -29,11 +38,10 @@ class DuniterIndexer with ChangeNotifier {
final client = HttpClient();
client.connectionTimeout = const Duration(milliseconds: 4000);
try {
final request =
await client.postUrl(Uri.parse('https://$endpoint/v1beta1/relay'));
final request = await client.postUrl(Uri.parse('$endpoint/v1/graphql'));
final response = await request.close();
if (response.statusCode != 200) {
log.w('Indexer $endpoint is offline');
log.d('INDEXER IS OFFILINE');
indexerEndpoint = '';
isLoadingIndexer = false;
notifyListeners();
@ -49,7 +57,7 @@ class DuniterIndexer with ChangeNotifier {
return true;
}
} catch (e) {
log.w('Indexer $endpoint is offline');
log.d('INDEXER IS OFFILINE');
indexerEndpoint = '';
isLoadingIndexer = false;
notifyListeners();
@ -57,6 +65,21 @@ class DuniterIndexer with ChangeNotifier {
}
}
// Future checkIndexerEndpointBackground() async {
// final oldEndpoint = indexerEndpoint;
// while (true) {
// await Future.delayed(const Duration(seconds: 30));
// final isValid = await checkIndexerEndpoint(oldEndpoint);
// if (!isValid) {
// log.d('INDEXER IS OFFILINE');
// indexerEndpoint = '';
// } else {
// // log.d('Indexer is online');
// indexerEndpoint = oldEndpoint;
// }
// }
// }
Future<String> getValidIndexerEndpoint() async {
// await configBox.delete('indexerEndpoint');
@ -65,14 +88,15 @@ class DuniterIndexer with ChangeNotifier {
.then((jsonStr) => jsonDecode(jsonStr));
// _listEndpoints.shuffle();
log.d(listIndexerEndpoints);
listIndexerEndpoints.add('Personnalisé');
if (configBox.containsKey('customIndexer')) {
return configBox.get('customIndexer');
// listIndexerEndpoints.insert(0, configBox.get('customIndexer'));
}
if (configBox.containsKey('indexerEndpoint') &&
listIndexerEndpoints.contains(configBox.get('indexerEndpoint'))) {
if (configBox.containsKey('indexerEndpoint')) {
if (await checkIndexerEndpoint(configBox.get('indexerEndpoint'))) {
return configBox.get('indexerEndpoint');
}
@ -86,7 +110,7 @@ class DuniterIndexer with ChangeNotifier {
client.connectionTimeout = const Duration(milliseconds: 3000);
do {
final listLenght = listIndexerEndpoints.length - 1;
int listLenght = listIndexerEndpoints.length - 1;
if (i >= listLenght) {
log.e('NO VALID INDEXER ENDPOINT FOUND');
indexerEndpoint = '';
@ -99,7 +123,7 @@ class DuniterIndexer with ChangeNotifier {
}
try {
final endpointPath = 'https://${listIndexerEndpoints[i]}/v1beta1/relay';
String endpointPath = '${listIndexerEndpoints[i]}/v1/graphql';
final request = await client.postUrl(Uri.parse(endpointPath));
final response = await request.close();
@ -127,70 +151,291 @@ class DuniterIndexer with ChangeNotifier {
}
} while (statusCode != 200);
log.i('Indexer: $indexerEndpoint');
log.i('INDEXER: $indexerEndpoint');
return indexerEndpoint;
}
List parseHistory(List blockchainTX, String address) {
List transBC = [];
Widget getNameByAddress(BuildContext context, String address,
[WalletData? wallet,
double size = 20,
bool canEdit = false,
Color color = Colors.black,
FontWeight fontWeight = FontWeight.w400,
FontStyle fontStyle = FontStyle.italic]) {
WalletOptionsProvider walletOptions =
Provider.of<WalletOptionsProvider>(context, listen: false);
if (indexerEndpoint == '') {
if (wallet == null) {
return const SizedBox();
} else {
if (canEdit) {
return walletOptions.walletName(context, wallet, size, color);
} else {
return walletOptions.walletNameController(context, wallet, size);
}
}
}
final httpLink = HttpLink(
'$indexerEndpoint/v1/graphql',
);
final client = ValueNotifier(
GraphQLClient(
cache: GraphQLCache(store: HiveStore()),
link: httpLink,
),
);
return GraphQLProvider(
client: client,
child: Query(
options: QueryOptions(
document: gql(
getNameByAddressQ), // this is the query string you just created
variables: {
'address': address,
},
// pollInterval: const Duration(seconds: 10),
),
builder: (QueryResult result,
{VoidCallback? refetch, FetchMore? fetchMore}) {
if (result.hasException) {
return Text(result.exception.toString());
}
if (result.isLoading) {
return const Text('Loading');
}
walletNameIndexer[address] =
result.data?['account_by_pk']?['identity']?['name'];
g1WalletsBox.put(
address,
G1WalletsList(
address: address, username: walletNameIndexer[address]));
// log.d(g1WalletsBox.toMap().values.first.username);
if (walletNameIndexer[address] == null) {
if (wallet == null) {
return const SizedBox();
} else {
if (canEdit) {
return walletOptions.walletName(context, wallet, size, color);
} else {
return walletOptions.walletNameController(
context, wallet, size);
}
}
}
return Text(
color == Colors.grey[700]!
? '(${walletNameIndexer[address]!})'
: truncate(walletNameIndexer[address]!, 20),
style: TextStyle(
fontSize: size,
color: color,
fontWeight: fontWeight,
fontStyle: fontStyle,
),
);
}),
);
}
Widget searchIdentity(BuildContext context, String name) {
// WalletOptionsProvider _walletOptions =
// Provider.of<WalletOptionsProvider>(context, listen: false);
CesiumPlusProvider cesiumPlusProvider =
Provider.of<CesiumPlusProvider>(context, listen: false);
WalletsProfilesProvider walletsProfiles =
Provider.of<WalletsProfilesProvider>(context, listen: false);
if (indexerEndpoint == '') {
return const Text('Aucun résultat');
}
log.d(indexerEndpoint);
final httpLink = HttpLink(
'$indexerEndpoint/v1/graphql',
);
final client = ValueNotifier(
GraphQLClient(
cache: GraphQLCache(
store: HiveStore()), // GraphQLCache(store: HiveStore())
link: httpLink,
),
);
return GraphQLProvider(
client: client,
child: Query(
options: QueryOptions(
document: gql(
searchAddressByNameQ), // this is the query string you just created
variables: {
'name': name,
},
// pollInterval: const Duration(seconds: 10),
),
builder: (QueryResult result,
{VoidCallback? refetch, FetchMore? fetchMore}) {
if (result.hasException) {
return Text(result.exception.toString());
}
if (result.isLoading) {
return Text('loading'.tr());
}
final List identities = result.data?['search_identity'] ?? [];
if (identities.isEmpty) {
return Text('noResult'.tr());
}
double avatarSize = 55;
return Expanded(
child: ListView(children: <Widget>[
for (Map profile in identities)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: ListTile(
key: keySearchResult(profile['pubkey']),
horizontalTitleGap: 40,
contentPadding: const EdgeInsets.all(5),
leading: cesiumPlusProvider.defaultAvatar(avatarSize),
title: Row(children: <Widget>[
Text(getShortPubkey(profile['pubkey']),
style: const TextStyle(
fontSize: 18,
fontFamily: 'Monospace',
fontWeight: FontWeight.w500),
textAlign: TextAlign.center),
]),
trailing: SizedBox(
width: 110,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
balance(context, profile['pubkey'], 16),
]),
]),
),
subtitle: Row(children: <Widget>[
Text(profile['name'] ?? '',
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.w500),
textAlign: TextAlign.center),
]),
dense: false,
isThreeLine: false,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
walletsProfiles.address = profile['pubkey'];
return WalletViewScreen(
address: profile['pubkey'],
username: g1WalletsBox
.get(profile['pubkey'])
?.id
?.username,
avatar:
g1WalletsBox.get(profile['pubkey'])?.avatar,
);
}),
);
}),
),
]),
);
}),
);
}
List parseHistory(blockchainTX, pubkey) {
var transBC = [];
int i = 0;
for (final transactionNode in blockchainTX) {
final transaction = transactionNode['node'];
final direction = transaction['fromId'] != address ? 'RECEIVED' : 'SENT';
for (final trans in blockchainTX) {
final transaction = trans['node'];
final direction =
transaction['issuer_pubkey'] != pubkey ? 'RECEIVED' : 'SENT';
transBC.add(i);
transBC[i] = [];
transBC[i].add(DateTime.parse(transaction['timestamp']));
final amountBrut = transaction['amount'];
final amount = removeDecimalZero(amountBrut / 100);
transBC[i].add(DateTime.parse(transaction['created_at']));
final int amountBrut = transaction['amount'];
final double amount = removeDecimalZero(amountBrut / 100);
if (direction == "RECEIVED") {
transBC[i].add(transaction['fromId']);
transBC[i].add(transaction['from']['identity']?['name'] ?? '');
transBC[i].add(transaction['issuer_pubkey']);
transBC[i].add(transaction['issuer']['identity']?['name'] ?? '');
} else if (direction == "SENT") {
transBC[i].add(transaction['toId']);
transBC[i].add(transaction['to']['identity']?['name'] ?? '');
transBC[i].add(transaction['receiver_pubkey']);
transBC[i].add(transaction['receiver']['identity']?['name'] ?? '');
}
transBC[i].add(amount);
transBC[i].add(direction);
// transBC[i].add(''); //transaction comment
i++;
}
return transBC;
}
FetchMoreOptions? mergeQueryResult(QueryResult result, FetchMoreOptions? opts,
String address, int nRepositories) {
final List<dynamic> blockchainTX =
(result.data!['transferConnection']['edges'] as List<dynamic>);
FetchMoreOptions? checkQueryResult(result, opts, pubkey) {
final List<dynamic>? blockchainTX =
(result.data['transaction_connection']['edges'] as List<dynamic>?);
// final List<dynamic> mempoolTX =
// (result.data['txsHistoryMp']['receiving'] as List<dynamic>);
pageInfo = result.data!['transferConnection']['pageInfo'];
pageInfo = result.data['transaction_connection']['pageInfo'];
fetchMoreCursor = pageInfo!['endCursor'];
// final hasNextPage = pageInfo!['hasNextPage'];
// log.d('endCursor: $fetchMoreCursor $hasNextPage');
if (fetchMoreCursor == null) nPage = 1;
log.d(fetchMoreCursor);
if (nPage == 1) {
nRepositories = 40;
} else if (nPage == 2) {
nRepositories = 100;
}
// nRepositories = 10;
nPage++;
if (fetchMoreCursor != null) {
opts = FetchMoreOptions(
variables: {'after': fetchMoreCursor, 'first': nRepositories},
variables: {'cursor': fetchMoreCursor, 'number': nRepositories},
updateQuery: (previousResultData, fetchMoreResultData) {
final List<dynamic> repos = [
...previousResultData!['transferConnection']['edges']
...previousResultData!['transaction_connection']['edges']
as List<dynamic>,
...fetchMoreResultData!['transferConnection']['edges']
...fetchMoreResultData!['transaction_connection']['edges']
as List<dynamic>
];
fetchMoreResultData['transferConnection']['edges'] = repos;
log.d('repos: $previousResultData');
log.d('repos: $fetchMoreResultData');
log.d('repos: $repos');
fetchMoreResultData['transaction_connection']['edges'] = repos;
return fetchMoreResultData;
},
);
}
log.d(
"###### DEBUG H Parse blockchainTX list. Cursor: $fetchMoreCursor ######");
if (fetchMoreCursor != null) {
transBC = parseHistory(blockchainTX, address);
transBC = parseHistory(blockchainTX, pubkey);
} else {
log.d("Activity start of $address");
log.i("###### DEBUG H - Début de l'historique");
}
return opts;
}
@ -199,119 +444,6 @@ class DuniterIndexer with ChangeNotifier {
return double.parse(result);
}
//// Manuals queries
Future<bool> isIdtyExist(String name) async {
final variables = <String, dynamic>{
'name': name,
};
final result = await _execQuery(isIdtyExistQ, variables);
log.d(result.data);
return result.data?['identity']?.isNotEmpty ?? false;
}
Future<DateTime> getBlockStart() async {
final result = await _execQuery(getBlockchainStartQ, {});
if (!result.hasException) {
startBlockchainTime = DateTime.parse(
result.data!['blockConnection']['edges'][0]['node']['timestamp']);
startBlockchainInitialized = true;
return startBlockchainTime;
}
return DateTime(0, 0, 0, 0, 0);
}
Future<QueryResult> _execQuery(
String query, Map<String, dynamic> variables) async {
final options = QueryOptions(document: gql(query), variables: variables);
// 5GMyvKsTNk9wDBy9jwKaX6mhSzmFFtpdK9KNnmrLoSTSuJHv
return await indexerClient.query(options);
}
Stream<QueryResult> subscribeHistoryIssued(String address) {
final variables = <String, dynamic>{
'address': address,
};
final options = SubscriptionOptions(
document: gql(subscribeHistoryIssuedQ),
variables: variables,
);
return indexerClient.subscribe(options);
}
Map computeHistoryView(repository, String address) {
final bool isUdUnit = configBox.get('isUdUnit') ?? false;
late double amount;
late String finalAmount;
final DateTime date = repository[0];
final dateForm = "${date.day} ${monthsInYear[date.month]!.substring(0, {
1,
2,
7,
9
}.contains(date.month) ? 4 : 3)}";
DateTime normalizeDate(DateTime inputDate) {
return DateTime(inputDate.year, inputDate.month, inputDate.day);
}
String getDateDelimiter() {
DateTime now = DateTime.now();
final transactionDate = normalizeDate(date.toLocal());
final todayDate = normalizeDate(now);
final yesterdayDate =
normalizeDate(now.subtract(const Duration(days: 1)));
final isSameWeek = weekNumber(transactionDate) == weekNumber(now) &&
transactionDate.year == now.year;
final isTodayOrYesterday =
transactionDate == todayDate || transactionDate == yesterdayDate;
if (transactionDate == todayDate) {
return "today".tr();
} else if (transactionDate == yesterdayDate) {
return "yesterday".tr();
} else if (isSameWeek && !isTodayOrYesterday) {
return "thisWeek".tr();
} else if (!isSameWeek && !isTodayOrYesterday) {
if (transactionDate.year == now.year) {
return monthsInYear[transactionDate.month]!;
} else {
return "${monthsInYear[transactionDate.month]} ${transactionDate.year}";
}
} else {
return '';
}
}
final dateDelimiter = getDateDelimiter();
amount = repository[4] == 'RECEIVED' ? repository[3] : repository[3] * -1;
if (isUdUnit) {
amount = round(amount / balanceRatio);
finalAmount = 'ud'.tr(args: ['$amount ']);
} else {
finalAmount = '$amount $currencyName';
}
bool isMigrationTime =
startBlockchainInitialized && date.compareTo(startBlockchainTime) < 0;
return {
'finalAmount': finalAmount,
'isMigrationTime': isMigrationTime,
'dateDelimiter': dateDelimiter,
'dateForm': dateForm,
};
}
int weekNumber(DateTime date) {
int dayOfYear = int.parse(DateFormat("D").format(date));
return ((dayOfYear - date.weekday + 10) / 7).floor();
}
// checkHistoryResult(
// QueryResult<Object?> result, FetchMoreOptions options, String address) {}
}

View File

@ -1,7 +1,6 @@
import 'dart:math';
import 'package:durt/durt.dart' as durt;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:gecko/globals.dart';
@ -9,21 +8,23 @@ import 'package:gecko/models/bip39_words.dart';
import 'package:gecko/models/chest_data.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/widgets/scan_derivations_info.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:polkawallet_sdk/api/apiKeyring.dart';
import 'package:provider/provider.dart';
import "package:unorm_dart/unorm_dart.dart" as unorm;
class GenerateWalletsProvider with ChangeNotifier {
GenerateWalletsProvider();
// NewWallet generatedWallet;
durt.NewWallet? actualWallet;
final walletNameFocus = FocusNode();
FocusNode walletNameFocus = FocusNode();
Color? askedWordColor = Colors.black;
bool isAskedWordValid = false;
var scanStatus = ScanDerivationsStatus.none;
int scanedValidWalletNumber = -1;
int scanedWalletNumber = -1;
int numberScan = 60;
int numberScan = 20;
late int nbrWord;
String? nbrWordAlpha;
@ -31,31 +32,31 @@ class GenerateWalletsProvider with ChangeNotifier {
String? generatedMnemonic;
bool walletIsGenerated = true;
final mnemonicController = TextEditingController();
final pin = TextEditingController();
TextEditingController mnemonicController = TextEditingController();
TextEditingController pin = TextEditingController();
// Import wallet
final cesiumID = TextEditingController();
final cesiumPWD = TextEditingController();
final cesiumPubkey = TextEditingController();
TextEditingController cesiumID = TextEditingController();
TextEditingController cesiumPWD = TextEditingController();
TextEditingController cesiumPubkey = TextEditingController();
bool isCesiumIDVisible = false;
bool isCesiumPWDVisible = false;
bool canImport = false;
late durt.CesiumWallet cesiumWallet;
// Import Chest
final cellController0 = TextEditingController();
final cellController1 = TextEditingController();
final cellController2 = TextEditingController();
final cellController3 = TextEditingController();
final cellController4 = TextEditingController();
final cellController5 = TextEditingController();
final cellController6 = TextEditingController();
final cellController7 = TextEditingController();
final cellController8 = TextEditingController();
final cellController9 = TextEditingController();
final cellController10 = TextEditingController();
final cellController11 = TextEditingController();
TextEditingController cellController0 = TextEditingController();
TextEditingController cellController1 = TextEditingController();
TextEditingController cellController2 = TextEditingController();
TextEditingController cellController3 = TextEditingController();
TextEditingController cellController4 = TextEditingController();
TextEditingController cellController5 = TextEditingController();
TextEditingController cellController6 = TextEditingController();
TextEditingController cellController7 = TextEditingController();
TextEditingController cellController8 = TextEditingController();
TextEditingController cellController9 = TextEditingController();
TextEditingController cellController10 = TextEditingController();
TextEditingController cellController11 = TextEditingController();
bool isFirstTimeSentenceComplete = true;
Future storeHDWChest(BuildContext context) async {
@ -85,14 +86,19 @@ class GenerateWalletsProvider with ChangeNotifier {
final expectedWord = mnemo.split(' ')[nbrWord];
final normInputWord = unorm.nfkd(inputWord);
log.i("Is $expectedWord equal to input $normInputWord ?");
if (expectedWord == normInputWord ||
(kDebugMode && inputWord == 'triche')) {
inputWord == 'triche' ||
inputWord == '3.14') {
log.d('Word is OK');
isAskedWordValid = true;
askedWordColor = Colors.green[600];
// walletNameFocus.nextFocus();
notifyListeners();
} else {
isAskedWordValid = false;
}
// notifyListeners();
}
String removeDiacritics(String str) {
@ -145,12 +151,84 @@ class GenerateWalletsProvider with ChangeNotifier {
return pin.text;
}
Future<Uint8List> printWallet(AsyncSnapshot<List>? mnemoList) async {
final ByteData fontData =
await rootBundle.load("assets/OpenSans-Regular.ttf");
final pw.Font ttf = pw.Font.ttf(fontData.buffer.asByteData());
final pdf = pw.Document();
// const imageProvider = AssetImage('assets/icon/gecko_final.png');
// final geckoLogo = await flutterImageProvider(imageProvider);
pw.Widget arrayCell(dataWord) {
return pw.SizedBox(
width: 120,
child: pw.Column(children: <pw.Widget>[
pw.Text(
dataWord.split(':')[0],
style: pw.TextStyle(
fontSize: 15, color: const PdfColor(0.5, 0, 0), font: ttf),
),
pw.Text(
dataWord.split(':')[1],
style: pw.TextStyle(
fontSize: 20, color: const PdfColor(0, 0, 0), font: ttf),
),
pw.SizedBox(height: 10)
]),
);
}
pdf.addPage(
pw.Page(
pageFormat: PdfPageFormat.a4,
build: (context) {
return pw.Column(
// mainAxisAlignment: pw.MainAxisAlignment.center,
// mainAxisSize: pw.MainAxisSize.max,
// crossAxisAlignment: pw.CrossAxisAlignment.center,
children: <pw.Widget>[
pw.Row(children: <pw.Widget>[
arrayCell(mnemoList!.data![0]),
arrayCell(mnemoList.data![1]),
arrayCell(mnemoList.data![2]),
arrayCell(mnemoList.data![3]),
]),
pw.Row(children: <pw.Widget>[
arrayCell(mnemoList.data![4]),
arrayCell(mnemoList.data![5]),
arrayCell(mnemoList.data![6]),
arrayCell(mnemoList.data![7]),
]),
pw.Row(children: <pw.Widget>[
arrayCell(mnemoList.data![8]),
arrayCell(mnemoList.data![9]),
arrayCell(mnemoList.data![10]),
arrayCell(mnemoList.data![11])
]),
pw.Expanded(
child: pw.Align(
alignment: pw.Alignment.bottomCenter,
child: pw.Text(
"Gardez cette feuille préciseusement, à labri des lézards indiscrets.",
style: pw.TextStyle(fontSize: 15, font: ttf),
)))
],
);
},
),
);
return pdf.save();
}
Future<void> generateCesiumWalletPubkey(
String cesiumID, String cesiumPWD) async {
cesiumWallet = durt.CesiumWallet(cesiumID, cesiumPWD);
String walletPubkey = cesiumWallet.pubkey;
cesiumPubkey.text = walletPubkey;
log.d(walletPubkey);
}
void cesiumIDisVisible() {
@ -166,11 +244,12 @@ class GenerateWalletsProvider with ChangeNotifier {
void resetCesiumImportView() {
cesiumID.text = cesiumPWD.text = cesiumPubkey.text = pin.text = '';
canImport = isCesiumIDVisible = isCesiumPWDVisible = false;
actualWallet = null;
notifyListeners();
}
Future<List<String>> generateWordList(BuildContext context) async {
final sub = Provider.of<SubstrateSdk>(context, listen: false);
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
generatedMnemonic = await sub.generateMnemonic(lang: appLang);
List<String> wordsList = [];
@ -197,7 +276,7 @@ class GenerateWalletsProvider with ChangeNotifier {
if (bip39Words(appLang).contains(word.toLowerCase())) {
for (var bipWord in bip39Words(appLang)) {
if (bipWord.startsWith(word)) {
isValid = nbrMatch == 0;
isValid = nbrMatch == 0 ? true : false;
if (checkRedondance) nbrMatch = nbrMatch + 1;
}
}
@ -290,70 +369,67 @@ class GenerateWalletsProvider with ChangeNotifier {
}
Future<bool> scanDerivations(BuildContext context) async {
final sub = Provider.of<SubstrateSdk>(context, listen: false);
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
final currentChestNumber = configBox.get('currentChest');
bool isAlive = false;
scanedValidWalletNumber = 0;
scanedWalletNumber = 0;
Map<String, int> addressToScan = {};
notifyListeners();
if (!sub.nodeConnected) {
return false;
}
scanStatus = ScanDerivationsStatus.rootScanning;
final hasRoot = await scanRootBalance(sub, currentChestNumber);
scanedWalletNumber = 1;
notifyListeners();
if (hasRoot) {
scanedValidWalletNumber = 1;
isAlive = true;
}
scanStatus = ScanDerivationsStatus.scanning;
for (int derivationNbr in [for (var i = 0; i < numberScan; i += 1) i]) {
for (var derivationNbr in [for (var i = 0; i < numberScan; i += 1) i]) {
final addressData = await sub.sdk.api.keyring.addressFromMnemonic(
sub.currencyParameters['ss58']!,
cryptoType: CryptoType.sr25519,
mnemonic: generatedMnemonic!,
derivePath: '//$derivationNbr');
addressToScan.putIfAbsent(addressData.address!, () => derivationNbr);
}
final balanceList =
await sub.getBalanceMulti(addressToScan.keys.toList()).timeout(
const Duration(seconds: 20),
onTimeout: () => {},
);
final Map balance = await sub.getBalance(addressData.address!).timeout(
const Duration(seconds: 1),
onTimeout: () => {'transferableBalance': 0},
);
// const balance = 0;
// Remove unused wallets
balanceList.removeWhere((key, value) => value['transferableBalance'] == 0);
scanedValidWalletNumber = balanceList.length + scanedWalletNumber;
log.d(
"${addressData.address!}: ${balance['transferableBalance']} $currencyName");
if (balance['transferableBalance'] != 0) {
isAlive = true;
String walletName = scanedValidWalletNumber == 0
? 'currentWallet'.tr()
: '${'wallet'.tr()} ${scanedValidWalletNumber + 1}';
await sub.importAccount(
mnemonic: generatedMnemonic!,
derivePath: '//$derivationNbr',
password: pin.text);
scanStatus = ScanDerivationsStatus.import;
for (String scannedWallet in balanceList.keys) {
isAlive = true;
String walletName = scanedWalletNumber == 0
? 'currentWallet'.tr()
: '${'wallet'.tr()} ${scanedWalletNumber + 1}';
await sub.importAccount(
mnemonic: generatedMnemonic!,
derivePath: "//${addressToScan[scannedWallet]}",
password: pin.text);
WalletData myWallet = WalletData(
chest: currentChestNumber,
address: scannedWallet,
number: scanedWalletNumber,
name: walletName,
derivation: addressToScan[scannedWallet],
imageDefaultPath: '${scanedWalletNumber % 4}.png',
isOwned: true);
await walletBox.put(myWallet.address, myWallet);
scanedWalletNumber++;
WalletData myWallet = WalletData(
version: dataVersion,
chest: currentChestNumber,
address: addressData.address!,
number: scanedValidWalletNumber,
name: walletName,
derivation: derivationNbr,
imageDefaultPath: '${scanedValidWalletNumber % 4}.png');
await walletBox.add(myWallet);
scanedValidWalletNumber = scanedValidWalletNumber + 1;
}
scanedWalletNumber = scanedWalletNumber + 1;
notifyListeners();
}
scanStatus = ScanDerivationsStatus.none;
scanedWalletNumber = scanedValidWalletNumber = -1;
log.d(scanedWalletNumber);
scanedWalletNumber = -1;
scanedValidWalletNumber = -1;
notifyListeners();
return isAlive;
}
@ -369,20 +445,21 @@ class GenerateWalletsProvider with ChangeNotifier {
onTimeout: () => {},
);
log.d(
"${addressData.address!}: ${balance['transferableBalance']} $currencyName");
if (balance['transferableBalance'] != 0) {
String walletName = 'myRootWallet'.tr();
await sub.importAccount(mnemonic: generatedMnemonic!, password: pin.text);
WalletData myWallet = WalletData(
version: dataVersion,
chest: currentChestNumber,
address: addressData.address!,
number: 0,
name: walletName,
derivation: -1,
imageDefaultPath: '0.png',
isOwned: true);
await walletBox.put(myWallet.address, myWallet);
scanedWalletNumber++;
imageDefaultPath: '0.png');
await walletBox.add(myWallet);
return true;
} else {
return false;

View File

@ -1,13 +1,24 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:convert';
import 'dart:io';
import 'dart:math';
// import 'package:audioplayers/audio_cache.dart';
// import 'package:audioplayers/audioplayers.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:async';
import 'package:gecko/globals.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/providers/wallet_options.dart';
import 'package:gecko/providers/wallets_profiles.dart';
import 'package:gecko/screens/myWallets/unlocking_wallet.dart';
import 'package:gecko/screens/myWallets/wallets_home.dart';
import 'package:gecko/screens/search.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:flutter/foundation.dart' show kDebugMode, kIsWeb;
import 'package:path_provider/path_provider.dart' as pp;
@ -18,11 +29,10 @@ import 'package:provider/provider.dart';
class HomeProvider with ChangeNotifier {
bool? isSearching;
Icon searchIcon = const Icon(Icons.search);
final searchQuery = TextEditingController();
final TextEditingController searchQuery = TextEditingController();
Widget appBarTitle = Text('Ğecko', style: TextStyle(color: Colors.grey[850]));
String homeMessage = "loading".tr();
String defaultMessage = "noLizard".tr();
bool isWalletBoxInit = false;
Future<void> initHive() async {
late Directory hivePath;
@ -48,21 +58,18 @@ class HomeProvider with ChangeNotifier {
// Init app folders
final documentDir = await getApplicationDocumentsDirectory();
avatarsDirectory = Directory('${documentDir.path}/avatars');
avatarsCacheDirectory = Directory('${documentDir.path}/avatarsCache');
imageDirectory = Directory('${documentDir.path}/images');
if (!await avatarsDirectory.exists()) {
await avatarsDirectory.create();
if (!await imageDirectory.exists()) {
await imageDirectory.create();
}
}
Future changeCurrencyUnit(BuildContext context) async {
final sub = Provider.of<SubstrateSdk>(context, listen: false);
final walletOptions =
Provider.of<WalletOptionsProvider>(context, listen: false);
final bool isUdUnit = configBox.get('isUdUnit') ?? false;
await configBox.put('isUdUnit', !isUdUnit);
walletOptions.balanceCache = {};
balanceCache = {};
sub.getBalanceRatio();
notifyListeners();
}
@ -105,6 +112,7 @@ class HomeProvider with ChangeNotifier {
configBox.put('endpoint', listEndpoints);
}
log.i('ENDPOINT: $listEndpoints');
return listEndpoints;
}
@ -114,11 +122,114 @@ class HomeProvider with ChangeNotifier {
return list[i];
}
void handleSearchStart() {
isSearching = true;
notifyListeners();
}
// void playSound(String customSound, double volume) async {
// await player.play('$customSound.wav',
// volume: volume, mode: PlayerMode.LOW_LATENCY, stayAwake: false);
// }
Widget bottomAppBar(BuildContext context) {
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
WalletsProfilesProvider historyProvider =
Provider.of<WalletsProfilesProvider>(context, listen: false);
final size = MediaQuery.of(context).size;
const bool showBottomBar = true;
return Visibility(
visible: showBottomBar,
child: Container(
color: yellowC,
width: size.width,
height: 80,
child:
// Stack(
// children: [
// // CustomPaint(
// // size: Size(size.width, 110),
// // painter: CustomRoundedButton(),
// // ),
Row(mainAxisAlignment: MainAxisAlignment.start, children: [
// SizedBox(width: 0),
const Spacer(),
const SizedBox(width: 11),
IconButton(
key: keyAppBarSearch,
iconSize: 40,
icon: const Image(image: AssetImage('assets/loupe-noire.png')),
onPressed: () {
Navigator.popUntil(
context,
ModalRoute.withName('/'),
);
Navigator.push(
context,
MaterialPageRoute(builder: (homeContext) {
return const SearchScreen();
}),
);
},
),
const SizedBox(width: 22),
const Spacer(),
IconButton(
key: keyAppBarQrcode,
iconSize: 70,
icon: const Image(image: AssetImage('assets/qrcode-scan.png')),
onPressed: () async {
Navigator.popUntil(
context,
ModalRoute.withName('/'),
);
historyProvider.scan(homeContext);
},
),
const Spacer(),
const SizedBox(width: 15),
IconButton(
key: keyAppBarChest,
iconSize: 60,
icon: const Image(image: AssetImage('assets/wallet.png')),
onPressed: () async {
WalletData? defaultWallet = myWalletProvider.getDefaultWallet();
String? pin;
if (myWalletProvider.pinCode == '') {
pin = await Navigator.push(
context,
MaterialPageRoute(
builder: (homeContext) {
return UnlockingWallet(wallet: defaultWallet);
},
),
);
}
if (pin != null || myWalletProvider.pinCode != '') {
Navigator.popUntil(
context,
ModalRoute.withName('/'),
);
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return const WalletsHome();
}),
);
}
},
),
const Spacer(),
]),
),
);
}
void reload() {
notifyListeners();
}

View File

@ -1,14 +1,10 @@
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:gecko/globals.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/screens/myWallets/unlocking_wallet.dart';
import 'package:gecko/widgets/commons/common_elements.dart';
import 'package:path_provider/path_provider.dart';
import 'package:gecko/screens/common_elements.dart';
import 'package:provider/provider.dart';
class MyWalletsProvider with ChangeNotifier {
@ -17,10 +13,8 @@ class MyWalletsProvider with ChangeNotifier {
late String mnemonic;
int? pinLenght;
bool isNewDerivationLoading = false;
WalletData? lastFlyBy;
WalletData? dragAddress;
bool isPinValid = false;
bool isPinLoading = true;
String lastFlyBy = '';
String dragAddress = '';
int getCurrentChest() {
if (configBox.get('currentChest') == null) {
@ -30,39 +24,29 @@ class MyWalletsProvider with ChangeNotifier {
return configBox.get('currentChest');
}
bool isWalletsExists() => chestBox.isNotEmpty;
bool checkIfWalletExist() {
if (chestBox.isEmpty) {
log.i('No wallets detected');
return false;
} else {
return true;
}
}
Future<List<WalletData>> readAllWallets([int? chest]) async {
final sub = Provider.of<SubstrateSdk>(homeContext, listen: false);
chest = chest ?? getCurrentChest();
List<WalletData> readAllWallets([int? chest]) {
chest = chest ?? configBox.get('currentChest') ?? 0;
listWallets.clear();
final wallets = walletBox.toMap().values.toList();
Map<String, WalletData> walletsToScan = {};
for (var walletFromBox in wallets) {
if (walletFromBox.chest != chest) {
continue;
walletBox.toMap().forEach((key, value) {
if (value.chest == chest) {
listWallets.add(value);
}
if (walletFromBox.identityStatus == IdtyStatus.unknown) {
walletsToScan.putIfAbsent(walletFromBox.address, (() => walletFromBox));
} else {
listWallets.add(walletFromBox);
}
}
});
// update all idty status in lists
int n = 0;
final idtyStatusList = await sub.idtyStatus(walletsToScan.keys.toList());
for (final wallet in walletsToScan.values) {
wallet.identityStatus = idtyStatusList[n];
walletBox.put(wallet.address, wallet);
listWallets.add(wallet);
n++;
}
return listWallets;
}
WalletData? getWalletDataById(List<int?> id) {
if (id.isEmpty) return WalletData(address: '', isOwned: true);
if (id.isEmpty) return WalletData();
int? chest = id[0];
int? nbr = id[1];
WalletData? targetedWallet;
@ -77,20 +61,6 @@ class MyWalletsProvider with ChangeNotifier {
return targetedWallet;
}
Future<bool> askPinCode() async {
final defaultWallet = getDefaultWallet();
if (pinCode.isEmpty) {
await Navigator.push(
homeContext,
MaterialPageRoute(
builder: (homeContext) => UnlockingWallet(wallet: defaultWallet),
),
);
}
return pinCode.isNotEmpty;
}
WalletData? getWalletDataByAddress(String address) {
WalletData? targetedWallet;
@ -106,18 +76,18 @@ class MyWalletsProvider with ChangeNotifier {
WalletData getDefaultWallet([int? chest]) {
if (chestBox.isEmpty) {
return WalletData(address: '', chest: 0, number: 0, isOwned: true);
return WalletData(chest: 0, number: 0);
} else {
chest ??= getCurrentChest();
int? defaultWalletNumber = chestBox.get(chest)!.defaultWallet;
return getWalletDataById([chest, defaultWalletNumber]) ??
WalletData(address: '', chest: chest, number: 0, isOwned: true);
WalletData(chest: chest, number: 0);
}
}
Future<int> deleteAllWallet(context) async {
final sub = Provider.of<SubstrateSdk>(context, listen: false);
final myWalletProvider =
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
try {
log.w('DELETE ALL WALLETS ?');
@ -130,17 +100,12 @@ class MyWalletsProvider with ChangeNotifier {
await configBox.delete('defaultWallet');
await sub.deleteAllAccounts();
final directory = await getApplicationDocumentsDirectory();
final avatarFolder = Directory('${directory.path}/avatars/');
if (await avatarFolder.exists()) {
await avatarFolder.delete(recursive: true);
await avatarFolder.create();
}
myWalletProvider.pinCode = '';
await Navigator.of(context)
.pushNamedAndRemoveUntil('/', (Route<dynamic> route) => false);
await Navigator.of(context).pushNamedAndRemoveUntil(
'/',
ModalRoute.withName('/'),
);
}
return 0;
} catch (e) {
@ -153,37 +118,36 @@ class MyWalletsProvider with ChangeNotifier {
isNewDerivationLoading = true;
notifyListeners();
final List idList = await getNextWalletNumberAndDerivation();
final List idList = getNextWalletNumberAndDerivation();
int newWalletNbr = idList[0];
int newDerivationNbr = number ?? idList[1];
int? chest = getCurrentChest();
final sub = Provider.of<SubstrateSdk>(context, listen: false);
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
WalletData defaultWallet = getDefaultWallet();
final address = await sub.derive(
context, defaultWallet.address, newDerivationNbr, pinCode);
context, defaultWallet.address!, newDerivationNbr, pinCode);
WalletData newWallet = WalletData(
version: dataVersion,
chest: chest,
address: address,
number: newWalletNbr,
name: name,
derivation: newDerivationNbr,
imageDefaultPath: '${newWalletNbr % 4}.png',
isOwned: true);
imageDefaultPath: '${newWalletNbr % 4}.png');
await walletBox.put(newWallet.address, newWallet);
await readAllWallets();
await walletBox.add(newWallet);
isNewDerivationLoading = false;
notifyListeners();
}
Future<void> generateRootWallet(context, String name) async {
final myWalletProvider =
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
isNewDerivationLoading = true;
@ -191,54 +155,48 @@ class MyWalletsProvider with ChangeNotifier {
int newWalletNbr;
int? chest = getCurrentChest();
List<WalletData> walletConfig = await readAllWallets(chest);
walletConfig.sort((p1, p2) {
return Comparable.compare(p1.number!, p2.number!);
});
List<WalletData> walletConfig = readAllWallets(chest);
if (walletConfig.isEmpty) {
newWalletNbr = 0;
} else {
newWalletNbr = walletConfig.last.number! + 1;
}
final sub = Provider.of<SubstrateSdk>(context, listen: false);
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
WalletData defaultWallet = myWalletProvider.getDefaultWallet();
final address =
await sub.generateRootKeypair(defaultWallet.address, pinCode);
await sub.generateRootKeypair(defaultWallet.address!, pinCode);
WalletData newWallet = WalletData(
version: dataVersion,
chest: chest,
address: address,
number: newWalletNbr,
name: name,
derivation: -1,
imageDefaultPath: '${newWalletNbr % 4}.png',
isOwned: true);
imageDefaultPath: '${newWalletNbr % 4}.png');
await walletBox.put(newWallet.address, newWallet);
await readAllWallets();
await walletBox.add(newWallet);
isNewDerivationLoading = false;
notifyListeners();
}
Future<List<int>> getNextWalletNumberAndDerivation(
{int? chestNumber, bool isOneshoot = false}) async {
List<int> getNextWalletNumberAndDerivation(
{int? chestNumber, bool isOneshoot = false}) {
int newDerivationNbr = 0;
int newWalletNbr = 0;
chestNumber ??= getCurrentChest();
listWallets.sort((p1, p2) {
return Comparable.compare(p1.number!, p2.number!);
});
List<WalletData> walletConfig = readAllWallets(chestNumber);
if (listWallets.isEmpty) {
if (walletConfig.isEmpty) {
newDerivationNbr = 2;
} else {
final lastWallet = listWallets.reduce(
WalletData lastWallet = walletConfig.reduce(
(curr, next) => curr.derivation! > next.derivation! ? curr : next);
if (lastWallet.derivation == -1) {
@ -247,14 +205,14 @@ class MyWalletsProvider with ChangeNotifier {
newDerivationNbr = lastWallet.derivation! + (isOneshoot ? 1 : 2);
}
newWalletNbr = listWallets.last.number! + 1;
newWalletNbr = walletConfig.last.number! + 1;
}
return [newWalletNbr, newDerivationNbr];
}
int lockPin = 0;
Future debounceResetPinCode([int minutes = 15]) async {
Future resetPinCode([int minutes = 15]) async {
lockPin++;
final actualLock = lockPin;
await Future.delayed(

View File

@ -3,16 +3,75 @@ import 'package:gecko/models/g1_wallets_list.dart';
import 'package:gecko/providers/wallets_profiles.dart';
class SearchProvider with ChangeNotifier {
final searchController = TextEditingController();
TextEditingController searchController = TextEditingController();
List searchResult = [];
int resultLenght = 0;
final cacheDuring = 20 * 60 * 1000; //First number is minutes
int cacheTime = 0;
void reload() {
notifyListeners();
}
// Future<List> searchBlockchain() async {
// searchResult.clear();
// int searchTime = DateTime.now().millisecondsSinceEpoch;
// WalletsProfilesProvider _walletProfiles = WalletsProfilesProvider('pubkey');
// if (cacheTime + cacheDuring <= searchTime) {
// g1WalletsBox.clear();
// // final url = Uri.parse('https://g1-stats.axiom-team.fr/data/forbes.json');
// // final response = await http.get(url);
// var dio = Dio();
// late Response response;
// try {
// response = await dio.get(
// 'https://g1-stats.axiom-team.fr/data/forbes.json',
// options: Options(
// sendTimeout: 5000,
// receiveTimeout: 10000,
// ),
// );
// // response = await http.post((Uri.parse(queryOptions[0])),
// // body: queryOptions[1], headers: queryOptions[2]);
// } catch (e) {
// log.e(e);
// }
// List<G1WalletsList> _listWallets = _parseG1Wallets(response.data)!;
// Map<String?, G1WalletsList> _mapWallets = {
// for (var e in _listWallets) e.pubkey: e
// };
// await g1WalletsBox.putAll(_mapWallets);
// cacheTime = DateTime.now().millisecondsSinceEpoch;
// }
// g1WalletsBox.toMap().forEach((key, value) {
// if ((value.id != null &&
// value.id!.username != null &&
// value.id!.username!
// .toLowerCase()
// .contains(searchController.text)) ||
// value.pubkey!.contains(searchController.text)) {
// searchResult.add(value);
// return;
// }
// });
// if (searchResult.isEmpty &&
// _walletProfiles.isPubkey(searchController.text)) {
// searchResult = [G1WalletsList(pubkey: searchController.text)];
// }
// return searchResult;
// }
Future<List<G1WalletsList>> searchAddress() async {
if (await isAddress(searchController.text)) {
final WalletsProfilesProvider walletProfiles =
WalletsProfilesProvider('pubkey');
if (walletProfiles.isAddress(searchController.text)) {
G1WalletsList wallet = G1WalletsList(address: searchController.text);
return [wallet];
} else {
@ -20,3 +79,11 @@ class SearchProvider with ChangeNotifier {
}
}
}
// List<G1WalletsList>? _parseG1Wallets(var responseBody) {
// final parsed = responseBody.cast<Map<String, dynamic>>();
// return parsed
// .map<G1WalletsList>((json) => G1WalletsList.fromJson(json))
// .toList();
// }

File diff suppressed because it is too large Load Diff

View File

@ -1,223 +0,0 @@
import 'dart:convert';
import 'dart:io';
import 'package:crypto/crypto.dart';
import 'package:flutter/material.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/queries_datapod.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:provider/provider.dart';
import 'package:uuid/uuid.dart';
class V2sDatapodProvider with ChangeNotifier {
late GraphQLClient datapodClient;
Future<Map<String, dynamic>> _setSignedVariables(
String address, Map<String, dynamic> messageToSign) async {
final myWalletProvider =
Provider.of<MyWalletsProvider>(homeContext, listen: false);
final sub = Provider.of<SubstrateSdk>(homeContext, listen: false);
final hashDocBytes = utf8.encode(jsonEncode(messageToSign));
final hashDoc = sha256.convert(hashDocBytes).toString().toUpperCase();
if (!await myWalletProvider.askPinCode()) return {};
final signature = await sub.signDatapod(hashDoc, address);
return <String, dynamic>{
...messageToSign,
'hash': hashDoc,
'signature': signature
};
}
Future<QueryResult?> _execQuery(
String query, Map<String, dynamic> variables) async {
//TODO: Switch to IPFS Datapod
return null;
// final QueryOptions options =
// QueryOptions(document: gql(query), variables: variables);
// return await datapodClient.query(options);
}
Future<bool> updateProfile(
{required String address,
String? title,
String? description,
String? avatar,
String? city,
List<Map<String, String>>? socials,
Map<String, double>? geoloc}) async {
final messageToSign = {
'address': address,
'description': description,
'avatarBase64': avatar,
'geoloc': geoloc,
'title': title,
'city': city,
'socials': socials
};
final variables = await _setSignedVariables(address, messageToSign);
if (variables.isEmpty) return false;
final result = await _execQuery(updateProfileQ, variables);
if (result?.hasException ?? true) {
log.e(result?.exception.toString());
return false;
}
log.d(result!.data!['updateProfile']['message']);
return true;
}
Future<bool> deleteProfile({required String address}) async {
final messageToSign = {'address': address};
final variables = await _setSignedVariables(address, messageToSign);
if (variables.isEmpty) return false;
final result = await _execQuery(deleteProfileQ, variables);
if (result?.hasException ?? true) {
log.e(result?.exception.toString());
return false;
}
log.d(result!.data!['deleteProfile']['message']);
return true;
}
Future<bool> migrateProfile(
{required String addressOld, required String addressNew}) async {
final messageToSign = {'addressOld': addressOld, 'addressNew': addressNew};
final variables = await _setSignedVariables(addressOld, messageToSign);
if (variables.isEmpty) return false;
final result = await _execQuery(migrateProfileQ, variables);
if (result?.hasException ?? true) {
log.e(result?.exception.toString());
return false;
}
log.d(result!.data!['migrateProfile']['message']);
return true;
}
Future<bool> addTransactionComment({
required String id,
required String issuer,
required String comment,
}) async {
final messageToSign = {
'id': id,
'address': issuer,
'comment': comment,
};
final variables = await _setSignedVariables(issuer, messageToSign);
if (variables.isEmpty) return false;
final result = await _execQuery(addTransactionCommentQ, variables);
if (result?.hasException ?? true) {
log.e(result?.exception.toString());
return false;
}
log.d(result!.data!['addTransaction']['message']);
return true;
}
Future<bool> setAvatar(String address, String avatarPath) async {
final avatarBytes = await File(avatarPath).readAsBytes();
final avatarString = base64Encode(avatarBytes);
return await updateProfile(address: address, avatar: avatarString);
}
Future<DateTime?> profileEditedAt(String address) async {
final variables = <String, dynamic>{
'address': address,
};
final result = await _execQuery(profileEditedAtQ, variables);
if (result?.hasException ?? true) {
// log.e(result?.exception.toString());
return null;
}
final String? profileDateData =
result!.data!['profiles_by_pk']?['updated_at'];
final profileDate =
profileDateData == null ? null : DateTime.tryParse(profileDateData);
return profileDate;
}
Future<Image> getRemoteAvatar(String address,
{double size = 20, String? uuid}) async {
final variables = <String, dynamic>{
'address': address,
};
final result = await _execQuery(getAvatarQ, variables);
if (result?.hasException ?? true) {
log.e(result?.exception.toString());
return defaultAvatar(size);
}
final String? avatar64 = result!.data!['profiles_by_pk']?['avatar64'];
if (avatar64 == null) {
return defaultAvatar(size);
}
final sanitizedAvatar64 =
avatar64.replaceAll('\n', '').replaceAll('\r', '').replaceAll(' ', '');
log.d('We save avatar for $address');
await saveAvatar(address, sanitizedAvatar64, uuid);
return Image.memory(
base64.decode(sanitizedAvatar64),
height: size,
fit: BoxFit.fitWidth,
);
}
Future<File> saveAvatar(String address, String data, String? uuid) async {
uuid = uuid ?? const Uuid().v4();
final file = File('${avatarsDirectory.path}/$address-$uuid');
return await file.writeAsBytes(base64.decode(data));
}
Future<File> cacheAvatar(String address, String data) async {
final uuid = const Uuid().v4();
final tempFile = File('${avatarsCacheDirectory.path}/$uuid$address');
final targetFile = File('${avatarsCacheDirectory.path}/$address');
try {
// Write to a temporary file first to prevent data race
await tempFile.writeAsBytes(base64.decode(data));
log.d('Caching avatar of $address');
return await tempFile.rename(targetFile.path);
} catch (e) {
log.e("An error occurred while caching avatar: $e");
rethrow;
}
}
Image getAvatarLocal(String address) {
final avatarFile = File('${avatarsCacheDirectory.path}/$address');
return Image.file(
avatarFile,
fit: BoxFit.cover,
);
}
Image defaultAvatar(double size) =>
Image.asset(('assets/icon_user.png'), height: scaleSize(size));
Future deleteAvatarsCacheDirectory() async {
if (await avatarsCacheDirectory.exists()) {
await avatarsCacheDirectory.delete(recursive: true);
}
}
Future deleteAvatarsDirectory() async {
if (await avatarsDirectory.exists()) {
await avatarsDirectory.delete(recursive: true);
}
}
reload() {
notifyListeners();
}
}

View File

@ -11,26 +11,29 @@ import 'package:gecko/providers/duniter_indexer.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/providers/v2s_datapod.dart';
import 'package:gecko/widgets/commons/common_elements.dart';
import 'package:gecko/screens/animated_text.dart';
import 'package:gecko/screens/common_elements.dart';
import 'package:gecko/screens/myWallets/unlocking_wallet.dart';
import 'package:gecko/screens/transaction_in_progress.dart';
import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:uuid/uuid.dart';
import 'package:truncate/truncate.dart';
class WalletOptionsProvider with ChangeNotifier {
final address = TextEditingController();
final _newWalletName = TextEditingController();
TextEditingController address = TextEditingController();
final TextEditingController _newWalletName = TextEditingController();
bool isWalletUnlock = false;
bool ischangedPin = false;
final newPin = TextEditingController();
TextEditingController newPin = TextEditingController();
bool isEditing = false;
bool isBalanceBlur = false;
final nameController = TextEditingController();
FocusNode walletNameFocus = FocusNode();
TextEditingController nameController = TextEditingController();
late bool isDefaultWallet;
bool canValidateNameBool = false;
Map<String, double> balanceCache = {};
Map<String, String> idtyStatusCache = {};
Future<NewWallet>? get badWallet => null;
int getPinLenght(walletNbr) {
return pinLength;
@ -48,34 +51,27 @@ class WalletOptionsProvider with ChangeNotifier {
}
Future<int> deleteWallet(context, WalletData wallet) async {
final sub = Provider.of<SubstrateSdk>(context, listen: false);
final datapod = Provider.of<V2sDatapodProvider>(context, listen: false);
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
final bool? answer = await (confirmPopup(
context, 'areYouSureToForgetWallet'.tr(args: [wallet.name!])));
if (answer ?? false) {
//Check if balance is null
if (balanceCache[wallet.address] != 0) {
final myWalletProvider =
final balance = await sub.getBalance(wallet.address!);
if (balance != {}) {
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
final defaultWallet = myWalletProvider.getDefaultWallet();
log.d(defaultWallet.address);
sub.pay(
fromAddress: wallet.address,
destAddress: defaultWallet.address,
fromAddress: wallet.address!,
destAddress: defaultWallet.address!,
amount: -1,
password: myWalletProvider.pinCode);
}
await walletBox.delete(wallet.address);
if (wallet.imageCustomPath != null) {
final avatarFile = File(wallet.imageCustomPath!);
if (await avatarFile.exists()) {
await avatarFile.delete();
}
}
datapod.deleteProfile(address: wallet.address);
await sub.deleteAccounts([wallet.address]);
await walletBox.delete(wallet.key);
await sub.deleteAccounts([wallet.address!]);
Navigator.pop(context);
}
@ -88,15 +84,14 @@ class WalletOptionsProvider with ChangeNotifier {
}
Future<String> changeAvatar() async {
final datapod = Provider.of<V2sDatapodProvider>(homeContext, listen: false);
// File _image;
final picker = ImagePicker();
XFile? pickedFile = await picker.pickImage(source: ImageSource.gallery);
if (pickedFile != null) {
File imageFile = File(pickedFile.path);
if (!await avatarsDirectory.exists()) {
if (!await imageDirectory.exists()) {
log.e("Image folder doesn't exist");
return '';
}
@ -119,31 +114,17 @@ class WalletOptionsProvider with ChangeNotifier {
],
);
final avatarUuid = const Uuid().v4();
final newPath = "${avatarsDirectory.path}/${address.text}-$avatarUuid";
final newPath = "${imageDirectory.path}/${pickedFile.name}";
if (croppedFile == null) {
if (croppedFile != null) {
await File(croppedFile.path).rename(newPath);
} else {
log.w('No image selected.');
return '';
}
// await imageFile.copy(newPath);
await File(croppedFile.path).rename(newPath);
final walletData =
MyWalletsProvider().getWalletDataByAddress(address.text);
if (walletData!.imageCustomPath != null) {
final avatarFile = File(walletData.imageCustomPath!);
await avatarFile.delete();
}
walletData.profileUpdatedTime = DateTime.now();
walletData.imageCustomPath = newPath;
await walletBox.put(address.text, walletData);
notifyListeners();
datapod.setAvatar(address.text, newPath);
log.i(newPath);
return newPath;
} else {
log.w('No image selected.');
@ -151,27 +132,107 @@ class WalletOptionsProvider with ChangeNotifier {
}
}
Future<String?> confirmIdentityPopup(BuildContext context) async {
final idtyName = TextEditingController();
final sub = Provider.of<SubstrateSdk>(context, listen: false);
final walletOptions =
Provider.of<WalletOptionsProvider>(context, listen: false);
final myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
final duniterIndexer = Provider.of<DuniterIndexer>(context, listen: false);
Widget idtyStatus(BuildContext context, String address,
{bool isOwner = false, Color color = Colors.black}) {
DuniterIndexer duniterIndexer =
Provider.of<DuniterIndexer>(context, listen: false);
bool canValidate = false;
bool idtyExist = false;
showText(String text,
[double size = 18, bool bold = false, bool smooth = true]) {
log.d('$address $text');
return AnimatedFadeOutIn<String>(
data: text,
duration: Duration(milliseconds: smooth ? 200 : 0),
builder: (value) => Text(
value,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: size,
color: bold ? color : Colors.black,
fontWeight: bold ? FontWeight.w500 : FontWeight.w400),
),
);
}
return Consumer<SubstrateSdk>(builder: (context, sub, _) {
return FutureBuilder(
future: sub.idtyStatus(address),
initialData: '',
builder: (context, snapshot) {
idtyStatusCache[address] = snapshot.data.toString();
switch (snapshot.data.toString()) {
case 'noid':
{
return showText('noIdentity'.tr());
}
case 'Created':
{
return showText('identityCreated'.tr());
}
case 'ConfirmedByOwner':
{
return isOwner
? showText('identityConfirmed'.tr())
: duniterIndexer.getNameByAddress(
context,
address,
null,
20,
true,
Colors.grey[700]!,
FontWeight.w500,
FontStyle.italic);
}
case 'Validated':
{
return isOwner
? showText('memberValidated'.tr(), 18, true)
: duniterIndexer.getNameByAddress(
context,
address,
null,
20,
true,
Colors.black,
FontWeight.w600,
FontStyle.normal);
}
case 'expired':
{
return showText('identityExpired'.tr());
}
}
return SizedBox(
child: showText('', 18, false, false),
);
});
});
}
Future<bool> isMember(BuildContext context, String address) async {
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
return await sub.idtyStatus(address) == 'Validated';
}
Future<String?> confirmIdentityPopup(BuildContext context) async {
TextEditingController idtyName = TextEditingController();
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
WalletOptionsProvider walletOptions =
Provider.of<WalletOptionsProvider>(context, listen: false);
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
return showDialog<String>(
context: context,
barrierDismissible: true,
barrierDismissible: true, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: Text(
'confirmYourIdentity'.tr(),
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500),
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
),
content: SizedBox(
height: 100,
@ -179,15 +240,7 @@ class WalletOptionsProvider with ChangeNotifier {
const SizedBox(height: 20),
TextField(
key: keyEnterIdentityUsername,
onChanged: (_) async {
idtyExist = await duniterIndexer.isIdtyExist(idtyName.text);
canValidate = !idtyExist &&
!await duniterIndexer.isIdtyExist(idtyName.text) &&
idtyName.text.length >= 2 &&
idtyName.text.length <= 32;
notifyListeners();
},
onChanged: (_) => notifyListeners(),
inputFormatters: <TextInputFormatter>[
// FilteringTextInputFormatter.allow(RegExp("[0-9a-zA-Z]")),
FilteringTextInputFormatter.deny(RegExp(r'^ ')),
@ -196,13 +249,8 @@ class WalletOptionsProvider with ChangeNotifier {
textAlign: TextAlign.center,
autofocus: true,
controller: idtyName,
style: const TextStyle(fontSize: 17),
),
const SizedBox(height: 10),
Consumer<WalletOptionsProvider>(builder: (context, wOptions, _) {
return Text(idtyExist ? "thisIdentityAlreadyExist".tr() : '',
style: TextStyle(color: Colors.red[500]));
})
style: const TextStyle(fontSize: 19),
)
]),
),
actions: <Widget>[
@ -213,53 +261,61 @@ class WalletOptionsProvider with ChangeNotifier {
builder: (context, wOptions, _) {
return TextButton(
key: keyConfirm,
onPressed: canValidate
? () async {
idtyName.text =
idtyName.text.trim().replaceAll(' ', '');
if (idtyName.text.length.clamp(3, 32) !=
idtyName.text.length) {
return;
}
if (!await myWalletProvider.askPinCode()) return;
final wallet = myWalletProvider
.getWalletDataByAddress(address.text);
await sub.setCurrentWallet(wallet!);
final transactionId = await sub.confirmIdentity(
walletOptions.address.text,
idtyName.text,
myWalletProvider.pinCode);
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return TransactionInProgress(
transactionId: transactionId,
transType: 'comfirmIdty',
fromAddress: getShortPubkey(wallet.address),
toAddress: getShortPubkey(wallet.address),
);
}),
);
}
: null,
child: Text(
"validate".tr(),
style: TextStyle(
fontSize: 21,
color: canValidate
? const Color(0xffD80000)
: Colors.grey[500]),
fontSize: 21,
color: idtyName.text.length.clamp(3, 64) ==
idtyName.text.length
? const Color(0xffD80000)
: Colors.grey,
),
),
onPressed: () async {
idtyName.text = idtyName.text.trim().replaceAll(' ', '');
if (idtyName.text.length.clamp(3, 64) ==
idtyName.text.length) {
WalletData? defaultWallet =
myWalletProvider.getDefaultWallet();
String? pin;
if (myWalletProvider.pinCode == '') {
pin = await Navigator.push(
context,
MaterialPageRoute(
builder: (homeContext) {
return UnlockingWallet(wallet: defaultWallet);
},
),
);
}
if (pin != null || myWalletProvider.pinCode != '') {
final wallet = myWalletProvider
.getWalletDataByAddress(address.text);
await sub.setCurrentWallet(wallet!);
sub.confirmIdentity(walletOptions.address.text,
idtyName.text, myWalletProvider.pinCode);
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return TransactionInProgress(
transType: 'comfirmIdty',
fromAddress: getShortPubkey(wallet.address!),
toAddress: getShortPubkey(wallet.address!),
);
}),
);
}
}
},
);
})
],
),
const SizedBox(height: 5)
const SizedBox(height: 20)
],
);
},
@ -267,7 +323,7 @@ class WalletOptionsProvider with ChangeNotifier {
}
Future<String?> editWalletName(BuildContext context, List<int?> wID) async {
final walletName = TextEditingController();
TextEditingController walletName = TextEditingController();
canValidateNameBool = false;
return showDialog<String>(
@ -315,7 +371,7 @@ class WalletOptionsProvider with ChangeNotifier {
if (canValidateNameBool) {
nameController.text = walletName.text;
_renameWallet(wID, walletName.text, isCesium: false);
notifyListeners();
// notifyListeners();
Navigator.pop(context);
}
},
@ -348,8 +404,8 @@ class WalletOptionsProvider with ChangeNotifier {
);
}
bool canValidateName(BuildContext context, final walletName) {
final myWalletProvider =
bool canValidateName(BuildContext context, TextEditingController walletName) {
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
bool isNameValid = walletName.text.length >= 2 &&
@ -380,4 +436,204 @@ class WalletOptionsProvider with ChangeNotifier {
await configBox.put('isCacheChecked', !isCacheChecked);
notifyListeners();
}
String? getAddress(int chest, int derivation) {
String? addressGet;
walletBox.toMap().forEach((key, value) {
if (value.chest == chest && value.derivation == derivation) {
addressGet = value.address!;
return;
}
});
address.text = addressGet ?? '';
return addressGet;
}
Widget walletNameController(BuildContext context, WalletData wallet,
[double size = 20]) {
nameController.text = wallet.name!;
return SizedBox(
width: 260,
child: Stack(children: <Widget>[
TextField(
key: keyWalletName,
autofocus: false,
focusNode: walletNameFocus,
enabled: isEditing,
controller: nameController,
minLines: 1,
maxLines: 3,
textAlign: TextAlign.center,
decoration: const InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
disabledBorder: InputBorder.none,
contentPadding: EdgeInsets.all(15.0),
),
style: TextStyle(
fontSize: isTall ? size : size * 0.9,
color: Colors.black,
fontWeight: FontWeight.w400,
),
),
Positioned(
right: 0,
child: InkWell(
key: keyRenameWallet,
onTap: () async {
// _isNewNameValid =
// walletProvider.editWalletName(wallet.id(), isCesium: false);
await editWalletName(context, wallet.id());
await Future.delayed(const Duration(milliseconds: 30));
walletNameFocus.requestFocus();
},
child: ClipRRect(
child: Image.asset(
isEditing
? 'assets/walletOptions/android-checkmark.png'
: 'assets/walletOptions/edit.png',
width: 25,
height: 25),
),
),
),
]),
);
}
Widget walletName(BuildContext context, WalletData wallet,
[double size = 20, Color color = Colors.black]) {
double newSize = wallet.name!.length <= 15 ? size : size - 2;
return Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Text(
truncate(wallet.name!, 20),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: isTall ? newSize : newSize * 0.9,
color: color,
fontWeight: FontWeight.w400,
fontStyle: FontStyle.italic,
),
softWrap: false,
overflow: TextOverflow.ellipsis,
),
]);
}
}
Map<String, double> balanceCache = {};
Widget balance(BuildContext context, String address, double size,
[Color color = Colors.black,
Color loadingColor = const Color(0xffd07316)]) {
return Column(children: <Widget>[
Consumer<SubstrateSdk>(builder: (context, sdk, _) {
return FutureBuilder(
future: sdk.getBalance(address),
builder: (BuildContext context,
AsyncSnapshot<Map<String, double>> globalBalance) {
if (globalBalance.connectionState != ConnectionState.done ||
globalBalance.hasError) {
if (balanceCache[address] != null &&
balanceCache[address] != -1) {
return Row(children: [
Text(balanceCache[address]!.toString(),
style: TextStyle(
fontSize: isTall ? size : size * 0.9, color: color)),
const SizedBox(width: 5),
udUnitDisplay(size, color),
]);
} else {
return SizedBox(
height: 15,
width: 15,
child: CircularProgressIndicator(
color: loadingColor,
strokeWidth: 2,
),
);
}
}
balanceCache[address] = globalBalance.data!['transferableBalance']!;
if (balanceCache[address] != -1) {
return Row(children: [
Text(
balanceCache[address]!.toString(),
style: TextStyle(
fontSize: isTall ? size : size * 0.9,
color: color,
),
),
const SizedBox(width: 5),
udUnitDisplay(size, color),
]);
} else {
return const Text('');
}
});
}),
]);
}
Widget getCerts(BuildContext context, String address, double size,
[Color color = Colors.black]) {
return Column(children: <Widget>[
Consumer<SubstrateSdk>(builder: (context, sdk, _) {
return FutureBuilder(
future: sdk.getCerts(address),
builder: (BuildContext context, AsyncSnapshot<List<int>> certs) {
// log.d(_certs.data);
return certs.data?[0] != 0 && certs.data != null
? Row(
children: [
Image.asset('assets/medal.png', height: 20),
const SizedBox(width: 1),
Text(certs.data?[0].toString() ?? '0',
style: const TextStyle(fontSize: 20)),
const SizedBox(width: 5),
Text(
"(${certs.data?[1].toString() ?? '0'})",
style: const TextStyle(fontSize: 14),
)
],
)
: const Text('');
});
}),
]);
}
Widget udUnitDisplay(double size, [Color color = Colors.black]) {
final bool isUdUnit = configBox.get('isUdUnit') ?? false;
return isUdUnit
? Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
'ud'.tr(args: ['']),
style:
TextStyle(fontSize: isTall ? size : size * 0.9, color: color),
),
Column(
children: [
Text(
currencyName,
style: TextStyle(
fontSize: (isTall ? size : size * 0.9) * 0.7,
fontWeight: FontWeight.w500,
color: color),
),
const SizedBox(height: 15)
],
)
],
)
: Text(currencyName,
style: TextStyle(fontSize: isTall ? size : size * 0.9, color: color));
}

View File

@ -1,15 +1,18 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/g1_wallets_list.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:gecko/providers/cesium_plus.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/providers/wallet_options.dart';
import 'package:gecko/screens/common_elements.dart';
import 'package:gecko/screens/wallet_view.dart';
import 'package:jdenticon_dart/jdenticon_dart.dart';
import 'package:permission_handler/permission_handler.dart';
// import 'package:qrscan/qrscan.dart' as scanner;
import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:provider/provider.dart';
@ -22,8 +25,8 @@ class WalletsProfilesProvider with ChangeNotifier {
bool isHistoryScreen = false;
String historySwitchButtun = "Voir l'historique";
String? rawSvg;
final payAmount = TextEditingController();
final payComment = TextEditingController();
TextEditingController payAmount = TextEditingController();
TextEditingController payComment = TextEditingController();
num? _balance;
Future<String> scan(context) async {
@ -34,22 +37,14 @@ class WalletsProfilesProvider with ChangeNotifier {
try {
barcode = await BarcodeScanner.scan();
} catch (e) {
log.e("BarcodeScanner ERR: $e");
log.e(e);
return 'false';
}
if (await isAddress(barcode.rawContent)) {
address = barcode.rawContent;
Navigator.popUntil(
context,
ModalRoute.withName('/'),
);
if (isAddress(barcode.rawContent)) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return WalletViewScreen(
address: barcode!.rawContent,
username: null,
);
return WalletViewScreen(address: barcode!.rawContent);
}),
);
} else {
@ -58,6 +53,51 @@ class WalletsProfilesProvider with ChangeNotifier {
return barcode.rawContent;
}
// Future<String> pay(BuildContext context, {int? derivation}) async {
// MyWalletsProvider _myWalletProvider =
// Provider.of<MyWalletsProvider>(context, listen: false);
// int? currentChest = configBox.get('currentChest');
// String result;
// derivation ??=
// _myWalletProvider.getDefaultWallet(currentChest)!.derivation!;
// result = await Gva(node: endPointGVA).pay(
// recipient: pubkey!,
// amount: double.parse(payAmount.text),
// mnemonic: _myWalletProvider.mnemonic,
// comment: payComment.text,
// derivation: derivation,
// lang: appLang);
// return result;
// }
bool isAddress(address) {
final RegExp regExp = RegExp(
r'^[a-zA-Z0-9]+$',
caseSensitive: false,
multiLine: false,
);
if (regExp.hasMatch(address) == true &&
address.length > 45 &&
address.length < 52) {
log.d("C'est une adresse !");
this.address = address;
return true;
} else {
return false;
}
}
// poka: Do99s6wQR2JLfhirPdpAERSjNbmjjECzGxHNJMiNKT3P
// Pi: D2meevcAHFTS2gQMvmRW5Hzi25jDdikk4nC4u1FkwRaU // For debug
// Boris: JE6mkuzSpT3ePciCPRTpuMT9fqPUVVLJz2618d33p7tn
// Matograine portefeuille: 9p5nHsES6xujFR7pw2yGy4PLKKHgWsMvsDHaHF64Uj25.
// Lion simone: 78jhpprYkMNF6i5kQPXfkAVBpd2aqcpieNsXTSW4c21f
void resetdHistory() {
notifyListeners();
}
@ -66,6 +106,21 @@ class WalletsProfilesProvider with ChangeNotifier {
return Jdenticon.toSvg(pubkey);
}
// Future<num> getBalance(String _pubkey) async {
// final url = Uri.parse(
// '$endPointGVA?query={%20balance(script:%20%22$_pubkey%22)%20{%20amount%20base%20}%20}');
// final response = await http.get(url);
// final result = json.decode(response.body);
// if (result['data']['balance'] == null) {
// balance = 0.0;
// } else {
// balance = removeDecimalZero(result['data']['balance']['amount'] / 100);
// }
// return balance;
// }
Future<num?> getBalance(String? pubkey) async {
while (_balance == null) {
await Future.delayed(const Duration(milliseconds: 50));
@ -74,18 +129,112 @@ class WalletsProfilesProvider with ChangeNotifier {
return _balance;
}
Widget headerProfileView(
BuildContext context, String address, String? username) {
const double avatarSize = 140;
WalletOptionsProvider walletOptions =
Provider.of<WalletOptionsProvider>(context, listen: false);
CesiumPlusProvider cesiumPlusProvider =
Provider.of<CesiumPlusProvider>(context, listen: false);
// SubstrateSdk _sub = Provider.of<SubstrateSdk>(context, listen: false);
return Stack(children: <Widget>[
Consumer<SubstrateSdk>(builder: (context, sub, _) {
bool isAccountExist = balanceCache[address] != 0;
return Container(
height: 180,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
isAccountExist ? yellowC : Colors.grey[400]!,
isAccountExist ? const Color(0xFFE7811A) : Colors.grey[600]!,
],
),
));
}),
Padding(
padding: const EdgeInsets.only(left: 30, right: 40),
child: Row(children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
height: 10,
color: yellowC, // Colors.grey[400],
),
Row(children: [
GestureDetector(
key: keyCopyAddress,
onTap: () {
Clipboard.setData(ClipboardData(text: address));
snackCopyKey(context);
},
child: Text(
getShortPubkey(address),
style: const TextStyle(
fontSize: 30,
fontWeight: FontWeight.w800,
),
),
),
]),
const SizedBox(height: 25),
balance(context, address, 22),
const SizedBox(height: 10),
walletOptions.idtyStatus(context, address,
isOwner: false, color: Colors.black),
getCerts(context, address, 14),
// if (username == null &&
// g1WalletsBox.get(address)?.username != null)
// SizedBox(
// width: 230,
// child: Text(
// g1WalletsBox.get(address)?.username ?? '',
// style: const TextStyle(
// fontSize: 27,
// color: Color(0xff814C00),
// ),
// ),
// ),
// if (username != null)
// SizedBox(
// width: 230,
// child: Text(
// username,
// style: const TextStyle(
// fontSize: 27,
// color: Color(0xff814C00),
// ),
// ),
// ),
const SizedBox(height: 55),
]),
const Spacer(),
Column(children: <Widget>[
ClipOval(
child: cesiumPlusProvider.defaultAvatar(avatarSize),
),
const SizedBox(height: 25),
]),
]),
),
CommonElements().offlineInfo(context),
]);
}
bool isContact(String address) {
return contactsBox.containsKey(address);
}
Future addContact(G1WalletsList profile) async {
// log.d(profile.username);
if (isContact(profile.address)) {
await contactsBox.delete(profile.address);
snackMessage(homeContext,
message: 'removedFromcontacts'.tr(), duration: 4);
} else {
await contactsBox.put(profile.address, profile);
snackMessage(homeContext, message: 'addedToContacts'.tr(), duration: 4);
}
notifyListeners();
}
@ -95,57 +244,11 @@ class WalletsProfilesProvider with ChangeNotifier {
}
}
// bool isAddress(address) {
// final RegExp regExp = RegExp(
// r'^[a-zA-Z0-9]+$',
// caseSensitive: false,
// multiLine: false,
// );
// if (regExp.hasMatch(address) == true &&
// address.length > 45 &&
// address.length < 52) {
// return true;
// } else {
// return false;
// }
// }
Future<bool> isAddress(String address) async {
final sub = Provider.of<SubstrateSdk>(homeContext, listen: false);
return await sub.sdk.api.account
.checkAddressFormat(address, sub.initSs58)
.timeout(const Duration(milliseconds: 300))
.onError((_, __) => false) ??
false;
}
snackMessage(context,
{required String message, int duration = 2, double fontSize = 14}) {
final snackBar = SnackBar(
backgroundColor: Colors.grey[900],
padding: EdgeInsets.all(scaleSize(19)),
content: Text(message, style: scaledTextStyle(fontSize: fontSize)),
duration: Duration(seconds: duration));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
snackCopyKey(context) {
final snackBar = SnackBar(
backgroundColor: Colors.grey[900],
padding: EdgeInsets.all(scaleSize(19)),
padding: const EdgeInsets.all(20),
content: Text("thisAddressHasBeenCopiedToClipboard".tr(),
style: scaledTextStyle(fontSize: 14)),
style: const TextStyle(fontSize: 16)),
duration: const Duration(seconds: 2));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
snackCopySeed(context) {
final snackBar = SnackBar(
backgroundColor: Colors.grey[900],
padding: EdgeInsets.all(scaleSize(19)),
content: Text("thisMnemonicHasBeenCopiedToClipboard".tr(),
style: scaledTextStyle(fontSize: 14)),
duration: const Duration(seconds: 4));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}

View File

@ -1,57 +1,365 @@
// ignore_for_file: must_be_immutable
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/models/queries_indexer.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:flutter/material.dart';
import 'package:gecko/providers/cesium_plus.dart';
import 'package:gecko/providers/duniter_indexer.dart';
import 'package:gecko/providers/home.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/widgets/bottom_app_bar.dart';
import 'package:gecko/widgets/header_profile.dart';
import 'package:gecko/widgets/history_query.dart';
import 'package:gecko/providers/wallets_profiles.dart';
import 'package:flutter/material.dart';
import 'package:gecko/screens/wallet_view.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:provider/provider.dart';
class ActivityScreen extends StatefulWidget {
const ActivityScreen({required this.address, this.username})
class ActivityScreen extends StatelessWidget with ChangeNotifier {
ActivityScreen({required this.address, required this.avatar, this.username})
: super(key: keyActivityScreen);
final ScrollController scrollController = ScrollController();
final double avatarsSize = 80;
final String address;
final String? username;
final Image avatar;
@override
State<ActivityScreen> createState() => _ActivityScreenState();
}
FetchMore? fetchMore;
FetchMoreOptions? opts;
class _ActivityScreenState extends State<ActivityScreen> {
@override
void initState() {
final sub = Provider.of<SubstrateSdk>(homeContext, listen: false);
sub.getOldOwnerKey(widget.address);
super.initState();
}
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
@override
Widget build(BuildContext context) {
final duniterIndexer = Provider.of<DuniterIndexer>(context, listen: true);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
WalletsProfilesProvider walletProfile =
Provider.of<WalletsProfilesProvider>(context, listen: false);
HomeProvider homeProvider =
Provider.of<HomeProvider>(context, listen: false);
return PopScope(
onPopInvoked: (_) {
duniterIndexer.refetch = duniterIndexer.transBC = null;
},
child: Scaffold(
appBar: AppBar(
elevation: 0,
toolbarHeight: scaleSize(57),
title: Text(
'accountActivity'.tr(),
style: scaledTextStyle(fontSize: 18),
),
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
elevation: 0,
toolbarHeight: 60 * ratio,
title: SizedBox(
height: 22,
child: Text('accountActivity'.tr()),
),
bottomNavigationBar: const GeckoBottomAppBar(),
body: Column(children: <Widget>[
HeaderProfile(address: widget.address, username: widget.username),
HistoryQuery(address: widget.address),
])),
),
bottomNavigationBar: homeProvider.bottomAppBar(context),
body: Column(children: <Widget>[
walletProfile.headerProfileView(context, address, username),
historyQuery(context),
]));
}
Widget historyQuery(context) {
DuniterIndexer duniterIndexer =
Provider.of<DuniterIndexer>(context, listen: false);
if (indexerEndpoint == '') {
Column(children: <Widget>[
const SizedBox(height: 50),
Text(
"noNetworkNoHistory".tr(),
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 18),
)
]);
}
final httpLink = HttpLink(
'$indexerEndpoint/v1beta1/relay',
);
final client = ValueNotifier(
GraphQLClient(
cache: GraphQLCache(),
link: httpLink,
),
);
return GraphQLProvider(
client: client,
child: Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Query(
options: QueryOptions(
document: gql(getHistoryByAddressQ),
variables: <String, dynamic>{
'address': address,
'number': 20,
'cursor': null
},
),
builder: (QueryResult result, {fetchMore, refetch}) {
if (result.isLoading && result.data == null) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (result.hasException) {
log.e('Error Indexer: ${result.exception}');
return Column(children: <Widget>[
const SizedBox(height: 50),
Text(
"noNetworkNoHistory".tr(),
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 18),
)
]);
} else if (result
.data?['transaction_connection']?['edges'].isEmpty) {
return Column(children: <Widget>[
const SizedBox(height: 50),
Text(
"noDataToDisplay".tr(),
style: const TextStyle(fontSize: 18),
)
]);
}
if (result.isNotLoading) {
// log.d(result.data);
opts = duniterIndexer.checkQueryResult(result, opts, address);
}
// Build history list
return NotificationListener(
child: Builder(
builder: (context) => Expanded(
child: ListView(
key: keyListTransactions,
controller: scrollController,
children: <Widget>[historyView(context, result)],
),
),
),
onNotification: (dynamic t) {
if (t is ScrollEndNotification &&
scrollController.position.pixels >=
scrollController.position.maxScrollExtent * 0.7 &&
duniterIndexer.pageInfo!['hasNextPage'] &&
result.isNotLoading) {
fetchMore!(opts!);
}
return true;
});
},
),
],
)),
);
}
Widget historyView(context, result) {
DuniterIndexer duniterIndexer =
Provider.of<DuniterIndexer>(context, listen: false);
return duniterIndexer.transBC == null
? Column(children: <Widget>[
const SizedBox(height: 50),
Text(
"noTransactionToDisplay".tr(),
style: const TextStyle(fontSize: 18),
)
])
: Column(children: <Widget>[
getTransactionTile(context, duniterIndexer),
if (result.isLoading && duniterIndexer.pageInfo!['hasPreviousPage'])
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
CircularProgressIndicator(),
],
),
if (!duniterIndexer.pageInfo!['hasNextPage'])
Column(
children: const <Widget>[
SizedBox(height: 15),
Text("Début de l'historique.",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20)),
SizedBox(height: 15)
],
)
]);
}
Widget getTransactionTile(
BuildContext context, DuniterIndexer duniterIndexer) {
CesiumPlusProvider cesiumPlusProvider =
Provider.of<CesiumPlusProvider>(context, listen: false);
int keyID = 0;
String? dateDelimiter;
String? lastDateDelimiter;
const double avatarSize = 200;
bool isTody = false;
bool isYesterday = false;
bool isThisWeek = false;
final Map<int, String> monthsInYear = {
1: "month1".tr(),
2: "month2".tr(),
3: "month3".tr(),
4: "month4".tr(),
5: "month5".tr(),
6: "month6".tr(),
7: "month7".tr(),
8: "month8".tr(),
9: "month9".tr(),
10: "month10".tr(),
11: "month11".tr(),
12: "month12".tr()
};
return Column(
children: duniterIndexer.transBC!.map((repository) {
// log.d('bbbbbbbbbbbbbbbbbbbbbb: ' + repository.toString());
DateTime now = DateTime.now();
DateTime date = repository[0];
String dateForm;
if ({4, 10, 11, 12}.contains(date.month)) {
dateForm = "${date.day} ${monthsInYear[date.month]!.substring(0, 3)}.";
} else if ({1, 2, 7, 9}.contains(date.month)) {
dateForm = "${date.day} ${monthsInYear[date.month]!.substring(0, 4)}.";
} else {
dateForm = "${date.day} ${monthsInYear[date.month]}";
}
int weekNumber(DateTime date) {
int dayOfYear = int.parse(DateFormat("D").format(date));
return ((dayOfYear - date.weekday + 10) / 7).floor();
}
final transactionDate = DateTime(date.year, date.month, date.day);
final todayDate = DateTime(now.year, now.month, now.day);
final yesterdayDate = DateTime(now.year, now.month, now.day - 1);
if (transactionDate == todayDate && !isTody) {
dateDelimiter = lastDateDelimiter = "today".tr();
isTody = true;
} else if (transactionDate == yesterdayDate && !isYesterday) {
dateDelimiter = lastDateDelimiter = "yesterday".tr();
isYesterday = true;
} else if (weekNumber(date) == weekNumber(now) &&
date.year == now.year &&
lastDateDelimiter != "thisWeek".tr() &&
transactionDate != yesterdayDate &&
transactionDate != todayDate &&
!isThisWeek) {
dateDelimiter = lastDateDelimiter = "thisWeek".tr();
isThisWeek = true;
} else if (lastDateDelimiter != monthsInYear[date.month] &&
lastDateDelimiter != "${monthsInYear[date.month]} ${date.year}" &&
transactionDate != todayDate &&
transactionDate != yesterdayDate &&
!(weekNumber(date) == weekNumber(now) && date.year == now.year)) {
if (date.year == now.year) {
dateDelimiter = lastDateDelimiter = monthsInYear[date.month];
} else {
dateDelimiter =
lastDateDelimiter = "${monthsInYear[date.month]} ${date.year}";
}
} else {
dateDelimiter = null;
}
final bool isUdUnit = configBox.get('isUdUnit') ?? false;
late double amount;
late String finalAmount;
amount = repository[4] == 'RECEIVED' ? repository[3] : repository[3] * -1;
if (isUdUnit) {
amount = round(amount / balanceRatio);
finalAmount = 'ud'.tr(args: ['$amount ']);
} else {
finalAmount = '$amount $currencyName';
}
return Column(children: <Widget>[
if (dateDelimiter != null)
Padding(
padding: const EdgeInsets.symmetric(vertical: 30),
child: Text(
dateDelimiter!,
style: const TextStyle(
fontSize: 23, color: orangeC, fontWeight: FontWeight.w300),
),
),
Padding(
padding: const EdgeInsets.only(right: 0),
child:
// Row(children: [Column(children: [],)],)
ListTile(
key: keyTransaction(keyID++),
contentPadding: const EdgeInsets.only(
left: 20, right: 30, top: 15, bottom: 15),
leading: ClipOval(
child: cesiumPlusProvider.defaultAvatar(avatarSize),
),
title: Padding(
padding: const EdgeInsets.only(bottom: 5),
child: Text(getShortPubkey(repository[1]),
style: const TextStyle(
fontSize: 18, fontFamily: 'Monospace')),
),
subtitle: RichText(
text: TextSpan(
style: TextStyle(
fontSize: 16,
color: Colors.grey[700],
),
children: <TextSpan>[
TextSpan(
text: dateForm,
),
if (repository[2] != '')
TextSpan(
text: ' · ',
style: TextStyle(
fontSize: 20,
color: Colors.grey[550],
),
),
TextSpan(
text: repository[2],
style: TextStyle(
fontStyle: FontStyle.italic,
color: Colors.grey[600],
),
),
],
),
),
trailing: Text(finalAmount,
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.w500),
textAlign: TextAlign.justify),
dense: false,
isThreeLine: false,
onTap: () {
duniterIndexer.nPage = 1;
// _cesiumPlusProvider.avatarCancelToken.cancel('cancelled');
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return WalletViewScreen(address: repository[1]);
}),
);
// Navigator.pop(context);
}),
),
]);
}).toList());
}
}

View File

@ -1,84 +0,0 @@
import 'package:accordion/controllers.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:gecko/globals.dart';
import 'package:flutter/material.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/widgets/certs_list.dart';
import 'package:gecko/widgets/certs_counter.dart';
import 'package:accordion/accordion.dart';
import 'package:gecko/widgets/commons/top_appbar.dart';
class CertificationsScreen extends StatelessWidget {
const CertificationsScreen(
{Key? key, required this.address, required this.username})
: super(key: key);
final String address;
final String username;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: backgroundColor,
appBar: GeckoAppBar('certificationsOf'.tr(args: [username])),
body: SafeArea(
child: Accordion(
paddingListTop: 7,
paddingListBottom: 10,
maxOpenSections: 1,
headerBackgroundColorOpened: orangeC,
scaleWhenAnimating: true,
openAndCloseAnimation: true,
headerPadding: EdgeInsets.symmetric(
vertical: scaleSize(6), horizontal: scaleSize(14)),
sectionOpeningHapticFeedback: SectionHapticFeedback.heavy,
sectionClosingHapticFeedback: SectionHapticFeedback.light,
children: [
AccordionSection(
isOpen: true,
leftIcon: Icon(
Icons.insights_rounded,
color: Colors.black,
size: scaleSize(20),
),
headerBackgroundColor: yellowC,
headerBackgroundColorOpened: orangeC,
header: Row(children: [
Text(
'received'.tr(),
style: scaledTextStyle(fontSize: 17),
),
ScaledSizedBox(width: 5),
CertsCounter(address: address)
]),
content: CertsList(
address: address, direction: CertDirection.received),
contentHorizontalPadding: 0,
contentBorderWidth: 1,
),
AccordionSection(
isOpen: false,
leftIcon:
const Icon(Icons.insights_rounded, color: Colors.black),
headerBackgroundColor: yellowC,
headerBackgroundColorOpened: orangeC,
header: Row(children: [
Text(
'sent'.tr(),
style: scaledTextStyle(fontSize: 17),
),
ScaledSizedBox(width: 5),
CertsCounter(address: address, isSent: true)
]),
content: CertsList(
address: address,
direction: CertDirection.sent,
),
contentHorizontalPadding: 0,
contentBorderWidth: 1,
// onOpenSection: () => print('onOpenSection ...'),
// onCloseSection: () => print('onCloseSection ...'),
),
]),
));
}
}

View File

@ -0,0 +1,338 @@
import 'package:dots_indicator/dots_indicator.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:provider/provider.dart';
class CommonElements {
// Exemple de Widget
Widget exemple(String data) {
return const Text('Coucou');
}
Widget buildImage(String assetName,
[double boxHeight = 440, double imageWidth = 350]) {
return Container(
padding: const EdgeInsets.all(0),
width: 440,
height: isTall ? boxHeight : boxHeight * 0.9,
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xffd2d4cf),
Color(0xffeaeae7),
],
),
border: Border.all(color: Colors.grey[900]!)),
child: Image.asset('assets/onBoarding/$assetName', width: imageWidth));
}
Widget buildText(String text, [double size = 20, bool isMd = false]) {
final mdStyle = MarkdownStyleSheet(
p: TextStyle(
fontSize: isTall ? size : size * 0.9,
color: Colors.black,
letterSpacing: 0.3),
textAlign: WrapAlignment.spaceBetween,
);
return Container(
padding: const EdgeInsets.all(12),
width: 440,
decoration: BoxDecoration(
color: Colors.white, border: Border.all(color: Colors.grey[900]!)),
child: isMd
? MarkdownBody(data: text, styleSheet: mdStyle)
: Text(text,
textAlign: TextAlign.justify,
style: TextStyle(
fontSize: isTall ? size : size * 0.9,
color: Colors.black,
letterSpacing: 0.3)),
);
}
Widget nextButton(
BuildContext context, String text, nextScreen, bool isFast) {
return SizedBox(
width: 380 * ratio,
height: 60 * ratio,
child: ElevatedButton(
key: keyGoNext,
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white, backgroundColor: orangeC,
elevation: 4, // foreground
),
onPressed: () {
Navigator.push(
context, FaderTransition(page: nextScreen, isFast: isFast));
},
child: Text(
text,
style: TextStyle(fontSize: 23 * ratio, fontWeight: FontWeight.w600),
),
),
);
}
Widget buildProgressBar(double pagePosition) {
return DotsIndicator(
dotsCount: 10,
position: pagePosition,
decorator: DotsDecorator(
spacing: const EdgeInsets.symmetric(horizontal: 10),
color: Colors.grey[300]!, // Inactive color
activeColor: orangeC,
),
);
}
Widget infoIntro(
BuildContext context,
String text,
String assetName,
String buttonText,
nextScreen,
double pagePosition, {
bool isMd = false,
bool isFast = false,
double boxHeight = 440,
double imageWidth = 350,
double textSize = 20,
}) {
return Column(children: <Widget>[
SizedBox(height: isTall ? 40 : 20),
buildProgressBar(pagePosition),
SizedBox(height: isTall ? 40 : 20),
buildText(text, textSize, isMd),
buildImage(assetName, boxHeight, imageWidth),
Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: nextButton(context, buttonText, nextScreen, false),
),
),
// const SizedBox(height: 40),
SizedBox(height: isTall ? 40 : 20),
]);
}
Widget roundButton(
AssetImage image,
ontap,
isAsync,
double imgHight,
EdgeInsets padding,
) {
return Container(
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey,
blurRadius: 4.0,
offset: Offset(2.0, 2.5),
spreadRadius: 0.5)
],
),
child: ClipOval(
child: Material(
color: const Color(0xffFFD58D), // button color
child: InkWell(
splashColor: orangeC, // inkwell color
child: Padding(
padding: padding,
child: Image(image: image, height: imgHight)),
onTap: () async {
await ontap;
}),
),
),
);
}
Widget offlineInfo(BuildContext context) {
final double screenWidth = MediaQuery.of(homeContext).size.width;
return Consumer<SubstrateSdk>(builder: (context, sub, _) {
return Visibility(
visible: !sub.nodeConnected,
child: Positioned(
top: 0,
child: Container(
height: 30,
width: screenWidth,
color: Colors.grey[800],
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'youAreOffline'.tr(),
style: TextStyle(color: Colors.grey[50]),
textAlign: TextAlign.center,
),
],
)),
),
);
});
}
}
class SmoothTransition extends PageRouteBuilder {
final Widget page;
SmoothTransition({required this.page})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) =>
TweenAnimationBuilder(
duration: const Duration(seconds: 5),
tween: Tween(begin: 200, end: 200),
builder: (BuildContext context, dynamic value, Widget? child) {
return page;
},
),
);
}
class FaderTransition extends PageRouteBuilder {
final Widget page;
final bool isFast;
FaderTransition({required this.page, required this.isFast})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) =>
page,
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) =>
FadeTransition(
opacity:
Tween(begin: 0.0, end: isFast ? 3.0 : 1.0).animate(animation),
child: child,
),
);
}
Future<bool?> confirmPopup(BuildContext context, String title) async {
return showDialog<bool>(
context: context,
barrierDismissible: true, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: backgroundColor,
content: Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
),
actions: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
key: keyConfirm,
child: Text(
"yes".tr(),
style: const TextStyle(
fontSize: 21,
color: Color(0xffD80000),
),
),
onPressed: () {
Navigator.pop(context, true);
},
),
const SizedBox(width: 20),
TextButton(
child: Text(
"no".tr(),
style: const TextStyle(fontSize: 21),
),
onPressed: () {
Navigator.pop(context, false);
},
),
const SizedBox(height: 120)
],
)
],
);
},
);
}
Future<void> infoPopup(BuildContext context, String title) async {
return showDialog<void>(
context: context,
barrierDismissible: true, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: backgroundColor,
content: Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
),
actions: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
key: keyInfoPopup,
child: const Text(
"D'accord",
style: TextStyle(
fontSize: 21,
color: Color(0xffD80000),
),
),
onPressed: () {
Navigator.pop(context, true);
},
),
],
)
],
);
},
);
}
// Widget geckoAppBar() {
// return AppBar(
// toolbarHeight: 60 * ratio,
// elevation: 0,
// leading: IconButton(
// icon: const Icon(Icons.arrow_back, color: Colors.black),
// onPressed: () {
// _walletOptions.isEditing = false;
// _walletOptions.isBalanceBlur = false;
// Navigator.pop(context);
// }),
// title: SizedBox(
// height: 22,
// child: Consumer<WalletOptionsProvider>(
// builder: (context, walletProvider, _) {
// return Text(_walletOptions.nameController.text);
// }),
// ),
// );
// }

View File

@ -1,60 +0,0 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:gecko/globals.dart';
import 'package:flutter/material.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:provider/provider.dart';
class DebugScreen extends StatelessWidget {
const DebugScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final sub = Provider.of<SubstrateSdk>(context);
return Scaffold(
backgroundColor: backgroundColor,
appBar: AppBar(
toolbarHeight: scaleSize(57), title: const Text('Debug screen')),
body: SafeArea(
child: Column(children: <Widget>[
const SizedBox(height: 40),
Center(
child: Column(
children: [
Text(
'node: ${sub.getConnectedEndpoint()}',
style: TextStyle(fontSize: 15, color: Colors.grey[700]),
),
const SizedBox(height: 15),
Text(
'blockN'.tr(args: [
sub.blocNumber.toString()
]), //'bloc N°${sub.blocNumber}',
style: TextStyle(fontSize: 15, color: Colors.grey[700]),
),
const SizedBox(height: 20),
SizedBox(
height: 50,
width: 210,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
elevation: 4,
backgroundColor: orangeC,
),
onPressed: () async => await sub.spawnBlock(),
child: const Text(
'Spawn a bloc',
style: TextStyle(
fontSize: 17, fontWeight: FontWeight.w600),
),
),
),
],
),
),
]),
));
}
}

View File

@ -1,155 +1,194 @@
// ignore_for_file: use_build_context_synchronously
import 'package:bubble/bubble.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/chest_data.dart';
import 'package:gecko/models/g1_wallets_list.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/models/stateful_wrapper.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:gecko/providers/chest_provider.dart';
import 'package:gecko/providers/duniter_indexer.dart';
import 'package:gecko/providers/home.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/providers/wallets_profiles.dart';
import 'package:flutter/material.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/providers/v2s_datapod.dart';
import 'package:gecko/widgets/bubble_speak.dart';
import 'package:gecko/widgets/commons/animated_text.dart';
import 'package:gecko/widgets/commons/common_elements.dart';
import 'package:gecko/screens/animated_text.dart';
import 'package:gecko/screens/common_elements.dart';
import 'package:gecko/screens/myWallets/restore_chest.dart';
import 'package:gecko/screens/myWallets/unlocking_wallet.dart';
import 'package:gecko/screens/myWallets/wallets_home.dart';
import 'package:gecko/screens/onBoarding/1.dart';
import 'package:gecko/widgets/drawer.dart';
import 'package:gecko/widgets/buttons/home_buttons.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:gecko/screens/search.dart';
import 'package:gecko/screens/settings.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:gecko/screens/my_contacts.dart';
class HomeScreen extends StatefulWidget {
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) async {
final homeProvider = Provider.of<HomeProvider>(context, listen: false);
final sub = Provider.of<SubstrateSdk>(context, listen: false);
final duniterIndexer =
Provider.of<DuniterIndexer>(context, listen: false);
final myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
final datapod = Provider.of<V2sDatapodProvider>(context, listen: false);
final bool isWalletsExists = myWalletProvider.isWalletsExists();
// Check if versionData non compatible, drop everything
if (configBox.get('dataVersion') == null) {
configBox.put('dataVersion', dataVersion);
}
if (isWalletsExists && (configBox.get('dataVersion')) < dataVersion) {
if (!sub.sdkReady && !sub.sdkLoading) sub.initApi();
await infoPopup(context, "chestNotCompatibleMustReinstallGecko".tr());
await Hive.deleteBoxFromDisk('walletBox');
await Hive.deleteBoxFromDisk('chestBox');
await datapod.deleteAvatarsDirectory();
await avatarsDirectory.create();
chestBox = await Hive.openBox<ChestData>("chestBox");
await configBox.delete('defaultWallet');
if (!sub.sdkReady && !sub.sdkLoading) await sub.initApi();
await sub.deleteAllAccounts();
configBox.put('dataVersion', dataVersion);
myWalletProvider.reload();
} else {
if (!sub.sdkReady && !sub.sdkLoading) await sub.initApi();
}
if (sub.sdkReady && !sub.nodeConnected) {
walletBox = await Hive.openBox<WalletData>("walletBox");
await Hive.deleteBoxFromDisk('g1WalletsBox');
await datapod.deleteAvatarsCacheDirectory();
await avatarsCacheDirectory.create();
g1WalletsBox = await Hive.openBox<G1WalletsList>("g1WalletsBox");
contactsBox = await Hive.openBox<G1WalletsList>("contactsBox");
homeProvider.isWalletBoxInit = true;
myWalletProvider.reload();
duniterIndexer.getValidIndexerEndpoint().then((validIndexerEndpoint) {
final wsLinkIndexer = WebSocketLink(
'wss://$validIndexerEndpoint/v1beta1/relay',
);
// const headerWebsocket =
// datapodEndpoint == '10.0.2.2:8080' ? 'ws' : 'wss';
// final wsLinkDatapod = WebSocketLink(
// '$headerWebsocket://$datapodEndpoint/v1/graphql',
// );
duniterIndexer.indexerClient = GraphQLClient(
cache: GraphQLCache(),
link: wsLinkIndexer,
);
// datapod.datapodClient = GraphQLClient(
// cache: GraphQLCache(),
// link: wsLinkDatapod,
// );
});
await homeProvider.getValidEndpoints();
if (configBox.get('isCacheChecked') == null) {
configBox.put('isCacheChecked', false);
}
Connectivity()
.onConnectivityChanged
.listen((ConnectivityResult result) async {
log.i('Network changed: $result');
if (result == ConnectivityResult.none) {
sub.nodeConnected = false;
await sub.sdk.api.setting.unsubscribeBestNumber();
homeProvider.changeMessage("notConnectedToInternet".tr(), 0);
sub.reload();
} else {
// Check if the phone is actually connected to the internet
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult != ConnectivityResult.none) {
await sub.connectNode();
}
}
});
}
});
super.initState();
}
@override
Widget build(BuildContext context) {
homeContext = context;
final myWalletProvider = Provider.of<MyWalletsProvider>(context);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context);
Provider.of<ChestProvider>(context);
final isWalletsExists = myWalletProvider.isWalletsExists();
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
isTall = (MediaQuery.of(context).size.height /
MediaQuery.of(context).size.width) >
1.75;
final bool isWalletsExists = myWalletProvider.checkIfWalletExist();
isTall = false;
ratio = 1;
if (MediaQuery.of(context).size.height >= 930) {
isTall = true;
ratio = 1.125;
}
return Scaffold(
resizeToAvoidBottomInset: false,
drawer: MainDrawer(isWalletsExists: isWalletsExists),
backgroundColor: yellowC,
body: isWalletsExists ? geckHome(context) : welcomeHome(context));
resizeToAvoidBottomInset: false,
drawer: Drawer(
child: Column(
children: <Widget>[
Expanded(
child: ListView(padding: EdgeInsets.zero, children: <Widget>[
DrawerHeader(
decoration: const BoxDecoration(
color: orangeC,
),
child: Column(children: const <Widget>[
SizedBox(height: 0),
Image(
image: AssetImage('assets/icon/gecko_final.png'),
height: 130),
]),
),
ListTile(
key: keyParameters,
title: Text('parameters'.tr()),
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return SettingsScreen();
}),
);
},
),
ListTile(
key: keyContacts,
title: Text('contactsManagement'.tr()),
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return const ContactsScreen();
}),
);
},
),
])),
Align(
alignment: FractionalOffset.bottomCenter,
child: Text('Ğecko v$appVersion')),
const SizedBox(height: 20)
],
),
),
// bottomNavigationBar: _homeProvider.bottomBar(context, 1),
backgroundColor: const Color(0xffF9F9F1),
body: Builder(
builder: (ctx) => StatefulWrapper(
onInit: () {
WidgetsBinding.instance.addPostFrameCallback((_) async {
DuniterIndexer duniterIndexer =
Provider.of<DuniterIndexer>(ctx, listen: false);
duniterIndexer.getValidIndexerEndpoint();
if (!sub.sdkReady && !sub.sdkLoading) await sub.initApi();
if (sub.sdkReady && !sub.nodeConnected) {
// Check if versionData non compatible, drop everything
if (walletBox.isNotEmpty &&
walletBox.getAt(0)!.version! < dataVersion) {
await infoPopup(
context, "chestNotCompatibleMustReinstallGecko".tr());
await walletBox.clear();
await chestBox.clear();
await configBox.delete('defaultWallet');
await sub.deleteAllAccounts();
myWalletProvider.reload();
}
// var connectivityResult =
// await (Connectivity().checkConnectivity());
// if (connectivityResult != ConnectivityResult.mobile &&
// connectivityResult != ConnectivityResult.wifi) {
// homeProvider.changeMessage(
// "notConnectedToInternet".tr(), 0);
// sub.nodeConnected = false;
// }
// TODO: fix random bad network status on startup
HomeProvider homeProvider =
Provider.of<HomeProvider>(ctx, listen: false);
Connectivity()
.onConnectivityChanged
.listen((ConnectivityResult result) async {
log.d('Network changed: $result');
if (result == ConnectivityResult.none) {
sub.nodeConnected = false;
await sub.sdk.api.setting.unsubscribeBestNumber();
homeProvider.changeMessage(
"notConnectedToInternet".tr(), 0);
sub.reload();
} else {
await sub.connectNode(ctx);
}
});
// await sub.connectNode(ctx);
}
// _duniterIndexer.checkIndexerEndpointBackground();
});
},
child: isWalletsExists ? geckHome(context) : welcomeHome(context)
// bottomNavigationBar: BottomNavigationBar(
// backgroundColor: backgroundColor,
// fixedColor: Colors.grey[850],
// unselectedItemColor: const Color(0xffBD935C),
// type: BottomNavigationBarType.fixed,
// onTap: (index) {
// _homeProvider.currentIndex = index;
// },
// currentIndex: _homeProvider.currentIndex,
// items: [
// BottomNavigationBarItem(
// icon: Image.asset('assets/block-space-disabled.png', height: 26),
// activeIcon: Image.asset('assets/blockchain.png', height: 26),
// label: 'Explorateur',
// ),
// const BottomNavigationBarItem(
// icon: Icon(Icons.lock),
// label: 'Mes portefeuilles',
// ),
// ],
// ),
),
),
);
}
}
Widget geckHome(context) {
MyWalletsProvider myWalletProvider = Provider.of<MyWalletsProvider>(context);
Provider.of<ChestProvider>(context);
final statusBarHeight = MediaQuery.of(context).padding.top;
WalletsProfilesProvider historyProvider =
Provider.of<WalletsProfilesProvider>(context);
final double statusBarHeight = MediaQuery.of(context).padding.top;
return Container(
decoration: const BoxDecoration(
image: DecorationImage(
@ -161,43 +200,42 @@ Widget geckHome(context) {
Column(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
Stack(children: <Widget>[
Positioned(
top: statusBarHeight + scaleSize(10),
left: scaleSize(15),
top: statusBarHeight + 10,
left: 15,
child: Builder(
builder: (context) => IconButton(
key: keyDrawerMenu,
icon: Icon(
icon: const Icon(
Icons.menu,
color: Colors.black,
size: scaleSize(35),
color: Colors.white,
size: 35,
),
onPressed: () => Scaffold.of(context).openDrawer(),
),
),
),
Align(
child: Image(
image: const AssetImage('assets/home/header.png'),
height: scaleSize(165)),
const Align(
child:
Image(image: AssetImage('assets/home/header.png'), height: 210),
),
]),
Padding(
padding: const EdgeInsets.only(top: 15),
padding: EdgeInsets.only(top: 15 * ratio),
child:
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
DefaultTextStyle(
textAlign: TextAlign.center,
style: scaledTextStyle(
style: const TextStyle(
color: Colors.white,
fontSize: 19,
fontSize: 24,
fontWeight: FontWeight.w700,
shadows: <Shadow>[
const Shadow(
Shadow(
offset: Offset(0, 0),
blurRadius: 20,
color: Colors.black,
),
const Shadow(
Shadow(
offset: Offset(0, 0),
blurRadius: 20,
color: Colors.black,
@ -214,7 +252,7 @@ Widget geckHome(context) {
),
]),
),
ScaledSizedBox(height: 15),
const SizedBox(height: 15),
Expanded(
flex: 1,
child: Container(
@ -228,7 +266,167 @@ Widget geckHome(context) {
],
),
),
child: const HomeButtons(),
child: Column(children: <Widget>[
const Spacer(),
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Column(children: <Widget>[
Container(
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.black,
boxShadow: [
BoxShadow(
blurRadius: 2,
offset: Offset(1, 1.5),
spreadRadius: 0.5)
],
),
child: ClipOval(
child: Material(
color: orangeC, // button color
child: InkWell(
key: keyOpenSearch,
child: Padding(
padding: const EdgeInsets.all(18),
child: Image(
image:
const AssetImage('assets/home/loupe.png'),
height: 62 * ratio),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return const SearchScreen();
}),
);
}),
),
),
),
const SizedBox(height: 12),
Text(
"searchWallet".tr(),
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 15 * ratio,
fontWeight: FontWeight.w500),
)
]),
const SizedBox(width: 120),
Column(children: <Widget>[
Container(
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.black,
boxShadow: [
BoxShadow(
blurRadius: 2,
offset: Offset(1, 1.5),
spreadRadius: 0.5)
],
),
child: ClipOval(
key: keyOpenWalletsHomme,
child: Material(
color: orangeC, // button color
child: InkWell(
child: Padding(
padding: const EdgeInsets.all(18),
child: Image(
image: const AssetImage(
'assets/home/wallet.png'),
height: 68 * ratio)),
onTap: () async {
WalletData? defaultWallet =
myWalletProvider.getDefaultWallet();
String? pin;
if (myWalletProvider.pinCode == '') {
pin = await Navigator.push(
context,
MaterialPageRoute(
builder: (homeContext) {
return UnlockingWallet(
wallet: defaultWallet);
},
),
);
}
if (pin != null || myWalletProvider.pinCode != '') {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return const WalletsHome();
}),
);
}
// log.d(_myWalletProvider.pinCode);
// Navigator.pushNamed(
// context, '/mywallets')));
}),
),
),
),
const SizedBox(height: 12),
Text(
"manageWallets".tr(),
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 15 * ratio,
fontWeight: FontWeight.w500),
)
])
]),
Padding(
padding: EdgeInsets.only(top: 35 * ratio),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Column(children: <Widget>[
Container(
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.black,
boxShadow: [
BoxShadow(
blurRadius: 2,
offset: Offset(1, 1.5),
spreadRadius: 0.5)
],
),
child: ClipOval(
child: Material(
color: orangeC, // button color
child: InkWell(
child: Padding(
padding: const EdgeInsets.all(18),
child: Image(
image: const AssetImage(
'assets/home/qrcode.png'),
height: 68 * ratio)),
onTap: () async {
await historyProvider.scan(context);
}),
),
),
),
const SizedBox(height: 12),
Text(
"scanQRCode".tr(),
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 15 * ratio,
fontWeight: FontWeight.w500),
)
])
]),
),
SizedBox(height: isTall ? 80 : 40)
]),
),
)
]),
@ -236,7 +434,7 @@ Widget geckHome(context) {
}
Widget welcomeHome(context) {
final statusBarHeight = MediaQuery.of(context).padding.top;
final double statusBarHeight = MediaQuery.of(context).padding.top;
return Container(
decoration: const BoxDecoration(
@ -249,38 +447,37 @@ Widget welcomeHome(context) {
Column(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
Stack(children: <Widget>[
Positioned(
top: statusBarHeight + scaleSize(10),
left: scaleSize(15),
top: statusBarHeight + 10,
left: 15,
child: Builder(
builder: (context) => IconButton(
key: keyDrawerMenu,
icon: Icon(
icon: const Icon(
Icons.menu,
color: Colors.black,
size: scaleSize(35),
color: Colors.white,
size: 35,
),
onPressed: () => Scaffold.of(context).openDrawer(),
),
),
),
Align(
child: Image(
image: const AssetImage('assets/home/header.png'),
height: scaleSize(165)),
const Align(
child:
Image(image: AssetImage('assets/home/header.png'), height: 210),
),
]),
Padding(
padding: const EdgeInsets.only(top: 1),
padding: EdgeInsets.only(top: 1 * ratio),
child:
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Text(
"fastAppDescription".tr(args: [currencyName]),
textAlign: TextAlign.center,
style: scaledTextStyle(
style: const TextStyle(
color: Colors.white,
fontSize: 20,
fontSize: 24,
fontWeight: FontWeight.w700,
shadows: const <Shadow>[
shadows: <Shadow>[
Shadow(
offset: Offset(0, 0),
blurRadius: 20,
@ -313,35 +510,39 @@ Widget welcomeHome(context) {
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 480),
child: Column(children: <Widget>[
const Spacer(flex: 4),
const Spacer(),
Row(children: <Widget>[
Expanded(
child: Stack(children: <Widget>[
Padding(
padding: EdgeInsets.only(top: scaleSize(55)),
const Padding(
padding: EdgeInsets.only(top: 55),
child: Image(
image: const AssetImage(
'assets/home/gecko-bienvenue.png'),
height: scaleSize(180),
image: AssetImage('assets/home/gecko-bienvenue.png'),
height: 220,
),
),
Positioned(
left: scaleSize(160),
top: 10,
child: BubbleSpeakWithTail(text: "noLizard".tr()),
left: 180,
child: bubbleSpeak("noLizard".tr()),
),
const Positioned(
left: 200,
top: 60,
child: Image(
image: AssetImage('assets/home/bout_de_bulle.png'),
),
),
]),
),
]),
ScaledSizedBox(
width: 330,
height: 60,
SizedBox(
width: 410,
height: 70,
child: ElevatedButton(
key: keyOnboardingNewChest,
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
elevation: 4,
backgroundColor: orangeC,
foregroundColor: Colors.white, elevation: 4,
backgroundColor: orangeC, // foreground
),
onPressed: () {
Navigator.push(
@ -355,21 +556,19 @@ Widget welcomeHome(context) {
},
child: Text(
'createWallet'.tr(),
style: scaledTextStyle(
fontSize: 21,
fontWeight: FontWeight.w600,
color: Colors.white),
style: const TextStyle(
fontSize: 24, fontWeight: FontWeight.w600),
),
),
),
ScaledSizedBox(height: scaleSize(25)),
ScaledSizedBox(
width: 330,
height: 60,
SizedBox(height: 25 * ratio),
SizedBox(
width: 410,
height: 70,
child: OutlinedButton(
key: keyRestoreChest,
style: OutlinedButton.styleFrom(
side: BorderSide(width: scaleSize(4), color: orangeC)),
side: const BorderSide(width: 4, color: orangeC)),
onPressed: () {
Navigator.push(
context,
@ -382,14 +581,14 @@ Widget welcomeHome(context) {
},
child: Text(
"restoreWallet".tr(),
style: scaledTextStyle(
fontSize: 21,
style: const TextStyle(
fontSize: 24,
color: orangeC,
fontWeight: FontWeight.w600),
),
),
),
const Spacer(flex: 3),
SizedBox(height: isTall ? 100 : 50)
]),
),
),
@ -398,3 +597,19 @@ Widget welcomeHome(context) {
]),
);
}
Widget bubbleSpeak(String text, {double? long, Key? textKey}) {
return Bubble(
padding: long == null
? const BubbleEdges.all(20)
: BubbleEdges.symmetric(horizontal: long, vertical: 30),
elevation: 5,
color: backgroundColor,
child: Text(
text,
key: textKey,
style: const TextStyle(
color: Colors.black, fontSize: 21, fontWeight: FontWeight.w400),
),
);
}

View File

@ -3,13 +3,17 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:durt/durt.dart';
import 'package:flutter/services.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/stateful_wrapper.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/widgets/commons/top_appbar.dart';
import 'package:gecko/screens/myWallets/unlocking_wallet.dart';
import 'package:provider/provider.dart';
class ChangePinScreen extends StatefulWidget with ChangeNotifier {
class ChangePinScreen extends StatelessWidget with ChangeNotifier {
ChangePinScreen(
{Key? keyMyWallets,
required this.walletName,
@ -18,35 +22,45 @@ class ChangePinScreen extends StatefulWidget with ChangeNotifier {
final String? walletName;
final MyWalletsProvider walletProvider;
@override
State<ChangePinScreen> createState() => _ChangePinScreenState();
}
class _ChangePinScreenState extends State<ChangePinScreen> {
final newPin = TextEditingController();
@override
void initState() {
newPin.text = randomSecretCode(pinLength);
super.initState();
}
final TextEditingController newPin = TextEditingController();
@override
Widget build(BuildContext context) {
final sub = Provider.of<SubstrateSdk>(context, listen: false);
final myWalletProvider =
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
return PopScope(
onPopInvoked: (_) {
return WillPopScope(
onWillPop: () {
newPin.text = '';
return Future<bool>.value(true);
},
child: Scaffold(
resizeToAvoidBottomInset: false,
appBar: GeckoAppBar(widget.walletName!),
appBar: AppBar(
elevation: 1,
toolbarHeight: 60 * ratio,
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.black),
onPressed: () {
newPin.text = '';
Navigator.of(context).pop();
}),
title: SizedBox(
height: 22,
child: Text(walletName!),
),
),
body: Center(
child: SafeArea(
child: Column(children: <Widget>[
StatefulWrapper(
onInit: () {
newPin.text = randomSecretCode(pinLength);
},
child: Container(),
),
const SizedBox(height: 80),
Text(
'choosePassword'.tr(),
@ -85,20 +99,31 @@ class _ChangePinScreenState extends State<ChangePinScreen> {
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
foregroundColor: Colors.black,
elevation: 12,
backgroundColor: Colors.green[400],
foregroundColor: Colors.black, elevation: 12,
backgroundColor: Colors.green[400], // foreground
),
onPressed: () async {
final defaultWallet = myWalletProvider.getDefaultWallet();
WalletData defaultWallet =
myWalletProvider.getDefaultWallet();
if (!await myWalletProvider.askPinCode()) return;
await sub.changePassword(context, defaultWallet.address,
widget.walletProvider.pinCode, newPin.text);
widget.walletProvider.pinCode = newPin.text;
newPin.text = '';
Navigator.pop(context);
String? pin;
if (myWalletProvider.pinCode == '') {
pin = await Navigator.push(
context,
MaterialPageRoute(
builder: (homeContext) {
return UnlockingWallet(wallet: defaultWallet);
},
),
);
}
if (pin != null || myWalletProvider.pinCode != '') {
await sub.changePassword(context, defaultWallet.address!,
walletProvider.pinCode, newPin.text);
walletProvider.pinCode = newPin.text;
newPin.text = '';
Navigator.pop(context);
}
},
child: Text(
'confirm'.tr(),

View File

@ -4,16 +4,18 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/chest_data.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:flutter/services.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:gecko/providers/chest_provider.dart';
import 'package:gecko/providers/home.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/screens/common_elements.dart';
import 'package:gecko/screens/myWallets/change_pin.dart';
import 'package:gecko/screens/myWallets/custom_derivations.dart';
import 'package:gecko/screens/myWallets/show_seed.dart';
import 'package:gecko/widgets/bottom_app_bar.dart';
import 'package:gecko/widgets/commons/offline_info.dart';
import 'package:gecko/widgets/commons/top_appbar.dart';
import 'package:gecko/screens/myWallets/unlocking_wallet.dart';
import 'package:provider/provider.dart';
class ChestOptions extends StatelessWidget {
@ -23,105 +25,132 @@ class ChestOptions extends StatelessWidget {
@override
Widget build(BuildContext context) {
final chestProvider = Provider.of<ChestProvider>(context, listen: false);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
ChestProvider chestProvider =
Provider.of<ChestProvider>(context, listen: false);
HomeProvider homeProvider =
Provider.of<HomeProvider>(context, listen: false);
ChestData currentChest = chestBox.get(configBox.get('currentChest'))!;
return Scaffold(
backgroundColor: backgroundColor,
resizeToAvoidBottomInset: false,
appBar: GeckoAppBar(currentChest.name!),
bottomNavigationBar: const GeckoBottomAppBar(),
appBar: AppBar(
elevation: 1,
toolbarHeight: 60 * ratio,
// leading: IconButton(
// icon: const Icon(Icons.arrow_back, color: Colors.black),
// onPressed: () {
// // Navigator.popUntil(
// // context,
// // ModalRoute.withName('/mywallets'),
// // );
// Navigator.pop(context);
// }),
title: SizedBox(
height: 22,
child: Text(currentChest.name!),
)),
bottomNavigationBar: homeProvider.bottomAppBar(context),
body: Stack(children: [
Builder(
builder: (ctx) => SafeArea(
child: Column(children: <Widget>[
ScaledSizedBox(height: 20),
SizedBox(height: 30 * ratio),
InkWell(
key: keyShowSeed,
onTap: () async {
final myWalletProvider =
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
if (!await myWalletProvider.askPinCode()) return;
Navigator.push(
WalletData? defaultWallet =
myWalletProvider.getDefaultWallet();
String? pin;
pin = await Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ShowSeed(
walletName: currentChest.name,
walletProvider: walletProvider,
);
}),
MaterialPageRoute(
builder: (homeContext) {
return UnlockingWallet(wallet: defaultWallet);
},
),
);
if (pin != null) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ShowSeed(
walletName: currentChest.name,
walletProvider: walletProvider,
);
}),
);
}
},
child: ScaledSizedBox(
height: 60,
child: SizedBox(
height: 50,
child: Row(children: <Widget>[
ScaledSizedBox(width: 20),
const SizedBox(width: 20),
Image.asset(
'assets/onBoarding/phrase_de_restauration_flou.png',
width: scaleSize(60),
width: 60,
),
ScaledSizedBox(width: 13),
ScaledSizedBox(
width: 270,
child: Text(
'displayMnemonic'.tr(),
style: scaledTextStyle(
fontSize: 17,
color: orangeC,
),
const SizedBox(width: 15),
Text(
'displayMnemonic'.tr(),
style: const TextStyle(
fontSize: 20,
color: orangeC,
),
),
]),
),
),
ScaledSizedBox(height: 2),
SizedBox(height: 10 * ratio),
Consumer<SubstrateSdk>(builder: (context, sub, _) {
return InkWell(
key: keyChangePin,
onTap: null,
// sub.nodeConnected
// ? () async {
// // await _chestProvider.changePin(context, cesiumWallet);
// String? pinResult = await Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) {
// return ChangePinScreen(
// walletName: currentChest.name,
// walletProvider: walletProvider,
// );
// },
// ),
// );
onTap: sub.nodeConnected
? () async {
// await _chestProvider.changePin(context, cesiumWallet);
String? pinResult = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return ChangePinScreen(
walletName: currentChest.name,
walletProvider: walletProvider,
);
},
),
);
// if (pinResult != null) {
// walletProvider.pinCode = pinResult;
// }
// }
// : null,
child: ScaledSizedBox(
height: 60,
if (pinResult != null) {
walletProvider.pinCode = pinResult;
}
}
: null,
child: SizedBox(
height: 50,
child: Row(children: <Widget>[
ScaledSizedBox(width: 30),
const SizedBox(width: 26),
Image.asset(
'assets/chests/secret_code.png',
height: scaleSize(22),
height: 25,
),
ScaledSizedBox(width: 18),
const SizedBox(width: 18),
Text(
'changePassword'.tr(),
style: scaledTextStyle(
fontSize: 17,
style: TextStyle(
fontSize: 20,
color: sub.nodeConnected
? Colors.grey[500]
? Colors.black
: Colors.grey[500]),
),
])),
);
}),
ScaledSizedBox(height: 2),
SizedBox(height: 10 * ratio),
Consumer<SubstrateSdk>(builder: (context, sub, _) {
return InkWell(
key: keycreateRootDerivation,
@ -137,19 +166,19 @@ class ChestOptions extends StatelessWidget {
);
}
: null,
child: ScaledSizedBox(
height: 60,
child: SizedBox(
height: 50,
child: Row(children: <Widget>[
ScaledSizedBox(width: 37),
Icon(
const SizedBox(width: 35),
const Icon(
Icons.manage_accounts,
size: scaleSize(31),
size: 33,
),
ScaledSizedBox(width: 23),
const SizedBox(width: 25),
Text(
'createDerivation'.tr(),
style: scaledTextStyle(
fontSize: 17,
style: TextStyle(
fontSize: 20,
color: sub.nodeConnected
? Colors.black
: Colors.grey[500]),
@ -158,26 +187,26 @@ class ChestOptions extends StatelessWidget {
),
);
}),
ScaledSizedBox(height: 2),
SizedBox(height: 10 * ratio),
InkWell(
key: keyDeleteChest,
onTap: () async {
await chestProvider.deleteChest(context, currentChest);
},
child: ScaledSizedBox(
height: 60,
child: SizedBox(
height: 50,
child: Row(children: <Widget>[
ScaledSizedBox(width: 32),
const SizedBox(width: 28),
Image.asset(
'assets/walletOptions/trash.png',
height: scaleSize(38),
height: 45,
),
ScaledSizedBox(width: 22),
const SizedBox(width: 20),
Text(
'deleteChest'.tr(),
style: scaledTextStyle(
fontSize: 17,
color: const Color(0xffD80000),
style: const TextStyle(
fontSize: 20,
color: Color(0xffD80000),
),
),
]),
@ -186,7 +215,7 @@ class ChestOptions extends StatelessWidget {
]),
),
),
const OfflineInfo(),
CommonElements().offlineInfo(context),
]),
);
}

View File

@ -1,13 +1,16 @@
// ignore_for_file: use_build_context_synchronously
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:flutter/material.dart';
import 'package:gecko/screens/myWallets/restore_chest.dart';
import 'package:gecko/screens/myWallets/unlocking_wallet.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:gecko/screens/myWallets/wallets_home.dart';
import 'package:gecko/screens/onBoarding/5.dart';
import 'package:provider/provider.dart';
@ -21,21 +24,27 @@ class ChooseChest extends StatefulWidget {
}
class _ChooseChestState extends State<ChooseChest> {
final tplController = TextEditingController();
TextEditingController tplController = TextEditingController();
CarouselController buttonCarouselController = CarouselController();
int? currentChest = configBox.get('currentChest');
@override
Widget build(BuildContext context) {
final myWalletProvider = Provider.of<MyWalletsProvider>(context);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context);
return Scaffold(
backgroundColor: backgroundColor,
appBar: AppBar(
toolbarHeight: scaleSize(57), title: Text('selectMyChest'.tr())),
toolbarHeight: 60 * ratio,
title: SizedBox(
height: 22,
child: Text('selectMyChest'.tr()),
)),
body: SafeArea(
child: Column(children: <Widget>[
const SizedBox(height: 160),
SizedBox(height: 160 * ratio),
CarouselSlider(
carouselController: buttonCarouselController,
options: CarouselOptions(
@ -96,25 +105,42 @@ class _ChooseChestState extends State<ChooseChest> {
);
}).toList(),
),
const SizedBox(height: 80),
SizedBox(height: 80 * ratio),
SizedBox(
width: 400,
height: 70,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
foregroundColor: Colors.black,
backgroundColor: orangeC,
backgroundColor: orangeC, // foreground
),
onPressed: () async {
await configBox.put('currentChest', currentChest);
myWalletProvider.pinCode = '';
if (!await myWalletProvider.askPinCode()) return;
WalletData? defaultWallet =
myWalletProvider.getDefaultWallet();
myWalletProvider.reload();
await Navigator.push(
context,
MaterialPageRoute(
builder: (homeContext) {
return UnlockingWallet(wallet: defaultWallet);
},
),
);
Navigator.popUntil(
context,
ModalRoute.withName('/'),
);
Navigator.pushNamed(context, '/mywallets');
if (myWalletProvider.pinCode != '') {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return const WalletsHome();
}),
);
}
},
child: Text(
'openThisChest'.tr(),

View File

@ -0,0 +1,235 @@
// ignore_for_file: use_build_context_synchronously, must_be_immutable
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart';
import 'package:gecko/globals.dart';
import 'package:flutter/material.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/providers/wallet_options.dart';
import 'package:gecko/screens/myWallets/wallets_home.dart';
import 'package:provider/provider.dart';
// import 'package:gecko/models/home.dart';
// import 'package:provider/provider.dart';
class ChooseWalletScreen extends StatelessWidget {
ChooseWalletScreen({Key? key, required this.pin}) : super(key: key);
final String pin;
WalletData? selectedWallet;
@override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
final int chest = configBox.get('currentChest');
return Scaffold(
backgroundColor: backgroundColor,
appBar: AppBar(
toolbarHeight: 60 * ratio,
title: SizedBox(
height: 22,
child: Text('choiceOfSourceWallet'.tr()),
)),
body: SafeArea(
child: Stack(children: [
myWalletsTiles(context, chest),
Positioned.fill(
bottom: 60,
child: Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
width: 470,
height: 70,
child: ElevatedButton(
key: keyConfirm,
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white, elevation: 4,
backgroundColor: orangeC, // foreground
),
onPressed: () async {
await sub.setCurrentWallet(selectedWallet!);
sub.reload();
// Navigator.pop(context);
Navigator.pop(context);
Navigator.pop(context);
},
child: Text(
'chooseThisWallet'.tr(),
style: const TextStyle(
fontSize: 24, fontWeight: FontWeight.w600),
),
),
),
),
),
]),
));
}
Widget myWalletsTiles(BuildContext context, int currentChest) {
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context);
// SubstrateSdk _sub = Provider.of<SubstrateSdk>(context, listen: false);
final bool isWalletsExists = myWalletProvider.checkIfWalletExist();
WalletData? defaultWallet = myWalletProvider.getDefaultWallet();
selectedWallet ??= defaultWallet;
myWalletProvider.readAllWallets(currentChest);
if (!isWalletsExists) {
return const Text('');
}
if (myWalletProvider.listWallets.isEmpty) {
return Column(children: const <Widget>[
Center(
child: Text(
'Veuillez générer votre premier portefeuille',
style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500),
)),
]);
}
List listWallets = myWalletProvider.listWallets;
final double screenWidth = MediaQuery.of(context).size.width;
int nTule = 2;
if (screenWidth >= 900) {
nTule = 4;
} else if (screenWidth >= 650) {
nTule = 3;
}
return CustomScrollView(slivers: <Widget>[
const SliverToBoxAdapter(child: SizedBox(height: 20)),
SliverGrid.count(
key: keyListWallets,
crossAxisCount: nTule,
childAspectRatio: 1,
crossAxisSpacing: 0,
mainAxisSpacing: 0,
children: <Widget>[
for (WalletData repository in listWallets as Iterable<WalletData>)
Padding(
padding: const EdgeInsets.all(16),
child: GestureDetector(
key: keySelectThisWallet(repository.address!),
onTap: () {
selectedWallet = repository;
myWalletProvider.reload();
},
child: ClipOvalShadow(
shadow: const Shadow(
color: Colors.transparent,
offset: Offset(0, 0),
blurRadius: 5,
),
clipper: CustomClipperOval(),
child: ClipRRect(
borderRadius:
const BorderRadius.all(Radius.circular(12)),
child: Column(children: <Widget>[
Expanded(
child: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
gradient: RadialGradient(
radius: 0.6,
colors: [
Colors.green[400]!,
const Color(0xFFE7E7A6),
],
)),
child: repository.imageCustomPath == null
? Image.asset(
'assets/avatars/${repository.imageDefaultPath}',
alignment: Alignment.bottomCenter,
scale: 0.5,
)
: Container(
width: 120,
height: 120,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.transparent,
image: DecorationImage(
fit: BoxFit.contain,
image: FileImage(
File(repository.imageCustomPath!),
),
),
),
),
)),
balanceBuilder(context, repository.address!,
selectedWallet!.address == repository.address!),
ListTile(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
bottom: Radius.circular(12),
),
),
tileColor:
repository.address == selectedWallet!.address
? orangeC
: const Color(0xffFFD58D),
title: Center(
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 5),
child: Text(
repository.name!,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 17.0,
color: repository.address ==
selectedWallet!.address
? const Color(0xffF9F9F1)
: Colors.black,
fontStyle: FontStyle.italic),
),
),
),
onTap: () async {
selectedWallet = repository;
myWalletProvider.reload();
},
)
]),
),
),
)),
]),
]);
}
Widget balanceBuilder(context, String address, bool isDefault) {
return Container(
width: double.infinity,
color: isDefault ? orangeC : yellowC,
child: SizedBox(
height: 30,
child: Column(children: [
const Spacer(),
// Text(
// '0.0 gd',
// textAlign: TextAlign.center,
// style: TextStyle(color: isDefault ? Colors.white : Colors.black),
// ),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
balance(
context, address, 16, isDefault ? Colors.white : Colors.black),
])
]),
),
);
}
}

View File

@ -1,12 +1,12 @@
// ignore_for_file: use_build_context_synchronously
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart';
import 'package:gecko/globals.dart';
import 'package:flutter/material.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/widgets/commons/top_appbar.dart';
import 'package:gecko/screens/myWallets/unlocking_wallet.dart';
import 'package:provider/provider.dart';
class CustomDerivation extends StatefulWidget {
@ -27,7 +27,8 @@ class _CustomDerivationState extends State<CustomDerivation> {
@override
Widget build(BuildContext context) {
final myWalletProvider =
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
final derivationList = <String>[
@ -35,7 +36,9 @@ class _CustomDerivationState extends State<CustomDerivation> {
for (var i = 0; i < 51; i += 1) i.toString()
];
for (WalletData wallet in myWalletProvider.listWallets) {
final listWallets = myWalletProvider.readAllWallets();
for (WalletData wallet in listWallets) {
derivationList.remove(wallet.derivation.toString());
if (wallet.derivation == -1) {
derivationList.remove('root');
@ -48,27 +51,28 @@ class _CustomDerivationState extends State<CustomDerivation> {
return Scaffold(
backgroundColor: backgroundColor,
appBar: GeckoAppBar('createCustomDerivation'.tr()),
appBar: AppBar(
toolbarHeight: 60 * ratio,
title: SizedBox(
height: 22,
child: Text('createCustomDerivation'.tr()),
)),
body: Center(
child: SafeArea(
child: Column(children: <Widget>[
const Spacer(),
Text(
'chooseDerivation'.tr(),
style: scaledTextStyle(fontSize: 17),
),
ScaledSizedBox(height: 20),
ScaledSizedBox(
const SizedBox(height: 20),
SizedBox(
width: 100,
child: DropdownButton<String>(
value: dropdownValue,
menuMaxHeight: 300,
icon: Icon(
Icons.arrow_downward,
size: scaleSize(20),
),
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: scaledTextStyle(color: orangeC),
style: const TextStyle(color: orangeC),
underline: Container(
height: 2,
color: orangeC,
@ -82,14 +86,14 @@ class _CustomDerivationState extends State<CustomDerivation> {
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: ScaledSizedBox(
child: SizedBox(
width: 75,
child: Row(children: [
const Spacer(),
Text(
value,
style: scaledTextStyle(
fontSize: 17, color: Colors.black),
style: const TextStyle(
fontSize: 20, color: Colors.black),
),
const Spacer(),
]),
@ -98,38 +102,56 @@ class _CustomDerivationState extends State<CustomDerivation> {
),
),
const Spacer(flex: 1),
ScaledSizedBox(
width: 240,
height: 55,
SizedBox(
width: 410,
height: 70,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
elevation: 4,
backgroundColor: orangeC,
foregroundColor: Colors.white, elevation: 4,
backgroundColor: orangeC, // foreground
),
onPressed: () async {
if (!await myWalletProvider.askPinCode()) return;
String newDerivationName =
'${'wallet'.tr()} ${myWalletProvider.listWallets.last.number! + 2}';
if (dropdownValue == 'root') {
await myWalletProvider.generateRootWallet(
context, 'rootWallet'.tr());
} else {
await myWalletProvider.generateNewDerivation(
WalletData? defaultWallet =
myWalletProvider.getDefaultWallet();
String? pin;
if (myWalletProvider.pinCode == '') {
pin = await Navigator.push(
context,
newDerivationName,
int.parse(dropdownValue!),
MaterialPageRoute(
builder: (homeContext) {
return UnlockingWallet(wallet: defaultWallet);
},
),
);
}
Navigator.popUntil(
context, ModalRoute.withName('/mywallets'));
if (pin != null || myWalletProvider.pinCode != '') {
String newDerivationName =
'${'wallet'.tr()} ${myWalletProvider.listWallets.last.number! + 2}';
if (dropdownValue == 'root') {
await myWalletProvider.generateRootWallet(
context, 'Portefeuille racine');
} else {
await myWalletProvider.generateNewDerivation(
context,
newDerivationName,
int.parse(dropdownValue!),
);
}
Navigator.pop(context);
Navigator.pop(context);
// Navigator.push(
// context,
// MaterialPageRoute(builder: (context) {
// return const WalletsHome();
// }),
// );
}
},
child: Text(
'validate'.tr(),
style: scaledTextStyle(
fontSize: 19,
fontWeight: FontWeight.w600,
color: Colors.white),
style: const TextStyle(
fontSize: 24, fontWeight: FontWeight.w600),
),
),
),

View File

@ -1,60 +1,76 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart';
import 'package:gecko/globals.dart';
import 'package:flutter/material.dart';
import 'package:gecko/models/migrate_wallet_checks.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/providers/wallet_options.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/widgets/certifications.dart';
import 'package:gecko/widgets/commons/top_appbar.dart';
import 'package:gecko/widgets/idty_status.dart';
import 'package:provider/provider.dart';
class ImportG1v1 extends StatelessWidget {
const ImportG1v1({Key? key}) : super(key: key);
static const int debouneTime = 600;
@override
Widget build(BuildContext context) {
final myWalletProvider =
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
WalletOptionsProvider walletOptions =
Provider.of<WalletOptionsProvider>(context, listen: false);
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
Timer? debounce;
const int debouneTime = 300;
WalletData selectedWallet = myWalletProvider.getDefaultWallet();
bool canValidate = false;
String validationStatus = '';
return PopScope(
onPopInvoked: (_) {
return WillPopScope(
onWillPop: () {
resetScreen(context);
return Future<bool>.value(true);
},
child: Scaffold(
backgroundColor: backgroundColor,
appBar: GeckoAppBar('importOldAccount'.tr()),
appBar: AppBar(
toolbarHeight: 60 * ratio,
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.black),
onPressed: () {
resetScreen(context);
Navigator.of(context).pop();
}),
title: SizedBox(
height: 22,
child: Text('importOldAccount'.tr()),
)),
body: SafeArea(
child: Consumer<SubstrateSdk>(builder: (context, sub, _) {
return FutureBuilder(
future: sub.getBalanceAndIdtyStatus(
sub.g1V1NewAddress, selectedWallet.address),
builder: (BuildContext context,
AsyncSnapshot<MigrateWalletChecks> status) {
sub.g1V1NewAddress, selectedWallet.address!),
builder: (BuildContext context, AsyncSnapshot<List> status) {
// log.d(_certs.data);
if (status.data == null) {
return Column(children: [
ScaledSizedBox(height: 80),
const SizedBox(height: 80),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ScaledSizedBox(
children: const [
SizedBox(
height: 35,
width: 35,
child: const CircularProgressIndicator(
child: CircularProgressIndicator(
color: orangeC,
strokeWidth: 4,
),
@ -63,38 +79,58 @@ class ImportG1v1 extends StatelessWidget {
]);
}
final statusData = status.data!;
final Map balance = status.data?[0] ?? {};
final String idtyStatus = status.data?[1];
final String myIdtyStatus = status.data?[2];
final bool hasConsumer = status.data?[3] ?? false;
// log.d('hasconsumer: $hasConsumer');
if (balance['transferableBalance'] != 0 && !hasConsumer) {
canValidate = true;
validationStatus = '';
} else {
canValidate = false;
validationStatus = hasConsumer
? 'youMustWaitBeforeCashoutThisAccount'.tr(args: ['X'])
: 'thisAccountIsEmpty'.tr();
}
if (idtyStatus != 'noid' && myIdtyStatus != 'noid') {
canValidate = false;
validationStatus =
'youCannotMigrateIdentityToExistingIdentity'.tr();
}
if (sub.g1V1NewAddress == '') {
validationStatus = '';
}
final bool isUdUnit = configBox.get('isUdUnit') ?? false;
final unit = isUdUnit ? 'ud'.tr(args: ['']) : currencyName;
return Column(children: <Widget>[
ScaledSizedBox(height: 10),
const SizedBox(height: 20),
TextFormField(
key: keyCesiumId,
autofocus: true,
autocorrect: false,
onChanged: (text) {
if (debounce?.isActive ?? false) {
debounce!.cancel();
}
debounce = Timer(
const Duration(milliseconds: debouneTime), () {
if (sub.csSalt.text != '' &&
sub.csPassword.text != '') {
sub.reload();
sub.csToV2Address(
sub.csSalt.text, sub.csPassword.text);
}
sub.reload();
sub.csToV2Address(
sub.csSalt.text, sub.csPassword.text);
});
},
keyboardType: TextInputType.text,
controller: sub.csSalt,
obscureText: !sub.isCesiumIDVisible,
style: scaledTextStyle(fontSize: 14),
obscureText: !sub
.isCesiumIDVisible, //This will obscure text dynamically
decoration: InputDecoration(
hintText: 'enterCesiumId'.tr(),
hintStyle: scaledTextStyle(fontSize: 14),
suffixIcon: IconButton(
key: keyCesiumIdVisible,
icon: Icon(
@ -102,7 +138,6 @@ class ImportG1v1 extends StatelessWidget {
? Icons.visibility_off
: Icons.visibility,
color: Colors.black,
size: scaleSize(22),
),
onPressed: () {
sub.cesiumIDisVisible();
@ -110,11 +145,10 @@ class ImportG1v1 extends StatelessWidget {
),
),
),
ScaledSizedBox(height: 7),
const SizedBox(height: 20),
TextFormField(
key: keyCesiumPassword,
autofocus: true,
autocorrect: false,
onChanged: (text) {
if (debounce?.isActive ?? false) {
debounce!.cancel();
@ -122,28 +156,23 @@ class ImportG1v1 extends StatelessWidget {
debounce = Timer(
const Duration(milliseconds: debouneTime), () {
sub.g1V1NewAddress = '';
if (sub.csSalt.text != '' &&
sub.csPassword.text != '') {
sub.reload();
sub.csToV2Address(
sub.csSalt.text, sub.csPassword.text);
}
sub.reload();
sub.csToV2Address(
sub.csSalt.text, sub.csPassword.text);
});
},
keyboardType: TextInputType.text,
controller: sub.csPassword,
obscureText: !sub.isCesiumIDVisible,
style: scaledTextStyle(fontSize: 14),
obscureText: !sub
.isCesiumIDVisible, //This will obscure text dynamically
decoration: InputDecoration(
hintText: 'enterCesiumPassword'.tr(),
hintStyle: scaledTextStyle(fontSize: 14),
suffixIcon: IconButton(
icon: Icon(
sub.isCesiumIDVisible
? Icons.visibility_off
: Icons.visibility,
color: Colors.black,
size: scaleSize(22),
),
onPressed: () {
sub.cesiumIDisVisible();
@ -151,84 +180,52 @@ class ImportG1v1 extends StatelessWidget {
),
),
),
ScaledSizedBox(height: 20),
Visibility(
visible: sub.g1V1OldPubkey != '',
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
children: [
GestureDetector(
key: keyCopyPubkey,
onTap: () {
Clipboard.setData(
ClipboardData(text: sub.g1V1OldPubkey));
snackCopyKey(context);
},
child: Text(
'v1: ${getShortPubkey(sub.g1V1OldPubkey)}',
style: scaledTextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
fontFamily: 'Monospace'),
),
),
ScaledSizedBox(height: 5),
GestureDetector(
key: keyCopyAddress,
onTap: () {
Clipboard.setData(
ClipboardData(text: sub.g1V1OldPubkey));
snackCopyKey(context);
},
child: Text(
'v2: ${getShortPubkey(sub.g1V1NewAddress)}',
style: scaledTextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
fontFamily: 'Monospace'),
),
),
],
),
ScaledSizedBox(width: 30),
Column(
children: [
Text(
'${statusData.balance['transferableBalance']} $unit',
style: scaledTextStyle(fontSize: 16),
),
IdentityStatus(
address: sub.g1V1NewAddress,
isOwner: false,
color: Colors.black),
ScaledSizedBox(width: 10),
Certifications(
address: sub.g1V1NewAddress, size: 14)
],
),
],
const SizedBox(height: 20),
GestureDetector(
key: keyCopyAddress,
onTap: () {
Clipboard.setData(
ClipboardData(text: sub.g1V1NewAddress));
snackCopyKey(context);
},
child: Text(
getShortPubkey(sub.g1V1NewAddress),
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
),
ScaledSizedBox(height: 20),
const SizedBox(height: 20),
Text(
'migrateToThisWallet'.tr(),
style: scaledTextStyle(fontSize: 16),
'${balance['transferableBalance']} $unit',
style: const TextStyle(fontSize: 17),
),
ScaledSizedBox(height: 5),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
walletOptions.idtyStatus(context, sub.g1V1NewAddress,
isOwner: false, color: Colors.black),
const SizedBox(width: 10),
getCerts(context, sub.g1V1NewAddress, 14)
],
),
const SizedBox(height: 30),
Text('selectDestWallet'.tr()),
const SizedBox(height: 5),
DropdownButtonHideUnderline(
key: keySelectWallet,
child: DropdownButton(
// alignment: AlignmentDirectional.topStart,
value: selectedWallet,
icon: const Icon(Icons.keyboard_arrow_down),
items: myWalletProvider.listWallets.map((wallet) {
return DropdownMenuItem(
key: keySelectThisWallet(wallet.address),
key: keySelectThisWallet(wallet.address!),
value: wallet,
child: Text(
wallet.name!,
style: scaledTextStyle(fontSize: 16),
style: const TextStyle(fontSize: 18),
),
);
}).toList(),
@ -238,18 +235,17 @@ class ImportG1v1 extends StatelessWidget {
},
),
),
ScaledSizedBox(height: 10),
ScaledSizedBox(
width: 320,
height: 50,
const SizedBox(height: 30),
SizedBox(
width: 380 * ratio,
height: 60 * ratio,
child: ElevatedButton(
key: keyConfirm,
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
elevation: 4,
backgroundColor: orangeC,
foregroundColor: Colors.white, elevation: 4,
backgroundColor: orangeC, // foreground
),
onPressed: statusData.canValidate
onPressed: canValidate
? () async {
WalletData? defaultWallet =
myWalletProvider.getDefaultWallet();
@ -267,25 +263,23 @@ class ImportG1v1 extends StatelessWidget {
);
}
final transactionId = await sub.migrateCsToV2(
sub.migrateCsToV2(
sub.csSalt.text,
sub.csPassword.text,
selectedWallet.address,
selectedWallet.address!,
destPassword:
pin ?? myWalletProvider.pinCode,
balance: statusData.balance,
idtyStatus: statusData.idtyStatus);
Navigator.pop(context);
await Navigator.push(
balance: balance,
idtyStatus: idtyStatus);
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return TransactionInProgress(
transactionId: transactionId,
transType: 'identityMigration',
fromAddress:
getShortPubkey(sub.g1V1NewAddress),
toAddress: getShortPubkey(
selectedWallet.address));
selectedWallet.address!));
}),
);
resetScreen(context);
@ -293,17 +287,17 @@ class ImportG1v1 extends StatelessWidget {
: null,
child: Text(
'migrateAccount'.tr(),
style: scaledTextStyle(
fontSize: 19, fontWeight: FontWeight.w600),
style: TextStyle(
fontSize: 23 * ratio,
fontWeight: FontWeight.w600),
),
),
),
ScaledSizedBox(height: 10),
const SizedBox(height: 10),
Text(
statusData.validationStatus,
validationStatus,
textAlign: TextAlign.center,
style: scaledTextStyle(
fontSize: 12, color: Colors.grey[600]),
style: TextStyle(fontSize: 15, color: Colors.grey[600]),
)
]);
});
@ -314,11 +308,10 @@ class ImportG1v1 extends StatelessWidget {
}
void resetScreen(BuildContext context) {
final sub = Provider.of<SubstrateSdk>(context, listen: false);
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
sub.csSalt.text = '';
sub.csPassword.text = '';
sub.g1V1NewAddress = '';
sub.g1V1OldPubkey = '';
}
}

View File

@ -1,16 +1,17 @@
// ignore_for_file: use_build_context_synchronously
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart';
import 'package:gecko/globals.dart';
import 'package:flutter/material.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/widgets/commons/common_elements.dart';
import 'package:gecko/screens/common_elements.dart';
import 'package:gecko/screens/myWallets/migrate_identity.dart';
import 'package:gecko/screens/myWallets/unlocking_wallet.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/wallet_data.dart';
// import 'package:gecko/providers/my_wallets.dart';
@ -26,43 +27,49 @@ class ManageMembership extends StatelessWidget {
@override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
final sub = Provider.of<SubstrateSdk>(context);
return Scaffold(
backgroundColor: backgroundColor,
appBar: GeckoAppBar('manageMembership'.tr()),
appBar: AppBar(
toolbarHeight: 60 * ratio,
title: SizedBox(
height: 22,
child: const Text('manageMembership').tr(),
)),
body: SafeArea(
child: Column(children: <Widget>[
ScaledSizedBox(height: 20),
const SizedBox(height: 20),
migrateIdentity(context),
ScaledSizedBox(height: 10),
const SizedBox(height: 10),
FutureBuilder(
future: sub.isSmith(address),
future: sub.isSmithGet(address),
builder: (BuildContext context, AsyncSnapshot<bool> isSmith) {
if (isSmith.data ?? false) {
return ScaledSizedBox(
height: 75,
return SizedBox(
height: 70,
child: Row(
children: <Widget>[
ScaledSizedBox(width: 17),
const SizedBox(width: 20),
Image.asset(
'assets/skull_Icon.png',
color: Colors.grey[500],
height: scaleSize(28),
height: 30,
),
ScaledSizedBox(width: 12),
const SizedBox(width: 16),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('revokeMyIdentity'.tr(),
style: scaledTextStyle(
fontSize: 18,
style: TextStyle(
fontSize: 20,
color: Colors.grey[500])),
ScaledSizedBox(height: 2),
const SizedBox(height: 5),
Text("youCannotRevokeThisIdentity".tr(),
style: scaledTextStyle(
fontSize: 13,
style: TextStyle(
fontSize: 14,
color: Colors.grey[500])),
]),
],
@ -71,6 +78,7 @@ class ManageMembership extends StatelessWidget {
return revokeMyIdentity(context);
}
})
// const SizedBox(height: 20),
]),
));
}
@ -86,13 +94,13 @@ class ManageMembership extends StatelessWidget {
}),
);
},
child: ScaledSizedBox(
height: 55,
child: Row(children: <Widget>[
ScaledSizedBox(width: 16),
Icon(Icons.change_circle_outlined, size: scaleSize(32)),
ScaledSizedBox(width: 11.5),
Text('Migrer mon identité', style: scaledTextStyle(fontSize: 18)),
child: SizedBox(
height: 60,
child: Row(children: const <Widget>[
SizedBox(width: 16),
Icon(Icons.change_circle_outlined, size: 35),
SizedBox(width: 11.5),
Text('Migrer mon identité', style: TextStyle(fontSize: 20)),
]),
),
);
@ -102,43 +110,58 @@ class ManageMembership extends StatelessWidget {
return InkWell(
key: keyRevokeIdty,
onTap: () async {
// TODOO: Generate revoke document, and understand extrinsic identity.revokeIdentity options
final answer = await confirmPopup(context,
'Êtes-vous certains de vouloir révoquer définitivement cette identité ?') ??
false;
if (!answer) return;
final myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
final sub = Provider.of<SubstrateSdk>(context, listen: false);
if (answer) {
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
if (!await myWalletProvider.askPinCode()) return;
// MyWalletsProvider mw = MyWalletsProvider();
// final wallet = mw.getWalletDataByAddress(address);
// await sub.setCurrentWallet(wallet!);
final transactionId =
await sub.revokeIdentity(address, myWalletProvider.pinCode);
WalletData? defaultWallet = myWalletProvider.getDefaultWallet();
String? pin;
if (myWalletProvider.pinCode == '') {
pin = await Navigator.push(
context,
MaterialPageRoute(
builder: (homeContext) {
return UnlockingWallet(wallet: defaultWallet);
},
),
);
}
if (pin != null || myWalletProvider.pinCode != '') {
sub.revokeIdentity(address, myWalletProvider.pinCode);
}
Navigator.pop(context);
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return TransactionInProgress(
transactionId: transactionId,
transType: 'revokeIdty',
fromAddress: getShortPubkey(address),
toAddress: getShortPubkey(address));
}),
);
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return TransactionInProgress(
transType: 'revokeIdty',
fromAddress: getShortPubkey(address),
toAddress: getShortPubkey(address));
}),
);
}
},
child: ScaledSizedBox(
height: 55,
child: SizedBox(
height: 60,
child: Row(children: <Widget>[
ScaledSizedBox(width: 20),
const SizedBox(width: 20),
Image.asset(
'assets/skull_Icon.png',
height: scaleSize(28),
height: 30,
),
ScaledSizedBox(width: 16),
Text('Révoquer mon adhésion', style: scaledTextStyle(fontSize: 18)),
const SizedBox(width: 16),
const Text('Révoquer mon adhésion', style: TextStyle(fontSize: 20)),
]),
),
);

View File

@ -1,21 +1,18 @@
// ignore_for_file: use_build_context_synchronously
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:gecko/globals.dart';
import 'package:flutter/material.dart';
import 'package:gecko/models/migrate_wallet_checks.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:gecko/providers/duniter_indexer.dart';
import 'package:gecko/providers/generate_wallets.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/providers/wallet_options.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/widgets/commons/top_appbar.dart';
import 'package:polkawallet_sdk/api/apiKeyring.dart';
import 'package:provider/provider.dart';
class MigrateIdentityScreen extends StatelessWidget {
@ -23,237 +20,240 @@ class MigrateIdentityScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final walletOptions =
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
// HomeProvider _homeProvider = Provider.of<HomeProvider>(context);
WalletOptionsProvider walletOptions =
Provider.of<WalletOptionsProvider>(context, listen: false);
final myWalletProvider =
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
final generatedWalletsProvider =
Provider.of<GenerateWalletsProvider>(context, listen: false);
final duniterIndexer = Provider.of<DuniterIndexer>(context, listen: false);
final sub = Provider.of<SubstrateSdk>(context, listen: false);
DuniterIndexer duniterIndexer =
Provider.of<DuniterIndexer>(context, listen: false);
final fromAddress = walletOptions.address.text;
final newMnemonicSentence = TextEditingController();
final newWalletAddress = TextEditingController();
final defaultWallet = myWalletProvider.getDefaultWallet();
final walletsList = myWalletProvider.listWallets.toList();
late WalletData selectedWallet;
if (fromAddress == defaultWallet.address) {
selectedWallet =
walletsList[fromAddress == walletsList[0].address ? 1 : 0];
} else {
selectedWallet = defaultWallet;
}
bool canValidate = false;
String validationStatus = '';
final mdStyle = MarkdownStyleSheet(
p: scaledTextStyle(fontSize: 16, color: Colors.black, letterSpacing: 0.3),
p: const TextStyle(fontSize: 18, color: Colors.black, letterSpacing: 0.3),
textAlign: WrapAlignment.center,
);
final bool isUdUnit = configBox.get('isUdUnit') ?? false;
final unit = isUdUnit ? 'ud'.tr(args: ['']) : currencyName;
var statusData = const MigrateWalletChecks.defaultValues();
var mnemonicIsValid = false;
int? matchDerivationNbr;
String matchInfo = '';
Future scanDerivations() async {
if (!await isAddress(newWalletAddress.text) ||
!await sub.isMnemonicValid(newMnemonicSentence.text) ||
!statusData.canValidate) {
mnemonicIsValid = false;
matchInfo = '';
walletOptions.reload();
return;
}
log.d('Scan derivations to find a match');
//Scan root wallet
final addressData = await sub.sdk.api.keyring.addressFromMnemonic(
sub.currencyParameters['ss58']!,
cryptoType: CryptoType.sr25519,
mnemonic: newMnemonicSentence.text,
if (walletsList.length < 2) {
return Column(
children: [
const SizedBox(height: 80),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text(
'Vous devez avoir au moins 2 portefeuilles\npour effecter cette opération',
style: TextStyle(fontSize: 20),
)
],
)
],
);
if (addressData.address == newWalletAddress.text) {
matchDerivationNbr = -1;
mnemonicIsValid = true;
walletOptions.reload();
return;
}
//Scan derivations
for (int derivationNbr in [
for (var i = 0; i < generatedWalletsProvider.numberScan; i += 1) i
]) {
final addressData = await sub.sdk.api.keyring.addressFromMnemonic(
sub.currencyParameters['ss58']!,
cryptoType: CryptoType.sr25519,
mnemonic: newMnemonicSentence.text,
derivePath: '//$derivationNbr');
if (addressData.address == newWalletAddress.text) {
matchDerivationNbr = derivationNbr;
mnemonicIsValid = true;
matchInfo = "youCanMigrateThisIdentity".tr();
break;
} else {
mnemonicIsValid = false;
}
}
if (!mnemonicIsValid) {
matchInfo = "addressNotBelongToMnemonic".tr();
}
walletOptions.reload();
}
return Scaffold(
backgroundColor: backgroundColor,
appBar: GeckoAppBar('migrateIdentity'.tr()),
appBar: AppBar(
toolbarHeight: 60 * ratio,
title: SizedBox(
height: 22,
child: Text('importOldAccount'.tr()),
)),
body: SafeArea(
child: Column(children: <Widget>[
const Row(children: []),
ScaledSizedBox(height: 18),
ScaledSizedBox(
width: 320,
child: MarkdownBody(
data: 'areYouSureMigrateIdentity'.tr(args: [
duniterIndexer.walletNameIndexer[fromAddress] ?? '???',
'${walletOptions.balanceCache[fromAddress]} $unit'
]),
styleSheet: mdStyle),
),
ScaledSizedBox(height: 55),
Text('migrateToThisWallet'.tr(),
style: scaledTextStyle(fontSize: 16)),
ScaledSizedBox(height: 5),
ScaledSizedBox(
width: 320,
child: TextField(
controller: newMnemonicSentence,
autofocus: true,
minLines: 2,
maxLines: 2,
style: scaledTextStyle(fontSize: 14),
decoration: InputDecoration(
icon: Image.asset(
'assets/onBoarding/phrase_de_restauration_flou.png',
width: scaleSize(30),
),
hintText: 'enterYourNewMnemonic'.tr(),
hintStyle: scaledTextStyle(fontSize: 14),
focusedBorder: const UnderlineInputBorder(
borderSide: BorderSide(color: orangeC),
),
),
onChanged: (newMnemonic) async {
await scanDerivations();
},
),
),
ScaledSizedBox(height: 5),
ScaledSizedBox(
width: 320,
child: TextField(
controller: newWalletAddress,
style: scaledTextStyle(fontSize: 14),
decoration: InputDecoration(
icon: Image.asset(
'assets/walletOptions/key.png',
height: scaleSize(30),
),
hintText: 'enterYourNewAddress'.tr(args: [currencyName]),
hintStyle: scaledTextStyle(fontSize: 14),
focusedBorder: const UnderlineInputBorder(
borderSide: BorderSide(color: orangeC),
),
),
onChanged: (newAddress) async {
if (await isAddress(newAddress)) {
statusData = await sub.getBalanceAndIdtyStatus(
fromAddress, newAddress);
await scanDerivations();
} else {
statusData = const MigrateWalletChecks.defaultValues();
matchInfo = '';
walletOptions.reload();
child: Consumer<SubstrateSdk>(builder: (context, sub, _) {
return FutureBuilder(
future: sub.getBalanceAndIdtyStatus(
fromAddress, selectedWallet.address!),
builder: (BuildContext context, AsyncSnapshot<List> status) {
if (status.data == null) {
return Column(children: [
const SizedBox(height: 80),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
SizedBox(
height: 35,
width: 35,
child: CircularProgressIndicator(
color: orangeC,
strokeWidth: 4,
),
),
]),
]);
}
},
),
),
const Spacer(flex: 2),
Consumer<WalletOptionsProvider>(builder: (context, _, __) {
return ScaledSizedBox(
width: 320,
height: 55,
child: ElevatedButton(
key: keyConfirm,
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
elevation: 4,
backgroundColor: orangeC,
),
onPressed: statusData.canValidate && mnemonicIsValid
? () async {
if (!await myWalletProvider.askPinCode()) return;
await sub.importAccount(
mnemonic: newMnemonicSentence.text,
derivePath: matchDerivationNbr == -1
? ''
: "//$matchDerivationNbr",
password: 'password');
// log.d('statusData: ${status.data}');
final transactionId = await sub.migrateIdentity(
fromAddress: fromAddress,
destAddress: newWalletAddress.text,
fromPassword: myWalletProvider.pinCode,
destPassword: 'password',
withBalance: true,
fromBalance: statusData.balance);
final Map balance = status.data?[0] ?? {};
final String idtyStatus = status.data?[1];
final String myIdtyStatus = status.data?[2];
final bool hasConsumer = status.data?[3] ?? false;
sub.deleteAccounts([newWalletAddress.text]);
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return TransactionInProgress(
transactionId: transactionId,
transType: 'identityMigration',
fromAddress: getShortPubkey(fromAddress),
toAddress:
getShortPubkey(newWalletAddress.text));
}),
// log.d('hasconsumer: $hasConsumer');
if (balance['transferableBalance'] != 0 && !hasConsumer) {
canValidate = true;
validationStatus = '';
} else {
canValidate = false;
validationStatus = hasConsumer
? 'youMustWaitBeforeCashoutThisAccount'.tr(args: ['X'])
: 'thisAccountIsEmpty'.tr();
}
if (idtyStatus != 'noid' && myIdtyStatus != 'noid') {
canValidate = false;
validationStatus =
'youCannotMigrateIdentityToExistingIdentity'.tr();
}
log.d(
'tatatata: ${sub.g1V1NewAddress}, ${selectedWallet.address!}, $balance, $idtyStatus, $myIdtyStatus');
final walletsList = myWalletProvider.listWallets.toList();
walletsList
.removeWhere((element) => element.address == fromAddress);
// walletsList.add(WalletData(address: 'custom', name: 'custom'));
final bool isUdUnit = configBox.get('isUdUnit') ?? false;
final unit = isUdUnit ? 'ud'.tr(args: ['']) : currencyName;
return Column(children: <Widget>[
Row(children: const []),
const SizedBox(height: 20),
SizedBox(
width: 350,
child: MarkdownBody(
data: 'areYouSureMigrateIdentity'.tr(args: [
duniterIndexer.walletNameIndexer[fromAddress] ??
'???',
'${balance['transferableBalance']} $unit'
]),
styleSheet: mdStyle),
),
// Text(
// 'areYouSureMigrateIdentity'.tr(args: [
// duniterIndexer
// .walletNameIndexer[fromAddress]!,
// '$balance $currencyName'
// ]),
// textAlign: TextAlign.center,
// ),
const SizedBox(height: 20),
Text(
sub.g1V1NewAddress,
style: const TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.bold,
fontFamily: 'Monospace'),
),
const SizedBox(height: 30),
Text('selectDestWallet'.tr()),
const SizedBox(height: 5),
DropdownButtonHideUnderline(
key: keySelectWallet,
child: DropdownButton(
// alignment: AlignmentDirectional.topStart,
value: selectedWallet,
icon: const Icon(Icons.keyboard_arrow_down),
items: walletsList.map((wallet) {
return DropdownMenuItem(
key: keySelectThisWallet(wallet.address!),
value: wallet,
child: Text(
wallet.name!,
style: const TextStyle(fontSize: 18),
),
);
}
: null,
child: Text(
'migrateIdentity'.tr(),
style: scaledTextStyle(
fontSize: 19,
fontWeight: FontWeight.w600,
color: Colors.white),
),
),
);
}),
Consumer<WalletOptionsProvider>(builder: (context, _, __) {
return ScaledSizedBox(
width: 320,
child: Column(
children: [
ScaledSizedBox(height: 10),
Text(
statusData.validationStatus,
textAlign: TextAlign.center,
style:
scaledTextStyle(fontSize: 12, color: Colors.grey[600]),
}).toList(),
onChanged: (WalletData? newSelectedWallet) {
selectedWallet = newSelectedWallet!;
sub.reload();
},
),
),
ScaledSizedBox(height: 5),
Text(
matchInfo,
textAlign: TextAlign.center,
style:
scaledTextStyle(fontSize: 12, color: Colors.grey[600]),
const SizedBox(height: 30),
SizedBox(
width: 380 * ratio,
height: 60 * ratio,
child: ElevatedButton(
key: keyConfirm,
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white, elevation: 4,
backgroundColor: orangeC, // foreground
),
onPressed: canValidate
? () async {
log.d('GOOO');
WalletData? defaultWallet =
myWalletProvider.getDefaultWallet();
String? pin;
if (myWalletProvider.pinCode == '') {
pin = await Navigator.push(
context,
MaterialPageRoute(
builder: (homeContext) {
return UnlockingWallet(
wallet: defaultWallet);
},
),
);
}
sub.migrateIdentity(
fromAddress: fromAddress,
destAddress: selectedWallet.address!,
fromPassword: pin ?? myWalletProvider.pinCode,
destPassword: pin ?? myWalletProvider.pinCode,
withBalance: true,
fromBalance: balance);
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return TransactionInProgress(
transType: 'identityMigration',
fromAddress: getShortPubkey(fromAddress),
toAddress: getShortPubkey(
selectedWallet.address!));
}),
);
}
: null,
child: Text(
'migrateIdentity'.tr(),
style: TextStyle(
fontSize: 23 * ratio, fontWeight: FontWeight.w600),
),
),
),
],
),
);
}),
const Spacer(),
]),
const SizedBox(height: 10),
Text(
validationStatus,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 15, color: Colors.grey[600]),
)
]);
});
}),
),
);
}

View File

@ -1,18 +1,14 @@
// ignore_for_file: use_build_context_synchronously
import 'package:bubble/bubble.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart';
import 'package:gecko/globals.dart';
import 'package:flutter/material.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:gecko/providers/generate_wallets.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/screens/common_elements.dart';
import 'package:gecko/screens/onBoarding/7.dart';
import 'package:gecko/screens/onBoarding/9.dart';
import 'package:gecko/widgets/commons/fader_transition.dart';
import 'package:gecko/widgets/commons/offline_info.dart';
import 'package:gecko/widgets/commons/top_appbar.dart';
import 'package:provider/provider.dart';
// import 'package:gecko/models/home.dart';
// import 'package:provider/provider.dart';
@ -23,27 +19,42 @@ class RestoreChest extends StatelessWidget {
@override
Widget build(BuildContext context) {
final genW = Provider.of<GenerateWalletsProvider>(context, listen: false);
final sub = Provider.of<SubstrateSdk>(context, listen: false);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
GenerateWalletsProvider genW =
Provider.of<GenerateWalletsProvider>(context, listen: false);
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
genW.actualWallet = null;
if (genW.isSentenceComplete(context)) {
genW.generatedMnemonic =
'${genW.cellController0.text} ${genW.cellController1.text} ${genW.cellController2.text} ${genW.cellController3.text} ${genW.cellController4.text} ${genW.cellController5.text} ${genW.cellController6.text} ${genW.cellController7.text} ${genW.cellController8.text} ${genW.cellController9.text} ${genW.cellController10.text} ${genW.cellController11.text}';
}
return PopScope(
onPopInvoked: (_) {
return WillPopScope(
onWillPop: () {
genW.resetImportView();
return Future<bool>.value(true);
},
child: Scaffold(
backgroundColor: backgroundColor,
appBar: GeckoAppBar('restoreAChest'.tr()),
appBar: AppBar(
toolbarHeight: 60 * ratio,
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.black),
onPressed: () {
genW.resetImportView();
Navigator.of(context).pop();
}),
title: SizedBox(
height: 22,
child: Text('restoreAChest'.tr()),
)),
body: SafeArea(
child: Stack(children: [
Column(children: <Widget>[
ScaledSizedBox(height: isTall ? 20 : 3),
SizedBox(height: isTall ? 30 : 15),
bubbleSpeak('toRestoreEnterMnemonic'.tr()),
ScaledSizedBox(height: isTall ? 20 : 5),
SizedBox(height: isTall ? 30 : 15),
Column(children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
@ -53,7 +64,7 @@ class RestoreChest extends StatelessWidget {
arrayCell(context, genW.cellController2),
arrayCell(context, genW.cellController3),
]),
ScaledSizedBox(height: isTall ? 10 : 3),
const SizedBox(height: 15),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
@ -62,7 +73,7 @@ class RestoreChest extends StatelessWidget {
arrayCell(context, genW.cellController6),
arrayCell(context, genW.cellController7),
]),
ScaledSizedBox(height: isTall ? 10 : 3),
const SizedBox(height: 15),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
@ -77,15 +88,14 @@ class RestoreChest extends StatelessWidget {
Expanded(
child: Align(
alignment: Alignment.center,
child: ScaledSizedBox(
width: 340,
height: 55,
child: SizedBox(
width: 410,
height: 70,
child: ElevatedButton(
key: keyGoNext,
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
elevation: 4,
backgroundColor: orangeC,
foregroundColor: Colors.white, elevation: 4,
backgroundColor: orangeC, // foreground
),
onPressed: () async {
if (await sub
@ -107,10 +117,8 @@ class RestoreChest extends StatelessWidget {
},
child: Text(
'restoreThisChest'.tr(),
style: scaledTextStyle(
fontSize: 19,
fontWeight: FontWeight.w600,
color: Colors.white),
style: const TextStyle(
fontSize: 24, fontWeight: FontWeight.w600),
),
),
),
@ -118,39 +126,38 @@ class RestoreChest extends StatelessWidget {
))
else
Column(children: [
ScaledSizedBox(height: 20),
ScaledSizedBox(
width: 210,
height: 55,
const SizedBox(height: 20),
SizedBox(
width: 190,
height: 60,
child: ElevatedButton(
key: keyPastMnemonic,
style: ElevatedButton.styleFrom(
foregroundColor: Colors.black,
elevation: 4,
backgroundColor: yellowC,
foregroundColor: Colors.black, elevation: 4,
backgroundColor: yellowC, // foreground
),
onPressed: () {
genW.pasteMnemonic(context);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Icon(
const Icon(
Icons.content_paste_go,
size: scaleSize(28),
size: 25,
),
const SizedBox(width: 10),
Text(
'pasteFromClipboard'.tr(),
textAlign: TextAlign.center,
style: scaledTextStyle(
fontSize: 16, fontWeight: FontWeight.w400),
style: const TextStyle(
fontSize: 17, fontWeight: FontWeight.w400),
),
],
)),
)
])
]),
const OfflineInfo(),
CommonElements().offlineInfo(context),
]),
),
),
@ -160,7 +167,7 @@ class RestoreChest extends StatelessWidget {
Widget bubbleSpeak(String text) {
return Bubble(
margin: const BubbleEdges.symmetric(horizontal: 20),
padding: BubbleEdges.all(scaleSize(15)),
padding: BubbleEdges.all(isTall ? 25 : 15),
borderWidth: 1,
borderColor: Colors.black,
radius: Radius.zero,
@ -169,19 +176,19 @@ class RestoreChest extends StatelessWidget {
text,
key: keyBubbleSpeak,
textAlign: TextAlign.justify,
style: scaledTextStyle(
color: Colors.black, fontSize: 17, fontWeight: FontWeight.w400),
style: const TextStyle(
color: Colors.black, fontSize: 19, fontWeight: FontWeight.w400),
),
);
}
Widget arrayCell(BuildContext context, final cellCtl) {
final generateWalletProvider =
Widget arrayCell(BuildContext context, TextEditingController cellCtl) {
GenerateWalletsProvider generateWalletProvider =
Provider.of<GenerateWalletsProvider>(context);
return Container(
width: scaleSize(87),
height: scaleSize(37),
width: 102,
height: 40 * ratio,
// ),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
@ -212,7 +219,7 @@ class RestoreChest extends StatelessWidget {
}
},
textAlign: TextAlign.center,
style: scaledTextStyle(fontSize: 17),
style: const TextStyle(fontSize: 20),
),
);
}
@ -220,7 +227,7 @@ class RestoreChest extends StatelessWidget {
Future<bool?> badMnemonicPopup(BuildContext context) async {
return showDialog<bool>(
context: context,
barrierDismissible: true,
barrierDismissible: true, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Phrase incorrecte'),

View File

@ -2,14 +2,11 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart';
import 'package:gecko/globals.dart';
import 'package:flutter/material.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/models/widgets_keys.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/widgets/commons/build_text.dart';
import 'package:gecko/widgets/commons/top_appbar.dart';
import 'package:gecko/screens/common_elements.dart';
import 'package:pdf/pdf.dart';
import 'package:printing/printing.dart';
import 'package:provider/provider.dart';
@ -26,28 +23,36 @@ class ShowSeed extends StatelessWidget {
@override
Widget build(BuildContext context) {
final myWalletProvider =
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
final sub = Provider.of<SubstrateSdk>(context, listen: false);
CommonElements common = CommonElements();
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
WalletData defaultWallet = myWalletProvider.getDefaultWallet();
return Scaffold(
backgroundColor: backgroundColor,
appBar: GeckoAppBar('myMnemonic'.tr()),
appBar: AppBar(
toolbarHeight: 60 * ratio,
title: SizedBox(
height: 22,
child: Text('myMnemonic'.tr()),
)),
body: SafeArea(
child: Column(children: <Widget>[
const Spacer(flex: 1),
FutureBuilder(
future:
sub.getSeed(defaultWallet.address, walletProvider.pinCode),
sub.getSeed(defaultWallet.address!, walletProvider.pinCode),
builder: (BuildContext context, AsyncSnapshot<String?> seed) {
if (seed.connectionState != ConnectionState.done ||
seed.hasError) {
return ScaledSizedBox(
return const SizedBox(
height: 15,
width: 15,
child: const CircularProgressIndicator(
child: CircularProgressIndicator(
color: orangeC,
strokeWidth: 2,
),
@ -60,82 +65,74 @@ class ShowSeed extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(children: [
BuildText(
text: 'keepYourMnemonicSecret'.tr(), size: 16),
ScaledSizedBox(height: 35),
common.buildText('keepYourMnemonicSecret'.tr()),
SizedBox(height: 35 * ratio),
sentanceArray(context, seed.data!.split(' ')),
ScaledSizedBox(height: 20),
Row(
children: [
ScaledSizedBox(
height: 39,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
foregroundColor: Colors.black,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
backgroundColor: orangeC,
elevation: 1,
),
onPressed: () {
Clipboard.setData(
ClipboardData(text: seed.data!));
snackCopySeed(context);
},
child: Row(children: <Widget>[
Image.asset(
'assets/walletOptions/copy-white.png',
height: scaleSize(24),
),
ScaledSizedBox(width: 7),
Text(
'copy'.tr(),
style: scaledTextStyle(
fontSize: 14, color: Colors.grey[50]),
)
]),
const SizedBox(height: 20),
SizedBox(
height: 40,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
foregroundColor: Colors.black,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
backgroundColor: orangeC,
elevation: 1, // foreground
),
ScaledSizedBox(width: 50),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return PrintWallet(seed.data);
}),
);
},
child: Image.asset(
'assets/printer.png',
height: scaleSize(38),
onPressed: () {
Clipboard.setData(
ClipboardData(text: seed.data));
snackCopyKey(context);
},
child: Row(children: <Widget>[
Image.asset(
'assets/walletOptions/copy-white.png',
height: 25,
),
),
],
const SizedBox(width: 7),
Text(
'copy'.tr(),
style: TextStyle(
fontSize: 15, color: Colors.grey[50]),
)
]),
),
),
const SizedBox(height: 30),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return PrintWallet(seed.data);
}),
);
},
child: Image.asset(
'assets/printer.png',
height: 42 * ratio,
),
),
]),
]);
}),
const Spacer(flex: 3),
ScaledSizedBox(
width: 240,
height: 55,
const Spacer(flex: 2),
SizedBox(
width: 380 * ratio,
height: 60 * ratio,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
elevation: 4,
backgroundColor: orangeC,
foregroundColor: Colors.white, elevation: 4,
backgroundColor: orangeC, // foreground
),
onPressed: () {
Navigator.pop(context);
},
child: Text(
'close'.tr(),
style: scaledTextStyle(
fontSize: 19,
fontWeight: FontWeight.w600,
color: Colors.white),
style: TextStyle(
fontSize: 23 * ratio, fontWeight: FontWeight.w600),
),
),
),
@ -144,56 +141,72 @@ class ShowSeed extends StatelessWidget {
));
}
snackCopyKey(context) {
const snackBar = SnackBar(
padding: EdgeInsets.all(20),
content: Text(
"Votre phrase de restauration a été copié dans votre presse-papier.",
style: TextStyle(fontSize: 16)),
duration: Duration(seconds: 2));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
Widget sentanceArray(BuildContext context, List mnemonic) {
return Container(
constraints: BoxConstraints(maxWidth: scaleSize(360)),
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: const Color(0xffeeeedd),
borderRadius: const BorderRadius.all(
Radius.circular(10),
)),
padding: EdgeInsets.all(scaleSize(14)),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(children: <Widget>[
arrayCell(mnemonic[0], 1),
arrayCell(mnemonic[1], 2),
arrayCell(mnemonic[2], 3),
arrayCell(mnemonic[3], 4),
]),
ScaledSizedBox(height: 15),
Row(children: <Widget>[
arrayCell(mnemonic[4], 5),
arrayCell(mnemonic[5], 6),
arrayCell(mnemonic[6], 7),
arrayCell(mnemonic[7], 8),
]),
ScaledSizedBox(height: 15),
Row(children: <Widget>[
arrayCell(mnemonic[8], 9),
arrayCell(mnemonic[9], 10),
arrayCell(mnemonic[10], 11),
arrayCell(mnemonic[11], 12),
]),
]));
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 3),
child: Container(
constraints: const BoxConstraints(maxWidth: 450),
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: const Color(0xffeeeedd),
borderRadius: const BorderRadius.all(
Radius.circular(10),
)),
padding: const EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(children: <Widget>[
arrayCell(mnemonic[0], 1),
arrayCell(mnemonic[1], 2),
arrayCell(mnemonic[2], 3),
arrayCell(mnemonic[3], 4),
]),
const SizedBox(height: 15),
Row(children: <Widget>[
arrayCell(mnemonic[4], 5),
arrayCell(mnemonic[5], 6),
arrayCell(mnemonic[6], 7),
arrayCell(mnemonic[7], 8),
]),
const SizedBox(height: 15),
Row(children: <Widget>[
arrayCell(mnemonic[8], 9),
arrayCell(mnemonic[9], 10),
arrayCell(mnemonic[10], 11),
arrayCell(mnemonic[11], 12),
]),
])),
);
}
Widget arrayCell(dataWord, int nbr) {
return ScaledSizedBox(
width: 82,
log.d(nbr);
return SizedBox(
width: 100,
child: Column(children: <Widget>[
Text(
nbr.toString(),
style: scaledTextStyle(fontSize: 11, color: const Color(0xff6b6b52)),
style:
TextStyle(fontSize: 13 * ratio, color: const Color(0xff6b6b52)),
),
Text(
dataWord,
key: keyMnemonicWord(dataWord),
style: scaledTextStyle(fontSize: 16, color: Colors.black),
style: TextStyle(fontSize: 17 * ratio, color: Colors.black),
),
]),
);
@ -217,10 +230,13 @@ class PrintWallet extends StatelessWidget {
}),
backgroundColor: yellowC,
foregroundColor: Colors.black,
toolbarHeight: scaleSize(57),
title: Text(
'printMyMnemonic'.tr(),
style: scaledTextStyle(fontWeight: FontWeight.w600),
toolbarHeight: 60 * ratio,
title: SizedBox(
height: 22,
child: Text(
'printMyMnemonic'.tr(),
style: const TextStyle(fontWeight: FontWeight.w600),
),
),
),
body: PdfPreview(
@ -237,19 +253,21 @@ class PrintWallet extends StatelessWidget {
await rootBundle.load("assets/OpenSans-Regular.ttf");
final pw.Font ttf = pw.Font.ttf(fontData.buffer.asByteData());
final pdf = pw.Document();
int nbr = 1;
final seedList = seed.split(' ');
const imageProvider = AssetImage('assets/icon/gecko_final.png');
final geckoLogo = await flutterImageProvider(imageProvider);
// const imageProvider = AssetImage('assets/icon/gecko_final.png');
// final geckoLogo = await flutterImageProvider(imageProvider);
pw.Widget arrayCell(String dataWord, int nbr) {
nbr++;
pw.Widget arrayCell(int number, String dataWord) {
return pw.SizedBox(
width: 120,
height: 70,
child: pw.Column(children: <pw.Widget>[
pw.Text(
number.toString(),
nbr.toString(),
style: pw.TextStyle(
fontSize: 15, color: const PdfColor(0.5, 0, 0), font: ttf),
),
@ -267,44 +285,38 @@ class PrintWallet extends StatelessWidget {
pw.Page(
pageFormat: PdfPageFormat.a4,
build: (context) {
return pw.Stack(children: <pw.Widget>[
pw.Positioned(top: 217, child: pw.Text('-'.padRight(130, '-'))),
pw.Positioned(bottom: 217, child: pw.Text('-'.padRight(130, '-'))),
pw.Column(
// mainAxisAlignment: pw.MainAxisAlignment.center,
// mainAxisSize: pw.MainAxisSize.max,
// crossAxisAlignment: pw.CrossAxisAlignment.center,
children: <pw.Widget>[
pw.SizedBox(height: 10),
pw.Row(children: <pw.Widget>[
arrayCell(1, seedList[0]),
arrayCell(2, seedList[1]),
arrayCell(3, seedList[2]),
arrayCell(4, seedList[3]),
]),
pw.Row(children: <pw.Widget>[
arrayCell(5, seedList[4]),
arrayCell(6, seedList[5]),
arrayCell(7, seedList[6]),
arrayCell(8, seedList[7]),
]),
pw.Row(children: <pw.Widget>[
arrayCell(9, seedList[8]),
arrayCell(10, seedList[9]),
arrayCell(11, seedList[10]),
arrayCell(12, seedList[11])
]),
pw.SizedBox(height: 105),
pw.Image(geckoLogo, height: 80),
pw.SizedBox(height: 205),
pw.Text(
"keepThisPaperSafe".tr(),
textAlign: pw.TextAlign.center,
style: pw.TextStyle(fontSize: 15, font: ttf),
)
],
)
]);
return pw.Column(
// mainAxisAlignment: pw.MainAxisAlignment.center,
// mainAxisSize: pw.MainAxisSize.max,
// crossAxisAlignment: pw.CrossAxisAlignment.center,
children: <pw.Widget>[
pw.Row(children: <pw.Widget>[
arrayCell(seedList[0], nbr),
arrayCell(seedList[1], nbr),
arrayCell(seedList[2], nbr),
arrayCell(seedList[3], nbr),
]),
pw.Row(children: <pw.Widget>[
arrayCell(seedList[4], nbr),
arrayCell(seedList[5], nbr),
arrayCell(seedList[6], nbr),
arrayCell(seedList[7], nbr),
]),
pw.Row(children: <pw.Widget>[
arrayCell(seedList[8], nbr),
arrayCell(seedList[9], nbr),
arrayCell(seedList[10], nbr),
arrayCell(seedList[11], nbr)
]),
pw.Expanded(
child: pw.Align(
alignment: pw.Alignment.bottomCenter,
child: pw.Text(
"Gardez cette feuille préciseusement, à labri des lézards indiscrets.",
style: pw.TextStyle(fontSize: 15, font: ttf),
)))
],
);
},
),
);

View File

@ -3,15 +3,15 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:gecko/models/chest_data.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/providers/wallet_options.dart';
import 'package:flutter/material.dart';
// import 'package:gecko/screens/myWallets/choose_chest.dart';
import 'package:gecko/screens/myWallets/choose_chest.dart';
import 'package:pin_code_fields/pin_code_fields.dart';
import 'package:provider/provider.dart';
import 'package:gecko/globals.dart';
@ -22,8 +22,6 @@ class UnlockingWallet extends StatelessWidget {
late int currentChestNumber;
late ChestData currentChest;
bool canUnlock = true;
final enterPin = TextEditingController();
FocusNode pinFocus = FocusNode(debugLabel: 'pinFocusNode');
// ignore: close_sinks
StreamController<ErrorAnimationType>? errorController;
@ -32,10 +30,10 @@ class UnlockingWallet extends StatelessWidget {
@override
Widget build(BuildContext context) {
final walletOptions =
Provider.of<WalletOptionsProvider>(context, listen: false);
final myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
WalletOptionsProvider walletOptions =
Provider.of<WalletOptionsProvider>(context);
// final double statusBarHeight = MediaQuery.of(context).padding.top;
currentChestNumber = configBox.get('currentChest');
currentChest = chestBox.get(currentChestNumber)!;
@ -43,185 +41,165 @@ class UnlockingWallet extends StatelessWidget {
int pinLenght = walletOptions.getPinLenght(wallet.number);
errorController = StreamController<ErrorAnimationType>();
// if (enterPin.text == '') myWalletProvider.isPinLoading = true;
return PopScope(
onPopInvoked: (_) {
myWalletProvider.isPinValid = false;
myWalletProvider.isPinLoading = true;
},
child: Scaffold(
backgroundColor: backgroundColor,
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Stack(children: <Widget>[
Positioned(
top: 10,
left: 15,
child: Builder(
builder: (context) => IconButton(
key: keyPopButton,
icon: Icon(
Icons.arrow_back,
color: Colors.black,
size: scaleSize(28),
),
onPressed: () {
myWalletProvider.isPinValid = false;
myWalletProvider.isPinLoading = true;
Navigator.pop(context);
},
return Scaffold(
backgroundColor: backgroundColor,
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Stack(children: <Widget>[
Positioned(
top: 10, //statusBarHeight + 10,
left: 15,
child: Builder(
builder: (context) => IconButton(
key: keyPopButton,
icon: const Icon(
Icons.arrow_back,
color: Colors.black,
size: 30,
),
onPressed: () => Navigator.pop(context),
),
),
Column(children: <Widget>[
ScaledSizedBox(height: isTall ? 80 : 65),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
currentChest.imageFile == null
? Image.asset(
'assets/chests/${currentChest.imageName}',
width: scaleSize(95),
)
: Image.file(
currentChest.imageFile!,
width: scaleSize(127),
),
ScaledSizedBox(width: 5),
ScaledSizedBox(
width: 250,
child: Text(
currentChest.name!,
textAlign: TextAlign.center,
style: scaledTextStyle(
fontSize: 22,
color: Colors.black,
fontWeight: FontWeight.w700),
)),
]),
ScaledSizedBox(height: isTall ? 30 : 15),
ScaledSizedBox(
width: 350,
child: Text(
'toUnlockEnterPassword'.tr(),
textAlign: TextAlign.center,
style: scaledTextStyle(
fontSize: 16,
color: Colors.black,
fontWeight: FontWeight.w400),
)),
ScaledSizedBox(height: isTall ? 30 : 15),
if (!myWalletProvider.isPinValid &&
!myWalletProvider.isPinLoading)
Text(
"thisIsNotAGoodCode".tr(),
style: scaledTextStyle(
color: Colors.red,
fontWeight: FontWeight.w500,
fontSize: 16),
),
ScaledSizedBox(height: isTall ? 8 : 0),
pinForm(context, pinLenght),
ScaledSizedBox(height: 8),
if (canUnlock)
Consumer<WalletOptionsProvider>(
builder: (context, sub, _) {
return InkWell(
key: keyCachePassword,
onTap: () {
walletOptions.changePinCacheChoice();
},
child: Row(children: [
ScaledSizedBox(height: 30),
const Spacer(),
Icon(
configBox.get('isCacheChecked')
? Icons.check_box
: Icons.check_box_outline_blank,
color: orangeC,
size: scaleSize(22),
),
ScaledSizedBox(width: 8),
Text(
'rememberPassword'.tr(),
style: scaledTextStyle(
fontSize: 15, color: Colors.grey[700]),
),
const Spacer()
]),
),
Column(children: <Widget>[
SizedBox(height: isTall ? 100 : 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
currentChest.imageFile == null
? Image.asset(
'assets/chests/${currentChest.imageName}',
width: isTall ? 130 : 100,
)
: Image.file(
currentChest.imageFile!,
width: isTall ? 130 : 100,
),
const SizedBox(width: 5),
SizedBox(
width: 250,
child: Text(
currentChest.name!,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 25,
color: Colors.black,
fontWeight: FontWeight.w700),
)),
]),
SizedBox(height: 30 * ratio),
SizedBox(
width: 400,
child: Text(
'toUnlockEnterPassword'.tr(),
style: const TextStyle(
fontSize: 19,
color: Colors.black,
fontWeight: FontWeight.w400),
)),
SizedBox(height: 40 * ratio),
pinForm(context, pinLenght),
SizedBox(height: 3 * ratio),
if (canUnlock)
InkWell(
key: keyCachePassword,
onTap: () {
walletOptions.changePinCacheChoice();
},
child: Row(children: [
const SizedBox(height: 30),
const Spacer(),
Icon(
configBox.get('isCacheChecked')
? Icons.check_box
: Icons.check_box_outline_blank,
color: orangeC,
),
const SizedBox(width: 8),
Text(
'rememberPassword'.tr(),
style: TextStyle(
fontSize: 16, color: Colors.grey[700]),
),
const Spacer()
]),
),
const SizedBox(height: 10),
// if (canUnlock)
InkWell(
key: keyChangeChest,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return const ChooseChest();
}),
);
}),
// const ScaledSizedBox(height: 10),
// if (canUnlock)
// InkWell(
// key: keyChangeChest,
// onTap: () {
// // Navigator.push(
// // context,
// // MaterialPageRoute(builder: (context) {
// // return const ChooseChest();
// // }),
// // );
// },
// child: ScaledSizedBox(
// width: 400,
// height: 50,
// child: Center(
// child: Text(
// 'changeChest'.tr(),
// style: const scaledTextStyle(
// fontSize: 22,
// color: Colors.grey, // orangeC
// fontWeight: FontWeight.w600),
// ),
// ),
// )),
]),
},
child: SizedBox(
width: 400,
height: 50,
child: Center(
child: Text(
'changeChest'.tr(),
style: const TextStyle(
fontSize: 22,
color: orangeC,
fontWeight: FontWeight.w600),
),
),
)),
]),
]),
)),
);
]),
));
}
Widget pinForm(context, pinLenght) {
final myWalletProvider = Provider.of<MyWalletsProvider>(context);
final sub = Provider.of<SubstrateSdk>(context, listen: false);
// var _walletPin = '';
// ignore: close_sinks
StreamController<ErrorAnimationType> errorController =
StreamController<ErrorAnimationType>();
TextEditingController enterPin = TextEditingController();
WalletOptionsProvider walletOptions =
Provider.of<WalletOptionsProvider>(context);
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context);
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
FocusNode pinFocus = FocusNode();
WalletData defaultWallet = myWalletProvider.getDefaultWallet();
// if (defaultWallet.address == null) {
// canUnlock = false;
// return Text(
// 'Impossible de retrouver votre\nportefeuille par défaut.\nID: ${defaultWallet.id()}',
// textAlign: TextAlign.center,
// style: const scaledTextStyle(
// color: Colors.redAccent, fontWeight: FontWeight.w500),
// );
// }
// defaultWallet.address = null;
if (defaultWallet.address == null) {
canUnlock = false;
return Text(
'Impossible de retrouver votre\nportefeuille par défaut.\nID: ${defaultWallet.id()}',
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.redAccent, fontWeight: FontWeight.w500),
);
}
return Form(
// key: keyPinForm,
child: Padding(
padding: EdgeInsets.symmetric(
vertical: scaleSize(3), horizontal: scaleSize(isTall ? 30 : 15)),
padding: EdgeInsets.symmetric(vertical: 5 * ratio, horizontal: 30),
child: PinCodeTextField(
key: keyPinForm,
textCapitalization: TextCapitalization.characters,
focusNode: pinFocus,
autoFocus: true,
appContext: context,
pastedTextStyle: scaledTextStyle(
pastedTextStyle: TextStyle(
color: Colors.green.shade600,
fontWeight: FontWeight.bold,
),
length: pinLenght,
obscureText: true,
obscuringCharacter: '*',
animationType: AnimationType.slide,
animationDuration: const Duration(milliseconds: 40),
useHapticFeedback: true,
animationType: AnimationType.fade,
validator: (v) {
if (v!.length < pinLenght) {
return "yourPasswordLengthIsX".tr(args: [pinLenght.toString()]);
@ -234,15 +212,17 @@ class UnlockingWallet extends StatelessWidget {
borderWidth: 4,
shape: PinCodeFieldShape.box,
borderRadius: BorderRadius.circular(5),
fieldHeight: scaleSize(47),
fieldWidth: scaleSize(47),
fieldHeight: 50 * ratio,
fieldWidth: 50,
activeFillColor: Colors.black,
),
showCursor: !kDebugMode,
showCursor: kDebugMode ? false : true,
cursorColor: Colors.black,
textStyle: scaledTextStyle(fontSize: 25, height: 1.6),
animationDuration: const Duration(milliseconds: 300),
textStyle: const TextStyle(fontSize: 20, height: 1.6),
backgroundColor: const Color(0xffF9F9F1),
enableActiveFill: false,
errorAnimationController: errorController,
controller: enterPin,
keyboardType: TextInputType.visiblePassword,
boxShadows: const [
@ -253,32 +233,28 @@ class UnlockingWallet extends StatelessWidget {
)
],
onCompleted: (pin) async {
myWalletProvider.isPinLoading = true;
myWalletProvider.pinCode = pin.toUpperCase();
final isValid = await sub.checkPassword(
defaultWallet.address, pin.toUpperCase());
defaultWallet.address!, pin.toUpperCase());
if (!isValid) {
await Future.delayed(const Duration(milliseconds: 20));
await Future.delayed(const Duration(milliseconds: 50));
errorController.add(ErrorAnimationType
.shake); // Triggering error shake animation
pinColor = Colors.red[600];
myWalletProvider.isPinLoading = false;
myWalletProvider.isPinValid = false;
myWalletProvider.pinCode = myWalletProvider.mnemonic = '';
enterPin.text = '';
walletOptions.reload();
pinFocus.requestFocus();
} else {
myWalletProvider.isPinValid = true;
myWalletProvider.isPinLoading = false;
pinColor = Colors.green[400];
myWalletProvider.debounceResetPinCode();
myWalletProvider.resetPinCode();
Navigator.pop(context, pin.toUpperCase());
}
},
onChanged: (value) {
if (enterPin.text != '') myWalletProvider.isPinLoading = true;
if (pinColor != const Color(0xFFA4B600)) {
pinColor = const Color(0xFFA4B600);
}
myWalletProvider.reload();
},
)),
);

View File

@ -2,28 +2,21 @@ import 'dart:async';
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.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/home.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/providers/wallet_options.dart';
import 'package:gecko/providers/wallets_profiles.dart';
import 'package:gecko/screens/certifications.dart';
import 'package:gecko/screens/common_elements.dart';
import 'package:gecko/screens/activity.dart';
import 'package:gecko/screens/myWallets/manage_membership.dart';
import 'package:gecko/screens/qrcode_fullscreen.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';
import 'package:gecko/widgets/name_by_address.dart';
import 'package:gecko/widgets/page_route_no_transition.dart';
import 'package:provider/provider.dart';
import 'package:flutter/services.dart';
import 'package:qr_flutter/qr_flutter.dart';
class WalletOptions extends StatelessWidget {
@ -33,62 +26,59 @@ class WalletOptions extends StatelessWidget {
@override
Widget build(BuildContext context) {
final walletOptions =
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
WalletOptionsProvider walletOptions =
Provider.of<WalletOptionsProvider>(context, listen: false);
WalletsProfilesProvider historyProvider =
Provider.of<WalletsProfilesProvider>(context, listen: false);
final myWalletProvider =
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
final duniterIndexer = Provider.of<DuniterIndexer>(context, listen: false);
HomeProvider homeProvider =
Provider.of<HomeProvider>(context, listen: false);
DuniterIndexer duniterIndexer =
Provider.of<DuniterIndexer>(context, listen: false);
final sub = Provider.of<SubstrateSdk>(context, listen: false);
// SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
// sub.spawnBlock();
// sub.spawnBlock(0, 20);
walletOptions.address.text = wallet.address;
log.d(walletOptions.address.text);
final currentChest = myWalletProvider.getCurrentChest();
final isWalletNameIndexed =
duniterIndexer.walletNameIndexer[walletOptions.address.text] != null;
final int currentChest = myWalletProvider.getCurrentChest();
// StreamSubscription<QueryResult>? subscription;
// final stream = duniterIndexer.subscribeHistoryIssued(wallet.address);
// final currentWallet = _myWalletProvider.getDefaultWallet();
// log.d(_walletOptions.getAddress(_currentChest, 3));
log.d("Wallet options: $currentChest:${wallet.derivation}");
// subscription = stream.listen((result) {
// if (result.hasException) {
// log.e(result.exception);
// } else {
// final Map transData =
// result.data?['account_by_pk']['transactions_issued'].first;
// final String receiver = transData['receiver_pubkey'];
// final double amount = transData['amount'] / 100;
// final createdAt = DateTime.parse(transData['created_at']);
// log.d('$receiver --- $amount --- $createdAt');
// subscription?.cancel();
// }
// });
return PopScope(
onPopInvoked: (_) {
return WillPopScope(
onWillPop: () {
walletOptions.isEditing = false;
walletOptions.isBalanceBlur = false;
myWalletProvider.reload();
Navigator.pop(context);
return Future<bool>.value(true);
},
child: Scaffold(
backgroundColor: backgroundColor,
resizeToAvoidBottomInset: false,
appBar: AppBar(
toolbarHeight: scaleSize(57),
toolbarHeight: 60 * ratio,
elevation: 0,
title: Consumer<WalletOptionsProvider>(
builder: (context, walletProvider, _) {
return Text(
isWalletNameIndexed
? duniterIndexer
.walletNameIndexer[walletOptions.address.text]!
: wallet.name!,
style: scaledTextStyle(fontSize: 19),
);
}),
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.black),
onPressed: () {
walletOptions.isEditing = false;
walletOptions.isBalanceBlur = false;
myWalletProvider.reload();
Navigator.pop(context);
}),
title: SizedBox(
height: 22,
child: Consumer<WalletOptionsProvider>(
builder: (context, walletProvider, _) {
return Text(wallet.name!);
}),
),
actions: [
InkWell(
onTap: () {
@ -101,21 +91,21 @@ class WalletOptions extends StatelessWidget {
}),
);
},
child: QrImageView(
child: QrImageWidget(
data: walletOptions.address.text,
version: QrVersions.auto,
size: scaleSize(70),
size: 80,
),
),
],
),
bottomNavigationBar: const GeckoBottomAppBar(),
bottomNavigationBar: homeProvider.bottomAppBar(context),
body: Stack(children: [
Builder(
builder: (ctx) => SafeArea(
child: Column(children: <Widget>[
Container(
height: 5,
height: isTall ? 5 : 0,
color: yellowC,
),
Consumer<WalletOptionsProvider>(
@ -130,100 +120,44 @@ class WalletOptions extends StatelessWidget {
backgroundColor,
],
)),
child: Row(children: <Widget>[
const Spacer(flex: 1),
ScaledSizedBox(width: 15),
avatar(walletProvider),
const Spacer(flex: 1),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Stack(children: [
ScaledSizedBox(
width: 230,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer<WalletOptionsProvider>(
builder: (context, walletProvider, _) {
return NameByAddress(
wallet: wallet,
size: 24,
color: Colors.black,
fontWeight: wallet.identityStatus ==
IdtyStatus.member
? FontWeight.w500
: FontWeight.w400,
fontStyle: FontStyle.normal);
})
],
),
),
ScaledSizedBox(width: 10),
if (duniterIndexer
.walletNameIndexer[wallet.address] ==
null)
Positioned(
right: 0,
child: InkWell(
key: keyRenameWallet,
onTap: () async {
await walletOptions.editWalletName(
context, wallet.id());
await Future.delayed(
const Duration(milliseconds: 30));
},
child: ClipRRect(
child: Image.asset(
walletOptions.isEditing
? 'assets/walletOptions/android-checkmark.png'
: 'assets/walletOptions/edit.png',
width: scaleSize(23),
height: scaleSize(23)),
),
),
),
]),
ScaledSizedBox(height: 5),
Balance(
address: walletProvider.address.text, size: 20),
ScaledSizedBox(width: 30),
InkWell(
onTap: () => sub.certsCounterCache[
walletProvider.address.text] !=
null
? {
Navigator.push(
context,
PageNoTransit(builder: (context) {
return CertificationsScreen(
address:
walletProvider.address.text,
username: duniterIndexer
.walletNameIndexer[
walletProvider
.address.text] ??
'');
}),
),
}
: null,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IdentityStatus(
address: walletOptions.address.text,
isOwner: true,
color: orangeC),
Certifications(
address: walletProvider.address.text,
size: 17)
]),
),
ScaledSizedBox(height: 10),
]),
const Spacer(flex: 2),
]),
child: Row(
// mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
const Spacer(flex: 1),
avatar(walletProvider),
const Spacer(flex: 1),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
duniterIndexer.getNameByAddress(
context,
walletProvider.address.text,
wallet,
27,
false,
Colors.black,
FontWeight.w400,
FontStyle.normal),
// SizedBox(height: isTall ? 5 : 0),
SizedBox(height: isTall ? 5 : 0),
balance(
context, walletProvider.address.text, 21),
const SizedBox(width: 30),
Column(
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
walletOptions.idtyStatus(
context, walletOptions.address.text,
isOwner: true, color: orangeC),
getCerts(context,
walletProvider.address.text, 15),
]),
SizedBox(height: 10 * ratio),
]),
const Spacer(flex: 2),
]),
);
}),
Expanded(
@ -231,36 +165,62 @@ class WalletOptions extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
ScaledSizedBox(height: 25),
// InkWell(
// onTap: () {
// Navigator.push(
// context,
// MaterialPageRoute(builder: (context) {
// return QrCodeFullscreen(
// _walletOptions.address.text,
// );
// }),
// );
// },
// child: QrImageWidget(
// data: _walletOptions.address.text,
// version: QrVersions.auto,
// size: isTall ? 150 : 80,
// ),
// ),
SizedBox(height: 30 * ratio),
Consumer<WalletOptionsProvider>(
builder: (context, walletProvider, _) {
final defaultWallet =
myWalletProvider.getDefaultWallet();
walletProvider.isDefaultWallet =
walletOptions.address.text ==
defaultWallet.address;
return Column(children: [
confirmIdentityButton(walletProvider),
pubkeyWidget(walletProvider, ctx),
ScaledSizedBox(height: 11),
SizedBox(height: 10 * ratio),
activityWidget(
context, historyProvider, walletProvider),
ScaledSizedBox(height: 11),
SizedBox(height: 12 * ratio),
setDefaultWalletWidget(
context,
walletProvider,
myWalletProvider,
walletOptions,
currentChest),
ScaledSizedBox(height: 11),
Column(children: [
if (!walletProvider.isDefaultWallet &&
!wallet.isMembre())
deleteWallet(
context, walletProvider, currentChest),
if (wallet.isMembre())
const ManageMembershipButton()
])
SizedBox(height: 17 * ratio),
// walletProvider.isMember(context, _walletOptions.address.text)
FutureBuilder(
future: walletProvider.isMember(
context, walletOptions.address.text),
builder: (BuildContext context,
AsyncSnapshot<bool> isMember) {
if (isMember.connectionState !=
ConnectionState.done ||
isMember.hasError) {
return const Text('');
}
return Column(children: [
if (!walletProvider.isDefaultWallet &&
!isMember.data!)
deleteWallet(context, walletProvider,
currentChest)
else
const SizedBox(),
if (isMember.data!)
manageMembership(context)
]);
}),
]);
}),
]),
@ -269,7 +229,7 @@ class WalletOptions extends StatelessWidget {
]),
),
),
const OfflineInfo(),
CommonElements().offlineInfo(context),
]),
),
);
@ -280,16 +240,21 @@ class WalletOptions extends StatelessWidget {
children: <Widget>[
InkWell(
onTap: () async {
await (walletProvider.changeAvatar());
final newPath = await (walletProvider.changeAvatar());
if (newPath != '') {
wallet.imageCustomPath = newPath;
walletBox.put(wallet.key, wallet);
}
walletProvider.reload();
},
child: wallet.imageCustomPath == null || wallet.imageCustomPath == ''
? Image.asset(
'assets/avatars/${wallet.imageDefaultPath}',
width: scaleSize(122),
width: 110,
)
: Container(
width: scaleSize(122),
height: scaleSize(122),
width: 150,
height: 150,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.transparent,
@ -312,7 +277,7 @@ class WalletOptions extends StatelessWidget {
},
child: Image.asset(
'assets/walletOptions/camera.png',
height: scaleSize(38),
height: 40,
),
),
),
@ -323,79 +288,80 @@ class WalletOptions extends StatelessWidget {
Widget confirmIdentityButton(WalletOptionsProvider walletProvider) {
return Consumer<SubstrateSdk>(builder: (context, sub, _) {
return FutureBuilder(
future: sub.idtyStatus([walletProvider.address.text]),
initialData: const [IdtyStatus.unknown],
builder:
(BuildContext context, AsyncSnapshot<List<IdtyStatus>> snapshot) {
if (!snapshot.hasData || snapshot.hasError) {
return const SizedBox.shrink();
}
if (snapshot.data!.first == IdtyStatus.unconfirmed) {
future: sub.idtyStatus(walletProvider.address.text),
initialData: '',
builder: (context, snapshot) {
if (snapshot.data == 'Created') {
return Column(children: [
ScaledSizedBox(
width: 310,
height: 55,
SizedBox(
width: 320,
height: 60,
child: ElevatedButton(
key: keyConfirmIdentity,
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
elevation: 4,
backgroundColor: orangeC,
foregroundColor: Colors.white, elevation: 4,
backgroundColor: orangeC, // foreground
),
onPressed: () {
walletProvider.confirmIdentityPopup(context);
// Navigator.push(
// context,
// MaterialPageRoute(builder: (context) {
// return const SearchResultScreen();
// }),
// );
},
child: Text(
'confirmMyIdentity'.tr(),
style: scaledTextStyle(
style: const TextStyle(
fontSize: 21, fontWeight: FontWeight.w600),
),
),
),
ScaledSizedBox(height: 7),
const SizedBox(height: 7),
Text(
"someoneCreatedYourIdentity".tr(args: [currencyName]),
style: scaledTextStyle(
style: TextStyle(
fontSize: 16,
color: Colors.grey[600],
fontStyle: FontStyle.italic,
),
),
ScaledSizedBox(height: 40),
const SizedBox(height: 40),
]);
} else {
return ScaledSizedBox();
return const SizedBox();
}
});
});
}
Widget pubkeyWidget(WalletOptionsProvider walletProvider, BuildContext ctx) {
final shortPubkey = getShortPubkey(walletProvider.address.text);
final String shortPubkey = getShortPubkey(walletProvider.address.text);
return GestureDetector(
key: keyCopyAddress,
onTap: () {
Clipboard.setData(ClipboardData(text: walletProvider.address.text));
snackCopyKey(ctx);
},
child: ScaledSizedBox(
child: SizedBox(
height: 50,
child: Row(children: <Widget>[
ScaledSizedBox(width: 26),
const SizedBox(width: 30),
Image.asset(
'assets/walletOptions/key.png',
height: scaleSize(42),
height: 45,
),
ScaledSizedBox(width: 19),
const SizedBox(width: 20),
Text(shortPubkey,
style: scaledTextStyle(
fontSize: 19,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.w800,
fontFamily: 'Monospace',
color: Colors.black)),
const Spacer(),
ScaledSizedBox(
height: 35,
const SizedBox(width: 15),
SizedBox(
height: 40,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
foregroundColor: Colors.black,
@ -403,7 +369,7 @@ class WalletOptions extends StatelessWidget {
borderRadius: BorderRadius.circular(8),
),
backgroundColor: orangeC,
elevation: 1,
elevation: 1, // foreground
),
onPressed: () {
Clipboard.setData(
@ -413,12 +379,16 @@ class WalletOptions extends StatelessWidget {
child: Row(children: <Widget>[
Image.asset(
'assets/walletOptions/copy-white.png',
height: scaleSize(23),
height: 25,
),
const SizedBox(width: 7),
Text(
'copy'.tr(),
style: TextStyle(fontSize: 15, color: Colors.grey[50]),
)
]),
),
),
const Spacer(),
]),
),
);
@ -431,23 +401,66 @@ class WalletOptions extends StatelessWidget {
return InkWell(
key: keyOpenActivity,
onTap: () {
// _historyProvider.nPage = 1;
Navigator.push(
context,
PageNoTransit(builder: (context) {
return ActivityScreen(address: walletProvider.address.text);
MaterialPageRoute(builder: (context) {
return ActivityScreen(
address: walletProvider.address.text,
avatar: wallet.imageCustomPath == null
? Image.asset(
'assets/avatars/${wallet.imageDefaultPath}',
width: 110,
)
: Image.asset(
wallet.imageCustomPath!,
width: 110,
));
}),
);
},
child: ScaledSizedBox(
child: SizedBox(
height: 50,
child: Row(children: <Widget>[
ScaledSizedBox(width: 26),
const SizedBox(width: 30),
Image.asset(
'assets/walletOptions/clock.png',
height: scaleSize(42),
height: 45,
),
ScaledSizedBox(width: 20),
Text("displayActivity".tr(), style: scaledTextStyle(fontSize: 18)),
const SizedBox(width: 22),
Text("displayActivity".tr(),
style:
const TextStyle(fontSize: 20, fontWeight: FontWeight.w500)),
]),
),
);
}
Widget manageMembership(BuildContext context) {
WalletOptionsProvider walletOptions =
Provider.of<WalletOptionsProvider>(context, listen: false);
return InkWell(
key: keyManageMembership,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ManageMembership(
address: walletOptions.address.text,
);
}),
);
},
child: SizedBox(
height: 40,
child: Row(children: <Widget>[
const SizedBox(width: 32),
Image.asset(
'assets/medal.png',
height: 45,
),
const SizedBox(width: 22),
Text('manageMembership'.tr(), style: const TextStyle(fontSize: 20)),
]),
),
);
@ -456,8 +469,8 @@ class WalletOptions extends StatelessWidget {
Widget setDefaultWalletWidget(
BuildContext context,
WalletOptionsProvider walletProvider,
final myWalletProvider,
final walletOptions,
MyWalletsProvider myWalletProvider,
WalletOptionsProvider walletOptions,
int currentChest) {
return Consumer<MyWalletsProvider>(builder: (context, myWalletProvider, _) {
WalletData defaultWallet = myWalletProvider.getDefaultWallet();
@ -469,34 +482,28 @@ class WalletOptions extends StatelessWidget {
await setDefaultWallet(context, currentChest);
}
: null,
child: ScaledSizedBox(
height: 60,
child: SizedBox(
height: 50,
child: Row(children: <Widget>[
ScaledSizedBox(width: isTall ? 28 : 23),
ScaledSizedBox(
height: 42,
child: CircleAvatar(
backgroundColor:
Colors.grey[walletProvider.isDefaultWallet ? 300 : 500],
child: Image.asset(
'assets/walletOptions/android-checkmark.png',
height: scaleSize(23),
),
const SizedBox(width: 31),
CircleAvatar(
backgroundColor:
Colors.grey[walletProvider.isDefaultWallet ? 300 : 500],
child: Image.asset(
'assets/walletOptions/android-checkmark.png',
height: 25,
),
),
ScaledSizedBox(width: isTall ? 21 : 18),
ScaledSizedBox(
width: 250,
child: Text(
walletProvider.isDefaultWallet
? 'thisWalletIsDefault'.tr()
: 'defineWalletAsDefault'.tr(),
style: scaledTextStyle(
fontSize: 18,
color: walletProvider.isDefaultWallet
? Colors.grey[500]
: Colors.black)),
),
const SizedBox(width: 22),
Text(
walletProvider.isDefaultWallet
? 'thisWalletIsDefault'.tr()
: 'defineWalletAsDefault'.tr(),
style: TextStyle(
fontSize: 20,
color: walletProvider.isDefaultWallet
? Colors.grey[500]
: Colors.black)),
]),
),
);
@ -504,66 +511,68 @@ class WalletOptions extends StatelessWidget {
}
Future setDefaultWallet(BuildContext context, int currentChest) async {
final sub = Provider.of<SubstrateSdk>(context, listen: false);
final myWalletProvider =
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
final walletOptions =
WalletOptionsProvider walletOptions =
Provider.of<WalletOptionsProvider>(context, listen: false);
// WalletData defaultWallet = _myWalletProvider.getDefaultWallet()!;
// defaultWallet = wallet;
await sub.setCurrentWallet(wallet);
await myWalletProvider.readAllWallets(currentChest);
myWalletProvider.readAllWallets(currentChest);
myWalletProvider.reload();
walletOptions.reload();
}
Widget deleteWallet(BuildContext context, WalletOptionsProvider walletOptions,
int currentChest) {
final sub = Provider.of<SubstrateSdk>(context, listen: false);
final myWalletProvider =
Widget deleteWallet(BuildContext context,
WalletOptionsProvider walletProvider, int currentChest) {
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
final defaultWallet = myWalletProvider.getDefaultWallet();
final bool isDefaultWallet =
walletOptions.address.text == defaultWallet.address;
walletProvider.address.text == defaultWallet.address;
// return Consumer<MyWalletsProvider>(
// builder: (context, _myWalletProvider, _) {
return FutureBuilder(
future: sub.hasAccountConsumers(wallet.address),
future: sub.hasAccountConsumers(wallet.address!),
builder: (BuildContext context, AsyncSnapshot<bool> hasConsumers) {
if (hasConsumers.connectionState != ConnectionState.done ||
hasConsumers.hasError ||
!hasConsumers.hasData) {
hasConsumers.hasError) {
return const Text('');
}
final double balance =
walletOptions.balanceCache[walletOptions.address.text] ?? -1;
balanceCache[walletProvider.address.text] ?? -1;
final bool canDelete = !isDefaultWallet &&
!hasConsumers.data! &&
(balance > 2 || balance == 0) &&
!wallet.hasIdentity();
(balance > 2 || balance == 0);
return InkWell(
key: keyDeleteWallet,
onTap: canDelete
? () async {
await walletOptions.deleteWallet(context, wallet);
WidgetsBinding.instance.addPostFrameCallback((_) async {
await walletProvider.deleteWallet(context, wallet);
WidgetsBinding.instance.addPostFrameCallback((_) {
myWalletProvider.listWallets =
await myWalletProvider.readAllWallets(currentChest);
myWalletProvider.readAllWallets(currentChest);
myWalletProvider.reload();
});
}
: null,
child: canDelete
? Row(children: <Widget>[
ScaledSizedBox(width: 27),
const SizedBox(width: 30),
Image.asset(
'assets/walletOptions/trash.png',
height: scaleSize(42),
height: 45,
),
ScaledSizedBox(width: 19),
const SizedBox(width: 19),
Text('deleteThisWallet'.tr(),
style: scaledTextStyle(
fontSize: 18, color: const Color(0xffD80000))),
style: const TextStyle(
fontSize: 20, color: Color(0xffD80000))),
])
: ScaledSizedBox(width: 30),
: const SizedBox(width: 30),
);
});
}

View File

@ -1,108 +1,200 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/chest_data.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/home.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:flutter/material.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/widgets/bottom_app_bar.dart';
import 'package:gecko/widgets/buttons/add_new_derivation_button.dart';
import 'package:gecko/widgets/buttons/chest_options_buttons.dart';
import 'package:gecko/widgets/commons/offline_info.dart';
import 'package:gecko/widgets/drag_tule_action.dart';
import 'package:gecko/widgets/drag_wallets_info.dart';
import 'package:gecko/widgets/wallet_tile.dart';
import 'package:gecko/widgets/wallet_tile_membre.dart';
import 'package:gecko/providers/wallet_options.dart';
import 'package:gecko/screens/common_elements.dart';
import 'package:gecko/screens/myWallets/chest_options.dart';
import 'package:gecko/screens/myWallets/choose_chest.dart';
import 'package:gecko/screens/myWallets/import_g1_v1.dart';
import 'package:gecko/screens/myWallets/unlocking_wallet.dart';
import 'package:gecko/screens/myWallets/wallet_options.dart';
import 'package:gecko/screens/wallet_view.dart';
import 'package:provider/provider.dart';
import 'package:tutorial_coach_mark/tutorial_coach_mark.dart';
class WalletsHome extends StatefulWidget {
class WalletsHome extends StatelessWidget {
const WalletsHome({Key? key}) : super(key: key);
@override
State<WalletsHome> createState() => _WalletsHomeState();
}
class _WalletsHomeState extends State<WalletsHome> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
final myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context);
HomeProvider homeProvider =
Provider.of<HomeProvider>(context, listen: false);
final currentChestNumber = myWalletProvider.getCurrentChest();
final int currentChestNumber = myWalletProvider.getCurrentChest();
final ChestData currentChest = chestBox.get(currentChestNumber)!;
myWalletProvider.listWallets =
myWalletProvider.readAllWallets(currentChestNumber);
return Scaffold(
backgroundColor: backgroundColor,
appBar: AppBar(
backgroundColor: yellowC,
toolbarHeight: scaleSize(57),
title: Row(
children: [
Image.asset(
'assets/chests/${currentChest.imageName}',
height: 32,
),
ScaledSizedBox(width: 17),
Text(
currentChest.name!,
style: scaledTextStyle(
color: Colors.grey[850],
fontSize: 17,
fontWeight: FontWeight.w500),
),
],
return WillPopScope(
onWillPop: () {
Navigator.popUntil(
context,
ModalRoute.withName('/'),
);
return Future<bool>.value(true);
},
child: Scaffold(
backgroundColor: backgroundColor,
appBar: AppBar(
elevation: 1,
toolbarHeight: 60 * ratio,
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.black),
onPressed: () {
Navigator.popUntil(
context,
ModalRoute.withName('/'),
);
}),
title: Text(currentChest.name!,
style: TextStyle(color: Colors.grey[850])),
backgroundColor: const Color(0xffFFD58D),
),
bottomNavigationBar: myWalletProvider.lastFlyBy == ''
? homeProvider.bottomAppBar(context)
: dragInfo(context),
body: SafeArea(
child: Stack(children: [
myWalletsTiles(context, currentChestNumber),
CommonElements().offlineInfo(context),
]),
),
),
bottomNavigationBar:
Consumer<MyWalletsProvider>(builder: (context, _, __) {
return myWalletProvider.lastFlyBy == null
? const GeckoBottomAppBar(
actualRoute: 'safeHome',
)
: DragWalletsInfo(
lastFlyBy: myWalletProvider.lastFlyBy!,
dragAddress: myWalletProvider.dragAddress!,
);
}),
body: FutureBuilder(
future: myWalletProvider.readAllWallets(currentChestNumber),
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done ||
snapshot.hasError) {
return Center(
child: ScaledSizedBox(
height: 50,
width: 50,
child: const CircularProgressIndicator(
color: orangeC,
strokeWidth: 3,
),
),
);
}
return SafeArea(
child: Stack(children: [
myWalletsTiles(context, currentChestNumber),
const OfflineInfo(),
]),
);
}),
);
}
Widget dragInfo(BuildContext context) {
final myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
final walletDataFrom =
myWalletProvider.getWalletDataByAddress(myWalletProvider.dragAddress);
final walletDataTo =
myWalletProvider.getWalletDataByAddress(myWalletProvider.lastFlyBy);
final bool isSameAddress =
myWalletProvider.dragAddress == myWalletProvider.lastFlyBy;
final double screenWidth = MediaQuery.of(homeContext).size.width;
return Container(
color: yellowC,
width: screenWidth,
height: 80,
child: Center(
child: Column(
children: [
const SizedBox(height: 5),
Text('${'executeATransfer'.tr()}:'),
MarkdownBody(data: '${'from'.tr()} **${walletDataFrom!.name}**'),
if (isSameAddress) Text('chooseATargetWallet'.tr()),
if (!isSameAddress)
MarkdownBody(data: 'Vers: **${walletDataTo!.name}**'),
],
)),
);
}
Widget chestOptions(
BuildContext context, MyWalletsProvider myWalletProvider) {
return Column(children: [
const SizedBox(height: 50),
SizedBox(
height: 80,
width: 420,
child: ElevatedButton.icon(
icon: Image.asset(
'assets/chests/config.png',
height: 60,
),
style: ElevatedButton.styleFrom(
foregroundColor: Colors.black, elevation: 2,
backgroundColor: floattingYellow, // foreground
),
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ChestOptions(walletProvider: myWalletProvider);
}),
),
label: Text(
" ${"manageChest".tr()}",
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.w700,
color: Color(0xff8a3c0f),
),
),
)),
const SizedBox(height: 30),
InkWell(
key: keyImportG1v1,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return const ImportG1v1();
}),
);
},
child: SizedBox(
width: 400,
height: 60,
child: Center(
child: Text('importG1v1'.tr(),
style: TextStyle(
fontSize: 22,
color: Colors.blue[900],
fontWeight: FontWeight.w500))),
),
),
const SizedBox(height: 5),
InkWell(
key: keyChangeChest,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return const ChooseChest();
}),
);
},
child: SizedBox(
width: 400,
height: 60,
child: Center(
child: Text('changeChest'.tr(),
style: const TextStyle(
fontSize: 22,
color: orangeC,
fontWeight: FontWeight.w500))),
),
),
const SizedBox(height: 30)
]);
}
Widget myWalletsTiles(BuildContext context, int currentChestNumber) {
final myWalletProvider = Provider.of<MyWalletsProvider>(context);
final bool isWalletsExists = myWalletProvider.isWalletsExists();
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context);
WalletOptionsProvider walletOptions =
Provider.of<WalletOptionsProvider>(context, listen: false);
final bool isWalletsExists = myWalletProvider.checkIfWalletExist();
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
if (!isWalletsExists) {
return const Text('');
@ -110,112 +202,354 @@ class _WalletsHomeState extends State<WalletsHome> {
if (myWalletProvider.listWallets.isEmpty) {
return Expanded(
child: Column(children: <Widget>[
child: Column(children: const <Widget>[
Center(
child: Text(
'Veuillez générer votre premier portefeuille',
style: scaledTextStyle(fontSize: 17, fontWeight: FontWeight.w500),
style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500),
)),
]));
}
// Get wallet list and sort by derivation number
List<WalletData> listWallets = myWalletProvider.listWallets;
listWallets.sort((p1, p2) {
return Comparable.compare(p1.number!, p2.number!);
});
List listWallets = myWalletProvider.listWallets;
WalletData? defaultWallet = myWalletProvider.getDefaultWallet();
final double screenWidth = MediaQuery.of(context).size.width;
int nTule = 2;
// Get first wallet with identity
final idtyWallet = listWallets.firstWhere(
(w) => w.hasIdentity(),
orElse: () => WalletData(address: ''),
);
List<WalletData> listWalletsWithoutIdty = listWallets.toList();
listWalletsWithoutIdty.removeWhere((w) => w.address == idtyWallet.address);
final screenWidth = MediaQuery.of(context).size.width;
int nTule;
if (screenWidth >= 700) {
if (screenWidth >= 900) {
nTule = 4;
} else if (screenWidth >= 450) {
} else if (screenWidth >= 650) {
nTule = 3;
} else {
nTule = 2;
}
// Offset followDragAnchorStrategy(
// Draggable<Object> d, BuildContext context, Offset point) {
// return Offset(d.feedbackOffset.dx - 30, d.feedbackOffset.dy - 0);
// }
final tutorialCoachMark = TutorialCoachMark(
targets: [
TargetFocus(
identify: "drag_and_drop",
keyTarget: keyDragAndDrop,
contents: [
TargetContent(
child: Column(
children: [
Image.asset('assets/drag-and-drop.png', height: scaleSize(115)),
ScaledSizedBox(height: 15),
Text(
'explainDraggableWallet'.tr(),
textAlign: TextAlign.center,
style: scaledTextStyle(
fontSize: 17, fontWeight: FontWeight.w500),
return CustomScrollView(slivers: <Widget>[
const SliverToBoxAdapter(child: SizedBox(height: 20)),
SliverGrid.count(
key: keyListWallets,
crossAxisCount: nTule,
childAspectRatio: 1,
crossAxisSpacing: 0,
mainAxisSpacing: 0,
children: <Widget>[
for (WalletData repository in listWallets as Iterable<WalletData>)
LongPressDraggable<String>(
delay: const Duration(milliseconds: 200),
data: repository.address!,
dragAnchorStrategy:
(Draggable<Object> _, BuildContext __, Offset ___) =>
const Offset(0, 0),
// feedbackOffset: const Offset(-500, -500),
// dragAnchorStrategy: childDragAnchorStrategy,
onDragStarted: () =>
myWalletProvider.dragAddress = repository.address!,
onDragEnd: (_) {
myWalletProvider.lastFlyBy = '';
myWalletProvider.dragAddress = '';
myWalletProvider.reload();
},
feedback: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: orangeC,
shape: const CircleBorder(),
padding: const EdgeInsets.all(15),
),
child: const SizedBox(
height: 35,
child: Image(image: AssetImage('assets/vector_white.png')),
),
),
],
))
],
alignSkip: Alignment.bottomRight,
enableOverlayTab: true,
),
],
colorShadow: orangeC,
textSkip: "skip".tr(),
paddingFocus: 10,
opacityShadow: 0.8,
child: DragTarget<String>(
onAccept: (senderAddress) async {
log.d(
'INTERPAY: sender: $senderAddress --- receiver: ${repository.address!}');
final walletData = myWalletProvider
.getWalletDataByAddress(senderAddress);
await sub.setCurrentWallet(walletData!);
sub.reload();
paymentPopup(context, repository.address!);
},
onMove: (details) {
if (repository.address! != myWalletProvider.lastFlyBy) {
myWalletProvider.lastFlyBy = repository.address!;
myWalletProvider.reload();
}
},
onWillAccept: (senderAddress) =>
senderAddress != repository.address!,
builder: (
BuildContext context,
List<dynamic> accepted,
List<dynamic> rejected,
) {
return Padding(
padding: const EdgeInsets.all(16),
child: GestureDetector(
key: keyOpenWallet(repository.address!),
onTap: () {
walletOptions.getAddress(
currentChestNumber, repository.derivation!);
Navigator.push(
context,
SmoothTransition(
page: WalletOptions(
wallet: repository,
),
),
);
},
child: ClipOvalShadow(
shadow: const Shadow(
color: Colors.transparent,
offset: Offset(0, 0),
blurRadius: 5,
),
clipper: CustomClipperOval(),
child: ClipRRect(
borderRadius:
const BorderRadius.all(Radius.circular(12)),
child: Column(children: <Widget>[
Expanded(
child: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
gradient: RadialGradient(
radius: 0.6,
colors: [
Colors.green[400]!,
const Color(0xFFE7E7A6),
],
)),
child:
// SvgPicture.asset('assets/chopp-gecko2.png',
// semanticsLabel: 'Gecko', height: 48),
repository.imageCustomPath == null ||
repository.imageCustomPath == ''
? Image.asset(
'assets/avatars/${repository.imageDefaultPath}',
alignment: Alignment.bottomCenter,
scale: 0.5,
)
: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.transparent,
image: DecorationImage(
fit: BoxFit.fitHeight,
image: FileImage(
File(repository
.imageCustomPath!),
),
),
),
),
)),
Stack(children: <Widget>[
balanceBuilder(
context,
repository.address!,
repository.address ==
defaultWallet.address),
nameBuilder(context, repository,
defaultWallet, currentChestNumber),
]),
]),
),
),
),
);
}),
),
Consumer<SubstrateSdk>(builder: (context, sub, _) {
return sub.nodeConnected
? addNewDerivation(context)
: const Text('');
}),
]),
SliverToBoxAdapter(child: chestOptions(context, myWalletProvider)),
]);
}
Widget balanceBuilder(context, String address, bool isDefault) {
return Container(
width: double.infinity,
color: isDefault ? orangeC : yellowC,
child: Padding(
padding: const EdgeInsets.only(left: 5, right: 5, top: 38),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
balance(
context,
address,
15,
isDefault ? Colors.white : Colors.black,
isDefault ? yellowC : orangeC)
],
)),
);
}
// configBox.delete('showDraggableTutorial');
final bool showDraggableTutorial =
configBox.get('showDraggableTutorial') ?? true;
if (listWallets.length > 1 && showDraggableTutorial) {
tutorialCoachMark.show(context: context);
configBox.put('showDraggableTutorial', false);
}
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: CustomScrollView(slivers: <Widget>[
SliverToBoxAdapter(child: ScaledSizedBox(height: 12)),
if (idtyWallet.address != '')
SliverToBoxAdapter(
child: DragTuleAction(
wallet: idtyWallet,
child: WalletTileMembre(repository: idtyWallet),
Widget nameBuilder(BuildContext context, WalletData repository,
WalletData defaultWallet, int currentChestNumber) {
WalletOptionsProvider walletOptions =
Provider.of<WalletOptionsProvider>(context, listen: false);
DuniterIndexer duniterIndexer =
Provider.of<DuniterIndexer>(context, listen: false);
return ListTile(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(bottom: Radius.circular(12))),
tileColor: repository.address == defaultWallet.address
? orangeC
: const Color(0xffFFD58D),
title: Center(
child: Padding(
padding: const EdgeInsets.only(left: 5, right: 5, bottom: 35, top: 5),
child: duniterIndexer.getNameByAddress(
context,
repository.address!,
repository,
20,
true,
repository.id()[1] == defaultWallet.id()[1]
? const Color(0xffF9F9F1)
: Colors.black),
),
),
onTap: () {
walletOptions.getAddress(currentChestNumber, repository.derivation!);
Navigator.push(
context,
SmoothTransition(
page: WalletOptions(
wallet: repository,
),
),
SliverGrid.count(
key: keyListWallets,
crossAxisCount: nTule,
childAspectRatio: 1,
crossAxisSpacing: 0,
mainAxisSpacing: 0,
children: <Widget>[
for (WalletData repository in listWalletsWithoutIdty)
DragTuleAction(
wallet: repository,
child: WalletTile(repository: repository),
),
Consumer<SubstrateSdk>(builder: (context, sub, _) {
return sub.nodeConnected &&
myWalletProvider.listWallets.length < maxWalletsInSafe
? const AddNewDerivationButton()
: const Text('');
}),
]),
const SliverToBoxAdapter(child: ChestOptionsButtons()),
]),
);
},
);
}
Widget addNewDerivation(context) {
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context);
String newDerivationName =
'${'wallet'.tr()} ${myWalletProvider.listWallets.last.number! + 2}';
return Padding(
padding: const EdgeInsets.all(16),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(12)),
child: Column(children: <Widget>[
Expanded(
child: InkWell(
key: keyAddDerivation,
onTap: () async {
if (!myWalletProvider.isNewDerivationLoading) {
WalletData? defaultWallet =
myWalletProvider.getDefaultWallet();
String? pin;
if (myWalletProvider.pinCode == '') {
pin = await Navigator.push(
context,
MaterialPageRoute(
builder: (homeContext) {
return UnlockingWallet(wallet: defaultWallet);
},
),
);
}
if (pin != null || myWalletProvider.pinCode != '') {
await myWalletProvider.generateNewDerivation(
context, newDerivationName);
}
}
},
child: Container(
width: double.infinity,
height: double.infinity,
decoration: const BoxDecoration(color: floattingYellow),
child: Center(
child: myWalletProvider.isNewDerivationLoading
? const SizedBox(
height: 60,
width: 60,
child: CircularProgressIndicator(
color: orangeC,
strokeWidth: 7,
),
)
: const Text(
'+',
style: TextStyle(
fontSize: 150,
fontWeight: FontWeight.w700,
color: Color(0xFFFCB437)),
)),
)),
),
])));
}
}
class CustomClipperOval extends CustomClipper<Rect> {
@override
Rect getClip(Size size) {
return Rect.fromCircle(
center: Offset(size.width / 2, size.width / 2),
radius: size.width / 2 + 3);
}
@override
bool shouldReclip(CustomClipper<Rect> oldClipper) {
return false;
}
}
class ClipOvalShadow extends StatelessWidget {
final Shadow shadow;
final CustomClipper<Rect> clipper;
final Widget child;
const ClipOvalShadow({
Key? key,
required this.shadow,
required this.clipper,
required this.child,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: _ClipOvalShadowPainter(
clipper: clipper,
shadow: shadow,
),
child: ClipRect(clipper: clipper, child: child),
);
}
}
class _ClipOvalShadowPainter extends CustomPainter {
final Shadow shadow;
final CustomClipper<Rect> clipper;
_ClipOvalShadowPainter({required this.shadow, required this.clipper});
@override
void paint(Canvas canvas, Size size) {
var paint = shadow.toPaint();
var clipRect = clipper.getClip(size).shift(const Offset(0, 0));
canvas.drawOval(clipRect, paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}

View File

@ -1,11 +1,17 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart';
import 'package:gecko/globals.dart';
import 'package:flutter/material.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:gecko/providers/cesium_plus.dart';
import 'package:gecko/models/g1_wallets_list.dart';
import 'package:gecko/providers/duniter_indexer.dart';
import 'package:gecko/providers/home.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/providers/wallet_options.dart';
import 'package:gecko/providers/wallets_profiles.dart';
import 'package:gecko/widgets/bottom_app_bar.dart';
import 'package:gecko/widgets/commons/offline_info.dart';
import 'package:gecko/widgets/commons/top_appbar.dart';
import 'package:gecko/widgets/contacts_list.dart';
import 'package:gecko/screens/common_elements.dart';
import 'package:gecko/screens/wallet_view.dart';
import 'package:provider/provider.dart';
class ContactsScreen extends StatelessWidget {
@ -13,10 +19,24 @@ class ContactsScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
Provider.of<WalletsProfilesProvider>(context, listen: true);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
CesiumPlusProvider cesiumPlusProvider =
Provider.of<CesiumPlusProvider>(context, listen: false);
WalletsProfilesProvider walletsProfilesClass =
Provider.of<WalletsProfilesProvider>(context, listen: true);
HomeProvider homeProvider =
Provider.of<HomeProvider>(context, listen: false);
DuniterIndexer duniterIndexer =
Provider.of<DuniterIndexer>(context, listen: false);
double avatarSize = 55;
final myContacts = contactsBox.toMap().values.toList();
// Order contacts by username
// for (var element in myContacts) {
// log.d('yooo: ${element.pubkey} ${element.username}');
// }
myContacts.sort((p1, p2) {
return Comparable.compare(p1.username?.toLowerCase() ?? 'zz',
p2.username?.toLowerCase() ?? 'zz');
@ -24,13 +44,96 @@ class ContactsScreen extends StatelessWidget {
return Scaffold(
backgroundColor: backgroundColor,
appBar: GeckoAppBar(
'contactsManagementWithNbr'.tr(args: ['${myContacts.length}'])),
bottomNavigationBar: const GeckoBottomAppBar(),
appBar: AppBar(
elevation: 1,
toolbarHeight: 60 * ratio,
title: SizedBox(
height: 22,
child: Text(
'contactsManagementWithNbr'.tr(args: ['${myContacts.length}'])),
),
),
bottomNavigationBar: homeProvider.bottomAppBar(context),
body: SafeArea(
child: Stack(children: [
ContactsList(myContacts: myContacts),
const OfflineInfo(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const SizedBox(height: 20),
if (myContacts.isEmpty)
Text('noContacts'.tr())
else
Expanded(
child: ListView(children: <Widget>[
for (G1WalletsList g1Wallet in myContacts)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: ListTile(
key: keySearchResult('keyID++'),
horizontalTitleGap: 40,
contentPadding: const EdgeInsets.all(5),
leading: cesiumPlusProvider
.defaultAvatar(avatarSize),
title: Row(children: <Widget>[
Text(getShortPubkey(g1Wallet.address),
style: const TextStyle(
fontSize: 18,
fontFamily: 'Monospace',
fontWeight: FontWeight.w500),
textAlign: TextAlign.center),
]),
trailing: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 110,
child: Row(
mainAxisAlignment:
MainAxisAlignment.end,
children: [
Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
balance(context,
g1Wallet.address, 16),
]),
]),
),
]),
subtitle: Row(children: <Widget>[
duniterIndexer.getNameByAddress(
context, g1Wallet.address)
]),
dense: false,
isThreeLine: false,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
walletsProfilesClass.address =
g1Wallet.address;
return WalletViewScreen(
address: g1Wallet.address,
username: g1WalletsBox
.get(g1Wallet.address)
?.id
?.username,
avatar: g1WalletsBox
.get(g1Wallet.address)
?.avatar,
);
}),
);
}),
),
]),
)
]),
),
CommonElements().offlineInfo(context),
]),
),
);

View File

@ -1,31 +1,43 @@
// ignore_for_file: file_names
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/screens/common_elements.dart';
import 'package:gecko/screens/onBoarding/2.dart';
import 'package:gecko/widgets/commons/intro_info.dart';
import 'package:gecko/widgets/commons/offline_info.dart';
import 'package:gecko/widgets/commons/top_appbar.dart';
class OnboardingStepOne extends StatelessWidget {
const OnboardingStepOne({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
CommonElements common = CommonElements();
return Scaffold(
backgroundColor: backgroundColor,
appBar: GeckoAppBar('newWallet'.tr()),
appBar: AppBar(
toolbarHeight: 60 * ratio,
title: SizedBox(
height: 22,
child: Text(
'newWallet'.tr(),
style: const TextStyle(fontWeight: FontWeight.w600),
),
),
),
extendBodyBehindAppBar: true,
body: SafeArea(
child: Stack(children: [
InfoIntro(
text: 'geckoGenerateYourWalletFromMnemonic'.tr(),
assetName: 'fabrication-de-portefeuille.png',
buttonText: '>',
nextScreen: const OnboardingStepTwo(),
pagePosition: 0,
common.infoIntro(
context,
'geckoGenerateYourWalletFromMnemonic'.tr(),
'fabrication-de-portefeuille.png',
'>',
const OnboardingStepTwo(),
0,
isMd: true,
),
const OfflineInfo(),
CommonElements().offlineInfo(context),
]),
),
);

View File

@ -1,24 +1,20 @@
// ignore_for_file: file_names
// ignore_for_file: must_be_immutable
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/models/widgets_keys.dart';
import 'package:gecko/providers/generate_wallets.dart';
import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/providers/wallet_options.dart';
import 'package:gecko/widgets/commons/build_progress_bar.dart';
import 'package:gecko/widgets/commons/build_text.dart';
import 'package:gecko/screens/common_elements.dart';
import 'package:gecko/screens/onBoarding/11_congratulations.dart';
import 'package:gecko/widgets/commons/fader_transition.dart';
import 'package:gecko/widgets/commons/offline_info.dart';
import 'package:gecko/widgets/commons/top_appbar.dart';
import 'package:gecko/widgets/scan_derivations_info.dart';
import 'package:pin_code_fields/pin_code_fields.dart';
import 'package:provider/provider.dart';
@ -30,110 +26,126 @@ class OnboardingStepTen extends StatelessWidget {
final formKey = GlobalKey<FormState>();
Color? pinColor = const Color(0xFFA4B600);
bool hasError = false;
final enterPin = TextEditingController();
FocusNode pinFocus = FocusNode(debugLabel: 'pinFocusNode');
@override
Widget build(BuildContext context) {
final generateWalletProvider =
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
GenerateWalletsProvider generateWalletProvider =
Provider.of<GenerateWalletsProvider>(context);
final walletOptions = Provider.of<WalletOptionsProvider>(context);
final sub = Provider.of<SubstrateSdk>(context);
final myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
final pinLenght = generateWalletProvider.pin.text.length;
WalletOptionsProvider walletOptions =
Provider.of<WalletOptionsProvider>(context);
CommonElements common = CommonElements();
final int pinLenght = generateWalletProvider.pin.text.length;
return PopScope(
onPopInvoked: (_) {
myWalletProvider.isPinValid = false;
myWalletProvider.isPinLoading = true;
},
child: Scaffold(
backgroundColor: backgroundColor,
appBar: GeckoAppBar('myPassword'.tr()),
body: SafeArea(
child: Stack(children: [
Column(children: <Widget>[
ScaledSizedBox(height: isTall ? 25 : 5),
const BuildProgressBar(pagePosition: 9),
ScaledSizedBox(height: isTall ? 25 : 5),
BuildText(text: "geckoWillCheckPassword".tr()),
ScaledSizedBox(height: isTall ? 25 : 0),
const ScanDerivationsInfo(),
Consumer<MyWalletsProvider>(builder: (context, mw, _) {
return Visibility(
visible: !myWalletProvider.isPinValid &&
!myWalletProvider.isPinLoading,
child: Text(
"thisIsNotAGoodCode".tr(),
style: scaledTextStyle(
fontSize: 16,
color: Colors.red,
fontWeight: FontWeight.w500),
),
);
}),
ScaledSizedBox(height: isTall ? 20 : 0),
Consumer<SubstrateSdk>(builder: (context, sub, _) {
return sub.nodeConnected
? pinForm(context, walletOptions, pinLenght, 1, 2)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"youHaveToBeConnectedToValidateChest".tr(),
style: scaledTextStyle(
fontSize: 17,
color: Colors.redAccent,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
]);
}),
Consumer<WalletOptionsProvider>(
builder: (context, walletOptions, _) {
return sub.nodeConnected
? InkWell(
key: keyCachePassword,
onTap: () {
walletOptions.changePinCacheChoice();
},
child: Row(children: [
ScaledSizedBox(height: isTall ? 30 : 0),
const Spacer(),
Icon(
configBox.get('isCacheChecked') ?? false
? Icons.check_box
: Icons.check_box_outline_blank,
color: orangeC,
size: scaleSize(22),
),
ScaledSizedBox(width: 8),
return Scaffold(
backgroundColor: backgroundColor,
appBar: AppBar(
toolbarHeight: 60 * ratio,
title: SizedBox(
height: 22,
child: Text(
'myPassword'.tr(),
style: const TextStyle(fontWeight: FontWeight.w600),
),
),
),
extendBodyBehindAppBar: true,
body: SafeArea(
child: Stack(children: [
Column(children: <Widget>[
SizedBox(height: isTall ? 40 : 20),
common.buildProgressBar(9),
SizedBox(height: isTall ? 40 : 20),
common.buildText("geckoWillCheckPassword".tr()),
SizedBox(height: isTall ? 80 : 20),
Visibility(
visible: generateWalletProvider.scanedValidWalletNumber != -1,
child: Padding(
padding: const EdgeInsets.only(bottom: 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("derivationsScanProgress".tr(args: [
'${generateWalletProvider.scanedWalletNumber}',
'${generateWalletProvider.numberScan + 1}'
])),
const SizedBox(width: 10),
const SizedBox(
height: 22,
width: 22,
child: CircularProgressIndicator(
color: orangeC,
strokeWidth: 3,
),
),
],
),
),
),
Consumer<SubstrateSdk>(builder: (context, sub, _) {
return sub.nodeConnected
? pinForm(context, walletOptions, pinLenght, 1, 2)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text(
'rememberPassword'.tr(),
style: scaledTextStyle(
fontSize: 15, color: Colors.grey[700]),
'Vous devez vous connecter à internet\npour valider votre coffre',
style: TextStyle(
fontSize: 20,
color: Colors.redAccent,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
const Spacer()
]))
: const Text('');
}),
]),
const OfflineInfo(),
]);
}),
Consumer<SubstrateSdk>(builder: (context, sub, _) {
return sub.nodeConnected
? InkWell(
key: keyCachePassword,
onTap: () {
walletOptions.changePinCacheChoice();
},
child: Row(children: [
const SizedBox(height: 30),
const Spacer(),
Icon(
configBox.get('isCacheChecked') ?? false
? Icons.check_box
: Icons.check_box_outline_blank,
color: orangeC,
),
const SizedBox(width: 8),
Text(
'rememberPassword'.tr(),
style: TextStyle(
fontSize: 16, color: Colors.grey[700]),
),
const Spacer()
]))
: const Text('');
}),
const SizedBox(height: 10),
]),
)),
);
CommonElements().offlineInfo(context),
]),
));
}
Widget pinForm(
context, final walletOptions, pinLenght, int walletNbr, int derivation) {
final myWalletProvider = Provider.of<MyWalletsProvider>(context);
final generateWalletProvider =
Widget pinForm(context, WalletOptionsProvider walletOptions, pinLenght,
int walletNbr, int derivation) {
// var _walletPin = '';
// ignore: close_sinks
StreamController<ErrorAnimationType> errorController =
StreamController<ErrorAnimationType>();
TextEditingController enterPin = TextEditingController();
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context);
GenerateWalletsProvider generateWalletProvider =
Provider.of<GenerateWalletsProvider>(context);
final sub = Provider.of<SubstrateSdk>(context, listen: false);
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
final currentChest = myWalletProvider.getCurrentChest();
final int currentChest = myWalletProvider.getCurrentChest();
return Form(
key: formKey,
@ -141,9 +153,6 @@ class OnboardingStepTen extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 30),
child: PinCodeTextField(
key: keyPinForm,
textCapitalization: TextCapitalization.characters,
// autoDisposeControllers: false,
focusNode: pinFocus,
autoFocus: true,
appContext: context,
pastedTextStyle: TextStyle(
@ -153,9 +162,7 @@ class OnboardingStepTen extends StatelessWidget {
length: pinLenght,
obscureText: true,
obscuringCharacter: '*',
useHapticFeedback: true,
animationType: AnimationType.slide,
animationDuration: const Duration(milliseconds: 40),
animationType: AnimationType.fade,
validator: (v) {
if (v!.length < pinLenght) {
return "yourPasswordLengthIsX".tr(args: [pinLenght.toString()]);
@ -168,15 +175,17 @@ class OnboardingStepTen extends StatelessWidget {
borderWidth: 4,
shape: PinCodeFieldShape.box,
borderRadius: BorderRadius.circular(5),
fieldHeight: scaleSize(47),
fieldWidth: scaleSize(47),
activeFillColor: Colors.black,
fieldHeight: 60,
fieldWidth: 50,
activeFillColor: hasError ? Colors.blueAccent : Colors.black,
),
showCursor: !kDebugMode,
showCursor: kDebugMode ? false : true,
cursorColor: Colors.black,
textStyle: const TextStyle(fontSize: 25, height: 1.6),
animationDuration: const Duration(milliseconds: 300),
textStyle: const TextStyle(fontSize: 20, height: 1.6),
backgroundColor: const Color(0xffF9F9F1),
enableActiveFill: false,
errorAnimationController: errorController,
controller: enterPin,
keyboardType: TextInputType.visiblePassword,
boxShadows: const [
@ -189,10 +198,9 @@ class OnboardingStepTen extends StatelessWidget {
onCompleted: (pin) async {
myWalletProvider.pinCode = pin.toUpperCase();
myWalletProvider.pinLenght = pinLenght;
log.d('$pin || ${generateWalletProvider.pin.text}');
if (pin.toUpperCase() == generateWalletProvider.pin.text) {
pinColor = Colors.green[500];
myWalletProvider.isPinLoading = false;
myWalletProvider.isPinValid = true;
await generateWalletProvider.storeHDWChest(context);
bool isAlive = false;
@ -206,40 +214,38 @@ class OnboardingStepTen extends StatelessWidget {
derivePath: '//2',
password: generateWalletProvider.pin.text);
WalletData myWallet = WalletData(
version: dataVersion,
chest: configBox.get('currentChest'),
address: address,
number: 0,
name: 'currentWallet'.tr(),
derivation: 2,
imageDefaultPath: '0.png',
isOwned: true);
await walletBox.put(myWallet.address, myWallet);
imageDefaultPath: '0.png');
await walletBox.add(myWallet);
}
await myWalletProvider.readAllWallets(currentChest);
myWalletProvider.readAllWallets(currentChest);
myWalletProvider.reload();
generateWalletProvider.generatedMnemonic = '';
myWalletProvider.debounceResetPinCode();
myWalletProvider.resetPinCode();
// sleep(const Duration(milliseconds: 500));
Navigator.push(
context,
FaderTransition(
page: const OnboardingStepEleven(), isFast: false),
);
} else {
errorController.add(ErrorAnimationType
.shake); // Triggering error shake animation
hasError = true;
myWalletProvider.isPinLoading = false;
myWalletProvider.isPinValid = false;
pinColor = Colors.red[600];
enterPin.text = '';
pinFocus.requestFocus();
walletOptions.reload();
}
},
onChanged: (value) {
if (enterPin.text != '') myWalletProvider.isPinLoading = true;
if (pinColor != const Color(0xFFA4B600)) {
pinColor = const Color(0xFFA4B600);
}
myWalletProvider.reload();
},
)),
);

View File

@ -1,98 +1,83 @@
// ignore_for_file: file_names
import 'dart:math';
import 'package:confetti/confetti.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.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/widgets/commons/build_text.dart';
import 'package:gecko/widgets/commons/top_appbar.dart';
import 'package:gecko/screens/common_elements.dart';
class OnboardingStepEleven extends StatelessWidget {
const OnboardingStepEleven({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final conffetiController =
ConfettiController(duration: const Duration(milliseconds: 500));
conffetiController.play();
return PopScope(
canPop: false,
child: Scaffold(
backgroundColor: backgroundColor,
appBar: GeckoAppBar('allGood'.tr()),
body: SafeArea(
child: Stack(children: [
Column(children: <Widget>[
ScaledSizedBox(height: isTall ? 25 : 5),
BuildText(
text: "yourChestAndWalletWereCreatedSuccessfully".tr()),
ScaledSizedBox(height: isTall ? 15 : 5),
Image.asset(
'assets/onBoarding/gecko-clin.gif',
height: scaleSize(isTall ? 330 : 280),
),
Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: finishButton(context)),
),
ScaledSizedBox(height: isTall ? 40 : 5),
]),
Align(
alignment: Alignment.topLeft,
child: ConfettiWidget(
confettiController: conffetiController,
blastDirection: pi * 0.1,
maxBlastForce: 10,
minBlastForce: 1,
emissionFrequency: 0.01,
numberOfParticles: 7,
shouldLoop: false,
gravity: 0.2,
),
),
Align(
alignment: Alignment.topRight,
child: ConfettiWidget(
confettiController: conffetiController,
blastDirection: pi * 0.9,
maxBlastForce: 10,
minBlastForce: 1,
emissionFrequency: 0.01,
numberOfParticles: 7,
shouldLoop: false,
gravity: 0.2,
),
),
]),
)),
);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
CommonElements common = CommonElements();
return Scaffold(
backgroundColor: backgroundColor,
appBar: AppBar(
toolbarHeight: 60 * ratio,
title: SizedBox(
height: 22,
child: Text(
'allGood'.tr(),
style: const TextStyle(fontWeight: FontWeight.w600),
),
),
),
extendBodyBehindAppBar: true,
body: SafeArea(
child: Column(children: <Widget>[
const SizedBox(height: 40),
common.buildText("yourChestAndWalletWereCreatedSuccessfully".tr()),
SizedBox(height: isTall ? 20 : 10),
Image.asset(
'assets/onBoarding/gecko-clin.gif',
height: isTall ? 400 : 300,
),
Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: finishButton(context)),
),
const SizedBox(height: 40),
]),
));
}
}
Widget finishButton(BuildContext context) {
return ScaledSizedBox(
width: 340,
height: 55,
return SizedBox(
width: 380 * ratio,
height: 60 * ratio,
child: ElevatedButton(
key: keyGoWalletsHome,
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
elevation: 4,
backgroundColor: orangeC,
),
onPressed: () {
Navigator.pushNamedAndRemoveUntil(
context, '/mywallets', ModalRoute.withName('/'));
},
child: Text(
"accessMyChest".tr(),
style: scaledTextStyle(
fontSize: 21, fontWeight: FontWeight.w600, color: Colors.white),
),
),
key: keyGoWalletsHome,
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white, elevation: 4,
backgroundColor: orangeC, // foreground
),
onPressed: () {
//TODO: fix bad widget ancestor when pupUntil (multi_chest test failed)
// Navigator.popUntil(homeContext, ModalRoute.withName('/'));
// Navigator.of(homeContext, rootNavigator: true)
// .popUntil(ModalRoute.withName('/'));
// while (Navigator.of(homeContext).canPop()) {
// Navigator.of(homeContext).pop();
// }
// Navigator.pushNamed(homeContext, '/mywallets');
Navigator.pushNamedAndRemoveUntil(
context, '/mywallets', (route) => route.isFirst);
// Navigator.pushNamedAndRemoveUntil(
// homeContext, '/mywallets', ModalRoute.withName('/'));
},
child: Text("accessMyChest".tr(),
style:
TextStyle(fontSize: 22 * ratio, fontWeight: FontWeight.w600))),
);
}

View File

@ -1,31 +1,43 @@
// ignore_for_file: file_names
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/screens/common_elements.dart';
import 'package:gecko/screens/onBoarding/3.dart';
import 'package:gecko/widgets/commons/intro_info.dart';
import 'package:gecko/widgets/commons/offline_info.dart';
import 'package:gecko/widgets/commons/top_appbar.dart';
class OnboardingStepTwo extends StatelessWidget {
const OnboardingStepTwo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
CommonElements common = CommonElements();
return Scaffold(
backgroundColor: backgroundColor,
appBar: GeckoAppBar('yourMnemonic'.tr()),
appBar: AppBar(
toolbarHeight: 60 * ratio,
title: SizedBox(
height: 22,
child: Text(
'yourMnemonic'.tr(),
style: const TextStyle(fontWeight: FontWeight.w600),
),
),
),
extendBodyBehindAppBar: true,
body: SafeArea(
child: Stack(children: [
InfoIntro(
text: 'keepThisMnemonicSecure'.tr(),
assetName:
'fabrication-de-portefeuille-impossible-sans-phrase.png',
buttonText: '>',
nextScreen: const OnboardingStepThree(),
pagePosition: 1),
const OfflineInfo(),
common.infoIntro(
context,
'keepThisMnemonicSecure'.tr(),
'fabrication-de-portefeuille-impossible-sans-phrase.png',
'>',
const OnboardingStepThree(),
1),
CommonElements().offlineInfo(context),
]),
),
);

Some files were not shown because too many files have changed in this diff Show More