diff --git a/assets/chopp-gecko.svg b/assets/chopp-gecko.svg new file mode 100755 index 0000000000000000000000000000000000000000..39b6a7b6388ebd59bf0529f0c2a9fe28a9f4cd5b --- /dev/null +++ b/assets/chopp-gecko.svg @@ -0,0 +1,9 @@ +<svg width="96" height="117" viewBox="0 0 96 117" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<rect width="96" height="115.59" transform="matrix(-1 0 0 1 96 0.480713)" fill="url(#pattern0)"/> +<defs> +<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1"> +<use xlink:href="#image0" transform="translate(-0.0160256) scale(0.00819088 0.00680272)"/> +</pattern> +<image id="image0" width="126" height="147" xlink:href="data:image/png;base64,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"/> +</defs> +</svg> diff --git a/assets/chopp-gecko2.png b/assets/chopp-gecko2.png new file mode 100755 index 0000000000000000000000000000000000000000..6e97b0b626fad33362a517736bacdb3d013b71cd Binary files /dev/null and b/assets/chopp-gecko2.png differ diff --git a/assets/walletOptions/QR_icon.png b/assets/walletOptions/QR_icon.png new file mode 100755 index 0000000000000000000000000000000000000000..6b6a887d727560ae6bfe018fd8f4eff3cb85858d Binary files /dev/null and b/assets/walletOptions/QR_icon.png differ diff --git a/assets/walletOptions/android-checkmark.png b/assets/walletOptions/android-checkmark.png new file mode 100755 index 0000000000000000000000000000000000000000..578859d6b12b932c7fc9668e0a5163c485ab242d Binary files /dev/null and b/assets/walletOptions/android-checkmark.png differ diff --git a/assets/walletOptions/camera.png b/assets/walletOptions/camera.png new file mode 100755 index 0000000000000000000000000000000000000000..ce9ad0fb1edb6469fea18f112feec17b1dea9968 Binary files /dev/null and b/assets/walletOptions/camera.png differ diff --git a/assets/walletOptions/clock.png b/assets/walletOptions/clock.png new file mode 100755 index 0000000000000000000000000000000000000000..cf9a80270f311ac67d260b6be3bae8e744fb85a5 Binary files /dev/null and b/assets/walletOptions/clock.png differ diff --git a/assets/walletOptions/copy-white.png b/assets/walletOptions/copy-white.png new file mode 100755 index 0000000000000000000000000000000000000000..819e852fc49f7a8a7a519f2b9247f9aeabc66335 Binary files /dev/null and b/assets/walletOptions/copy-white.png differ diff --git a/assets/walletOptions/edit.png b/assets/walletOptions/edit.png new file mode 100755 index 0000000000000000000000000000000000000000..a45788da6b98ff24811ebacd61d02ba9716887d4 Binary files /dev/null and b/assets/walletOptions/edit.png differ diff --git a/assets/walletOptions/ellipse1.png b/assets/walletOptions/ellipse1.png new file mode 100755 index 0000000000000000000000000000000000000000..e3ff279bc09fb77afc7254897a936d551e2b07a5 Binary files /dev/null and b/assets/walletOptions/ellipse1.png differ diff --git a/assets/walletOptions/icon_oeuil.png b/assets/walletOptions/icon_oeuil.png new file mode 100755 index 0000000000000000000000000000000000000000..f707647c214f59072e8349f8bbf994aa3770b676 Binary files /dev/null and b/assets/walletOptions/icon_oeuil.png differ diff --git a/assets/walletOptions/icon_oeuil_close.png b/assets/walletOptions/icon_oeuil_close.png new file mode 100644 index 0000000000000000000000000000000000000000..4a13e67ea0a5d9bae6eaf7f3e67d4ac6c5131406 Binary files /dev/null and b/assets/walletOptions/icon_oeuil_close.png differ diff --git a/assets/walletOptions/key.png b/assets/walletOptions/key.png new file mode 100755 index 0000000000000000000000000000000000000000..45c0e85ae5630256f68c58881ae717c3bd1d7049 Binary files /dev/null and b/assets/walletOptions/key.png differ diff --git a/assets/walletOptions/trash.png b/assets/walletOptions/trash.png new file mode 100755 index 0000000000000000000000000000000000000000..6dc972b9aaebf7ecb89d33a1dafa12d586692e89 Binary files /dev/null and b/assets/walletOptions/trash.png differ diff --git a/lib/globals.dart b/lib/globals.dart index 78a6a021757004bf4aa3614a920164c89772fb97..e500877bf0e1dd4c4b6856f39aacaf361332fc35 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -1,8 +1,13 @@ import 'dart:io'; import 'package:shared_preferences/shared_preferences.dart'; +// Files paths Directory appPath; Directory walletsDirectory; +File defaultWalletFile; +File currentChestFile; + +String defaultWallet; String appVersion; SharedPreferences prefs; String endPointGVA; diff --git a/lib/main.dart b/lib/main.dart index 50db8865b5e7bfc257d66b7de62a543bf2d78b28..f5aa7bc107b594224bca77f522001b6fa9717e42 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -25,12 +25,14 @@ Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); HomeProvider _homeProvider = HomeProvider(); + MyWalletsProvider _walletsProvider = MyWalletsProvider(); await _homeProvider.getAppPath(); await _homeProvider.createDefaultAvatar(); + await _walletsProvider.initWalletFolder(); appVersion = await _homeProvider.getAppVersion(); prefs = await SharedPreferences.getInstance(); - final HiveStore _store = - await HiveStore.open(path: '${appPath.path}/gqlCache'); + // final HiveStore _store = + // await HiveStore.open(path: '${appPath.path}/gqlCache'); // Get a valid GVA endpoint endPointGVA = await _homeProvider.getValidEndpoint(); @@ -49,7 +51,7 @@ Future<void> main() async { await SentryFlutter.init((options) { options.dsn = 'https://c09587b46eaa42e8b9fda28d838ed180@o496840.ingest.sentry.io/5572110'; - }, appRunner: () => runApp(Gecko(endPointGVA, _store))); + }, appRunner: () => runApp(Gecko(endPointGVA))); // runZoned<Future<void>>( // () async { @@ -66,14 +68,13 @@ Future<void> main() async { } else { print('Debug mode enabled: No sentry alerte'); - runApp(Gecko(endPointGVA, _store)); + runApp(Gecko(endPointGVA)); } } class Gecko extends StatelessWidget { - Gecko(this.randomEndpoint, this._store); + Gecko(this.randomEndpoint); final String randomEndpoint; - final HiveStore _store; @override Widget build(BuildContext context) { @@ -84,7 +85,7 @@ class Gecko extends StatelessWidget { final _client = ValueNotifier( GraphQLClient( - cache: GraphQLCache(store: _store), + cache: GraphQLCache(), link: _httpLink, ), ); diff --git a/lib/models/cesiumPlus.dart b/lib/models/cesiumPlus.dart index bf59c6c5f3fac2d9162f441ba249dac9ebd45f06..7cbe5e4fcbf93bc4622a31df048a421060978bcf 100644 --- a/lib/models/cesiumPlus.dart +++ b/lib/models/cesiumPlus.dart @@ -67,7 +67,7 @@ class CesiumPlusProvider with ChangeNotifier { String _name; List queryOptions = await _buildQuery(_pubkey); - final response = await http.post(queryOptions[0], + final response = await http.post((Uri.parse(queryOptions[0])), body: queryOptions[1], headers: queryOptions[2]); // print('RESULT CESIUM QUERY: ${response.body}'); //For debug final responseJson = json.decode(response.body); @@ -87,7 +87,7 @@ class CesiumPlusProvider with ChangeNotifier { Future<List> getAvatar(String _pubkey) async { List queryOptions = await _buildQuery(_pubkey); - final response = await http.post(queryOptions[0], + final response = await http.post((Uri.parse(queryOptions[0])), body: queryOptions[1], headers: queryOptions[2]); // print('RESULT CESIUM QUERY: ${response.body}'); //For debug final responseJson = json.decode(response.body); diff --git a/lib/models/generateWallets.dart b/lib/models/generateWallets.dart index a7bc55d6dc62d405a83ad701c7b42be7c400a4d1..d06555602a740f3fcc729c2dd8542142f918be16 100644 --- a/lib/models/generateWallets.dart +++ b/lib/models/generateWallets.dart @@ -10,7 +10,6 @@ import 'package:gecko/globals.dart'; import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart' as pw; import 'package:printing/printing.dart'; -import 'package:truncate/truncate.dart'; class GenerateWalletsProvider with ChangeNotifier { GenerateWalletsProvider(); @@ -39,50 +38,38 @@ class GenerateWalletsProvider with ChangeNotifier { bool canImport = false; bool isPinChanged = false; - Future storeWallet(NewWallet wallet, String _name, BuildContext context, - {bool isHD = false}) async { - int nbrWallet; - if (isHD) { - nbrWallet = 0; - } else { - nbrWallet = 1; - } + Future storeHDWChest( + NewWallet _wallet, String _name, BuildContext context) async { + // Directory walletDirectory; - Directory walletNbrDirectory; - do { - nbrWallet++; - walletNbrDirectory = Directory('${walletsDirectory.path}/$nbrWallet'); - } while (await walletNbrDirectory.exists()); + final Directory hdDirectory = Directory('${walletsDirectory.path}/0'); + await hdDirectory.create(); - final walletFile = File('${walletNbrDirectory.path}/wallet.dewif'); + final configFile = File('${hdDirectory.path}/list.conf'); + File _currentChestFile = File('${walletsDirectory.path}/currentChest.conf'); - await walletNbrDirectory.create(); - await walletFile.writeAsString(wallet.dewif); + final dewifFile = File('${hdDirectory.path}/wallet.dewif'); - final configFile = File('${walletNbrDirectory.path}/config.txt'); + // List<String> _lastConfig = []; + // _lastConfig = await masterConfigFile.readAsLines(); + // final int _lastDerivation = int.parse(_lastConfig.last.split(':')[2]); + // final int _derivationNbr = _lastDerivation + 3; - if (isHD) { - final int _derivationNbr = 3; - List _pubkeysTmp = await DubpRust.getBip32DewifAccountsPublicKeys( - dewif: wallet.dewif, - secretCode: wallet.pin, - accountsIndex: [_derivationNbr]); - String _pubkey = _pubkeysTmp[0]; + final int _derivationNbr = 3; + List _pubkeysTmp = await DubpRust.getBip32DewifAccountsPublicKeys( + dewif: _wallet.dewif, + secretCode: _wallet.pin, + accountsIndex: [_derivationNbr]); + String _pubkey = _pubkeysTmp[0]; - await configFile - .writeAsString('$nbrWallet:$_name:$_derivationNbr:$_pubkey'); - // Navigator.pop(context, true); - } else { - final int _derivationNbr = -1; - String _pubkey = await DubpRust.getDewifPublicKey( - dewif: wallet.dewif, - pin: wallet.pin, - ); - await configFile - .writeAsString('$nbrWallet:$_name:$_derivationNbr:$_pubkey'); + await configFile.writeAsString('0:0:$_name:$_derivationNbr:$_pubkey'); + await dewifFile.writeAsString(_wallet.dewif); + bool isCurrentChestExist = _currentChestFile.existsSync(); + if (isCurrentChestExist) { + await _currentChestFile.delete(); } - - // Navigator.pop(context, true); + await _currentChestFile.create(); + await _currentChestFile.writeAsString('0'); return _name; } @@ -167,11 +154,9 @@ class GenerateWalletsProvider with ChangeNotifier { generatedMnemonic = await DubpRust.genMnemonic(language: Language.french); this.actualWallet = await generateWallet(this.generatedMnemonic); walletIsGenerated = true; - // notifyListeners(); } catch (e) { print(e); } - // await checkIfWalletExist(); return generatedMnemonic; } @@ -259,12 +244,12 @@ class GenerateWalletsProvider with ChangeNotifier { } Future importWallet(context, _cesiumID, _cesiumPWD) async { - String _walletPubkey = await DubpRust.getLegacyPublicKey( - salt: _cesiumID, password: _cesiumPWD); - String shortPubkey = truncate(_walletPubkey, 9, - omission: "...", position: TruncatePosition.end); - await storeWallet( - actualWallet, 'Portefeuille Cesium - $shortPubkey', context); + // String _walletPubkey = await DubpRust.getLegacyPublicKey( + // salt: _cesiumID, password: _cesiumPWD); + // String shortPubkey = truncate(_walletPubkey, 9, + // omission: "...", position: TruncatePosition.end); + // await storeWallet( + // actualWallet, 'Portefeuille Cesium - $shortPubkey', context); cesiumID.text = ''; cesiumPWD.text = ''; cesiumPubkey.text = ''; diff --git a/lib/models/history.dart b/lib/models/history.dart index d8da067a412e36208d892824af3dc7ba2c4317b9..13768bf11d0ff11f09e4b85d67d1e0f867397177 100644 --- a/lib/models/history.dart +++ b/lib/models/history.dart @@ -4,6 +4,7 @@ import 'package:gecko/globals.dart'; import 'package:gecko/models/home.dart'; import 'package:gecko/screens/history.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; +import 'package:jdenticon_dart/jdenticon_dart.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; import 'package:qrscan/qrscan.dart' as scanner; @@ -24,6 +25,7 @@ class HistoryProvider with ChangeNotifier { Map pageInfo; bool isHistoryScreen = false; String historySwitchButtun = "Voir l'historique"; + String rawSvg; Future scan(context) async { await Permission.camera.request(); @@ -43,7 +45,7 @@ class HistoryProvider with ChangeNotifier { return barcode; } - String isPubkey(context, pubkey) { + String isPubkey(context, pubkey, {bool goHistory}) { HomeProvider _homeProvider = Provider.of<HomeProvider>(context, listen: false); final RegExp regExp = new RegExp( @@ -62,8 +64,16 @@ class HistoryProvider with ChangeNotifier { this.outputPubkey.text = pubkey; - isHistoryScreen = false; - historySwitchButtun = "Voir l'historique"; + if (goHistory == null) goHistory = false; + + if (goHistory) { + isHistoryScreen = true; + historySwitchButtun = "Payer"; + } else { + isHistoryScreen = false; + historySwitchButtun = "Voir l'historique"; + } + _homeProvider.handleSearchEnd(); Navigator.push( context, @@ -101,7 +111,6 @@ class HistoryProvider with ChangeNotifier { // Lion simone: 78jhpprYkMNF6i5kQPXfkAVBpd2aqcpieNsXTSW4c21f List parseHistory(txs, _pubkey) { - // print(txs); var transBC = []; int i = 0; @@ -135,7 +144,7 @@ class HistoryProvider with ChangeNotifier { transBC[i].add(date); // print( // "DEBUG date et comment: ${date.toString()} -- ${transaction['comment'].toString()}"); - int amountBrut = int.parse(output.split(':')[0]); + final int amountBrut = int.parse(output.split(':')[0]); final base = int.parse(output.split(':')[1]); final int applyBase = base - currentBase; final num amount = @@ -242,6 +251,10 @@ class HistoryProvider with ChangeNotifier { notifyListeners(); } + String generateIdenticon(String _pubkey) { + return Jdenticon.toSvg(_pubkey); + } + // num getBalance(_pubkey) { // getBalance(_pubkey); // } diff --git a/lib/models/myWallets.dart b/lib/models/myWallets.dart index 4473f8765b836a7afde3c4f57e319e2aded19fcf..ed89498d11b4fb51f2e960da9a4c2c990247ca41 100644 --- a/lib/models/myWallets.dart +++ b/lib/models/myWallets.dart @@ -8,13 +8,50 @@ import 'package:provider/provider.dart'; class MyWalletsProvider with ChangeNotifier { String listWallets; + Future initWalletFolder() async { + await getDefaultWallet(); + + final bool isWalletFolderExist = await walletsDirectory.exists(); + if (!isWalletFolderExist) { + await Directory(walletsDirectory.path).create(); + } + + File _currentChestFile = File('${walletsDirectory.path}/currentChest.conf'); + + await _currentChestFile.create(); + await _currentChestFile.writeAsString('0'); + + final bool isChestsExist = + await Directory('${walletsDirectory.path}/0').exists(); + if (!isChestsExist) { + await Directory('${walletsDirectory.path}/0').create(); + await Directory('${walletsDirectory.path}/1').create(); + await File('${walletsDirectory.path}/0/list.conf').create(); + await File('${walletsDirectory.path}/0/order.conf').create(); + await File('${walletsDirectory.path}/1/list.conf').create(); + await File('${walletsDirectory.path}/1/order.conf').create(); + } + } + + int getCurrentChest() { + File _currentChestFile = File('${walletsDirectory.path}/currentChest.conf'); + + bool isCurrentChestExist = _currentChestFile.existsSync(); + if (!isCurrentChestExist) { + _currentChestFile.createSync(); + _currentChestFile.writeAsString('0'); + } + return int.parse(_currentChestFile.readAsStringSync()); + } + bool checkIfWalletExist() { if (appPath == null) { return false; } - List contents = walletsDirectory.listSync(); - if (contents.length == 0) { + final String _walletList = getAllWalletsNames(0); + + if (_walletList == '') { print('No wallets detected'); return false; } else { @@ -23,12 +60,7 @@ class MyWalletsProvider with ChangeNotifier { } } - String getAllWalletsNames() { - final bool _isWalletsExists = checkIfWalletExist(); - if (!_isWalletsExists) { - return ''; - } - + String getAllWalletsNames(int _chest) { if (listWallets != null && listWallets.isNotEmpty) { listWallets = ''; } @@ -36,24 +68,39 @@ class MyWalletsProvider with ChangeNotifier { listWallets = ''; } + print(walletsDirectory.path); + // int i = 0; - walletsDirectory - .listSync(recursive: false, followLinks: false) - .forEach((_wallet) { - File _walletConfig = File('${_wallet.path}/config.txt'); - _walletConfig.readAsLinesSync().forEach((element) { - if (listWallets != '') { - listWallets += '\n'; - } - listWallets += element; - // listWallets += "${element.split(':')[0]}:${element.split(':')[1]}:${element.split(':')[2]}" - }); + File _walletConfig = File('${walletsDirectory.path}/$_chest/list.conf'); + _walletConfig.readAsLinesSync().forEach((element) { + if (listWallets != '') { + listWallets += '\n'; + } + listWallets += element; + // listWallets += "${element.split(':')[0]}:${element.split(':')[1]}:${element.split(':')[2]}" }); print(listWallets); return listWallets; } + Future getDefaultWallet() async { + defaultWalletFile = File('${appPath.path}/defaultWallet'); + + bool isdefaultWalletFile = await defaultWalletFile.exists(); + + if (!isdefaultWalletFile) { + await File(defaultWalletFile.path).create(); + } + + try { + defaultWallet = await defaultWalletFile.readAsString(); + } catch (e) { + defaultWallet = '0:0'; + } + if (defaultWallet == '') defaultWallet = '0:0'; + } + Future<int> deleteAllWallet(context) async { try { print('DELETE THAT ?: $walletsDirectory'); @@ -63,6 +110,7 @@ class MyWalletsProvider with ChangeNotifier { if (_answer) { await walletsDirectory.delete(recursive: true); await walletsDirectory.create(); + await initWalletFolder(); notifyListeners(); Navigator.pop(context); } @@ -95,7 +143,7 @@ class MyWalletsProvider with ChangeNotifier { onPressed: () { WidgetsBinding.instance.addPostFrameCallback((_) { _myWalletProvider.listWallets = - _myWalletProvider.getAllWalletsNames(); + _myWalletProvider.getAllWalletsNames(getCurrentChest()); _myWalletProvider.rebuildWidget(); }); Navigator.pop(context, true); @@ -107,22 +155,26 @@ class MyWalletsProvider with ChangeNotifier { ); } - Future<void> generateNewDerivation( - context, String _name, int _walletNbr) async { + Future<void> generateNewDerivation(context, String _name) async { int _newDerivationNbr; - final _walletConfig = - File('${walletsDirectory.path}/$_walletNbr/config.txt'); + int _newWalletNbr; + final _walletConfig = File('${walletsDirectory.path}/0/list.conf'); if (await _walletConfig.readAsString() == '') { _newDerivationNbr = 3; + _newWalletNbr = 0; } else { String _lastWallet = await _walletConfig.readAsLines().then((value) => value.last); - int _lastDerivation = int.parse(_lastWallet.split(':')[2]); + int _lastDerivation = int.parse(_lastWallet.split(':')[3]); _newDerivationNbr = _lastDerivation + 3; + + int _lastWalletNbr = int.parse(_lastWallet.split(':')[1]); + _newWalletNbr = _lastWalletNbr + 1; } - await _walletConfig.writeAsString('\n$_walletNbr:$_name:$_newDerivationNbr', + await _walletConfig.writeAsString( + '\n0:$_newWalletNbr:$_name:$_newDerivationNbr', mode: FileMode.append); print(await _walletConfig.readAsString()); diff --git a/lib/models/queries.dart b/lib/models/queries.dart index 52754abd76f2bd85c81dd095c0e248f762c97a8d..72ba0e9e90d9547ec4118edb227975900a726032 100644 --- a/lib/models/queries.dart +++ b/lib/models/queries.dart @@ -1,7 +1,7 @@ const String getHistory = r''' query ($pubkey: String!, $number: Int!, $cursor: String) { txsHistoryBc( - pubkeyOrScript: $pubkey + script: $pubkey pagination: { pageSize: $number, ord: DESC, cursor: $cursor } ) { both { @@ -52,8 +52,13 @@ const String getHistory = r''' const String getBalance = r''' query ($pubkey: String!) { - balance(script: $pubkey) { - amount - base + balance(script: $pubkey) { + amount + base + } + currentUd { + amount + base + } } '''; diff --git a/lib/models/walletOptions.dart b/lib/models/walletOptions.dart index 034f9a51341ec88ab0ac71cf3c5bf024696bc500..31dfb81ec863ef14793e06e24c1d603fdecc0fc9 100644 --- a/lib/models/walletOptions.dart +++ b/lib/models/walletOptions.dart @@ -1,9 +1,15 @@ import 'dart:io'; +import 'dart:typed_data'; +import 'package:crypto/crypto.dart'; import 'package:dubp/dubp.dart'; +import 'package:fast_base58/fast_base58.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'dart:async'; import 'package:gecko/globals.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:truncate/truncate.dart'; +import 'package:qrscan/qrscan.dart' as scanner; class WalletOptionsProvider with ChangeNotifier { TextEditingController pubkey = TextEditingController(); @@ -11,6 +17,11 @@ class WalletOptionsProvider with ChangeNotifier { bool isWalletUnlock = false; bool ischangedPin = false; TextEditingController newPin = new TextEditingController(); + bool isEditing = false; + bool isBalanceBlur = true; + FocusNode walletNameFocus = FocusNode(); + TextEditingController nameController = TextEditingController(); + String walletID; Future<NewWallet> get badWallet => null; @@ -65,8 +76,7 @@ class WalletOptionsProvider with ChangeNotifier { int _walletNbr, String _pin, int _pinLenght, int derivation) async { isWalletUnlock = false; try { - File _walletFile = - File('${walletsDirectory.path}/$_walletNbr/wallet.dewif'); + File _walletFile = File('${walletsDirectory.path}/0/wallet.dewif'); String _localDewif = await _walletFile.readAsString(); String _localPubkey; @@ -107,8 +117,7 @@ class WalletOptionsProvider with ChangeNotifier { int getPinLenght(_walletNbr) { String _localDewif; if (_walletNbr is int) { - File _walletFile = - File('${walletsDirectory.path}/$_walletNbr/wallet.dewif'); + File _walletFile = File('${walletsDirectory.path}/0/wallet.dewif'); _localDewif = _walletFile.readAsStringSync(); } else { _localDewif = _walletNbr; @@ -120,22 +129,36 @@ class WalletOptionsProvider with ChangeNotifier { return _pinLenght; } - Future _renameWallet(_walletName, _newName, _walletNbr, _derivation) async { - final _walletConfig = - File('${walletsDirectory.path}/$_walletNbr/config.txt'); + Future _renameWallet(_walletID, _newName) async { + final _walletConfig = File('${walletsDirectory.path}/0/list.conf'); String newConfig = await _walletConfig.readAsLines().then((List<String> lines) { int nbrLines = lines.length; - int _index = lines.indexOf('$_walletNbr:$_walletName:$_derivation'); - print(nbrLines); + // print(lines); + // print(nbrLines); + // int _index = lines.indexOf('0:$_walletNbr:$_walletName:$_derivation'); if (nbrLines != 1) { - lines.removeWhere((element) => - element.contains('$_walletNbr:$_walletName:$_derivation')); - lines.insert(_index, '$_walletNbr:$_newName:$_derivation'); + for (String wLine in lines) { + String wID = "${wLine.split(':')[0]}:${wLine.split(':')[1]}"; + print( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + print(wLine); + String deri = wLine.split(':')[3]; + print("($wID == $_walletID ???"); + if (wID == _walletID) { + lines.remove(wLine); + lines.add('$_walletID:$_newName:$deri'); + // return '$_walletID:$_newName:$deri'; + print('OOUUUUUUUIIIIIIIIIIIIIIIIIII'); + } + } + // lines.removeWhere((element) => + // '${element.split(':')[0]}:${element.split(':')[1]}' == _walletID); + // lines.add('$_walletID:$_newName:$deri'); return lines.join('\n'); } else { - return '$_walletNbr:$_newName:$_derivation'; + return 'true'; } }); @@ -172,8 +195,8 @@ class WalletOptionsProvider with ChangeNotifier { child: Text("Valider"), onPressed: () { WidgetsBinding.instance.addPostFrameCallback((_) async { - await _renameWallet(_walletName, this._newWalletName.text, - _walletNbr, _derivation); + // await _renameWallet(_walletName, this._newWalletName.text, + // _walletNbr, _derivation); }); // notifyListeners(); Navigator.pop(context, true); @@ -185,18 +208,37 @@ class WalletOptionsProvider with ChangeNotifier { ); } + Future<bool> editWalletName(_wID) async { + bool nameState; + if (isEditing) { + if (!nameController.text.contains(':') && + nameController.text.length <= 39) { + await _renameWallet(_wID, nameController.text); + nameState = true; + } else { + nameState = false; + } + } else { + walletNameFocus.requestFocus(); + nameState = true; + } + + isEditing ? isEditing = false : isEditing = true; + notifyListeners(); + return nameState; + } + Future<int> deleteWallet(context, _walletNbr, _name, _derivation) async { final bool _answer = await _confirmDeletingWallet(context, _name); if (_answer) { - final _walletConfig = - File('${walletsDirectory.path}/$_walletNbr/config.txt'); + final _walletConfig = File('${walletsDirectory.path}/0/list.conf'); if (_derivation != -1) { String newConfig = await _walletConfig.readAsLines().then((List<String> lines) { - lines.removeWhere( - (element) => element.contains('$_walletNbr:$_name:$_derivation')); + lines.removeWhere((element) => + element.contains('0:$_walletNbr:$_name:$_derivation')); return lines.join('\n'); }); @@ -289,6 +331,53 @@ class WalletOptionsProvider with ChangeNotifier { ScaffoldMessenger.of(context).showSnackBar(snackBar); } + String getShortPubkey(String pubkey) { + List<int> pubkeyByte = Base58Decode(pubkey); + Digest pubkeyS256 = sha256.convert(sha256.convert(pubkeyByte).bytes); + String pubkeyCheksum = Base58Encode(pubkeyS256.bytes); + String pubkeyChecksumShort = truncate(pubkeyCheksum, 3, + omission: "", position: TruncatePosition.end); + + String pubkeyShort = truncate(pubkey, 5, + omission: String.fromCharCode(0x2026), + position: TruncatePosition.end) + + truncate(pubkey, 4, omission: "", position: TruncatePosition.start) + + ':$pubkeyChecksumShort'; + + return pubkeyShort; + } + + void bluringBalance() { + isBalanceBlur = !isBalanceBlur; + notifyListeners(); + } + + Future<Uint8List> generateQRcode(String _pubkey) async { + return await scanner.generateBarCode(_pubkey); + } + + Future defAsDefaultWallet(String _id) async { + await defaultWalletFile.delete(); + await defaultWalletFile.create(); + await defaultWalletFile + .writeAsString(_id) + .then((value) => notifyListeners()); + } + + Future changeAvatar() async { + File _image; + final picker = ImagePicker(); + + final pickedFile = await picker.getImage(source: ImageSource.gallery); + + if (pickedFile != null) { + _image = File(pickedFile.path); + return _image; + } else { + print('No image selected.'); + } + } + void reloadBuild() { notifyListeners(); } diff --git a/lib/screens/history.dart b/lib/screens/history.dart index a8b4cb127f5347e9085a204de220b2dc70a4a9b8..966c79d5a66a890f2163d64e39671f723ef9da41 100644 --- a/lib/screens/history.dart +++ b/lib/screens/history.dart @@ -1,5 +1,4 @@ import 'dart:io'; - import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/models/cesiumPlus.dart'; @@ -11,6 +10,7 @@ import 'package:flutter/foundation.dart'; import 'dart:ui'; import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:provider/provider.dart'; +import 'package:flutter_svg/flutter_svg.dart'; // ignore: must_be_immutable class HistoryScreen extends StatelessWidget with ChangeNotifier { @@ -25,6 +25,7 @@ class HistoryScreen extends StatelessWidget with ChangeNotifier { FetchMore fetchMore; FetchMoreOptions opts; + final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); @override Widget build(BuildContext context) { @@ -37,6 +38,7 @@ class HistoryScreen extends StatelessWidget with ChangeNotifier { WidgetsBinding.instance.addPostFrameCallback((_) {}); return Scaffold( + key: _scaffoldKey, appBar: AppBar( title: _homeProvider.appBarExplorer, actions: [ @@ -153,8 +155,14 @@ class HistoryScreen extends StatelessWidget with ChangeNotifier { return const Text('Aucune donnée à afficher.'); } - final num balance = _historyProvider - .removeDecimalZero(result.data['balance']['amount'] / 100); + num balance; + + if (result.data['balance'] == null) { + balance = 0.0; + } else { + balance = _historyProvider + .removeDecimalZero(result.data['balance']['amount'] / 100); + } opts = _historyProvider.checkQueryResult( result, opts, _outputPubkey.text); @@ -179,7 +187,7 @@ class HistoryScreen extends StatelessWidget with ChangeNotifier { if (_isFirstExec) Container( padding: const EdgeInsets.fromLTRB( - 12, 0, 5, 0), + 20, 0, 30, 0), child: FutureBuilder( future: _cesiumPlusProvider.getAvatar( @@ -235,7 +243,21 @@ class HistoryScreen extends StatelessWidget with ChangeNotifier { Container( padding: const EdgeInsets.fromLTRB( 30, 0, 5, 0), // .only(right: 15), - child: Text('TODO')), + child: Card( + child: Column( + children: <Widget>[ + SvgPicture.string( + _historyProvider + .generateIdenticon( + _historyProvider + .pubkey), + fit: BoxFit.contain, + height: 64, + width: 64, + ), + ], + ), + )), SizedBox(width: 0) ]), if (_isFirstExec) @@ -254,7 +276,7 @@ class HistoryScreen extends StatelessWidget with ChangeNotifier { initialData: '...', builder: (context, snapshot) { return Text( - snapshot.data != '' + snapshot.data != null ? snapshot.data : '-', style: diff --git a/lib/screens/myWallets/confirmWalletStorage.dart b/lib/screens/myWallets/confirmWalletStorage.dart index 3b6385f73a35b1228ecc75bf480d77009712349b..9c6069cfecd872411050132a31016f059e441c6b 100644 --- a/lib/screens/myWallets/confirmWalletStorage.dart +++ b/lib/screens/myWallets/confirmWalletStorage.dart @@ -28,6 +28,7 @@ class ConfirmStoreWallet extends StatelessWidget with ChangeNotifier { Provider.of<GenerateWalletsProvider>(context); MyWalletsProvider _myWalletProvider = Provider.of<MyWalletsProvider>(context); + final int _currentChest = _myWalletProvider.getCurrentChest(); this._mnemonicController.text = generatedMnemonic; return WillPopScope( @@ -126,11 +127,10 @@ class ConfirmStoreWallet extends StatelessWidget with ChangeNotifier { .isAskedWordValid && this.walletName.text != '') ? () async { - await _generateWalletProvider.storeWallet( + await _generateWalletProvider.storeHDWChest( generatedWallet, walletName.text, - context, - isHD: true); + context); _generateWalletProvider.isAskedWordValid = false; _generateWalletProvider.askedWordColor = @@ -138,8 +138,8 @@ class ConfirmStoreWallet extends StatelessWidget with ChangeNotifier { WidgetsBinding.instance .addPostFrameCallback((_) { _myWalletProvider.listWallets = - _myWalletProvider - .getAllWalletsNames(); + _myWalletProvider.getAllWalletsNames( + _currentChest); _myWalletProvider.rebuildWidget(); }); } diff --git a/lib/screens/myWallets/walletOptions-old.dart b/lib/screens/myWallets/walletOptions-old.dart new file mode 100644 index 0000000000000000000000000000000000000000..621335c184395aabc7a9c8a2ddd558dc33bb39f5 --- /dev/null +++ b/lib/screens/myWallets/walletOptions-old.dart @@ -0,0 +1,166 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:gecko/models/myWallets.dart'; +import 'package:gecko/models/walletOptions.dart'; +import 'dart:async'; +import 'package:provider/provider.dart'; +import 'package:flutter/services.dart'; + +// ignore: must_be_immutable +class WalletOptionsOld extends StatelessWidget with ChangeNotifier { + WalletOptionsOld( + {Key keyMyWallets, + @required this.walletNbr, + @required this.walletName, + @required this.derivation}) + : super(key: keyMyWallets); + int walletNbr; + String walletName; + int derivation; + + @override + Widget build(BuildContext context) { + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); + print("Build walletOptions"); + WalletOptionsProvider _walletOptions = + Provider.of<WalletOptionsProvider>(context); + MyWalletsProvider _myWalletProvider = + Provider.of<MyWalletsProvider>(context); + + // _walletOptions.isWalletUnlock = false; + print("Is unlock ? ${_walletOptions.isWalletUnlock}"); + + final int _currentChest = _myWalletProvider.getCurrentChest(); + + return WillPopScope( + onWillPop: () { + _walletOptions.isWalletUnlock = false; + Navigator.popUntil( + context, + ModalRoute.withName('/mywallets'), + ); + return Future<bool>.value(true); + }, + child: Scaffold( + resizeToAvoidBottomInset: false, + appBar: AppBar( + leading: IconButton( + icon: Icon(Icons.arrow_back, color: Colors.black), + onPressed: () { + _walletOptions.isWalletUnlock = false; + Navigator.popUntil( + context, + ModalRoute.withName('/mywallets'), + ); + }), + title: SizedBox( + height: 22, + child: Text(walletName), + )), + body: Builder( + builder: (ctx) => SafeArea( + child: Column(children: <Widget>[ + Expanded( + child: Column(children: <Widget>[ + SizedBox(height: 15), + Text( + 'Clé publique:', + style: TextStyle( + fontSize: 15.0, + color: Colors.grey[600], + fontWeight: FontWeight.w400), + ), + SizedBox(height: 15), + GestureDetector( + onTap: () { + Clipboard.setData(ClipboardData( + text: _walletOptions.pubkey.text)); + _walletOptions.snackCopyKey(ctx); + }, + child: Text( + _walletOptions.pubkey.text, + style: TextStyle( + fontSize: 14.0, + color: Colors.black, + fontWeight: FontWeight.bold, + fontFamily: 'Monospace'), + )), + Expanded( + child: Align( + alignment: Alignment.bottomCenter, + child: SizedBox( + height: 50, + width: 300, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 5, + primary: Color( + 0xffFFD68E), //Color(0xffFFD68E), // background + onPrimary: Colors.black, // foreground + ), + onPressed: () => _walletOptions + .renameWalletAlerte( + context, + walletName, + walletNbr, + derivation) + .then((_result) { + if (_result == true) { + WidgetsBinding.instance + .addPostFrameCallback((_) { + _myWalletProvider + .listWallets = + _myWalletProvider + .getAllWalletsNames( + _currentChest); + _myWalletProvider + .rebuildWidget(); + }); + Navigator.popUntil( + context, + ModalRoute.withName( + '/mywallets'), + ); + } + }), + child: Text('Renommer ce portefeuille', + style: TextStyle(fontSize: 20)))))), + SizedBox(height: 30), + SizedBox( + height: 50, + width: 300, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 6, + primary: Colors + .redAccent, //Color(0xffFFD68E), // background + onPrimary: Colors.black, // foreground + ), + onPressed: () async { + await _walletOptions.deleteWallet(context, + walletNbr, walletName, derivation); + WidgetsBinding.instance + .addPostFrameCallback((_) { + _myWalletProvider.listWallets = + _myWalletProvider + .getAllWalletsNames(_currentChest); + _myWalletProvider.rebuildWidget(); + }); + }, + child: Text('Supprimer ce portefeuille', + style: TextStyle(fontSize: 20)))), + SizedBox(height: 50), + Text( + 'Portefeuille déverrouillé', + style: TextStyle( + color: Colors.green, + fontWeight: FontWeight.w700, + fontSize: 15), + ), + SizedBox(height: 10) + ])), + ]), + )), + )); + } +} diff --git a/lib/screens/myWallets/walletOptions.dart b/lib/screens/myWallets/walletOptions.dart index 221e9e10070b4b7bf9e9834f9ff61c120401fb49..82d96f299a4a059d57654cc91432eb68de4472f6 100644 --- a/lib/screens/myWallets/walletOptions.dart +++ b/lib/screens/myWallets/walletOptions.dart @@ -1,7 +1,12 @@ +import 'dart:ui'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:gecko/globals.dart'; +import 'package:gecko/models/history.dart'; import 'package:gecko/models/myWallets.dart'; +import 'package:gecko/models/queries.dart'; import 'package:gecko/models/walletOptions.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; import 'dart:async'; import 'package:provider/provider.dart'; import 'package:flutter/services.dart'; @@ -17,6 +22,9 @@ class WalletOptions extends StatelessWidget with ChangeNotifier { int walletNbr; String walletName; int derivation; + int _nbrLinesName = 1; + bool _isNewNameValid = false; + bool isDefaultWallet; @override Widget build(BuildContext context) { @@ -26,137 +34,390 @@ class WalletOptions extends StatelessWidget with ChangeNotifier { Provider.of<WalletOptionsProvider>(context); MyWalletsProvider _myWalletProvider = Provider.of<MyWalletsProvider>(context); + HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context); - // _walletOptions.isWalletUnlock = false; - print("Is unlock ? ${_walletOptions.isWalletUnlock}"); + final int _currentChest = _myWalletProvider.getCurrentChest(); + final String shortPubkey = + _walletOptions.getShortPubkey(_walletOptions.pubkey.text); + + if (_walletOptions.nameController.text == null || + _isNewNameValid == false) { + _walletOptions.nameController.text = walletName; + } else { + walletName = _walletOptions.nameController.text; + } + _walletOptions.walletID = '0:$walletNbr'; + + _walletOptions.nameController.text.length >= 15 + ? _nbrLinesName = 2 + : _nbrLinesName = 1; + if (_walletOptions.nameController.text.length >= 26 && isTall) + _nbrLinesName = 3; + + defaultWallet == _walletOptions.walletID + ? isDefaultWallet = true + : isDefaultWallet = false; + + // print(_walletOptions.generateQRcode(_walletOptions.pubkey.text)); return WillPopScope( - onWillPop: () { - _walletOptions.isWalletUnlock = false; - Navigator.popUntil( - context, - ModalRoute.withName('/mywallets'), - ); - return Future<bool>.value(true); - }, - child: Scaffold( + onWillPop: () { + _walletOptions.isEditing = false; + _walletOptions.isBalanceBlur = true; + Navigator.popUntil( + context, + ModalRoute.withName('/'), + ); + Navigator.pushNamed(context, '/mywallets'); + return Future<bool>.value(true); + }, + child: Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar( leading: IconButton( icon: Icon(Icons.arrow_back, color: Colors.black), onPressed: () { - _walletOptions.isWalletUnlock = false; + _walletOptions.isEditing = false; + _walletOptions.isBalanceBlur = true; Navigator.popUntil( context, - ModalRoute.withName('/mywallets'), + ModalRoute.withName('/'), ); + Navigator.pushNamed(context, '/mywallets'); }), title: SizedBox( height: 22, - child: Text(walletName), + child: Text(_walletOptions.nameController.text), )), body: Builder( - builder: (ctx) => SafeArea( - child: Column(children: <Widget>[ - Expanded( + builder: (ctx) => SafeArea( + child: Column(children: <Widget>[ + Container( + height: isTall ? 15 : 0, + color: Color(0xffFFD68E), + ), + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0xffFFD68E), + Color(0xfffafafa), + ], + )), + child: Row(children: <Widget>[ + SizedBox(width: 25), + InkWell( + onTap: () async { + await _walletOptions.changeAvatar(); + print('CHANGE AVATAR'); + }, + child: Image.asset( + 'assets/chopp-gecko2.png', + )), + InkWell( + onTap: () async { + await _walletOptions.changeAvatar(); + print('CHANGE AVATAR'); + }, child: Column(children: <Widget>[ - SizedBox(height: 15), - Text( - 'Clé publique:', - style: TextStyle( - fontSize: 15.0, - color: Colors.grey[600], - fontWeight: FontWeight.w400), - ), - SizedBox(height: 15), - GestureDetector( - onTap: () { - Clipboard.setData(ClipboardData( - text: _walletOptions.pubkey.text)); - _walletOptions.snackCopyKey(ctx); - }, - child: Text( - _walletOptions.pubkey.text, - style: TextStyle( - fontSize: 14.0, - color: Colors.black, - fontWeight: FontWeight.bold, - fontFamily: 'Monospace'), - )), - Expanded( - child: Align( - alignment: Alignment.bottomCenter, - child: SizedBox( - height: 50, - width: 300, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - elevation: 5, - primary: Color( - 0xffFFD68E), //Color(0xffFFD68E), // background - onPrimary: Colors.black, // foreground - ), - onPressed: () => _walletOptions - .renameWalletAlerte( - context, - walletName, - walletNbr, - derivation) - .then((_result) { - if (_result == true) { - WidgetsBinding.instance - .addPostFrameCallback((_) { - _myWalletProvider - .listWallets = - _myWalletProvider - .getAllWalletsNames(); - _myWalletProvider - .rebuildWidget(); - }); - Navigator.popUntil( - context, - ModalRoute.withName( - '/mywallets'), - ); - } - }), - child: Text('Renommer ce portefeuille', - style: TextStyle(fontSize: 20)))))), - SizedBox(height: 30), - SizedBox( - height: 50, - width: 300, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - elevation: 6, - primary: Colors - .redAccent, //Color(0xffFFD68E), // background - onPrimary: Colors.black, // foreground - ), - onPressed: () async { - await _walletOptions.deleteWallet(context, - walletNbr, walletName, derivation); - WidgetsBinding.instance - .addPostFrameCallback((_) { - _myWalletProvider.listWallets = - _myWalletProvider.getAllWalletsNames(); - _myWalletProvider.rebuildWidget(); - }); + Image.asset( + 'assets/walletOptions/camera.png', + ), + SizedBox(height: 100) + ])), + // SizedBox(width: 20), + Column(children: <Widget>[ + Row(children: <Widget>[ + Column(children: <Widget>[ + SizedBox( + width: 260, + child: TextField( + // autofocus: true, + focusNode: _walletOptions.walletNameFocus, + enabled: _walletOptions.isEditing, + controller: _walletOptions.nameController, + maxLines: _nbrLinesName, + textAlign: TextAlign.center, + decoration: InputDecoration( + border: InputBorder.none, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + disabledBorder: InputBorder.none, + contentPadding: EdgeInsets.all(15.0), + ), + style: TextStyle( + fontSize: isTall ? 27 : 23, + color: Colors.black, + fontWeight: FontWeight.w400, + fontFamily: 'Monospace')), + ), + SizedBox(height: isTall ? 5 : 0), + Query( + options: QueryOptions( + document: gql(getBalance), + variables: { + 'pubkey': _walletOptions.pubkey.text, + }, + pollInterval: Duration(seconds: 1), + ), + builder: (QueryResult result, + {VoidCallback refetch, FetchMore fetchMore}) { + if (result.hasException) { + return Text(result.exception.toString()); + } + + if (result.isLoading) { + return Text('Loading'); + } + // List repositories = result.data['viewer']['repositories']['nodes']; + String wBalanceUD; + if (result.data['balance'] == null) { + wBalanceUD = '0.0'; + } else { + int wBalanceG1 = + result.data['balance']['amount']; + int currentUD = + result.data['currentUd']['amount']; + double wBalanceUDBrut = + wBalanceG1 / currentUD; // .toString(); + wBalanceUD = double.parse( + (wBalanceUDBrut).toStringAsFixed(2)) + .toString(); + } + return Row(children: <Widget>[ + ImageFiltered( + imageFilter: ImageFilter.blur( + sigmaX: _walletOptions.isBalanceBlur + ? 6 + : 0, + sigmaY: _walletOptions.isBalanceBlur + ? 5 + : 0), + child: Text('$wBalanceUD', + style: TextStyle( + fontSize: isTall ? 20 : 18, + color: Colors.black)), + ), + Text(' DU', + style: TextStyle( + fontSize: isTall ? 20 : 18, + color: Colors.black)) + ]); + + // Text( + // '$wBalanceUD DU', + // style: TextStyle( + // fontSize: 20, color: Colors.black), + // ); + }, + ), + SizedBox(height: 5), + InkWell( + onTap: () { + _walletOptions.bluringBalance(); + }, + child: Image.asset( + _walletOptions.isBalanceBlur + ? 'assets/walletOptions/icon_oeuil.png' + : 'assets/walletOptions/icon_oeuil_close.png', + )), + ]), + SizedBox(width: 0), + Column(children: <Widget>[ + InkWell( + onTap: () async { + // _walletOptions.isEditing = true; + // _walletOptions.reloadBuild(); + // _walletOptions.walletNameFocus + // .requestFocus(); + _isNewNameValid = await _walletOptions + .editWalletName(_walletOptions.walletID); + // .then((_) { + // _walletOptions.walletNameFocus + // .requestFocus(); + // _walletOptions.reloadBuild(); + // }); + + // .then( + // (_result) { + // if (_result == true) { + // WidgetsBinding.instance + // .addPostFrameCallback((_) { + // _myWalletProvider.listWallets = + // _myWalletProvider + // .getAllWalletsNames( + // _currentChest); + // _myWalletProvider.rebuildWidget(); + // }); + // Navigator.popUntil( + // context, + // ModalRoute.withName('/mywallets'), + // ); + // } + // }, + // ); }, - child: Text('Supprimer ce portefeuille', - style: TextStyle(fontSize: 20)))), - SizedBox(height: 50), - Text( - 'Portefeuille déverrouillé', + child: ClipRRect( + child: Image.asset( + _walletOptions.isEditing + ? 'assets/walletOptions/android-checkmark.png' + : 'assets/walletOptions/edit.png', + width: 20, + height: 20), + )), + // Image.asset( + // 'assets/walletOptions/edit.png', + // ), + SizedBox( + height: 60, + ) + ]) + ]), + ]), + ])), + SizedBox(height: 4 * ratio), + FutureBuilder( + future: _walletOptions + .generateQRcode(_walletOptions.pubkey.text), + builder: (context, snapshot) { + return snapshot.data != null + ? Image.memory(snapshot.data, + height: isTall ? 300 : 270) + : Text('-', style: TextStyle(fontSize: 20)); + }), + SizedBox(height: 15 * ratio), + GestureDetector( + onTap: () { + Clipboard.setData( + ClipboardData(text: _walletOptions.pubkey.text)); + _walletOptions.snackCopyKey(ctx); + }, + child: SizedBox( + height: 50, + child: Row(children: <Widget>[ + SizedBox(width: 30), + Image.asset( + 'assets/walletOptions/key.png', + ), + SizedBox(width: 10), + Text("${shortPubkey.split(':')[0]}:", + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.w800, + fontFamily: 'Monospace', + color: Colors.black)), + Text(shortPubkey.split(':')[1], + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.w800, + fontFamily: 'Monospace')), + SizedBox(width: 15), + SizedBox( + height: 40, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: + new BorderRadius.circular(8), + ), + elevation: 1, + primary: Color(0xffD28928), // background + onPrimary: Colors.black, // foreground + ), + onPressed: () { + Clipboard.setData(ClipboardData( + text: _walletOptions.pubkey.text)); + _walletOptions.snackCopyKey(ctx); + }, + child: Row(children: <Widget>[ + Image.asset( + 'assets/walletOptions/copy-white.png', + ), + SizedBox(width: 7), + Text('Copier', + style: TextStyle( + fontSize: 15, + color: Colors.grey[50])) + ]))), + ]))), + SizedBox(height: 10 * ratio), + InkWell( + onTap: () { + _historyProvider.isPubkey(ctx, _walletOptions.pubkey.text, + goHistory: true); + }, + child: SizedBox( + height: 50, + child: Row(children: <Widget>[ + SizedBox(width: 30), + Image.asset( + 'assets/walletOptions/clock.png', + ), + SizedBox(width: 12), + Text('Historique des transactions', + style: + TextStyle(fontSize: 20, color: Colors.black)), + ]))), + SizedBox(height: 12 * ratio), + InkWell( + onTap: !isDefaultWallet + ? () async { + await _walletOptions + .defAsDefaultWallet(_walletOptions.walletID) + .then((value) => { + _myWalletProvider + .getAllWalletsNames(_currentChest), + _myWalletProvider.rebuildWidget() + }); + } + : null, + child: SizedBox( + height: 50, + child: Row(children: <Widget>[ + SizedBox(width: 31), + CircleAvatar( + backgroundColor: + Colors.grey[isDefaultWallet ? 300 : 500], + child: Image.asset( + 'assets/walletOptions/android-checkmark.png', + )), + SizedBox(width: 12), + Text( + isDefaultWallet + ? 'Ce portefeuille est celui par defaut' + : 'Définir comme portefeuille par défaut', + style: TextStyle( + fontSize: 20, + color: isDefaultWallet + ? Colors.grey[500] + : Colors.black)), + ]))), + SizedBox(height: 17 * ratio), + InkWell( + onTap: () async { + await _walletOptions.deleteWallet( + context, walletNbr, walletName, derivation); + WidgetsBinding.instance.addPostFrameCallback((_) { + _myWalletProvider.listWallets = + _myWalletProvider.getAllWalletsNames(_currentChest); + _myWalletProvider.rebuildWidget(); + }); + }, + child: Row(children: <Widget>[ + SizedBox(width: 33), + Image.asset( + 'assets/walletOptions/trash.png', + ), + SizedBox(width: 14), + Text('Supprimer ce portefeuille', style: TextStyle( - color: Colors.green, - fontWeight: FontWeight.w700, - fontSize: 15), - ), - SizedBox(height: 10) - ])), - ]), - )), - )); + fontSize: 20, color: Color(0xffD80000))), + ])), + ]), + ), + )), + ); } } diff --git a/lib/screens/myWallets/walletsHome.dart b/lib/screens/myWallets/walletsHome.dart index 9f6dace71ae4701e16e129adeb956b78827fb53c..ff9f033c7f9b61d308cc23a04d8364efd563aa2a 100644 --- a/lib/screens/myWallets/walletsHome.dart +++ b/lib/screens/myWallets/walletsHome.dart @@ -1,4 +1,5 @@ import 'package:flutter/services.dart'; +import 'package:gecko/globals.dart'; import 'package:gecko/models/myWallets.dart'; import 'package:gecko/models/walletOptions.dart'; import 'package:flutter/material.dart'; @@ -19,12 +20,18 @@ class WalletsHome extends StatelessWidget { WalletOptionsProvider _walletOptions = Provider.of<WalletOptionsProvider>(context); _walletOptions.isWalletUnlock = false; - myWalletProvider.listWallets = myWalletProvider.getAllWalletsNames(); + + final int _currentChest = myWalletProvider.getCurrentChest(); + + myWalletProvider.listWallets = + myWalletProvider.getAllWalletsNames(_currentChest); final bool isWalletsExists = myWalletProvider.checkIfWalletExist(); if (myWalletProvider.listWallets != '') { firstWalletDerivation = - int.parse(myWalletProvider.listWallets.split('\n')[0].split(':')[2]); + int.parse(myWalletProvider.listWallets.split('\n')[0].split(':')[3]); + + myWalletProvider.getDefaultWallet(); } return Scaffold( @@ -57,10 +64,10 @@ class WalletsHome extends StatelessWidget { body: SafeArea( child: !isWalletsExists ? NoKeyChainScreen() - : myWalletsList(context))); + : myWalletsTiles(context))); } - Widget myWalletsList(BuildContext context) { + Widget myWalletsTiles(BuildContext context) { MyWalletsProvider _myWalletProvider = Provider.of<MyWalletsProvider>(context); @@ -72,39 +79,101 @@ class WalletsHome extends StatelessWidget { if (_myWalletProvider.listWallets == '') { return Expanded( - child: Center( - child: Text( - 'Veuillez générer votre premier portefeuille', - style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500), - ))); + child: Column(children: <Widget>[ + Center( + child: Text( + 'Veuillez générer votre premier portefeuille', + style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500), + )), + ])); } List _listWallets = _myWalletProvider.listWallets.split('\n'); + // final int nbrOfWallets = _listWallets.length; + // print(_listWallets); + // print("${_listWallets[0].split(':')[0]}:${_listWallets[0].split(':')[2]}"); + // print(defaultWallet); + + return GridView.count( + crossAxisCount: 2, + childAspectRatio: 1, + crossAxisSpacing: 0, + mainAxisSpacing: 0, + children: <Widget>[ + for (String _repository in _listWallets) + Padding( + padding: EdgeInsets.all(16), + child: GestureDetector( + onTap: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) { + return UnlockingWallet( + walletNbr: int.parse(_repository.split(':')[1]), + walletName: _repository.split(':')[2], + derivation: int.parse(_repository.split(':')[3])); + })); + }, + child: ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(12)), + child: Column(children: <Widget>[ + Expanded( + child: Container( + width: double.infinity, + height: double.infinity, + decoration: BoxDecoration( + gradient: RadialGradient( + radius: 1, + colors: [ + Colors.green[100], + Colors.green[500], + ], + )), + child: + // SvgPicture.asset('assets/chopp-gecko2.png', + // semanticsLabel: 'Gecko', height: 48), + Image.asset( + 'assets/chopp-gecko2.png', + ), + )), + ListTile( + // contentPadding: const EdgeInsets.only(left: 7.0), + tileColor: + "${_repository.split(':')[0]}:${_repository.split(':')[1]}" == + defaultWallet + ? Color(0xffD28928) + : Color(0xffFFD58D), + // leading: Text('IMAGE'), - return Expanded( - child: ListView(children: <Widget>[ - SizedBox(height: 8), - for (String _repository in _listWallets) - ListTile( - contentPadding: const EdgeInsets.only(left: 7.0), - leading: Padding( - padding: const EdgeInsets.all(6.0), - child: Text("0 Ğ1", style: TextStyle(fontSize: 14.0))), - // subtitle: Text(_repository.split(':')[3], - // style: TextStyle(fontSize: 12.0, fontFamily: 'Monospace')), - title: - Text(_repository.split(':')[1], style: TextStyle(fontSize: 16.0)), - dense: true, - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) { - return UnlockingWallet( - walletNbr: int.parse(_repository.split(':')[0]), - walletName: _repository.split(':')[1], - derivation: int.parse(_repository.split(':')[2])); - })); - }, - ) - ])); + // subtitle: Text(_repository.split(':')[3], + // style: TextStyle(fontSize: 12.0, fontFamily: 'Monospace')), + title: Center( + child: Padding( + padding: + EdgeInsets.symmetric(horizontal: 5), + child: Text(_repository.split(':')[2], + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16.0, + color: + "${_repository.split(':')[0]}:${_repository.split(':')[1]}" == + defaultWallet + ? Color(0xffF9F9F1) + : Colors.black)))), + // dense: true, + onTap: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) { + return UnlockingWallet( + walletNbr: + int.parse(_repository.split(':')[1]), + walletName: _repository.split(':')[2], + derivation: + int.parse(_repository.split(':')[3])); + })); + }, + ) + ])))) + ]); } Widget addNewDerivation(context, int _walletNbr) { @@ -142,7 +211,7 @@ class WalletsHome extends StatelessWidget { onPressed: () async { await _myWalletProvider .generateNewDerivation( - context, _newDerivationName.text, _walletNbr) + context, _newDerivationName.text) .then((_) => _newDerivationName.text == ''); }, child: Text("Créer")), diff --git a/lib/screens/onBoarding/12.dart b/lib/screens/onBoarding/12.dart index 73f44ee356f3ad4c082f4d03bf259661bf7b5895..2caee891963e6bc49508e57f306a7c715366c90f 100644 --- a/lib/screens/onBoarding/12.dart +++ b/lib/screens/onBoarding/12.dart @@ -61,6 +61,8 @@ class OnboardingStepFourteen extends StatelessWidget { GenerateWalletsProvider _generateWalletProvider = Provider.of<GenerateWalletsProvider>(context); + final int _currentChest = _myWalletProvider.getCurrentChest(); + return Form( key: formKey, child: Padding( @@ -116,10 +118,9 @@ class OnboardingStepFourteen extends StatelessWidget { if (resultWallet) { pinColor = Colors.green[500]; print(generatedWallet.pin); - await _generateWalletProvider.storeWallet( - generatedWallet, 'Mon portefeuille courant', context, - isHD: true); - _myWalletProvider.getAllWalletsNames(); + await _generateWalletProvider.storeHDWChest( + generatedWallet, 'Mon portefeuille courant', context); + _myWalletProvider.getAllWalletsNames(_currentChest); _walletOptions.reloadBuild(); _myWalletProvider.rebuildWidget(); Navigator.push( diff --git a/pubspec.lock b/pubspec.lock index e89bf23d80273b1ab4d418fba76735a78bdde945..c5f9205958ca0d7f7ce9bcc6072acfb708eb9f0f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -295,7 +295,14 @@ packages: name: image_picker url: "https://pub.dartlang.org" source: hosted - version: "0.7.2" + version: "0.7.3" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" image_picker_platform_interface: dependency: transitive description: @@ -317,6 +324,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.3" + jdenticon_dart: + dependency: "direct main" + description: + name: jdenticon_dart + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" js: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 7179ea67a9291823f5351922e163b4533b4829d4..1d2d785469a7e649d7cf818a4b886287b91aa873 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ description: Pay with G1. # pub.dev using `pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 0.0.1+20 +version: 0.0.1+22 environment: sdk: ">=2.7.0 <3.0.0" @@ -19,7 +19,7 @@ dependencies: qrscan: ^0.2.22 permission_handler: ^6.0.1 image_gallery_saver: ^1.6.8 - image_picker: ^0.7.2 + image_picker: ^0.7.3 # graphql_flutter: ^4.0.1 #^3.1.0 graphql_flutter: ^5.0.0-nullsafety.1 provider: ^4.3.2+3 @@ -45,6 +45,7 @@ dependencies: flutter_svg: ^0.20.0-nullsafety responsive_framework: ^0.0.14 responsive_builder: ^0.3.0 + jdenticon_dart: ^2.0.0 flutter_icons: android: "ic_launcher" @@ -67,3 +68,4 @@ flutter: - assets/icon/ - assets/onBoarding/ - assets/onBoarding/progress_bar/ + - assets/walletOptions/