diff --git a/assets/chests/1.png b/assets/chests/1.png index 327ce154552d3853601beeefaaa3092f077c2a80..419b1ac2b45e597f610bf9cd701e0ad66a1c3ebd 100644 Binary files a/assets/chests/1.png and b/assets/chests/1.png differ diff --git a/assets/chests/2.png b/assets/chests/2.png index bfb1a08b87c0ba50fef7bf00f1f177035a74875c..4ed6b34a1da92f08aaeca060380e46360d99d6f6 100644 Binary files a/assets/chests/2.png and b/assets/chests/2.png differ diff --git a/assets/chests/3.png b/assets/chests/3.png index fb93100c5609aff37e2f6efacd518d8752d582a7..66add243bf6a77eecb62c3945b075f4b8cdd6647 100644 Binary files a/assets/chests/3.png and b/assets/chests/3.png differ diff --git a/assets/chests/4.png b/assets/chests/4.png index a759503d57cc20db41bd48bafdaa496208db359c..7bc245f8e3c63372262c9fcb2f52fc087943068f 100644 Binary files a/assets/chests/4.png and b/assets/chests/4.png differ diff --git a/assets/chests/5.png b/assets/chests/5.png index 84d14905f6c474914b34991af36e52f9055d17f3..8852a6916a3b93b5ce68b98c618514924fbe9d5b 100644 Binary files a/assets/chests/5.png and b/assets/chests/5.png differ diff --git a/assets/chests/6.png b/assets/chests/6.png index 8f1556b586de45b602da90a7c0eff06917ed5288..1de8739db7b9c63d8cdec4d49237faa67a345f2c 100644 Binary files a/assets/chests/6.png and b/assets/chests/6.png differ diff --git a/assets/chests/7.png b/assets/chests/7.png index c34e084e185bf02f9607332d6c31d043f012e4ec..06bae0eea0f1ee98946b3d0bc804b7adfeb1c7f0 100644 Binary files a/assets/chests/7.png and b/assets/chests/7.png differ diff --git a/assets/chests/secret_code.png b/assets/chests/secret_code.png old mode 100755 new mode 100644 index 10ef999edbde1ee10c2b680667db3849947799a3..495717efc3c27a9cb9a57a017244e518cba01f92 Binary files a/assets/chests/secret_code.png and b/assets/chests/secret_code.png differ diff --git a/assets/chests/vector.png b/assets/chests/vector.png old mode 100755 new mode 100644 index 3fb42dba56fc072f97f8c4f62c36281a758e0237..9eb11a70397c24a644105146b15fcd2bab8470a7 Binary files a/assets/chests/vector.png and b/assets/chests/vector.png differ diff --git a/assets/copy_key.png b/assets/copy_key.png new file mode 100644 index 0000000000000000000000000000000000000000..8957f50508e12053f69ec383265e0a3b71494d92 Binary files /dev/null and b/assets/copy_key.png differ diff --git a/assets/home/background.jpg b/assets/home/background.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fed2fc8a1e72ea1c8e9d76e1cad0e89e75f9a465 Binary files /dev/null and b/assets/home/background.jpg differ diff --git a/assets/home/background.png b/assets/home/background.png new file mode 100755 index 0000000000000000000000000000000000000000..55f24c739a5fd978b6fcf87e5b7262b4a3d75003 Binary files /dev/null and b/assets/home/background.png differ diff --git a/assets/home/bout_de_bulle.png b/assets/home/bout_de_bulle.png new file mode 100755 index 0000000000000000000000000000000000000000..30b6325893633ab441111aedb260691cf0122de3 Binary files /dev/null and b/assets/home/bout_de_bulle.png differ diff --git a/assets/home/gecko-bienvenue.png b/assets/home/gecko-bienvenue.png new file mode 100644 index 0000000000000000000000000000000000000000..aad8e6a7d216d0769ebae560248e0b090f8dd293 Binary files /dev/null and b/assets/home/gecko-bienvenue.png differ diff --git a/assets/home/header.png b/assets/home/header.png new file mode 100755 index 0000000000000000000000000000000000000000..9d66bb0536c7227578904c95655a4d3de16b389a Binary files /dev/null and b/assets/home/header.png differ diff --git a/assets/home/header.svg b/assets/home/header.svg new file mode 100755 index 0000000000000000000000000000000000000000..171ccc89145c7ac17edbb45f53910ea1d5817b5d --- /dev/null +++ b/assets/home/header.svg @@ -0,0 +1,14 @@ +<svg width="215" height="215" viewBox="0 0 215 215" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<mask id="mask0_1500_379" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="-105" width="215" height="320"> +<path d="M51.3455 31.2015V-4.84328V-64.0593C73.2909 -76.9326 118.473 -102.937 123.636 -103.966C128.8 -104.996 152.467 -82.0821 163.655 -70.4963L168.818 15.7537L193.345 68.5336L214 163.795L168.818 214C131.812 205.418 56.5091 188.254 51.3455 188.254C44.8909 188.254 25.5273 177.955 20.3636 175.381C16.2327 173.321 5.73334 148.776 1 136.761L20.3636 107.153L57.8 81.4067V63.3843L51.3455 31.2015Z" fill="#C4C4C4" stroke="black"/> +</mask> +<g mask="url(#mask0_1500_379)"> +<rect x="385.691" y="-149.022" width="409.366" height="410.509" transform="rotate(90 385.691 -149.022)" fill="url(#pattern0)"/> +</g> +<defs> +<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1"> +<use xlink:href="#image0_1500_379" transform="translate(-0.00139659) scale(0.00138507)"/> +</pattern> +<image id="image0_1500_379" width="724" height="724" xlink:href="data:image/png;base64,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"/> +</defs> +</svg> diff --git a/assets/home/loupe.png b/assets/home/loupe.png new file mode 100644 index 0000000000000000000000000000000000000000..79293d6031b3bb7b8b47e1285218a6269750ae16 Binary files /dev/null and b/assets/home/loupe.png differ diff --git a/assets/home/loupe.svg b/assets/home/loupe.svg new file mode 100755 index 0000000000000000000000000000000000000000..a9fc7ef31f24eba9a61bcd1ffe747bf2641eb1d0 --- /dev/null +++ b/assets/home/loupe.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="58" height="54" fill="none" xmlns:v="https://vecta.io/nano"><path fill="url(#A)" d="M0 .28h58v53.268H0z"/><defs><pattern id="A" patternContentUnits="objectBoundingBox" width="1" height="1"><use xlink:href="#B" transform="matrix(.004608 0 0 .005018 0 -.021834)"/></pattern><image id="B" width="217" height="208" xlink:href="data:image/png;base64,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"/></defs></svg> \ No newline at end of file diff --git a/assets/home/qrcode.png b/assets/home/qrcode.png new file mode 100644 index 0000000000000000000000000000000000000000..407a5be9c3843db16b3822e6a70b92cbd1a5f737 Binary files /dev/null and b/assets/home/qrcode.png differ diff --git a/assets/home/wallet.png b/assets/home/wallet.png new file mode 100755 index 0000000000000000000000000000000000000000..ddadaec3020e0b14690b8354a68d55cbfa6a54d2 Binary files /dev/null and b/assets/home/wallet.png differ diff --git a/assets/home/wallet.svg b/assets/home/wallet.svg new file mode 100644 index 0000000000000000000000000000000000000000..7fa92172e0f8f5991e0571e4fa90e4030d64b29d --- /dev/null +++ b/assets/home/wallet.svg @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + x="0px" + y="0px" + viewBox="0 0 100 125" + id="svg10" + sodipodi:docname="wallet.svg" + inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"> + <metadata + id="metadata16"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs14"> + <clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath138"> + <rect + style="fill:#000000;fill-opacity:1" + id="rect140" + width="91.747101" + height="91.747101" + x="4.4194174" + y="957.15405" /> + </clipPath> + </defs> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1920" + inkscape:window-height="1015" + id="namedview12" + showgrid="true" + inkscape:zoom="5.6568543" + inkscape:cx="35.337997" + inkscape:cy="80.718182" + inkscape:window-x="1920" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="svg10" /> + <g + transform="translate(0,-952.36218)" + id="g4" + clip-path="url(#clipPath138)" + inkscape:export-xdpi="96" + inkscape:export-ydpi="96" + style="fill:#ffffff"> + <path + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" + d="M 50.90625 6 A 2.0002 2.0002 0 0 0 49.84375 6.5 L 30.0625 24.46875 L 18.1875 24.46875 C 15.543683 24.44965 13.123155 25.17569 11.25 26.46875 C 9.3768448 27.76181 7.9999596 29.75493 8 32.09375 L 8.03125 86.625 C 8.03684 88.8921 9.3819629 90.8203 11.1875 92.0625 C 12.993037 93.3047 15.308353 94 17.84375 94 L 77.6875 94 C 80.644255 94 83.374448 92.089642 84.4375 89.25 C 86.113053 89.114967 87.744729 88.6459 89.125 87.6875 C 90.81563 86.5136 92 84.44345 92 82.03125 L 92 32.09375 C 92 30.15621 91.430591 28.26221 90.1875 26.8125 C 88.944409 25.36279 86.997793 24.47521 84.8125 24.46875 L 74.96875 24.4375 L 69.5 13.25 A 2.0002 2.0002 0 0 0 67.53125 12.125 A 2.0002 2.0002 0 0 0 66.78125 12.34375 L 60.46875 15.59375 L 52.71875 6.6875 A 2.0002 2.0002 0 0 0 50.90625 6 z M 51.03125 10.84375 L 56.8125 17.5 L 26.5 33.125 L 51.03125 10.84375 z M 66.8125 16.84375 L 75.1875 33.96875 L 33.5625 34 L 66.8125 16.84375 z M 76.9375 28.4375 L 84.8125 28.46875 C 86.009294 28.47275 86.654841 28.85275 87.15625 29.4375 C 87.657659 30.0223 88 30.92895 88 32.09375 L 88 50.1875 L 67.625 50.1875 C 65.302589 50.1875 63.593739 52.225 63.59375 54.4375 L 63.59375 64.5 C 63.593768 66.7125 65.302709 68.78125 67.625 68.78125 L 88 68.78125 L 88 82.03125 C 88 83.31875 87.603802 83.87855 86.84375 84.40625 C 86.37816 84.729568 85.69117 84.975519 84.90625 85.125 L 84.90625 73.5625 A 2.0002 2.0002 0 1 0 80.90625 73.5625 L 80.90625 86.625 C 80.90625 88.7156 79.289498 90 77.6875 90 L 17.84375 90 C 16.046389 90 14.457371 89.48295 13.4375 88.78125 C 12.417629 88.07965 12.033004 87.30545 12.03125 86.59375 L 12 36.84375 C 13.469144 37.606227 15.201391 38.000302 17.0625 38 L 78.5 37.96875 C 79.316994 37.96862 79.724605 38.2578 80.15625 38.84375 C 80.587895 39.4297 80.906252 40.37836 80.90625 41.34375 L 80.90625 44.53125 A 2.0002 2.0002 0 1 0 84.90625 44.53125 L 84.90625 41.34375 C 84.906254 39.6214 84.425391 37.89465 83.375 36.46875 C 82.541915 35.337841 81.241284 34.452882 79.71875 34.125 L 76.9375 28.4375 z M 18.15625 28.46875 A 2.0002 2.0002 0 0 0 18.1875 28.46875 L 25.6875 28.46875 L 19.59375 34 L 17.0625 34 C 15.604391 34.00024 14.294499 33.60485 13.4375 33.0625 C 12.580501 32.52015 12.218298 31.89481 12.15625 31.40625 A 2.0002 2.0002 0 0 0 12.15625 31.375 C 12.362153 30.832741 12.788717 30.262578 13.53125 29.75 C 14.615705 29.00139 16.267859 28.45508 18.15625 28.46875 z M 67.625 54.1875 L 88 54.1875 L 88 64.78125 L 67.65625 64.78125 C 67.655285 64.784472 67.64577 64.78125 67.625 64.78125 C 67.611788 64.759611 67.59375 64.685125 67.59375 64.5 L 67.59375 54.4375 C 67.593749 54.1412 67.708223 54.1875 67.625 54.1875 z M 71.5 55.6875 A 2.0002 2.0002 0 0 0 69.71875 57.71875 L 69.71875 61.25 A 2.0002 2.0002 0 1 0 73.71875 61.25 L 73.71875 57.71875 A 2.0002 2.0002 0 0 0 71.5 55.6875 z " + transform="translate(0,952.36218)" + id="path2" /> + </g> + <text + x="0" + y="115" + fill="#000000" + font-size="5px" + font-weight="bold" + font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif" + id="text6">Created by counloucon</text> + <text + x="0" + y="120" + fill="#000000" + font-size="5px" + font-weight="bold" + font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif" + id="text8">from the Noun Project</text> +</svg> diff --git a/assets/loupe-noire.png b/assets/loupe-noire.png new file mode 100644 index 0000000000000000000000000000000000000000..bb8773daed29701653cfd986c341ee483c24953e Binary files /dev/null and b/assets/loupe-noire.png differ diff --git a/assets/printer.png b/assets/printer.png old mode 100755 new mode 100644 index ced5f5d0576f3f152f291dd4fb721220161033c0..aab7940cfbc6d9e7a23bd518a97ae29625ba5b72 Binary files a/assets/printer.png and b/assets/printer.png differ diff --git a/assets/vector_white.png b/assets/vector_white.png new file mode 100644 index 0000000000000000000000000000000000000000..347caaa90e815dde4964a9d48af0ae8eee90ec6d Binary files /dev/null and b/assets/vector_white.png differ diff --git a/assets/walletOptions/android-checkmark.png b/assets/walletOptions/android-checkmark.png old mode 100755 new mode 100644 index 578859d6b12b932c7fc9668e0a5163c485ab242d..ef913f5ca47860d7dfb4bb5589ff682275883071 Binary files a/assets/walletOptions/android-checkmark.png and b/assets/walletOptions/android-checkmark.png differ diff --git a/assets/walletOptions/camera.png b/assets/walletOptions/camera.png old mode 100755 new mode 100644 index ce9ad0fb1edb6469fea18f112feec17b1dea9968..49fe6ba44958f2f38774b13529b2b82b906da54e Binary files a/assets/walletOptions/camera.png and b/assets/walletOptions/camera.png differ diff --git a/assets/walletOptions/clock.png b/assets/walletOptions/clock.png old mode 100755 new mode 100644 index cf9a80270f311ac67d260b6be3bae8e744fb85a5..a4a684f2f9126fa416841623fce040b2759fcf67 Binary files a/assets/walletOptions/clock.png and b/assets/walletOptions/clock.png differ diff --git a/assets/walletOptions/copy-white.png b/assets/walletOptions/copy-white.png old mode 100755 new mode 100644 index 819e852fc49f7a8a7a519f2b9247f9aeabc66335..d085408ead44558311055d4250314e690d8e307a Binary files a/assets/walletOptions/copy-white.png and b/assets/walletOptions/copy-white.png differ diff --git a/assets/walletOptions/edit.png b/assets/walletOptions/edit.png old mode 100755 new mode 100644 index a45788da6b98ff24811ebacd61d02ba9716887d4..3963753a74507d2dba0a1c6d3d741c1f284a5745 Binary files a/assets/walletOptions/edit.png and b/assets/walletOptions/edit.png differ diff --git a/assets/walletOptions/icon_oeuil.png b/assets/walletOptions/icon_oeuil.png old mode 100755 new mode 100644 index f707647c214f59072e8349f8bbf994aa3770b676..75c12ec985c01d131ea5ea80328ffcd115e02cca Binary files a/assets/walletOptions/icon_oeuil.png and b/assets/walletOptions/icon_oeuil.png differ diff --git a/assets/walletOptions/icon_oeuil_close.png b/assets/walletOptions/icon_oeuil_close.png index 4a13e67ea0a5d9bae6eaf7f3e67d4ac6c5131406..db1dc512d666d476b3bc37ae9a5ac99aabe2401d 100644 Binary files a/assets/walletOptions/icon_oeuil_close.png and b/assets/walletOptions/icon_oeuil_close.png differ diff --git a/assets/walletOptions/key.png b/assets/walletOptions/key.png old mode 100755 new mode 100644 index 45c0e85ae5630256f68c58881ae717c3bd1d7049..bfabc3d6d799db5bc2de58d119059d59aa58b04e Binary files a/assets/walletOptions/key.png and b/assets/walletOptions/key.png differ diff --git a/assets/walletOptions/trash.png b/assets/walletOptions/trash.png old mode 100755 new mode 100644 index 6dc972b9aaebf7ecb89d33a1dafa12d586692e89..e2c1cbc3b48ac396404355b9a97e587c58b7637e Binary files a/assets/walletOptions/trash.png and b/assets/walletOptions/trash.png differ diff --git a/lib/globals.dart b/lib/globals.dart index 115be3dc70d2f839fbeacb2a461aa0d835ab506f..70cd2ae8ae2890d91149a9de387419bf4dc5f104 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:flutter/material.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:hive/hive.dart'; import 'package:logger/logger.dart'; @@ -16,9 +17,10 @@ int ramSys; Box<WalletData> walletBox; Box<ChestData> chestBox; Box configBox; +Box<G1WalletsList> g1WalletsBox; -// String cesiumPod = "https://g1.data.le-sou.org"; -String cesiumPod = "https://g1.data.e-is.pro"; +String cesiumPod = "https://g1.data.le-sou.org"; +// String cesiumPod = "https://g1.data.e-is.pro"; // Responsive ratios bool isTall; diff --git a/lib/main.dart b/lib/main.dart index d53b3674a629839505b8b99a371b16e6592e9aea..3377e3ab9c3998cf213be150fab164e851f9b34f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -23,16 +23,20 @@ import 'package:gecko/models/cesium_plus.dart'; import 'package:gecko/models/change_pin.dart'; import 'package:gecko/models/chest_data.dart'; import 'package:gecko/models/chest_provider.dart'; +import 'package:gecko/models/g1_wallets_list.dart'; import 'package:gecko/models/generate_wallets.dart'; -import 'package:gecko/models/history.dart'; +import 'package:gecko/models/wallets_profiles.dart'; import 'package:gecko/models/home.dart'; import 'package:gecko/models/my_wallets.dart'; +import 'package:gecko/models/search.dart'; import 'package:gecko/models/wallet_data.dart'; import 'package:gecko/models/wallet_options.dart'; import 'package:gecko/screens/home.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:gecko/screens/myWallets/wallets_home.dart'; +import 'package:gecko/screens/search.dart'; +import 'package:gecko/screens/search_result.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:path_provider/path_provider.dart'; @@ -41,17 +45,14 @@ import 'package:flutter/foundation.dart'; import 'package:responsive_framework/responsive_framework.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:flutter_driver/driver_extension.dart'; const bool enableSentry = true; Future<void> main() async { - enableFlutterDriverExtension(); WidgetsFlutterBinding.ensureInitialized(); HomeProvider _homeProvider = HomeProvider(); appPath = await getApplicationDocumentsDirectory(); - await _homeProvider.createDefaultAvatar(); appVersion = await _homeProvider.getAppVersion(); prefs = await SharedPreferences.getInstance(); @@ -59,15 +60,21 @@ Future<void> main() async { await Hive.initFlutter(appPath.path); Hive.registerAdapter(WalletDataAdapter()); Hive.registerAdapter(ChestDataAdapter()); + Hive.registerAdapter(G1WalletsListAdapter()); + Hive.registerAdapter(IdAdapter()); walletBox = await Hive.openBox<WalletData>("walletBox"); chestBox = await Hive.openBox<ChestData>("chestBox"); configBox = await Hive.openBox("configBox"); + g1WalletsBox = await Hive.openBox<G1WalletsList>("g1WalletsBox"); + + g1WalletsBox.clear(); // final HiveStore _store = // await HiveStore.open(path: '${appPath.path}/gqlCache'); // Get a valid GVA endpoint - endPointGVA = 'https://g1.librelois.fr/gva'; + // endPointGVA = 'https://g1.librelois.fr/gva'; + endPointGVA = 'https://duniter-g1.p2p.legal/gva'; // await _homeProvider.getValidEndpoint(); // if (endPointGVA == 'HS') { @@ -76,6 +83,8 @@ Future<void> main() async { // _homeProvider.playSound('start', 0.2); // } + HttpOverrides.global = MyHttpOverrides(); + if (kReleaseMode && enableSentry) { // CatcherOptions debugOptions = CatcherOptions(DialogReportMode(), [ // SentryHandler(SentryClient(SentryOptions( @@ -107,8 +116,6 @@ Future<void> main() async { } else { print('Debug mode enabled: No sentry alerte'); - HttpOverrides.global = MyHttpOverrides(); - runApp(Gecko(endPointGVA)); } } @@ -134,54 +141,58 @@ class Gecko extends StatelessWidget { // HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context); // HistoryProvider('').snackNode(context); return MultiProvider( - providers: [ - // Provider(create: (context) => HistoryProvider()), - ChangeNotifierProvider(create: (_) => HomeProvider()), - ChangeNotifierProvider(create: (_) => HistoryProvider('')), - ChangeNotifierProvider(create: (_) => MyWalletsProvider()), - ChangeNotifierProvider(create: (_) => ChestProvider()), - ChangeNotifierProvider(create: (_) => GenerateWalletsProvider()), - ChangeNotifierProvider(create: (_) => WalletOptionsProvider()), - ChangeNotifierProvider(create: (_) => ChangePinProvider()), - ChangeNotifierProvider(create: (_) => CesiumPlusProvider()) - ], - child: GraphQLProvider( - client: _client, - child: MaterialApp( - 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( - color: Color(0xffFFD58D), - foregroundColor: Color(0xFF000000), - ), - primaryColor: const Color(0xffFFD58D), - textTheme: const TextTheme( - bodyText1: TextStyle(), - bodyText2: TextStyle(), - ).apply( - bodyColor: const Color(0xFF000000), - ), - colorScheme: ColorScheme.fromSwatch() - .copyWith(secondary: Colors.grey[850]), + providers: [ + // Provider(create: (context) => HistoryProvider()), + ChangeNotifierProvider(create: (_) => HomeProvider()), + ChangeNotifierProvider(create: (_) => WalletsProfilesProvider('')), + ChangeNotifierProvider(create: (_) => MyWalletsProvider()), + ChangeNotifierProvider(create: (_) => ChestProvider()), + ChangeNotifierProvider(create: (_) => GenerateWalletsProvider()), + ChangeNotifierProvider(create: (_) => WalletOptionsProvider()), + ChangeNotifierProvider(create: (_) => ChangePinProvider()), + ChangeNotifierProvider(create: (_) => SearchProvider()), + ChangeNotifierProvider(create: (_) => CesiumPlusProvider()) + ], + child: GraphQLProvider( + client: _client, + child: MaterialApp( + 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( + color: Color(0xffFFD58D), + foregroundColor: Color(0xFF000000), ), - home: const HomeScreen(), - initialRoute: "/", - routes: { - '/mywallets': (context) => WalletsHome(), - }, + primaryColor: const Color(0xffFFD58D), + textTheme: const TextTheme( + bodyText1: TextStyle(), + bodyText2: TextStyle(), + ).apply( + bodyColor: const Color(0xFF000000), + ), + colorScheme: + ColorScheme.fromSwatch().copyWith(secondary: Colors.grey[850]), ), - )); + home: const HomeScreen(), + initialRoute: "/", + routes: { + '/mywallets': (context) => WalletsHome(), + '/search': (context) => const SearchScreen(), + '/searchResult': (context) => const SearchResultScreen(), + }, + ), + ), + ); } } diff --git a/lib/models/bip39_words.dart b/lib/models/bip39_words.dart new file mode 100644 index 0000000000000000000000000000000000000000..0809642c0bd76637cca428ca1b12be28db46d06f --- /dev/null +++ b/lib/models/bip39_words.dart @@ -0,0 +1,2050 @@ +List<String> bip39Words = [ + 'abaisser', + 'abandon', + 'abdiquer', + 'abeille', + 'abolir', + 'aborder', + 'aboutir', + 'aboyer', + 'abrasif', + 'abreuver', + 'abriter', + 'abroger', + 'abrupt', + 'absence', + 'absolu', + 'absurde', + 'abusif', + 'abyssal', + 'acadeÌmie', + 'acajou', + 'acarien', + 'accabler', + 'accepter', + 'acclamer', + 'accolade', + 'accroche', + 'accuser', + 'acerbe', + 'achat', + 'acheter', + 'aciduler', + 'acier', + 'acompte', + 'acqueÌrir', + 'acronyme', + 'acteur', + 'actif', + 'actuel', + 'adepte', + 'adeÌquat', + 'adheÌsif', + 'adjectif', + 'adjuger', + 'admettre', + 'admirer', + 'adopter', + 'adorer', + 'adoucir', + 'adresse', + 'adroit', + 'adulte', + 'adverbe', + 'aeÌrer', + 'aeÌronef', + 'affaire', + 'affecter', + 'affiche', + 'affreux', + 'affubler', + 'agacer', + 'agencer', + 'agile', + 'agiter', + 'agrafer', + 'agreÌable', + 'agrume', + 'aider', + 'aiguille', + 'ailier', + 'aimable', + 'aisance', + 'ajouter', + 'ajuster', + 'alarmer', + 'alchimie', + 'alerte', + 'algeÌ€bre', + 'algue', + 'alieÌner', + 'aliment', + 'alleÌger', + 'alliage', + 'allouer', + 'allumer', + 'alourdir', + 'alpaga', + 'altesse', + 'alveÌole', + 'amateur', + 'ambigu', + 'ambre', + 'ameÌnager', + 'amertume', + 'amidon', + 'amiral', + 'amorcer', + 'amour', + 'amovible', + 'amphibie', + 'ampleur', + 'amusant', + 'analyse', + 'anaphore', + 'anarchie', + 'anatomie', + 'ancien', + 'aneÌantir', + 'angle', + 'angoisse', + 'anguleux', + 'animal', + 'annexer', + 'annonce', + 'annuel', + 'anodin', + 'anomalie', + 'anonyme', + 'anormal', + 'antenne', + 'antidote', + 'anxieux', + 'apaiser', + 'apeÌritif', + 'aplanir', + 'apologie', + 'appareil', + 'appeler', + 'apporter', + 'appuyer', + 'aquarium', + 'aqueduc', + 'arbitre', + 'arbuste', + 'ardeur', + 'ardoise', + 'argent', + 'arlequin', + 'armature', + 'armement', + 'armoire', + 'armure', + 'arpenter', + 'arracher', + 'arriver', + 'arroser', + 'arsenic', + 'arteÌriel', + 'article', + 'aspect', + 'asphalte', + 'aspirer', + 'assaut', + 'asservir', + 'assiette', + 'associer', + 'assurer', + 'asticot', + 'astre', + 'astuce', + 'atelier', + 'atome', + 'atrium', + 'atroce', + 'attaque', + 'attentif', + 'attirer', + 'attraper', + 'aubaine', + 'auberge', + 'audace', + 'audible', + 'augurer', + 'aurore', + 'automne', + 'autruche', + 'avaler', + 'avancer', + 'avarice', + 'avenir', + 'averse', + 'aveugle', + 'aviateur', + 'avide', + 'avion', + 'aviser', + 'avoine', + 'avouer', + 'avril', + 'axial', + 'axiome', + 'badge', + 'bafouer', + 'bagage', + 'baguette', + 'baignade', + 'balancer', + 'balcon', + 'baleine', + 'balisage', + 'bambin', + 'bancaire', + 'bandage', + 'banlieue', + 'bannieÌ€re', + 'banquier', + 'barbier', + 'baril', + 'baron', + 'barque', + 'barrage', + 'bassin', + 'bastion', + 'bataille', + 'bateau', + 'batterie', + 'baudrier', + 'bavarder', + 'belette', + 'beÌlier', + 'belote', + 'beÌneÌfice', + 'berceau', + 'berger', + 'berline', + 'bermuda', + 'besace', + 'besogne', + 'beÌtail', + 'beurre', + 'biberon', + 'bicycle', + 'bidule', + 'bijou', + 'bilan', + 'bilingue', + 'billard', + 'binaire', + 'biologie', + 'biopsie', + 'biotype', + 'biscuit', + 'bison', + 'bistouri', + 'bitume', + 'bizarre', + 'blafard', + 'blague', + 'blanchir', + 'blessant', + 'blinder', + 'blond', + 'bloquer', + 'blouson', + 'bobard', + 'bobine', + 'boire', + 'boiser', + 'bolide', + 'bonbon', + 'bondir', + 'bonheur', + 'bonifier', + 'bonus', + 'bordure', + 'borne', + 'botte', + 'boucle', + 'boueux', + 'bougie', + 'boulon', + 'bouquin', + 'bourse', + 'boussole', + 'boutique', + 'boxeur', + 'branche', + 'brasier', + 'brave', + 'brebis', + 'breÌ€che', + 'breuvage', + 'bricoler', + 'brigade', + 'brillant', + 'brioche', + 'brique', + 'brochure', + 'broder', + 'bronzer', + 'brousse', + 'broyeur', + 'brume', + 'brusque', + 'brutal', + 'bruyant', + 'buffle', + 'buisson', + 'bulletin', + 'bureau', + 'burin', + 'bustier', + 'butiner', + 'butoir', + 'buvable', + 'buvette', + 'cabanon', + 'cabine', + 'cachette', + 'cadeau', + 'cadre', + 'cafeÌine', + 'caillou', + 'caisson', + 'calculer', + 'calepin', + 'calibre', + 'calmer', + 'calomnie', + 'calvaire', + 'camarade', + 'cameÌra', + 'camion', + 'campagne', + 'canal', + 'caneton', + 'canon', + 'cantine', + 'canular', + 'capable', + 'caporal', + 'caprice', + 'capsule', + 'capter', + 'capuche', + 'carabine', + 'carbone', + 'caresser', + 'caribou', + 'carnage', + 'carotte', + 'carreau', + 'carton', + 'cascade', + 'casier', + 'casque', + 'cassure', + 'causer', + 'caution', + 'cavalier', + 'caverne', + 'caviar', + 'ceÌdille', + 'ceinture', + 'ceÌleste', + 'cellule', + 'cendrier', + 'censurer', + 'central', + 'cercle', + 'ceÌreÌbral', + 'cerise', + 'cerner', + 'cerveau', + 'cesser', + 'chagrin', + 'chaise', + 'chaleur', + 'chambre', + 'chance', + 'chapitre', + 'charbon', + 'chasseur', + 'chaton', + 'chausson', + 'chavirer', + 'chemise', + 'chenille', + 'cheÌquier', + 'chercher', + 'cheval', + 'chien', + 'chiffre', + 'chignon', + 'chimeÌ€re', + 'chiot', + 'chlorure', + 'chocolat', + 'choisir', + 'chose', + 'chouette', + 'chrome', + 'chute', + 'cigare', + 'cigogne', + 'cimenter', + 'cineÌma', + 'cintrer', + 'circuler', + 'cirer', + 'cirque', + 'citerne', + 'citoyen', + 'citron', + 'civil', + 'clairon', + 'clameur', + 'claquer', + 'classe', + 'clavier', + 'client', + 'cligner', + 'climat', + 'clivage', + 'cloche', + 'clonage', + 'cloporte', + 'cobalt', + 'cobra', + 'cocasse', + 'cocotier', + 'coder', + 'codifier', + 'coffre', + 'cogner', + 'coheÌsion', + 'coiffer', + 'coincer', + 'coleÌ€re', + 'colibri', + 'colline', + 'colmater', + 'colonel', + 'combat', + 'comeÌdie', + 'commande', + 'compact', + 'concert', + 'conduire', + 'confier', + 'congeler', + 'connoter', + 'consonne', + 'contact', + 'convexe', + 'copain', + 'copie', + 'corail', + 'corbeau', + 'cordage', + 'corniche', + 'corpus', + 'correct', + 'corteÌ€ge', + 'cosmique', + 'costume', + 'coton', + 'coude', + 'coupure', + 'courage', + 'couteau', + 'couvrir', + 'coyote', + 'crabe', + 'crainte', + 'cravate', + 'crayon', + 'creÌature', + 'creÌditer', + 'creÌmeux', + 'creuser', + 'crevette', + 'cribler', + 'crier', + 'cristal', + 'criteÌ€re', + 'croire', + 'croquer', + 'crotale', + 'crucial', + 'cruel', + 'crypter', + 'cubique', + 'cueillir', + 'cuilleÌ€re', + 'cuisine', + 'cuivre', + 'culminer', + 'cultiver', + 'cumuler', + 'cupide', + 'curatif', + 'curseur', + 'cyanure', + 'cycle', + 'cylindre', + 'cynique', + 'daigner', + 'damier', + 'danger', + 'danseur', + 'dauphin', + 'deÌbattre', + 'deÌbiter', + 'deÌborder', + 'deÌbrider', + 'deÌbutant', + 'deÌcaler', + 'deÌcembre', + 'deÌchirer', + 'deÌcider', + 'deÌclarer', + 'deÌcorer', + 'deÌcrire', + 'deÌcupler', + 'deÌdale', + 'deÌductif', + 'deÌesse', + 'deÌfensif', + 'deÌfiler', + 'deÌfrayer', + 'deÌgager', + 'deÌgivrer', + 'deÌglutir', + 'deÌgrafer', + 'deÌjeuner', + 'deÌlice', + 'deÌloger', + 'demander', + 'demeurer', + 'deÌmolir', + 'deÌnicher', + 'deÌnouer', + 'dentelle', + 'deÌnuder', + 'deÌpart', + 'deÌpenser', + 'deÌphaser', + 'deÌplacer', + 'deÌposer', + 'deÌranger', + 'deÌrober', + 'deÌsastre', + 'descente', + 'deÌsert', + 'deÌsigner', + 'deÌsobeÌir', + 'dessiner', + 'destrier', + 'deÌtacher', + 'deÌtester', + 'deÌtourer', + 'deÌtresse', + 'devancer', + 'devenir', + 'deviner', + 'devoir', + 'diable', + 'dialogue', + 'diamant', + 'dicter', + 'diffeÌrer', + 'digeÌrer', + 'digital', + 'digne', + 'diluer', + 'dimanche', + 'diminuer', + 'dioxyde', + 'directif', + 'diriger', + 'discuter', + 'disposer', + 'dissiper', + 'distance', + 'divertir', + 'diviser', + 'docile', + 'docteur', + 'dogme', + 'doigt', + 'domaine', + 'domicile', + 'dompter', + 'donateur', + 'donjon', + 'donner', + 'dopamine', + 'dortoir', + 'dorure', + 'dosage', + 'doseur', + 'dossier', + 'dotation', + 'douanier', + 'double', + 'douceur', + 'douter', + 'doyen', + 'dragon', + 'draper', + 'dresser', + 'dribbler', + 'droiture', + 'duperie', + 'duplexe', + 'durable', + 'durcir', + 'dynastie', + 'eÌblouir', + 'eÌcarter', + 'eÌcharpe', + 'eÌchelle', + 'eÌclairer', + 'eÌclipse', + 'eÌclore', + 'eÌcluse', + 'eÌcole', + 'eÌconomie', + 'eÌcorce', + 'eÌcouter', + 'eÌcraser', + 'eÌcreÌmer', + 'eÌcrivain', + 'eÌcrou', + 'eÌcume', + 'eÌcureuil', + 'eÌdifier', + 'eÌduquer', + 'effacer', + 'effectif', + 'effigie', + 'effort', + 'effrayer', + 'effusion', + 'eÌgaliser', + 'eÌgarer', + 'eÌjecter', + 'eÌlaborer', + 'eÌlargir', + 'eÌlectron', + 'eÌleÌgant', + 'eÌleÌphant', + 'eÌleÌ€ve', + 'eÌligible', + 'eÌlitisme', + 'eÌloge', + 'eÌlucider', + 'eÌluder', + 'emballer', + 'embellir', + 'embryon', + 'eÌmeraude', + 'eÌmission', + 'emmener', + 'eÌmotion', + 'eÌmouvoir', + 'empereur', + 'employer', + 'emporter', + 'emprise', + 'eÌmulsion', + 'encadrer', + 'encheÌ€re', + 'enclave', + 'encoche', + 'endiguer', + 'endosser', + 'endroit', + 'enduire', + 'eÌnergie', + 'enfance', + 'enfermer', + 'enfouir', + 'engager', + 'engin', + 'englober', + 'eÌnigme', + 'enjamber', + 'enjeu', + 'enlever', + 'ennemi', + 'ennuyeux', + 'enrichir', + 'enrobage', + 'enseigne', + 'entasser', + 'entendre', + 'entier', + 'entourer', + 'entraver', + 'eÌnumeÌrer', + 'envahir', + 'enviable', + 'envoyer', + 'enzyme', + 'eÌolien', + 'eÌpaissir', + 'eÌpargne', + 'eÌpatant', + 'eÌpaule', + 'eÌpicerie', + 'eÌpideÌmie', + 'eÌpier', + 'eÌpilogue', + 'eÌpine', + 'eÌpisode', + 'eÌpitaphe', + 'eÌpoque', + 'eÌpreuve', + 'eÌprouver', + 'eÌpuisant', + 'eÌquerre', + 'eÌquipe', + 'eÌriger', + 'eÌrosion', + 'erreur', + 'eÌruption', + 'escalier', + 'espadon', + 'espeÌ€ce', + 'espieÌ€gle', + 'espoir', + 'esprit', + 'esquiver', + 'essayer', + 'essence', + 'essieu', + 'essorer', + 'estime', + 'estomac', + 'estrade', + 'eÌtageÌ€re', + 'eÌtaler', + 'eÌtanche', + 'eÌtatique', + 'eÌteindre', + 'eÌtendoir', + 'eÌternel', + 'eÌthanol', + 'eÌthique', + 'ethnie', + 'eÌtirer', + 'eÌtoffer', + 'eÌtoile', + 'eÌtonnant', + 'eÌtourdir', + 'eÌtrange', + 'eÌtroit', + 'eÌtude', + 'euphorie', + 'eÌvaluer', + 'eÌvasion', + 'eÌventail', + 'eÌvidence', + 'eÌviter', + 'eÌvolutif', + 'eÌvoquer', + 'exact', + 'exageÌrer', + 'exaucer', + 'exceller', + 'excitant', + 'exclusif', + 'excuse', + 'exeÌcuter', + 'exemple', + 'exercer', + 'exhaler', + 'exhorter', + 'exigence', + 'exiler', + 'exister', + 'exotique', + 'expeÌdier', + 'explorer', + 'exposer', + 'exprimer', + 'exquis', + 'extensif', + 'extraire', + 'exulter', + 'fable', + 'fabuleux', + 'facette', + 'facile', + 'facture', + 'faiblir', + 'falaise', + 'fameux', + 'famille', + 'farceur', + 'farfelu', + 'farine', + 'farouche', + 'fasciner', + 'fatal', + 'fatigue', + 'faucon', + 'fautif', + 'faveur', + 'favori', + 'feÌbrile', + 'feÌconder', + 'feÌdeÌrer', + 'feÌlin', + 'femme', + 'feÌmur', + 'fendoir', + 'feÌodal', + 'fermer', + 'feÌroce', + 'ferveur', + 'festival', + 'feuille', + 'feutre', + 'feÌvrier', + 'fiasco', + 'ficeler', + 'fictif', + 'fideÌ€le', + 'figure', + 'filature', + 'filetage', + 'filieÌ€re', + 'filleul', + 'filmer', + 'filou', + 'filtrer', + 'financer', + 'finir', + 'fiole', + 'firme', + 'fissure', + 'fixer', + 'flairer', + 'flamme', + 'flasque', + 'flatteur', + 'fleÌau', + 'fleÌ€che', + 'fleur', + 'flexion', + 'flocon', + 'flore', + 'fluctuer', + 'fluide', + 'fluvial', + 'folie', + 'fonderie', + 'fongible', + 'fontaine', + 'forcer', + 'forgeron', + 'formuler', + 'fortune', + 'fossile', + 'foudre', + 'fougeÌ€re', + 'fouiller', + 'foulure', + 'fourmi', + 'fragile', + 'fraise', + 'franchir', + 'frapper', + 'frayeur', + 'freÌgate', + 'freiner', + 'frelon', + 'freÌmir', + 'freÌneÌsie', + 'freÌ€re', + 'friable', + 'friction', + 'frisson', + 'frivole', + 'froid', + 'fromage', + 'frontal', + 'frotter', + 'fruit', + 'fugitif', + 'fuite', + 'fureur', + 'furieux', + 'furtif', + 'fusion', + 'futur', + 'gagner', + 'galaxie', + 'galerie', + 'gambader', + 'garantir', + 'gardien', + 'garnir', + 'garrigue', + 'gazelle', + 'gazon', + 'geÌant', + 'geÌlatine', + 'geÌlule', + 'gendarme', + 'geÌneÌral', + 'geÌnie', + 'genou', + 'gentil', + 'geÌologie', + 'geÌomeÌ€tre', + 'geÌranium', + 'germe', + 'gestuel', + 'geyser', + 'gibier', + 'gicler', + 'girafe', + 'givre', + 'glace', + 'glaive', + 'glisser', + 'globe', + 'gloire', + 'glorieux', + 'golfeur', + 'gomme', + 'gonfler', + 'gorge', + 'gorille', + 'goudron', + 'gouffre', + 'goulot', + 'goupille', + 'gourmand', + 'goutte', + 'graduel', + 'graffiti', + 'graine', + 'grand', + 'grappin', + 'gratuit', + 'gravir', + 'grenat', + 'griffure', + 'griller', + 'grimper', + 'grogner', + 'gronder', + 'grotte', + 'groupe', + 'gruger', + 'grutier', + 'gruyeÌ€re', + 'gueÌpard', + 'guerrier', + 'guide', + 'guimauve', + 'guitare', + 'gustatif', + 'gymnaste', + 'gyrostat', + 'habitude', + 'hachoir', + 'halte', + 'hameau', + 'hangar', + 'hanneton', + 'haricot', + 'harmonie', + 'harpon', + 'hasard', + 'heÌlium', + 'heÌmatome', + 'herbe', + 'heÌrisson', + 'hermine', + 'heÌron', + 'heÌsiter', + 'heureux', + 'hiberner', + 'hibou', + 'hilarant', + 'histoire', + 'hiver', + 'homard', + 'hommage', + 'homogeÌ€ne', + 'honneur', + 'honorer', + 'honteux', + 'horde', + 'horizon', + 'horloge', + 'hormone', + 'horrible', + 'houleux', + 'housse', + 'hublot', + 'huileux', + 'humain', + 'humble', + 'humide', + 'humour', + 'hurler', + 'hydromel', + 'hygieÌ€ne', + 'hymne', + 'hypnose', + 'idylle', + 'ignorer', + 'iguane', + 'illicite', + 'illusion', + 'image', + 'imbiber', + 'imiter', + 'immense', + 'immobile', + 'immuable', + 'impact', + 'impeÌrial', + 'implorer', + 'imposer', + 'imprimer', + 'imputer', + 'incarner', + 'incendie', + 'incident', + 'incliner', + 'incolore', + 'indexer', + 'indice', + 'inductif', + 'ineÌdit', + 'ineptie', + 'inexact', + 'infini', + 'infliger', + 'informer', + 'infusion', + 'ingeÌrer', + 'inhaler', + 'inhiber', + 'injecter', + 'injure', + 'innocent', + 'inoculer', + 'inonder', + 'inscrire', + 'insecte', + 'insigne', + 'insolite', + 'inspirer', + 'instinct', + 'insulter', + 'intact', + 'intense', + 'intime', + 'intrigue', + 'intuitif', + 'inutile', + 'invasion', + 'inventer', + 'inviter', + 'invoquer', + 'ironique', + 'irradier', + 'irreÌel', + 'irriter', + 'isoler', + 'ivoire', + 'ivresse', + 'jaguar', + 'jaillir', + 'jambe', + 'janvier', + 'jardin', + 'jauger', + 'jaune', + 'javelot', + 'jetable', + 'jeton', + 'jeudi', + 'jeunesse', + 'joindre', + 'joncher', + 'jongler', + 'joueur', + 'jouissif', + 'journal', + 'jovial', + 'joyau', + 'joyeux', + 'jubiler', + 'jugement', + 'junior', + 'jupon', + 'juriste', + 'justice', + 'juteux', + 'juveÌnile', + 'kayak', + 'kimono', + 'kiosque', + 'label', + 'labial', + 'labourer', + 'laceÌrer', + 'lactose', + 'lagune', + 'laine', + 'laisser', + 'laitier', + 'lambeau', + 'lamelle', + 'lampe', + 'lanceur', + 'langage', + 'lanterne', + 'lapin', + 'largeur', + 'larme', + 'laurier', + 'lavabo', + 'lavoir', + 'lecture', + 'leÌgal', + 'leÌger', + 'leÌgume', + 'lessive', + 'lettre', + 'levier', + 'lexique', + 'leÌzard', + 'liasse', + 'libeÌrer', + 'libre', + 'licence', + 'licorne', + 'lieÌ€ge', + 'lieÌ€vre', + 'ligature', + 'ligoter', + 'ligue', + 'limer', + 'limite', + 'limonade', + 'limpide', + 'lineÌaire', + 'lingot', + 'lionceau', + 'liquide', + 'lisieÌ€re', + 'lister', + 'lithium', + 'litige', + 'littoral', + 'livreur', + 'logique', + 'lointain', + 'loisir', + 'lombric', + 'loterie', + 'louer', + 'lourd', + 'loutre', + 'louve', + 'loyal', + 'lubie', + 'lucide', + 'lucratif', + 'lueur', + 'lugubre', + 'luisant', + 'lumieÌ€re', + 'lunaire', + 'lundi', + 'luron', + 'lutter', + 'luxueux', + 'machine', + 'magasin', + 'magenta', + 'magique', + 'maigre', + 'maillon', + 'maintien', + 'mairie', + 'maison', + 'majorer', + 'malaxer', + 'maleÌfice', + 'malheur', + 'malice', + 'mallette', + 'mammouth', + 'mandater', + 'maniable', + 'manquant', + 'manteau', + 'manuel', + 'marathon', + 'marbre', + 'marchand', + 'mardi', + 'maritime', + 'marqueur', + 'marron', + 'marteler', + 'mascotte', + 'massif', + 'mateÌriel', + 'matieÌ€re', + 'matraque', + 'maudire', + 'maussade', + 'mauve', + 'maximal', + 'meÌchant', + 'meÌconnu', + 'meÌdaille', + 'meÌdecin', + 'meÌditer', + 'meÌduse', + 'meilleur', + 'meÌlange', + 'meÌlodie', + 'membre', + 'meÌmoire', + 'menacer', + 'mener', + 'menhir', + 'mensonge', + 'mentor', + 'mercredi', + 'meÌrite', + 'merle', + 'messager', + 'mesure', + 'meÌtal', + 'meÌteÌore', + 'meÌthode', + 'meÌtier', + 'meuble', + 'miauler', + 'microbe', + 'miette', + 'mignon', + 'migrer', + 'milieu', + 'million', + 'mimique', + 'mince', + 'mineÌral', + 'minimal', + 'minorer', + 'minute', + 'miracle', + 'miroiter', + 'missile', + 'mixte', + 'mobile', + 'moderne', + 'moelleux', + 'mondial', + 'moniteur', + 'monnaie', + 'monotone', + 'monstre', + 'montagne', + 'monument', + 'moqueur', + 'morceau', + 'morsure', + 'mortier', + 'moteur', + 'motif', + 'mouche', + 'moufle', + 'moulin', + 'mousson', + 'mouton', + 'mouvant', + 'multiple', + 'munition', + 'muraille', + 'mureÌ€ne', + 'murmure', + 'muscle', + 'museÌum', + 'musicien', + 'mutation', + 'muter', + 'mutuel', + 'myriade', + 'myrtille', + 'mysteÌ€re', + 'mythique', + 'nageur', + 'nappe', + 'narquois', + 'narrer', + 'natation', + 'nation', + 'nature', + 'naufrage', + 'nautique', + 'navire', + 'neÌbuleux', + 'nectar', + 'neÌfaste', + 'neÌgation', + 'neÌgliger', + 'neÌgocier', + 'neige', + 'nerveux', + 'nettoyer', + 'neurone', + 'neutron', + 'neveu', + 'niche', + 'nickel', + 'nitrate', + 'niveau', + 'noble', + 'nocif', + 'nocturne', + 'noirceur', + 'noisette', + 'nomade', + 'nombreux', + 'nommer', + 'normatif', + 'notable', + 'notifier', + 'notoire', + 'nourrir', + 'nouveau', + 'novateur', + 'novembre', + 'novice', + 'nuage', + 'nuancer', + 'nuire', + 'nuisible', + 'numeÌro', + 'nuptial', + 'nuque', + 'nutritif', + 'obeÌir', + 'objectif', + 'obliger', + 'obscur', + 'observer', + 'obstacle', + 'obtenir', + 'obturer', + 'occasion', + 'occuper', + 'oceÌan', + 'octobre', + 'octroyer', + 'octupler', + 'oculaire', + 'odeur', + 'odorant', + 'offenser', + 'officier', + 'offrir', + 'ogive', + 'oiseau', + 'oisillon', + 'olfactif', + 'olivier', + 'ombrage', + 'omettre', + 'onctueux', + 'onduler', + 'oneÌreux', + 'onirique', + 'opale', + 'opaque', + 'opeÌrer', + 'opinion', + 'opportun', + 'opprimer', + 'opter', + 'optique', + 'orageux', + 'orange', + 'orbite', + 'ordonner', + 'oreille', + 'organe', + 'orgueil', + 'orifice', + 'ornement', + 'orque', + 'ortie', + 'osciller', + 'osmose', + 'ossature', + 'otarie', + 'ouragan', + 'ourson', + 'outil', + 'outrager', + 'ouvrage', + 'ovation', + 'oxyde', + 'oxygeÌ€ne', + 'ozone', + 'paisible', + 'palace', + 'palmareÌ€s', + 'palourde', + 'palper', + 'panache', + 'panda', + 'pangolin', + 'paniquer', + 'panneau', + 'panorama', + 'pantalon', + 'papaye', + 'papier', + 'papoter', + 'papyrus', + 'paradoxe', + 'parcelle', + 'paresse', + 'parfumer', + 'parler', + 'parole', + 'parrain', + 'parsemer', + 'partager', + 'parure', + 'parvenir', + 'passion', + 'pasteÌ€que', + 'paternel', + 'patience', + 'patron', + 'pavillon', + 'pavoiser', + 'payer', + 'paysage', + 'peigne', + 'peintre', + 'pelage', + 'peÌlican', + 'pelle', + 'pelouse', + 'peluche', + 'pendule', + 'peÌneÌtrer', + 'peÌnible', + 'pensif', + 'peÌnurie', + 'peÌpite', + 'peÌplum', + 'perdrix', + 'perforer', + 'peÌriode', + 'permuter', + 'perplexe', + 'persil', + 'perte', + 'peser', + 'peÌtale', + 'petit', + 'peÌtrir', + 'peuple', + 'pharaon', + 'phobie', + 'phoque', + 'photon', + 'phrase', + 'physique', + 'piano', + 'pictural', + 'pieÌ€ce', + 'pierre', + 'pieuvre', + 'pilote', + 'pinceau', + 'pipette', + 'piquer', + 'pirogue', + 'piscine', + 'piston', + 'pivoter', + 'pixel', + 'pizza', + 'placard', + 'plafond', + 'plaisir', + 'planer', + 'plaque', + 'plastron', + 'plateau', + 'pleurer', + 'plexus', + 'pliage', + 'plomb', + 'plonger', + 'pluie', + 'plumage', + 'pochette', + 'poeÌsie', + 'poeÌ€te', + 'pointe', + 'poirier', + 'poisson', + 'poivre', + 'polaire', + 'policier', + 'pollen', + 'polygone', + 'pommade', + 'pompier', + 'ponctuel', + 'pondeÌrer', + 'poney', + 'portique', + 'position', + 'posseÌder', + 'posture', + 'potager', + 'poteau', + 'potion', + 'pouce', + 'poulain', + 'poumon', + 'pourpre', + 'poussin', + 'pouvoir', + 'prairie', + 'pratique', + 'preÌcieux', + 'preÌdire', + 'preÌfixe', + 'preÌlude', + 'preÌnom', + 'preÌsence', + 'preÌtexte', + 'preÌvoir', + 'primitif', + 'prince', + 'prison', + 'priver', + 'probleÌ€me', + 'proceÌder', + 'prodige', + 'profond', + 'progreÌ€s', + 'proie', + 'projeter', + 'prologue', + 'promener', + 'propre', + 'prospeÌ€re', + 'proteÌger', + 'prouesse', + 'proverbe', + 'prudence', + 'pruneau', + 'psychose', + 'public', + 'puceron', + 'puiser', + 'pulpe', + 'pulsar', + 'punaise', + 'punitif', + 'pupitre', + 'purifier', + 'puzzle', + 'pyramide', + 'quasar', + 'querelle', + 'question', + 'quieÌtude', + 'quitter', + 'quotient', + 'racine', + 'raconter', + 'radieux', + 'ragondin', + 'raideur', + 'raisin', + 'ralentir', + 'rallonge', + 'ramasser', + 'rapide', + 'rasage', + 'ratisser', + 'ravager', + 'ravin', + 'rayonner', + 'reÌactif', + 'reÌagir', + 'reÌaliser', + 'reÌanimer', + 'recevoir', + 'reÌciter', + 'reÌclamer', + 'reÌcolter', + 'recruter', + 'reculer', + 'recycler', + 'reÌdiger', + 'redouter', + 'refaire', + 'reÌflexe', + 'reÌformer', + 'refrain', + 'refuge', + 'reÌgalien', + 'reÌgion', + 'reÌglage', + 'reÌgulier', + 'reÌiteÌrer', + 'rejeter', + 'rejouer', + 'relatif', + 'relever', + 'relief', + 'remarque', + 'remeÌ€de', + 'remise', + 'remonter', + 'remplir', + 'remuer', + 'renard', + 'renfort', + 'renifler', + 'renoncer', + 'rentrer', + 'renvoi', + 'replier', + 'reporter', + 'reprise', + 'reptile', + 'requin', + 'reÌserve', + 'reÌsineux', + 'reÌsoudre', + 'respect', + 'rester', + 'reÌsultat', + 'reÌtablir', + 'retenir', + 'reÌticule', + 'retomber', + 'retracer', + 'reÌunion', + 'reÌussir', + 'revanche', + 'revivre', + 'reÌvolte', + 'reÌvulsif', + 'richesse', + 'rideau', + 'rieur', + 'rigide', + 'rigoler', + 'rincer', + 'riposter', + 'risible', + 'risque', + 'rituel', + 'rival', + 'rivieÌ€re', + 'rocheux', + 'romance', + 'rompre', + 'ronce', + 'rondin', + 'roseau', + 'rosier', + 'rotatif', + 'rotor', + 'rotule', + 'rouge', + 'rouille', + 'rouleau', + 'routine', + 'royaume', + 'ruban', + 'rubis', + 'ruche', + 'ruelle', + 'rugueux', + 'ruiner', + 'ruisseau', + 'ruser', + 'rustique', + 'rythme', + 'sabler', + 'saboter', + 'sabre', + 'sacoche', + 'safari', + 'sagesse', + 'saisir', + 'salade', + 'salive', + 'salon', + 'saluer', + 'samedi', + 'sanction', + 'sanglier', + 'sarcasme', + 'sardine', + 'saturer', + 'saugrenu', + 'saumon', + 'sauter', + 'sauvage', + 'savant', + 'savonner', + 'scalpel', + 'scandale', + 'sceÌleÌrat', + 'sceÌnario', + 'sceptre', + 'scheÌma', + 'science', + 'scinder', + 'score', + 'scrutin', + 'sculpter', + 'seÌance', + 'seÌcable', + 'seÌcher', + 'secouer', + 'seÌcreÌter', + 'seÌdatif', + 'seÌduire', + 'seigneur', + 'seÌjour', + 'seÌlectif', + 'semaine', + 'sembler', + 'semence', + 'seÌminal', + 'seÌnateur', + 'sensible', + 'sentence', + 'seÌparer', + 'seÌquence', + 'serein', + 'sergent', + 'seÌrieux', + 'serrure', + 'seÌrum', + 'service', + 'seÌsame', + 'seÌvir', + 'sevrage', + 'sextuple', + 'sideÌral', + 'sieÌ€cle', + 'sieÌger', + 'siffler', + 'sigle', + 'signal', + 'silence', + 'silicium', + 'simple', + 'sinceÌ€re', + 'sinistre', + 'siphon', + 'sirop', + 'sismique', + 'situer', + 'skier', + 'social', + 'socle', + 'sodium', + 'soigneux', + 'soldat', + 'soleil', + 'solitude', + 'soluble', + 'sombre', + 'sommeil', + 'somnoler', + 'sonde', + 'songeur', + 'sonnette', + 'sonore', + 'sorcier', + 'sortir', + 'sosie', + 'sottise', + 'soucieux', + 'soudure', + 'souffle', + 'soulever', + 'soupape', + 'source', + 'soutirer', + 'souvenir', + 'spacieux', + 'spatial', + 'speÌcial', + 'spheÌ€re', + 'spiral', + 'stable', + 'station', + 'sternum', + 'stimulus', + 'stipuler', + 'strict', + 'studieux', + 'stupeur', + 'styliste', + 'sublime', + 'substrat', + 'subtil', + 'subvenir', + 'succeÌ€s', + 'sucre', + 'suffixe', + 'suggeÌrer', + 'suiveur', + 'sulfate', + 'superbe', + 'supplier', + 'surface', + 'suricate', + 'surmener', + 'surprise', + 'sursaut', + 'survie', + 'suspect', + 'syllabe', + 'symbole', + 'symeÌtrie', + 'synapse', + 'syntaxe', + 'systeÌ€me', + 'tabac', + 'tablier', + 'tactile', + 'tailler', + 'talent', + 'talisman', + 'talonner', + 'tambour', + 'tamiser', + 'tangible', + 'tapis', + 'taquiner', + 'tarder', + 'tarif', + 'tartine', + 'tasse', + 'tatami', + 'tatouage', + 'taupe', + 'taureau', + 'taxer', + 'teÌmoin', + 'temporel', + 'tenaille', + 'tendre', + 'teneur', + 'tenir', + 'tension', + 'terminer', + 'terne', + 'terrible', + 'teÌtine', + 'texte', + 'theÌ€me', + 'theÌorie', + 'theÌrapie', + 'thorax', + 'tibia', + 'tieÌ€de', + 'timide', + 'tirelire', + 'tiroir', + 'tissu', + 'titane', + 'titre', + 'tituber', + 'toboggan', + 'toleÌrant', + 'tomate', + 'tonique', + 'tonneau', + 'toponyme', + 'torche', + 'tordre', + 'tornade', + 'torpille', + 'torrent', + 'torse', + 'tortue', + 'totem', + 'toucher', + 'tournage', + 'tousser', + 'toxine', + 'traction', + 'trafic', + 'tragique', + 'trahir', + 'train', + 'trancher', + 'travail', + 'treÌ€fle', + 'tremper', + 'treÌsor', + 'treuil', + 'triage', + 'tribunal', + 'tricoter', + 'trilogie', + 'triomphe', + 'tripler', + 'triturer', + 'trivial', + 'trombone', + 'tronc', + 'tropical', + 'troupeau', + 'tuile', + 'tulipe', + 'tumulte', + 'tunnel', + 'turbine', + 'tuteur', + 'tutoyer', + 'tuyau', + 'tympan', + 'typhon', + 'typique', + 'tyran', + 'ubuesque', + 'ultime', + 'ultrason', + 'unanime', + 'unifier', + 'union', + 'unique', + 'unitaire', + 'univers', + 'uranium', + 'urbain', + 'urticant', + 'usage', + 'usine', + 'usuel', + 'usure', + 'utile', + 'utopie', + 'vacarme', + 'vaccin', + 'vagabond', + 'vague', + 'vaillant', + 'vaincre', + 'vaisseau', + 'valable', + 'valise', + 'vallon', + 'valve', + 'vampire', + 'vanille', + 'vapeur', + 'varier', + 'vaseux', + 'vassal', + 'vaste', + 'vecteur', + 'vedette', + 'veÌgeÌtal', + 'veÌhicule', + 'veinard', + 'veÌloce', + 'vendredi', + 'veÌneÌrer', + 'venger', + 'venimeux', + 'ventouse', + 'verdure', + 'veÌrin', + 'vernir', + 'verrou', + 'verser', + 'vertu', + 'veston', + 'veÌteÌran', + 'veÌtuste', + 'vexant', + 'vexer', + 'viaduc', + 'viande', + 'victoire', + 'vidange', + 'videÌo', + 'vignette', + 'vigueur', + 'vilain', + 'village', + 'vinaigre', + 'violon', + 'vipeÌ€re', + 'virement', + 'virtuose', + 'virus', + 'visage', + 'viseur', + 'vision', + 'visqueux', + 'visuel', + 'vital', + 'vitesse', + 'viticole', + 'vitrine', + 'vivace', + 'vivipare', + 'vocation', + 'voguer', + 'voile', + 'voisin', + 'voiture', + 'volaille', + 'volcan', + 'voltiger', + 'volume', + 'vorace', + 'vortex', + 'voter', + 'vouloir', + 'voyage', + 'voyelle', + 'wagon', + 'xeÌnon', + 'yacht', + 'zeÌ€bre', + 'zeÌnith', + 'zeste', + 'zoologie' +]; diff --git a/lib/models/cesium_plus.dart b/lib/models/cesium_plus.dart index 8d73d005d88be993e18930081c483c687304036c..e2659c1c1ffdb2af9c3bb77fabe961cf4a777359 100644 --- a/lib/models/cesium_plus.dart +++ b/lib/models/cesium_plus.dart @@ -1,15 +1,18 @@ import 'dart:convert'; import 'dart:io'; +import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:gecko/globals.dart'; -import 'package:http/http.dart' as http; import 'package:path_provider/path_provider.dart'; +// import 'package:http/http.dart' as http; class CesiumPlusProvider with ChangeNotifier { TextEditingController cesiumName = TextEditingController(); - int iAvatar = 0; - bool isComplete = false; + Image defaultAvatar(double size) => + Image.asset(('assets/icon_user.png'), height: size); + + CancelToken avatarCancelToken = CancelToken(); Future<List> _buildQuery(_pubkey) async { var queryGetAvatar = json.encode({ @@ -61,45 +64,95 @@ class CesiumPlusProvider with ChangeNotifier { Future<String> getName(String _pubkey) async { String _name; + if (g1WalletsBox.get(_pubkey).csName != null) { + return g1WalletsBox.get(_pubkey).csName; + } + List queryOptions = await _buildQuery(_pubkey); - final response = await http.post((Uri.parse(queryOptions[0])), - body: queryOptions[1], headers: queryOptions[2]); - final responseJson = json.decode(response.body); - if (responseJson['hits']['hits'].toString() == '[]') { + + var dio = Dio(); + 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 = - responseJson['hits']['hits'][0]['_source'].containsKey("title"); + response.data['hits']['hits'][0]['_source'].containsKey("title"); if (!_nameExist) { return ''; } - _name = responseJson['hits']['hits'][0]['_source']['title']; + _name = response.data['hits']['hits'][0]['_source']['title']; + + g1WalletsBox.get(_pubkey).csName = _name; return _name; } - Future<List> getAvatar(String _pubkey) async { + 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); - final response = await http.post((Uri.parse(queryOptions[0])), - body: queryOptions[1], headers: queryOptions[2]); - final responseJson = json.decode(response.body); - if (responseJson['hits']['hits'].toString() == '[]') { - return [File(appPath.path + '/default_avatar.png')]; + + 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); } - final bool avatarExist = - responseJson['hits']['hits'][0]['_source'].containsKey("avatar"); - if (!avatarExist) { - return [File(appPath.path + '/default_avatar.png')]; + + if (response.data['hits']['hits'].toString() == '[]' || + !response.data['hits']['hits'][0]['_source'].containsKey("avatar")) { + return defaultAvatar(size); } + final _avatar = - responseJson['hits']['hits'][0]['_source']['avatar']['_content']; + response.data['hits']['hits'][0]['_source']['avatar']['_content']; var avatarFile = - File('${(await getTemporaryDirectory()).path}/avatar$iAvatar.png'); + File('${(await getTemporaryDirectory()).path}/avatar_$_pubkey.png'); await avatarFile.writeAsBytes(base64.decode(_avatar)); - iAvatar++; - isComplete = true; - return [avatarFile]; + final finalAvatar = Image.file( + avatarFile, + height: size, + fit: BoxFit.fitWidth, + ); + + g1WalletsBox.get(_pubkey).avatar = finalAvatar; + + return finalAvatar; } } diff --git a/lib/models/g1_wallets_list.dart b/lib/models/g1_wallets_list.dart new file mode 100644 index 0000000000000000000000000000000000000000..98fef2b6b23905060bac85140f007c95a5ce157c --- /dev/null +++ b/lib/models/g1_wallets_list.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; +import 'package:hive_flutter/hive_flutter.dart'; + +part 'g1_wallets_list.g.dart'; + +@HiveType(typeId: 2) +class G1WalletsList { + @HiveField(0) + String pubkey; + + @HiveField(1) + double balance; + + @HiveField(3) + Id id; + + @HiveField(4) + Image avatar; + + @HiveField(5) + String username; + + @HiveField(6) + String csName; + + @HiveField(7) + bool isMembre; + + G1WalletsList({ + this.pubkey, + this.balance, + this.id, + this.avatar, + this.username, + this.csName, + this.isMembre, + }); + + G1WalletsList.fromJson(Map<String, dynamic> json) { + pubkey = json['pubkey']; + balance = json['balance']; + id = json['id'] != null ? Id.fromJson(json['id']) : null; + } + + Map<String, dynamic> toJson() { + final Map<String, dynamic> data = <String, dynamic>{}; + data['pubkey'] = pubkey; + data['balance'] = balance; + if (id != null) { + data['id'] = id.toJson(); + } + return data; + } +} + +@HiveType(typeId: 3) +class Id { + bool isMember; + String username; + + Id({this.isMember, this.username}); + + Id.fromJson(Map<String, dynamic> json) { + isMember = json['isMember']; + username = json['username']; + } + + Map<String, dynamic> toJson() { + final Map<String, dynamic> data = <String, dynamic>{}; + data['isMember'] = isMember; + data['username'] = username; + return data; + } +} diff --git a/lib/models/g1_wallets_list.g.dart b/lib/models/g1_wallets_list.g.dart new file mode 100644 index 0000000000000000000000000000000000000000..266daed94c90700ff7f64e0dee7fa6dd85a77032 --- /dev/null +++ b/lib/models/g1_wallets_list.g.dart @@ -0,0 +1,90 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ignore_for_file: unused_local_variable + +part of 'g1_wallets_list.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class G1WalletsListAdapter extends TypeAdapter<G1WalletsList> { + @override + final int typeId = 2; + + @override + G1WalletsList read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = <int, dynamic>{ + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return G1WalletsList( + pubkey: fields[0] as String, + balance: fields[1] as double, + id: fields[3] as Id, + avatar: fields[4] as Image, + username: fields[5] as String, + csName: fields[6] as String, + isMembre: fields[7] as bool, + ); + } + + @override + void write(BinaryWriter writer, G1WalletsList obj) { + writer + ..writeByte(7) + ..writeByte(0) + ..write(obj.pubkey) + ..writeByte(1) + ..write(obj.balance) + ..writeByte(3) + ..write(obj.id) + ..writeByte(4) + ..write(obj.avatar) + ..writeByte(5) + ..write(obj.username) + ..writeByte(6) + ..write(obj.csName) + ..writeByte(7) + ..write(obj.isMembre); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is G1WalletsListAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} + +class IdAdapter extends TypeAdapter<Id> { + @override + final int typeId = 3; + + @override + Id read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = <int, dynamic>{ + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return Id(); + } + + @override + void write(BinaryWriter writer, Id obj) { + writer.writeByte(0); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is IdAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} diff --git a/lib/models/generate_wallets.dart b/lib/models/generate_wallets.dart index 1266dfc2ad2d3c90daf6e590af94d5b2cbf54ac0..c75e25537a7f67cd4663ffc9d0c4c11c4f59a62e 100644 --- a/lib/models/generate_wallets.dart +++ b/lib/models/generate_wallets.dart @@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; +import 'package:gecko/models/bip39_words.dart'; import 'package:gecko/models/chest_data.dart'; import 'package:gecko/models/wallet_data.dart'; import 'package:pdf/pdf.dart'; @@ -39,6 +40,21 @@ class GenerateWalletsProvider with ChangeNotifier { bool canImport = false; bool isPinChanged = false; + // Import Chest + 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( NewWallet _wallet, String _name, BuildContext context) async { int chestNumber = 0; @@ -140,7 +156,7 @@ class GenerateWalletsProvider with ChangeNotifier { Future<String> generateMnemonic() async { try { generatedMnemonic = await DubpRust.genMnemonic(language: Language.french); - actualWallet = await generateWallet(generatedMnemonic); + actualWallet = await generateWallet(generatedMnemonic, isImport: false); walletIsGenerated = true; } catch (e) { log.e(e); @@ -148,7 +164,8 @@ class GenerateWalletsProvider with ChangeNotifier { return generatedMnemonic; } - Future<NewWallet> generateWallet(generatedMnemonic) async { + Future<NewWallet> generateWallet(String generatedMnemonic, + {@required bool isImport}) async { try { actualWallet = await DubpRust.genWalletFromMnemonic( language: Language.french, @@ -159,8 +176,10 @@ class GenerateWalletsProvider with ChangeNotifier { log.e(e); } - mnemonicController.text = generatedMnemonic; - pin.text = actualWallet.pin; + if (!isImport) { + mnemonicController.text = generatedMnemonic; + pin.text = actualWallet.pin; + } // notifyListeners(); return actualWallet; @@ -288,15 +307,9 @@ class GenerateWalletsProvider with ChangeNotifier { notifyListeners(); } - void resetImportView() { - cesiumID.text = ''; - cesiumPWD.text = ''; - cesiumPubkey.text = ''; - pin.text = ''; - canImport = false; - isPinChanged = false; - isCesiumIDVisible = false; - isCesiumPWDVisible = false; + void resetCesiumImportView() { + cesiumID.text = cesiumPWD.text = cesiumPubkey.text = pin.text = ''; + canImport = isPinChanged = isCesiumIDVisible = isCesiumPWDVisible = false; actualWallet = null; notifyListeners(); } @@ -315,6 +328,83 @@ class GenerateWalletsProvider with ChangeNotifier { return _wordsList; } + bool isBipWord(String word) { + notifyListeners(); + + // Needed for bad encoding of UTF-8 + word = word.replaceAll('é', 'eÌ'); + word = word.replaceAll('è', 'eÌ€'); + return bip39Words.contains(word); + } + + bool isBipWordsList(List words) { + bool isValid = true; + for (String word in words) { + // Needed for bad encoding of UTF-8 + word = word.replaceAll('é', 'eÌ'); + word = word.replaceAll('è', 'eÌ€'); + if (!bip39Words.contains(word)) { + isValid = false; + } + } + return isValid; + } + + void resetImportView() { + cellController0.text = cellController1.text = cellController2.text = + cellController3.text = cellController4.text = cellController5.text = + cellController6.text = cellController7.text = cellController8.text = + cellController9.text = + cellController10.text = cellController11.text = ''; + isFirstTimeSentenceComplete = true; + notifyListeners(); + } + + bool isSentenceComplete(BuildContext context) { + if (isBipWordsList( + [ + cellController0.text, + cellController1.text, + cellController2.text, + cellController3.text, + cellController4.text, + cellController5.text, + cellController6.text, + cellController7.text, + cellController8.text, + cellController9.text, + cellController10.text, + cellController11.text + ], + )) { + if (isFirstTimeSentenceComplete) { + FocusScope.of(context).unfocus(); + } + isFirstTimeSentenceComplete = false; + return true; + } else { + return false; + } + } + + Future<bool> isSentenceValid() async { + String inputMnemonic = + '${cellController0.text} ${cellController1.text} ${cellController2.text} ${cellController3.text} ${cellController4.text} ${cellController5.text} ${cellController6.text} ${cellController7.text} ${cellController8.text} ${cellController9.text} ${cellController10.text} ${cellController11.text}'; + + // Needed for bad encoding of UTF-8 + inputMnemonic = inputMnemonic.replaceAll('é', 'eÌ'); + inputMnemonic = inputMnemonic.replaceAll('è', 'eÌ€'); + + NewWallet generatedWallet = + await generateWallet(inputMnemonic, isImport: true); + + if (generatedWallet == null) { + return false; + } else { + return true; + } + } + void reloadBuild() { notifyListeners(); } diff --git a/lib/models/home.dart b/lib/models/home.dart index 69fe956f85dbf4eba110c63f4deb7d581c75ec31..f6612114ff715faad4e9de0784891fd4191ea97a 100644 --- a/lib/models/home.dart +++ b/lib/models/home.dart @@ -8,7 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'dart:async'; import 'package:gecko/globals.dart'; -import 'package:gecko/screens/history.dart'; +import 'package:gecko/screens/old_history_pay.dart'; import 'package:gecko/screens/myWallets/wallets_home.dart'; import 'package:package_info/package_info.dart'; @@ -21,7 +21,8 @@ class HomeProvider with ChangeNotifier { Widget appBarExplorer = Text('Explorateur', style: TextStyle(color: Colors.grey[850])); - List currentTab = [HistoryScreen(), WalletsHome()]; + List currentTab = [OldHistoryScreen(), WalletsHome()]; + bool isFirstBuild = true; // AudioCache player = AudioCache(prefix: 'sounds/'); get currentIndex => _currentIndex; @@ -99,16 +100,6 @@ class HomeProvider with ChangeNotifier { return _endpoint; } - Future createDefaultAvatar() async { - File defaultAvatar = File(appPath.path + '/default_avatar.png'); - final bool isAvatarExist = await defaultAvatar.exists(); - if (!isAvatarExist) { - final byteData = await rootBundle.load('assets/icon_user.png'); - await defaultAvatar.writeAsBytes(byteData.buffer - .asUint8List(byteData.offsetInBytes, byteData.lengthInBytes)); - } - } - T getRandomElement<T>(List<T> list) { final random = Random(); var i = random.nextInt(list.length); @@ -139,6 +130,22 @@ class HomeProvider with ChangeNotifier { notifyListeners(); } + void snackNode(context) { + if (isFirstBuild) { + String _message; + if (endPointGVA == 'HS') { + _message = + "Aucun noeud Duniter disponible, veuillez réessayer ultérieurement"; + } else { + _message = "Vous êtes connecté au noeud\n${endPointGVA.split('/')[2]}"; + } + final snackBar = SnackBar( + content: Text(_message), duration: const Duration(seconds: 2)); + isFirstBuild = false; + ScaffoldMessenger.of(context).showSnackBar(snackBar); + } + } + void rebuildWidget() { notifyListeners(); } diff --git a/lib/models/my_wallets.dart b/lib/models/my_wallets.dart index 69531080779ec5a18b3d7694ed19b0e95833635c..e8a6305a3c1524bf6ed8b11d6515d7036fa8c6cb 100644 --- a/lib/models/my_wallets.dart +++ b/lib/models/my_wallets.dart @@ -71,8 +71,13 @@ class MyWalletsProvider with ChangeNotifier { await walletBox.clear(); await chestBox.clear(); await configBox.delete('defaultWallet'); + // await Future.delayed(const Duration(milliseconds: 50)); + // notifyListeners(); - Navigator.pop(context); + await Navigator.of(context).pushNamedAndRemoveUntil( + '/', + ModalRoute.withName('/'), + ); } return 0; } catch (e) { diff --git a/lib/models/queries.dart b/lib/models/queries.dart index 72ba0e9e90d9547ec4118edb227975900a726032..ed47ac85fe619153b75784cb473e5f32cbdd1ee6 100644 --- a/lib/models/queries.dart +++ b/lib/models/queries.dart @@ -29,14 +29,12 @@ const String getHistory = r''' issuers comment outputs - writtenTime } sending { currency issuers comment outputs - writtenTime } } currentUd { @@ -62,3 +60,36 @@ const String getBalance = r''' } } '''; + +const String getWallets = r''' +query ($number: Int!, $cursor: String) { + wallets(pagination: {ord: ASC, pageSize: $number, cursor: $cursor}) { + pageInfo { + hasNextPage + endCursor + } + edges { + node { + script + balance { + amount + base + } + idty { + isMember + username + } + } + } + } +} +'''; + +const String getId = r''' +query ($pubkey: PubKeyGva!) { + idty(pubkey: $pubkey) { + isMember + username + } +} +'''; diff --git a/lib/models/search.dart b/lib/models/search.dart new file mode 100644 index 0000000000000000000000000000000000000000..5fdbb61e3a1fdec48a661b34ebebb96d9d621bd1 --- /dev/null +++ b/lib/models/search.dart @@ -0,0 +1,80 @@ +import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:gecko/globals.dart'; +import 'package:gecko/models/g1_wallets_list.dart'; +import 'package:gecko/models/wallets_profiles.dart'; + +class SearchProvider with ChangeNotifier { + TextEditingController searchController = TextEditingController(); + List searchResult = []; + final cacheDuring = 20 * 60 * 1000; //First number is minutes + int cacheTime = 0; + + void rebuildWidget() { + 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(); + 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; + } +} + +List<G1WalletsList> _parseG1Wallets(var responseBody) { + final parsed = responseBody.cast<Map<String, dynamic>>(); + + return parsed + .map<G1WalletsList>((json) => G1WalletsList.fromJson(json)) + .toList(); +} diff --git a/lib/models/history.dart b/lib/models/wallets_profiles.dart similarity index 78% rename from lib/models/history.dart rename to lib/models/wallets_profiles.dart index 19a15fa4192da471a2c9d52ea97aa26e07f5f98f..bb13f7ca12a572fa2c219b3a163e51459450afdc 100644 --- a/lib/models/history.dart +++ b/lib/models/wallets_profiles.dart @@ -2,14 +2,12 @@ import 'package:dubp/dubp.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:gecko/globals.dart'; -import 'package:gecko/models/home.dart'; import 'package:gecko/models/my_wallets.dart'; import 'package:gecko/models/wallet_data.dart'; -import 'package:gecko/screens/history.dart'; +import 'package:gecko/screens/wallet_view.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; import 'dart:math'; import 'package:intl/intl.dart'; @@ -17,13 +15,13 @@ import 'package:truncate/truncate.dart'; import 'package:crypto/crypto.dart'; import 'package:fast_base58/fast_base58.dart'; -class HistoryProvider with ChangeNotifier { +class WalletsProfilesProvider with ChangeNotifier { + WalletsProfilesProvider(this.pubkey); + String pubkey = ''; String pubkeyShort = ''; - HistoryProvider(this.pubkey); final TextEditingController outputPubkey = TextEditingController(); List transBC; - bool isFirstBuild = true; String fetchMoreCursor; Map pageInfo; bool isHistoryScreen = false; @@ -31,6 +29,9 @@ class HistoryProvider with ChangeNotifier { String rawSvg; TextEditingController payAmount = TextEditingController(); TextEditingController payComment = TextEditingController(); + num balance; + int nRepositories = 20; + int nPage = 1; Future scan(context) async { await Permission.camera.request(); @@ -41,9 +42,14 @@ class HistoryProvider with ChangeNotifier { log.e(e); return 'false'; } - if (barcode != null) { + if (barcode != null && isPubkey(barcode)) { outputPubkey.text = barcode; - isPubkey(context, barcode); + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return WalletViewScreen(pubkey: pubkey); + }), + ); } else { return 'false'; } @@ -56,10 +62,17 @@ class HistoryProvider with ChangeNotifier { WalletData defaultWallet = _myWalletModel.getDefaultWallet(currentChest); String dewif = chestBox.get(currentChest).dewif; + int derivation; + + if (chestBox.get(currentChest).isCesium) { + derivation = 0; + } else { + derivation = defaultWallet.derivation; + } try { await DubpRust.simplePaymentFromTransparentAccount( - accountIndex: defaultWallet.derivation, + accountIndex: derivation, amount: double.parse(payAmount.text), txComment: payComment.text, dewif: dewif, @@ -74,9 +87,7 @@ class HistoryProvider with ChangeNotifier { } } - String isPubkey(context, pubkey, {bool goHistory}) { - HomeProvider _homeProvider = - Provider.of<HomeProvider>(context, listen: false); + bool isPubkey(pubkey) { final RegExp regExp = RegExp( r'^[a-zA-Z0-9]+$', caseSensitive: false, @@ -86,36 +97,25 @@ class HistoryProvider with ChangeNotifier { if (regExp.hasMatch(pubkey) == true && pubkey.length > 42 && pubkey.length < 45) { - log.d("C'est une pubkey !!!"); + log.d("C'est une pubkey !"); this.pubkey = pubkey; - getShortPubkey(pubkey); + // getShortPubkey(pubkey); - outputPubkey.text = pubkey; + // outputPubkey.text = pubkey; - goHistory ??= false; - - if (goHistory) { - isHistoryScreen = true; - historySwitchButtun = "Payer"; - } else { - isHistoryScreen = false; - historySwitchButtun = "Voir l'historique"; - } - - _homeProvider.handleSearchEnd(); - Navigator.push( - context, - MaterialPageRoute(builder: (context) { - return HistoryScreen(); - }), - ); - notifyListeners(); + // Navigator.push( + // context, + // MaterialPageRoute(builder: (context) { + // return const WalletViewScreen(); + // }), + // ); + // notifyListeners(); - return pubkey; + return true; + } else { + return false; } - - return ''; } String getShortPubkey(String pubkey) { @@ -200,13 +200,25 @@ class HistoryProvider with ChangeNotifier { FetchMoreOptions checkQueryResult(result, opts, _pubkey) { final List<dynamic> blockchainTX = (result.data['txsHistoryBc']['both']['edges'] as List<dynamic>); + // final List<dynamic> mempoolTX = + // (result.data['txsHistoryMp']['receiving'] as List<dynamic>); pageInfo = result.data['txsHistoryBc']['both']['pageInfo']; - fetchMoreCursor = pageInfo['endCursor']; + if (fetchMoreCursor == null) nPage = 1; + + if (nPage == 1) { + nRepositories = 40; + } else if (nPage == 2) { + nRepositories = 100; + } + log.d(nPage); + log.d(nRepositories); + nPage++; + if (fetchMoreCursor != null) { opts = FetchMoreOptions( - variables: {'cursor': fetchMoreCursor}, + variables: {'cursor': fetchMoreCursor, 'number': nRepositories}, updateQuery: (previousResultData, fetchMoreResultData) { final List<dynamic> repos = [ ...previousResultData['txsHistoryBc']['both']['edges'] @@ -232,22 +244,6 @@ class HistoryProvider with ChangeNotifier { return opts; } - void snackNode(context) { - if (isFirstBuild) { - String _message; - if (endPointGVA == 'HS') { - _message = - "Aucun noeud Duniter disponible, veuillez réessayer ultérieurement"; - } else { - _message = "Vous êtes connecté au noeud\n${endPointGVA.split('/')[2]}"; - } - final snackBar = SnackBar( - content: Text(_message), duration: const Duration(seconds: 2)); - isFirstBuild = false; - ScaffoldMessenger.of(context).showSnackBar(snackBar); - } - } - void resetdHistory() { outputPubkey.text = ''; notifyListeners(); @@ -260,6 +256,7 @@ class HistoryProvider with ChangeNotifier { snackCopyKey(context) { const snackBar = SnackBar( + padding: EdgeInsets.all(20), content: Text("Cette clé publique a été copié dans votre presse-papier."), duration: Duration(seconds: 2)); @@ -279,4 +276,27 @@ class HistoryProvider with ChangeNotifier { String generateIdenticon(String _pubkey) { 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)); + } + + return balance; + } } diff --git a/lib/screens/avatar_fullscreen.dart b/lib/screens/avatar_fullscreen.dart new file mode 100644 index 0000000000000000000000000000000000000000..8f833dc9ffbccf43831e2dc7b4f9c61c06f00591 --- /dev/null +++ b/lib/screens/avatar_fullscreen.dart @@ -0,0 +1,54 @@ +import 'package:flutter/services.dart'; +import 'package:gecko/globals.dart'; +import 'package:flutter/material.dart'; +// import 'package:gecko/models/home.dart'; +// import 'package:provider/provider.dart'; + +// ignore: must_be_immutable +class AvatarFullscreen extends StatelessWidget { + TextEditingController tplController = TextEditingController(); + + AvatarFullscreen(this.avatar, {this.title, this.color, Key key}) + : super(key: key); + final Image avatar; + final String title; + final Color color; + + @override + Widget build(BuildContext context) { + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); + // HomeProvider _homeProvider = Provider.of<HomeProvider>(context); + return Scaffold( + appBar: AppBar( + elevation: 0, + backgroundColor: color ?? Colors.black, + toolbarHeight: 60 * ratio, + leading: IconButton( + icon: Icon(Icons.arrow_back, color: orangeC), + onPressed: () { + Navigator.pop(context); + }), + title: SizedBox( + height: 22, + child: Text( + title ?? 'Photo de profil', + style: TextStyle(color: orangeC), + ), + )), + body: SafeArea( + child: SizedBox.expand( + child: Container( + color: color ?? Colors.black, + // alignment: Alignment.center, + // height: MediaQuery.of(context).size.height, + // width: MediaQuery.of(context).size.width, + child: Image( + image: avatar.image, + height: avatar.height, + fit: BoxFit.fitWidth), + ), + ), + ), + ); + } +} diff --git a/lib/screens/common_elements.dart b/lib/screens/common_elements.dart index b9f265458b2de9dc1ff78c5670a0fee6ee559e4c..bb36379774114e721f8bc503f6459fcaebd29a75 100644 --- a/lib/screens/common_elements.dart +++ b/lib/screens/common_elements.dart @@ -34,14 +34,15 @@ class CommonElements { margin: const BubbleEdges.fromLTRB(10, 0, 20, 10), // nip: BubbleNip.leftTop, child: RichText( - key: textKey, - text: TextSpan( - style: const TextStyle( - fontSize: 18.0, - color: Colors.black, - ), - children: text, - )), + key: textKey, + text: TextSpan( + style: const TextStyle( + fontSize: 18.0, + color: Colors.black, + ), + children: text, + ), + ), ); } diff --git a/lib/screens/history.dart b/lib/screens/history.dart index 7701a56ba2bb83fd6d2733b0adeb72b9072bd103..d8a3fe17c0d54a407ac372d594a45d0a67b35787 100644 --- a/lib/screens/history.dart +++ b/lib/screens/history.dart @@ -1,124 +1,64 @@ -import 'dart:io'; import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/models/cesium_plus.dart'; -import 'package:gecko/models/home.dart'; -import 'package:gecko/models/my_wallets.dart'; import 'package:gecko/models/queries.dart'; -import 'package:gecko/models/history.dart'; +import 'package:gecko/models/wallets_profiles.dart'; import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; -import 'package:gecko/models/wallet_data.dart'; -import 'package:gecko/screens/myWallets/unlocking_wallet.dart'; +import 'package:gecko/screens/avatar_fullscreen.dart'; +import 'package:gecko/screens/wallet_view.dart'; import 'dart:ui'; import 'package:graphql_flutter/graphql_flutter.dart'; +import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; -import 'package:flutter_svg/flutter_svg.dart'; // ignore: must_be_immutable class HistoryScreen extends StatelessWidget with ChangeNotifier { - final TextEditingController _outputPubkey = TextEditingController(); - ScrollController scrollController = ScrollController(); - final nRepositories = 20; - // HistoryProvider _historyProvider; - final _formKey = GlobalKey<FormState>(); - final FocusNode _pubkeyFocus = FocusNode(); - List cesiumData; + HistoryScreen({@required this.pubkey, this.avatar, this.username, Key key}) + : super(key: key); + final ScrollController scrollController = ScrollController(); final double avatarsSize = 80; + final String pubkey; + final String username; + final Image avatar; FetchMore fetchMore; FetchMoreOptions opts; - final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); - HistoryScreen({Key key}) : super(key: key); + final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); @override Widget build(BuildContext context) { SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); - HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context); - HomeProvider _homeProvider = Provider.of<HomeProvider>(context); - _outputPubkey.text = _historyProvider.pubkey; - log.i('Build pubkey : ' + _historyProvider.pubkey); + WalletsProfilesProvider _historyProvider = + Provider.of<WalletsProfilesProvider>(context, listen: false); + CesiumPlusProvider _cesiumPlusProvider = + Provider.of<CesiumPlusProvider>(context, listen: false); + log.i('Build pubkey : ' + pubkey); WidgetsBinding.instance.addPostFrameCallback((_) {}); + _historyProvider.balance = _historyProvider.transBC = null; + return Scaffold( key: _scaffoldKey, appBar: AppBar( + elevation: 0, toolbarHeight: 60 * ratio, - title: _homeProvider.appBarExplorer, - actions: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: IconButton( - icon: _homeProvider.searchIcon, - color: Colors.grey[850], - onPressed: () { - if (_homeProvider.searchIcon.icon == Icons.search) { - _homeProvider.searchIcon = Icon( - Icons.close, - color: Colors.grey[850], - ); - _homeProvider.appBarExplorer = TextField( - autofocus: true, - controller: _homeProvider.searchQuery, - onChanged: (text) { - log.d("Clé tappé: $text"); - final String searchResult = - _historyProvider.isPubkey(context, text); - if (searchResult != '') { - _homeProvider.currentIndex = 0; - } - }, - style: TextStyle( - color: Colors.grey[850], - ), - decoration: InputDecoration( - prefixIcon: - Icon(Icons.search, color: Colors.grey[850]), - hintText: "Rechercher ...", - hintStyle: TextStyle(color: Colors.grey[850])), - ); - _homeProvider.handleSearchStart(); - } else { - _homeProvider.handleSearchEnd(); - } - })) - ], - backgroundColor: const Color(0xffFFD58D), - ), - floatingActionButton: SizedBox( - height: 80.0, - width: 80.0, - child: FittedBox( - child: FloatingActionButton( - heroTag: "buttonScan", - onPressed: () async { - await _historyProvider.scan(context); - }, - child: SizedBox( - height: 40.0, - width: 40.0, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 3), - child: Image.asset('assets/qrcode-scan.png'))), - backgroundColor: - floattingYellow, //smoothYellow, //Color.fromARGB(500, 204, 255, 255), - ), + title: const SizedBox( + height: 22, + child: Text('Historique des transactions'), ), ), body: Column(children: <Widget>[ - const SizedBox(height: 0), - if (_historyProvider.pubkey != '') - historyQuery(context, _historyProvider), + headerProfileView(context, _historyProvider, _cesiumPlusProvider), + historyQuery(context, _cesiumPlusProvider), ])); } - Widget historyQuery(context, HistoryProvider _historyProvider) { - _pubkeyFocus.unfocus(); - // HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context); - CesiumPlusProvider _cesiumPlusProvider = - Provider.of<CesiumPlusProvider>(context); - bool _isFirstExec = true; + Widget historyQuery(context, CesiumPlusProvider _cesiumPlusProvider) { + WalletsProfilesProvider _historyProvider = + Provider.of<WalletsProfilesProvider>(context, listen: true); + return Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.start, @@ -128,8 +68,8 @@ class HistoryScreen extends StatelessWidget with ChangeNotifier { options: QueryOptions( document: gql(getHistory), variables: <String, dynamic>{ - 'pubkey': _historyProvider.pubkey, - 'number': nRepositories, + 'pubkey': pubkey, + 'number': 10, 'cursor': null }, ), @@ -146,182 +86,47 @@ class HistoryScreen extends StatelessWidget with ChangeNotifier { SizedBox(height: 50), Text( "Aucun noeud GVA valide n'a pu être trouvé.\nVeuillez réessayer ultérieurement.", - style: TextStyle(fontSize: 17.0), + style: TextStyle(fontSize: 18), + ) + ]); + } else if (result.data == null) { + return Column(children: const <Widget>[ + SizedBox(height: 50), + Text( + "Aucune donnée à afficher.", + style: TextStyle(fontSize: 18), ) ]); } - if (result.data == null && result.exception.toString() == null) { - return const Text('Aucune donnée à afficher.'); - } - - num balance; - if (result.data['balance'] == null) { - balance = 0.0; + _historyProvider.balance = 0.0; } else { - balance = _historyProvider + _historyProvider.balance = _historyProvider .removeDecimalZero(result.data['balance']['amount'] / 100); } - opts = _historyProvider.checkQueryResult( - result, opts, _outputPubkey.text); - - // _historyProvider.transBC = null; + if (result.isNotLoading) { + // log.d(result.data); + opts = _historyProvider.checkQueryResult(result, opts, pubkey); + } // Build history list return NotificationListener( child: Builder( - builder: (context) => Expanded( - child: ListView( - key: const Key('listTransactions'), - controller: scrollController, - children: <Widget>[ - const SizedBox(height: 20), - if (_historyProvider.pubkey != '') - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - if (_isFirstExec) - Container( - padding: const EdgeInsets.fromLTRB( - 20, 0, 30, 0), - child: FutureBuilder( - future: - _cesiumPlusProvider.getAvatar( - _historyProvider.pubkey), - initialData: [ - File(appPath.path + - '/default_avatar.png') - ], - builder: (BuildContext context, - AsyncSnapshot<List> _avatar) { - cesiumData = _avatar.data; - // _cesiumPlusProvider.isComplete = true; - if (_avatar.connectionState != - ConnectionState.done) { - return Image.file( - File(appPath.path + - '/default_avatar.png'), - height: avatarsSize); - } - if (_avatar.hasError) { - return Image.file( - File(appPath.path + - '/default_avatar.png'), - height: avatarsSize); - } - if (_avatar.hasData) { - return SingleChildScrollView( - padding: - const EdgeInsets.all( - 0.0), - child: Image.file( - _avatar.data[0], - height: avatarsSize)); - } - return Image.file( - File(appPath.path + - '/default_avatar.png'), - height: avatarsSize); - })), - GestureDetector( - key: const Key('copyPubkey'), - onTap: () { - Clipboard.setData(ClipboardData( - text: _historyProvider.pubkey)); - _historyProvider.snackCopyKey(context); - }, - child: Text( - _historyProvider.getShortPubkey( - _historyProvider.pubkey), - style: const TextStyle( - fontSize: 22, - fontWeight: FontWeight.w800, - fontFamily: 'Monospace')), - ), - Container( - padding: const EdgeInsets.fromLTRB( - 30, 0, 5, 0), // .only(right: 15), - child: Card( - child: Column( - children: <Widget>[ - SvgPicture.string( - _historyProvider - .generateIdenticon( - _historyProvider - .pubkey), - fit: BoxFit.contain, - height: 64, - width: 64, - ), - ], - ), - )), - const SizedBox(width: 0) - ]), - if (_isFirstExec) - Row( - mainAxisAlignment: - MainAxisAlignment.spaceAround, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - padding: const EdgeInsets.fromLTRB( - 0, 0, 0, 0), - // padding: const EdgeInsets., - child: FutureBuilder( - future: _cesiumPlusProvider.getName( - _historyProvider.pubkey), - initialData: '...', - builder: (context, snapshot) { - return Text(snapshot.data ?? '-', - style: const TextStyle( - fontSize: 20)); - })) - ]), - const SizedBox(height: 18), - if (_isFirstExec) - Container( - padding: - const EdgeInsets.fromLTRB(0, 0, 0, 0), - child: Text(balance.toString() + ' Äž1', - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 18.0))), - const SizedBox(height: 20), - ElevatedButton( - key: const Key('switchPayHistory'), - style: ElevatedButton.styleFrom( - elevation: 1, - primary: Colors.grey[50], // background - onPrimary: Colors.black, // foreground - ), - onPressed: () { - _historyProvider.switchProfileView(); - }, - child: Text( - _historyProvider.historySwitchButtun, - style: TextStyle( - fontSize: 15, color: orangeC))), - // const Divider( - // color: Colors.grey, - // height: 5, - // thickness: 0.5, - // indent: 0, - // endIndent: 0, - // ), - _historyProvider.isHistoryScreen - ? historyView(context, result) - : payView(context, _historyProvider), - ], - ))), + builder: (context) => Expanded( + child: ListView( + key: const Key('listTransactions'), + controller: scrollController, + children: <Widget>[historyView(context, result)], + ), + ), + ), onNotification: (t) { if (t is ScrollEndNotification && scrollController.position.pixels >= - scrollController.position.maxScrollExtent * 0.7) { + scrollController.position.maxScrollExtent * 0.7 && + _historyProvider.pageInfo['hasPreviousPage']) { fetchMore(opts); } return true; @@ -332,125 +137,28 @@ class HistoryScreen extends StatelessWidget with ChangeNotifier { )); } - Widget payView(context, HistoryProvider _historyProvider) { - MyWalletsProvider _myWalletProvider = MyWalletsProvider(); - WalletData defaultWallet = - _myWalletProvider.getDefaultWallet(configBox.get('currentChest')); - - return Stack( - clipBehavior: Clip.hardEdge, - children: <Widget>[ - Form( - key: _formKey, - child: Column( - mainAxisSize: MainAxisSize.min, - children: <Widget>[ - const SizedBox(height: 20), - const Text('Commentaire:', style: TextStyle(fontSize: 20.0)), - Padding( - padding: const EdgeInsets.all(8.0), - child: TextField( - controller: _historyProvider.payComment, - maxLines: 2, - textAlign: TextAlign.center, - decoration: const InputDecoration(), - style: const TextStyle( - fontSize: 22, - color: Colors.black, - fontWeight: FontWeight.bold))), - const SizedBox(height: 20), - const Text('Montant (DU/Äž1):', style: TextStyle(fontSize: 20.0)), - Padding( - padding: const EdgeInsets.all(8.0), - child: TextFormField( - style: const TextStyle(fontSize: 22), - controller: _historyProvider.payAmount, - textAlign: TextAlign.center, - maxLines: 1, - keyboardType: TextInputType.number, - decoration: InputDecoration( - contentPadding: const EdgeInsets.symmetric( - vertical: 25.0, horizontal: 10.0), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10.0)), - ), - inputFormatters: <TextInputFormatter>[ - FilteringTextInputFormatter.allow(RegExp(r'(^\d*\.?\d*)')) - ], - ), - ), - Padding( - padding: const EdgeInsets.only(top: 15), - child: OutlinedButton( - style: OutlinedButton.styleFrom( - side: BorderSide(width: 2, color: orangeC)), - onPressed: () { - // if (_formKey.currentState.validate()) { - // _formKey.currentState.save(); - // } - // _historyProvider.pay(payAmount.text, payComment.text); - Navigator.push(context, - MaterialPageRoute(builder: (context) { - return UnlockingWallet( - wallet: defaultWallet, action: "pay"); - })); - }, - child: Padding( - padding: const EdgeInsets.all(12), - child: Text("PAYER", - style: TextStyle( - fontSize: 25, color: Colors.grey[850]))), - )) - ], - ), - ), - ], - ); - } - Widget historyView(context, result) { - HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context); - int keyID = 0; + WalletsProfilesProvider _historyProvider = + Provider.of<WalletsProfilesProvider>(context, listen: false); return _historyProvider.transBC == null - ? const Text('Aucune transaction à afficher.') + ? Column(children: const <Widget>[ + SizedBox(height: 50), + Text( + "Aucune transaction à afficher.", + style: TextStyle(fontSize: 18), + ) + ]) : Column(children: <Widget>[ - for (var repository in _historyProvider.transBC) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 5.0), - child: ListTile( - key: Key('transaction${keyID++}'), - contentPadding: const EdgeInsets.all(5.0), - leading: Text(repository[1].toString(), - style: TextStyle( - fontSize: 12, - color: Colors.grey[800], - fontWeight: FontWeight.w700), - textAlign: TextAlign.center), - title: Text(repository[3], - style: const TextStyle( - fontSize: 15.0, fontFamily: 'Monospace'), - textAlign: TextAlign.center), - subtitle: Text(repository[6] != '' ? repository[6] : '-', - style: const TextStyle(fontSize: 12.0), - textAlign: TextAlign.center), - trailing: Text("${repository[4]} Äž1", - style: const TextStyle(fontSize: 14.0), - textAlign: TextAlign.justify), - dense: true, - isThreeLine: false, - onTap: () { - // this._outputPubkey.text = repository[2]; - _historyProvider.isPubkey(context, repository[2]); - })), - if (result.isLoading) + getTransactionTile(context, _historyProvider), + if (result.isLoading && + _historyProvider.pageInfo['hasPreviousPage']) Row( mainAxisAlignment: MainAxisAlignment.center, children: const <Widget>[ CircularProgressIndicator(), ], ), - // if (_historyProvider.isTheEnd) // What I did before ... if (!_historyProvider.pageInfo['hasPreviousPage']) Column( children: const <Widget>[ @@ -463,4 +171,384 @@ class HistoryScreen extends StatelessWidget with ChangeNotifier { ) ]); } + + Widget getTransactionTile( + BuildContext context, WalletsProfilesProvider _historyProvider) { + 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; + + const Map<int, String> monthsInYear = { + 1: "Janvier", + 2: "Février", + 3: "Mars", + 4: "Avril", + 5: "Mai", + 6: "Juin", + 7: "Juillet", + 8: "Aout", + 9: "Septembre", + 10: "Octobre", + 11: "Novembre", + 12: "Décembre" + }; + + return Column( + children: _historyProvider.transBC.map((repository) { + DateTime now = DateTime.now(); + DateTime date = DateTime.fromMillisecondsSinceEpoch(repository[0] * 1000); + + 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(); + } + + if (DateTime(date.year, date.month, date.day) == + DateTime(now.year, now.month, now.day) && + !isTody) { + dateDelimiter = lastDateDelimiter = "Aujourd'hui"; + isTody = true; + } else if (DateTime(date.year, date.month, date.day) == + DateTime(now.year, now.month, now.day - 1) && + !isYesterday) { + dateDelimiter = lastDateDelimiter = "Hier"; + isYesterday = true; + } else if (weekNumber(date) == weekNumber(now) && + date.year == now.year && + lastDateDelimiter != "Cette semaine" && + DateTime(date.year, date.month, date.day) != + DateTime(now.year, now.month, now.day - 1) && + !isThisWeek) { + dateDelimiter = lastDateDelimiter = "Cette semaine"; + isThisWeek = true; + } else if (lastDateDelimiter != monthsInYear[date.month] && + lastDateDelimiter != "${monthsInYear[date.month]} ${date.year}" && + DateTime(date.year, date.month, date.day) != + DateTime(now.year, now.month, now.day) && + DateTime(date.year, date.month, date.day) != + DateTime(now.year, now.month, now.day - 1) && + !(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; + } + + return Column(children: <Widget>[ + if (dateDelimiter != null) + Padding( + padding: const EdgeInsets.symmetric(vertical: 30), + child: Text( + dateDelimiter, + style: TextStyle( + fontSize: 23, color: orangeC, fontWeight: FontWeight.w300), + ), + ), + Padding( + padding: const EdgeInsets.only(right: 0), + child: + // Row(children: [Column(children: [],)],) + ListTile( + key: Key('transaction${keyID++}'), + contentPadding: const EdgeInsets.only( + left: 20, right: 30, top: 15, bottom: 15), + leading: g1WalletsBox.get(repository[2])?.avatar == null + ? FutureBuilder( + future: _cesiumPlusProvider.getAvatar( + repository[2], _avatarSize), + builder: (BuildContext context, + AsyncSnapshot<Image> _avatar) { + if (_avatar.connectionState != + ConnectionState.done || + _avatar.hasError) { + return Stack(children: [ + _cesiumPlusProvider.defaultAvatar(_avatarSize), + Positioned( + top: 8, + right: 0, + width: 12, + height: 12, + child: CircularProgressIndicator( + strokeWidth: 1, + color: orangeC, + ), + ), + ]); + } + if (_avatar.hasData) { + g1WalletsBox.get(repository[2]).avatar = + _avatar.data; + return ClipOval(child: _avatar.data); + } else { + g1WalletsBox.get(repository[2]).avatar = + _cesiumPlusProvider + .defaultAvatar(repository[2]); + return _cesiumPlusProvider + .defaultAvatar(_avatarSize); + } + }) + : ClipOval( + child: Image( + image: g1WalletsBox.get(repository[2]).avatar.image, + height: _avatarSize, + ), + ), + title: Padding( + padding: EdgeInsets.only( + bottom: 5, top: repository[6] != '' ? 0 : 0), + child: Text(repository[3], + 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[6] != '') + TextSpan( + text: ' · ', + style: TextStyle( + fontSize: 20, + color: Colors.grey[550], + ), + ), + TextSpan( + text: repository[6], + style: TextStyle( + fontStyle: FontStyle.italic, + color: Colors.grey[600], + ), + ), + ], + ), + ), + trailing: Text("${repository[4]} Äž1", + style: const TextStyle( + fontSize: 18, fontWeight: FontWeight.w500), + textAlign: TextAlign.justify), + dense: false, + isThreeLine: false, + onTap: () { + _historyProvider.nPage = 1; + // _cesiumPlusProvider.avatarCancelToken.cancel('cancelled'); + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return WalletViewScreen(pubkey: repository[2]); + }), + ); + // Navigator.pop(context); + }), + ), + ]); + }).toList()); + } + + Widget headerProfileView( + BuildContext context, + WalletsProfilesProvider _historyProvider, + CesiumPlusProvider _cesiumPlusProvider) { + const double _avatarSize = 140; + + return Column(children: <Widget>[ + Container( + height: 10, + color: yellowC, + ), + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + yellowC, + const Color(0xFFE7811A), + ], + )), + child: Padding( + padding: const EdgeInsets.only(left: 30, right: 40), + child: Row(children: <Widget>[ + Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: <Widget>[ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: <Widget>[ + Row(children: [ + GestureDetector( + key: const Key('copyPubkey'), + onTap: () { + Clipboard.setData(ClipboardData(text: pubkey)); + _historyProvider.snackCopyKey(context); + }, + child: Text( + _historyProvider.getShortPubkey(pubkey), + style: const TextStyle( + fontSize: 30, + fontWeight: FontWeight.w800, + ), + ), + ), + ]), + const SizedBox(height: 10), + if (username == null) + Query( + options: QueryOptions( + document: gql(getId), + variables: { + 'pubkey': pubkey, + }, + ), + builder: (QueryResult result, + {VoidCallback refetch, FetchMore fetchMore}) { + if (result.isLoading || result.hasException) { + return const Text('...'); + } else if (result.data['idty'] == null || + result.data['idty']['username'] == null) { + return const Text(''); + } else { + return SizedBox( + width: 230, + child: Text( + result?.data['idty']['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: 25), + ]), + FutureBuilder( + future: _historyProvider.getBalance(pubkey), + builder: + (BuildContext context, AsyncSnapshot<num> _balance) { + if (_balance.connectionState != ConnectionState.done || + _balance.hasError) { + return const Text('...'); + } + return Text( + "${_balance.data.toString()} Äž1", + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 22, fontWeight: FontWeight.w500), + ); + }), + const SizedBox(height: 30), + ]), + const Spacer(), + Column(children: <Widget>[ + if (avatar == null) + FutureBuilder( + future: _cesiumPlusProvider.getAvatar(pubkey, _avatarSize), + builder: + (BuildContext context, AsyncSnapshot<Image> _avatar) { + if (_avatar.connectionState != ConnectionState.done) { + return Stack(children: [ + ClipOval( + child: + _cesiumPlusProvider.defaultAvatar(_avatarSize), + ), + Positioned( + top: 15, + right: 45, + width: 51, + height: 51, + child: CircularProgressIndicator( + strokeWidth: 5, + color: orangeC, + ), + ), + ]); + } + if (_avatar.hasData) { + return GestureDetector( + key: const Key('openAvatar'), + onTap: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return AvatarFullscreen(_avatar.data); + }), + ); + }, + child: ClipOval( + child: Image( + image: _avatar.data.image, + height: _avatarSize, + fit: BoxFit.cover, + ), + ), + ); + } + return ClipOval( + child: _cesiumPlusProvider.defaultAvatar(_avatarSize), + ); + }), + if (avatar != null) + GestureDetector( + key: const Key('openAvatar'), + onTap: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return AvatarFullscreen(avatar); + }), + ); + }, + child: ClipOval( + child: Image( + image: avatar.image, + height: _avatarSize, + fit: BoxFit.cover, + ), + ), + ), + const SizedBox(height: 25), + ]), + ]), + ), + ), + ]); + } } diff --git a/lib/screens/home.dart b/lib/screens/home.dart index f305064fc4705241b73096c7844e32b61f8d0512..c79723e1e030bf9a87457a809ac3cd0f12fc79a8 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -1,13 +1,16 @@ +import 'package:bubble/bubble.dart'; import 'package:dubp/dubp.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/models/chest_provider.dart'; -import 'package:gecko/models/history.dart'; -import 'package:gecko/models/home.dart'; +import 'package:gecko/models/wallets_profiles.dart'; import 'package:flutter/material.dart'; +import 'package:gecko/models/home.dart'; import 'package:gecko/models/my_wallets.dart'; import 'package:gecko/models/wallet_data.dart'; +import 'package:gecko/screens/myWallets/restore_chest.dart'; import 'package:gecko/screens/myWallets/unlocking_wallet.dart'; -import 'package:gecko/screens/onBoarding/0_no_keychain_found.dart'; +import 'package:gecko/screens/onBoarding/1.dart'; +import 'package:gecko/screens/search.dart'; import 'dart:ui'; import 'package:gecko/screens/settings.dart'; import 'package:flutter/services.dart'; @@ -19,21 +22,14 @@ class HomeScreen extends StatelessWidget { @override Widget build(BuildContext context) { SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); - HomeProvider _homeProvider = Provider.of<HomeProvider>(context); - HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context); - HistoryProvider _historyStatic = HistoryProvider(''); + // HomeProvider _homeProvider = Provider.of<HomeProvider>(context); MyWalletsProvider _myWalletProvider = Provider.of<MyWalletsProvider>(context); Provider.of<ChestProvider>(context); + HomeProvider homeClass = HomeProvider(); final bool isWalletsExists = _myWalletProvider.checkIfWalletExist(); - // walletBox.toMap().forEach((key, value) { - // if (value.chest == 0) { - // print('$key: ${value.derivation}'); - // } - // }); - isTall = false; ratio = 1; if (MediaQuery.of(context).size.height >= 930) { @@ -86,95 +82,225 @@ class HomeScreen extends StatelessWidget { ], ), ), - appBar: AppBar( - toolbarHeight: 60 * ratio, - leading: Builder( - builder: (context) => IconButton( - key: const Key('drawerMenu'), - icon: Icon(Icons.menu, color: Colors.grey[850]), - onPressed: () => Scaffold.of(context).openDrawer(), - )), - title: _homeProvider.appBarTitle, - actions: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: IconButton( - key: const Key('searchIcon'), - icon: _homeProvider.searchIcon, - color: Colors.grey[850], - onPressed: () { - if (_homeProvider.searchIcon.icon == Icons.search) { - _homeProvider.searchIcon = Icon( - Icons.close, - color: Colors.grey[850], - ); - _homeProvider.appBarTitle = TextField( - key: const Key('searchInput'), - autofocus: true, - controller: _homeProvider.searchQuery, - onChanged: (text) { - log.d("Clé tappé: $text"); - final String searchResult = - _historyProvider.isPubkey(context, text); - if (searchResult != '') { - _homeProvider.currentIndex = 0; - } - }, - style: TextStyle( - color: Colors.grey[850], - ), - decoration: InputDecoration( - prefixIcon: - Icon(Icons.search, color: Colors.grey[850]), - hintText: "Rechercher ...", - hintStyle: TextStyle(color: Colors.grey[850])), - ); - _homeProvider.handleSearchStart(); - } else { - _homeProvider.handleSearchEnd(); - } - })) - ], - backgroundColor: const Color(0xffFFD58D), - ), backgroundColor: const Color(0xffF9F9F1), body: Builder( builder: (ctx) => StatefulWrapper( - onInit: () { - WidgetsBinding.instance.addPostFrameCallback((_) { - DubpRust.setup(); - _historyStatic.snackNode(ctx); - }); - }, - child: Column(children: <Widget>[ - Padding( - padding: const EdgeInsets.only(top: 20), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: const <Widget>[ - SizedBox(width: 7), - Image( - image: AssetImage('assets/icon/gecko_final.png'), - height: 180), - ]), + onInit: () { + WidgetsBinding.instance.addPostFrameCallback((_) { + DubpRust.setup(); + if (isWalletsExists) homeClass.snackNode(ctx); + }); + }, + 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', + // ), + // ], + // ), ), - Padding( - padding: const EdgeInsets.only(top: 15), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: const <Widget>[ - Text( - "y'a pas de lézard !", - textAlign: TextAlign.center, - style: TextStyle( - color: Colors.black, - fontSize: 17, - fontStyle: FontStyle.italic), - ) - ]), + ), + ); + } +} + +Widget geckHome(context) { + MyWalletsProvider _myWalletProvider = Provider.of<MyWalletsProvider>(context); + Provider.of<ChestProvider>(context); + + WalletsProfilesProvider _historyProvider = + Provider.of<WalletsProfilesProvider>(context); + final double statusBarHeight = MediaQuery.of(context).padding.top; + return Container( + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage("assets/home/background.jpg"), + fit: BoxFit.cover, + ), + ), + child: + Column(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ + Stack(children: <Widget>[ + Positioned( + top: statusBarHeight + 10, + left: 15, + child: Builder( + builder: (context) => IconButton( + key: const Key('drawerMenu'), + icon: const Icon( + Icons.menu, + color: Colors.white, + size: 35, + ), + onPressed: () => Scaffold.of(context).openDrawer(), ), + ), + ), + const Align( + child: + Image(image: AssetImage('assets/home/header.png'), height: 210), + ), + ]), + Padding( + padding: EdgeInsets.only(top: 15 * ratio), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: const <Widget>[ + Text( + "y'a pas de lézard ;-)", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 24, + fontWeight: FontWeight.w700, + shadows: <Shadow>[ + Shadow( + offset: Offset(0, 0), + blurRadius: 20, + color: Colors.black, + ), + Shadow( + offset: Offset(0, 0), + blurRadius: 20, + color: Colors.black, + ), + ], + ), + ) + ]), + ), + Expanded( + flex: 1, + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + Colors.black.withOpacity(0.9), + ], + ), + ), + child: Column(children: <Widget>[ + const Spacer(), + Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ + Column(children: <Widget>[ + Container( + child: ClipOval( + child: Material( + color: orangeC, // button color + child: InkWell( + child: const Padding( + padding: EdgeInsets.all(18), + child: Image( + image: AssetImage('assets/home/loupe.png'), + height: 70), + ), + onTap: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return const SearchScreen(); + }), + ); + }), + ), + ), + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: Colors.black, + boxShadow: [ + BoxShadow( + blurRadius: 2, + offset: Offset(1, 1.5), + spreadRadius: 0.5) + ], + ), + ), + const SizedBox(height: 12), + const Text( + "Rechercher un\nportefeuille", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 17, + fontWeight: FontWeight.w500), + ) + ]), + const SizedBox(width: 120), + Column(children: <Widget>[ + Container( + child: ClipOval( + key: const Key('manageWallets'), + child: Material( + color: orangeC, // button color + child: InkWell( + child: const Padding( + padding: EdgeInsets.all(18), + child: Image( + image: AssetImage('assets/home/wallet.png'), + height: 75)), + onTap: () { + WalletData defaultWallet = + _myWalletProvider.getDefaultWallet( + configBox.get('currentChest')); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return UnlockingWallet( + wallet: defaultWallet, + action: "mywallets", + ); + }, + ), + ); + + // Navigator.pushNamed( + // context, '/mywallets'))); + }), + ), + ), + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: Colors.black, + boxShadow: [ + BoxShadow( + blurRadius: 2, + offset: Offset(1, 1.5), + spreadRadius: 0.5) + ], + ), + ), + const SizedBox(height: 12), + const Text( + "Gérer mes\nportefeuilles", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 17, + fontWeight: FontWeight.w500), + ) + ]) + ]), Padding( - padding: EdgeInsets.only(top: isTall ? 100 : 60), + padding: const EdgeInsets.only(top: 40), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ @@ -182,15 +308,14 @@ class HomeScreen extends StatelessWidget { Container( child: ClipOval( child: Material( - color: const Color(0xffFFD58D), // button color + color: orangeC, // button color child: InkWell( - splashColor: orangeC, // inkwell color child: const Padding( - padding: EdgeInsets.all(22), + padding: EdgeInsets.all(18), child: Image( image: AssetImage( - 'assets/qrcode-scan.png'), - height: 60)), + 'assets/home/qrcode.png'), + height: 75)), onTap: () async { await _historyProvider.scan(context); }), @@ -198,157 +323,194 @@ class HomeScreen extends StatelessWidget { ), decoration: const BoxDecoration( shape: BoxShape.circle, - color: Colors.white, + color: Colors.black, boxShadow: [ BoxShadow( - color: Colors.grey, - blurRadius: 4.0, - offset: Offset(2.0, 2.5), + blurRadius: 2, + offset: Offset(1, 1.5), spreadRadius: 0.5) ], ), ), const SizedBox(height: 12), const Text( - "Payer par QR-Code", + "Scanner un\nQR code", textAlign: TextAlign.center, - style: TextStyle(color: Colors.black, fontSize: 16), + style: TextStyle( + color: Colors.white, + fontSize: 17, + fontWeight: FontWeight.w500), ) ]) ]), ), - Padding( - padding: const EdgeInsets.only(top: 50), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: <Widget>[ - Column(children: <Widget>[ - Container( - child: ClipOval( - child: Material( - color: const Color(0xffFFD58D), // button color - child: InkWell( - splashColor: orangeC, // inkwell color - child: const Padding( - padding: EdgeInsets.symmetric( - horizontal: 20, vertical: 16), - child: Image( - image: - AssetImage('assets/blockchain.png'), - height: 70)), - onTap: () { - // Navigator.push( - // context, - // MaterialPageRoute( - // builder: (context) { - // return TemplateScreen(); - // }), - // ); - }), - ), + SizedBox(height: isTall ? 80 : 40) + ]), + ), + ) + ]), + ); +} + +Widget welcomeHome(context) { + final double statusBarHeight = MediaQuery.of(context).padding.top; + + return Container( + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage("assets/home/background.jpg"), + fit: BoxFit.cover, + ), + ), + child: + Column(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ + Stack(children: <Widget>[ + Positioned( + top: statusBarHeight + 10, + left: 15, + child: Builder( + builder: (context) => IconButton( + key: const Key('drawerMenu'), + icon: const Icon( + Icons.menu, + color: Colors.white, + size: 35, + ), + onPressed: () => Scaffold.of(context).openDrawer(), + ), + ), + ), + const Align( + child: + Image(image: AssetImage('assets/home/header.png'), height: 210), + ), + ]), + Padding( + padding: EdgeInsets.only(top: 1 * ratio), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: const <Widget>[ + Text( + "L’application de paiement Äž1\nplus rapide qu’un reptile du Vietnam", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 24, + fontWeight: FontWeight.w700, + shadows: <Shadow>[ + Shadow( + offset: Offset(0, 0), + blurRadius: 20, + color: Colors.black, + ), + Shadow( + offset: Offset(0, 0), + blurRadius: 20, + color: Colors.black, + ), + ], + ), + ) + ]), + ), + Expanded( + flex: 1, + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + Colors.black.withOpacity(0.9), + ], + ), + ), + child: Center( + child: Column(children: <Widget>[ + const Spacer(), + Row(children: <Widget>[ + Expanded( + child: Stack(children: <Widget>[ + const Padding( + padding: EdgeInsets.only(top: 55), + child: Image( + image: AssetImage('assets/home/gecko-bienvenue.png'), + height: 220, ), - 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) - ], + ), + Positioned( + left: 180, + child: bubbleSpeak("y'a pas de lézard !"), + ), + const Positioned( + left: 200, + top: 60, + child: Image( + image: AssetImage('assets/home/bout_de_bulle.png'), ), ), - const SizedBox(height: 12), - const Text( - "Explorer\n", - textAlign: TextAlign.center, - style: TextStyle(color: Colors.black, fontSize: 16), - ) ]), - const SizedBox(width: 140), - Column(children: <Widget>[ - Container( - child: ClipOval( - key: const Key('manageWallets'), - child: Material( - color: const Color(0xffFFD58D), // button color - child: InkWell( - splashColor: orangeC, // inkwell color - child: const Padding( - padding: EdgeInsets.all(23), - child: Image( - image: AssetImage('assets/lock.png'), - height: 57)), - onTap: () { - WalletData defaultWallet = - _myWalletProvider.getDefaultWallet( - configBox.get('currentChest')); - isWalletsExists - ? Navigator.push(context, - MaterialPageRoute(builder: (context) { - return UnlockingWallet( - wallet: defaultWallet, - action: "mywallets", - ); - })) - - // Navigator.pushNamed( - // context, '/mywallets') - : Navigator.push(context, - MaterialPageRoute(builder: (context) { - return const NoKeyChainScreen(); - })); - }), - ), + ), + ]), + SizedBox( + width: 410, + height: 70, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 4, + primary: orangeC, // background + onPrimary: Colors.white, // foreground + ), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return OnboardingStepOne(); + }, ), - 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: const Text( + 'Créer un portefeuille', + style: + TextStyle(fontSize: 24, fontWeight: FontWeight.w600), + ), + ), + ), + SizedBox(height: 25 * ratio), + SizedBox( + width: 410, + height: 70, + child: OutlinedButton( + style: OutlinedButton.styleFrom( + side: BorderSide(width: 4, color: orangeC)), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const RestoreChest(); + }, ), - ), - const SizedBox(height: 12), - const Text( - "Gérer mes\nportefeuilles", - textAlign: TextAlign.center, - style: TextStyle(color: Colors.black, fontSize: 16), - ) - ]) - ]), - ) - ]), - // bottomNavigationBar: BottomNavigationBar( - // backgroundColor: Color(0xffFFD58D), - // fixedColor: Colors.grey[850], - // unselectedItemColor: 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', - // ), - // BottomNavigationBarItem( - // icon: Icon(Icons.lock), - // label: 'Mes portefeuilles', - // ), - // ], - // ), - ), - ), - ); - } + ); + }, + child: Text( + "Restaurer mes portefeuilles", + style: TextStyle( + fontSize: 24, + color: orangeC, + fontWeight: FontWeight.w600), + ), + ), + ), + SizedBox(height: isTall ? 100 : 50) + ]), + ), + )) + ]), + ); } class StatefulWrapper extends StatefulWidget { @@ -374,3 +536,19 @@ class _StatefulWrapperState extends State<StatefulWrapper> { return widget.child; } } + +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: Colors.white, + child: Text( + text, + key: textKey, + style: const TextStyle( + color: Colors.black, fontSize: 21, fontWeight: FontWeight.w400), + ), + ); +} diff --git a/lib/screens/myWallets/cesium_wallet_options.dart b/lib/screens/myWallets/cesium_wallet_options.dart index 1d8854d802f41eb702f8c5c742e553e3e2cff634..ac7ffedd398f2861f9426d752506b7985a4f3542 100644 --- a/lib/screens/myWallets/cesium_wallet_options.dart +++ b/lib/screens/myWallets/cesium_wallet_options.dart @@ -5,10 +5,11 @@ import 'package:flutter/material.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/models/chest_data.dart'; import 'package:gecko/models/chest_provider.dart'; -import 'package:gecko/models/history.dart'; +import 'package:gecko/models/wallets_profiles.dart'; import 'package:gecko/models/my_wallets.dart'; import 'package:gecko/models/queries.dart'; import 'package:gecko/models/wallet_options.dart'; +import 'package:gecko/screens/history.dart'; import 'package:gecko/screens/myWallets/change_pin.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:provider/provider.dart'; @@ -17,336 +18,404 @@ import 'package:flutter/services.dart'; int _nbrLinesName = 1; bool _isNewNameValid = false; -Widget cesiumWalletOptions(BuildContext context, ChestData cesiumWallet, - MyWalletsProvider _myWalletProvider) { - SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); - WalletOptionsProvider _walletOptions = - Provider.of<WalletOptionsProvider>(context); - ChestProvider _chestProvider = - Provider.of<ChestProvider>(context, listen: false); - HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context); +class CesiumWalletOptions extends StatelessWidget { + const CesiumWalletOptions( + {Key key, Key keyMyWallets, @required this.cesiumWallet}) + : super(key: key); - final String shortPubkey = - _walletOptions.getShortPubkey(_walletOptions.pubkey.text); + final ChestData cesiumWallet; - if (_walletOptions.nameController.text == null || _isNewNameValid == false) { - _walletOptions.nameController.text = cesiumWallet.name; - } else { - cesiumWallet.name = _walletOptions.nameController.text; - } + @override + Widget build(BuildContext context) { + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); + WalletOptionsProvider _walletOptions = + Provider.of<WalletOptionsProvider>(context, listen: false); + ChestProvider _chestProvider = + Provider.of<ChestProvider>(context, listen: false); + WalletsProfilesProvider _historyProvider = + Provider.of<WalletsProfilesProvider>(context, listen: false); + MyWalletsProvider _myWalletProvider = + Provider.of<MyWalletsProvider>(context, listen: false); - _walletOptions.nameController.text.length >= 15 - ? _nbrLinesName = 2 - : _nbrLinesName = 1; - if (_walletOptions.nameController.text.length >= 26 && isTall) { - _nbrLinesName = 3; - } + final String shortPubkey = + _walletOptions.getShortPubkey(_walletOptions.pubkey.text); + + if (_walletOptions.nameController.text == null || + _isNewNameValid == false) { + _walletOptions.nameController.text = cesiumWallet.name; + } else { + cesiumWallet.name = _walletOptions.nameController.text; + } + + _walletOptions.nameController.text.length >= 15 + ? _nbrLinesName = 2 + : _nbrLinesName = 1; + if (_walletOptions.nameController.text.length >= 26 && isTall) { + _nbrLinesName = 3; + } - return Scaffold( - resizeToAvoidBottomInset: false, - body: Builder( - builder: (ctx) => SafeArea( - child: Column(children: <Widget>[ - Container( - height: isTall ? 30 : 15, - color: yellowC, + return WillPopScope( + onWillPop: () { + _walletOptions.isEditing = false; + _walletOptions.isBalanceBlur = true; + Navigator.popUntil( + context, + ModalRoute.withName('/'), + ); + return Future<bool>.value(true); + }, + child: Scaffold( + resizeToAvoidBottomInset: false, + appBar: AppBar( + toolbarHeight: 60 * ratio, + elevation: 0, + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.black), + onPressed: () { + _walletOptions.isEditing = false; + _walletOptions.isBalanceBlur = true; + Navigator.popUntil( + context, + ModalRoute.withName('/'), + ); + }), + title: SizedBox( + height: 22, + child: Consumer<WalletOptionsProvider>( + builder: (context, walletProvider, _) { + return Text(_walletOptions.nameController.text); + }), ), - Container( - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - yellowC, - const Color(0xfffafafa), - ], - )), - child: Row(children: <Widget>[ - const SizedBox(width: 25), - InkWell( - onTap: () async { - File newAvatar = await _walletOptions.changeAvatar(); - if (newAvatar != null) { - cesiumWallet.imageFile = newAvatar; - } - _walletOptions.reloadBuild(); - }, - child: cesiumWallet.imageFile == null - ? Image.asset( - 'assets/chests/${cesiumWallet.imageName}', - width: 110, - ) - : Image.file(cesiumWallet.imageFile, width: 110), - ), - InkWell( - onTap: () async { - File newAvatar = await _walletOptions.changeAvatar(); - if (newAvatar != null) { - cesiumWallet.imageFile = newAvatar; - } - _walletOptions.reloadBuild(); - }, - child: Column(children: <Widget>[ - Image.asset( - 'assets/walletOptions/camera.png', - ), - const SizedBox(height: 100) - ])), - Column(children: <Widget>[ - Row(children: <Widget>[ + ), + body: Builder( + builder: (ctx) => SafeArea( + child: Column(children: <Widget>[ + Consumer<WalletOptionsProvider>( + builder: (context, walletProvider, _) { + return Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + yellowC, + const Color(0xfffafafa), + ], + )), + child: Row(children: <Widget>[ + const SizedBox(width: 25), + InkWell( + onTap: () async { + File newAvatar = await _walletOptions.changeAvatar(); + if (newAvatar != null) { + cesiumWallet.imageFile = newAvatar; + } + _walletOptions.reloadBuild(); + }, + child: cesiumWallet.imageFile == null + ? Image.asset( + 'assets/chests/${cesiumWallet.imageName}', + width: 110, + ) + : Image.file(cesiumWallet.imageFile, width: 110), + ), + InkWell( + onTap: () async { + File newAvatar = await _walletOptions.changeAvatar(); + if (newAvatar != null) { + cesiumWallet.imageFile = newAvatar; + } + _walletOptions.reloadBuild(); + }, + child: Column(children: <Widget>[ + Image.asset( + 'assets/walletOptions/camera.png', + height: 40, + ), + const SizedBox(height: 80) + ])), Column(children: <Widget>[ - SizedBox( - width: 260, - child: TextField( - key: const Key('walletName'), - autofocus: false, - focusNode: _walletOptions.walletNameFocus, - enabled: _walletOptions.isEditing, - controller: _walletOptions.nameController, - maxLines: _nbrLinesName, - textAlign: TextAlign.center, - decoration: const InputDecoration( - border: InputBorder.none, - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none, - disabledBorder: InputBorder.none, - contentPadding: EdgeInsets.all(15.0), + Row(children: <Widget>[ + Column(children: <Widget>[ + SizedBox( + width: 260, + child: TextField( + key: const Key('walletName'), + autofocus: false, + focusNode: _walletOptions.walletNameFocus, + enabled: _walletOptions.isEditing, + controller: _walletOptions.nameController, + maxLines: _nbrLinesName, + 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 ? 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), ), - 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()); - } + builder: (QueryResult result, + {VoidCallback refetch, FetchMore fetchMore}) { + if (result.hasException) { + return Text(result.exception.toString()); + } - if (result.isLoading) { - return const Text('Loading'); - } + if (result.isLoading) { + return const 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)) - ]); + // 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), - // ); - }, - ), - const SizedBox(height: 5), - InkWell( - key: const Key('displayBalance'), - onTap: () { - _walletOptions.bluringBalance(); - }, - child: Image.asset( - _walletOptions.isBalanceBlur - ? 'assets/walletOptions/icon_oeuil.png' - : 'assets/walletOptions/icon_oeuil_close.png', - )), - ]), - const SizedBox(width: 0), - Column(children: <Widget>[ - InkWell( - key: const Key('renameWallet'), - onTap: () async { - _isNewNameValid = _walletOptions.editWalletName( - [cesiumWallet.key, 0], - isCesium: cesiumWallet.isCesium); - await Future.delayed( - const Duration(milliseconds: 30)); - _walletOptions.walletNameFocus.requestFocus(); - }, - child: ClipRRect( + // Text( + // '$wBalanceUD DU', + // style: TextStyle( + // fontSize: 20, color: Colors.black), + // ); + }, + ), + const SizedBox(height: 5), + InkWell( + key: const Key('displayBalance'), + onTap: () { + _walletOptions.bluringBalance(); + }, child: Image.asset( - _walletOptions.isEditing - ? 'assets/walletOptions/android-checkmark.png' - : 'assets/walletOptions/edit.png', - width: 20, - height: 20), - )), - const SizedBox( - height: 60, - ) - ]) + _walletOptions.isBalanceBlur + ? 'assets/walletOptions/icon_oeuil.png' + : 'assets/walletOptions/icon_oeuil_close.png', + height: 35, + ), + ), + ]), + const SizedBox(width: 0), + Column(children: <Widget>[ + InkWell( + key: const Key('renameWallet'), + onTap: () async { + _isNewNameValid = _walletOptions.editWalletName( + [cesiumWallet.key, 0], + isCesium: cesiumWallet.isCesium); + await Future.delayed( + const Duration(milliseconds: 30)); + _walletOptions.walletNameFocus.requestFocus(); + }, + child: ClipRRect( + child: Image.asset( + _walletOptions.isEditing + ? 'assets/walletOptions/android-checkmark.png' + : 'assets/walletOptions/edit.png', + width: 20, + height: 20), + )), + const 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) - : const Text('-', style: TextStyle(fontSize: 20)); + ); }), - SizedBox(height: 15 * ratio), - GestureDetector( - key: const Key('copyPubkey'), - onTap: () { - Clipboard.setData( - ClipboardData(text: _walletOptions.pubkey.text)); - _walletOptions.snackCopyKey(ctx); - }, - child: SizedBox( + 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) + : const Text('-', style: TextStyle(fontSize: 20)); + }), + SizedBox(height: 15 * ratio), + GestureDetector( + key: const Key('copyPubkey'), + onTap: () { + Clipboard.setData( + ClipboardData(text: _walletOptions.pubkey.text)); + _walletOptions.snackCopyKey(ctx); + }, + child: SizedBox( + height: 50, + child: Row(children: <Widget>[ + const SizedBox(width: 30), + Image.asset( + 'assets/walletOptions/key.png', + height: 45, + ), + const SizedBox(width: 20), + Text("${shortPubkey.split(':')[0]}:", + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.w800, + fontFamily: 'Monospace', + color: Colors.black)), + Text(shortPubkey.split(':')[1], + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.w800, + fontFamily: 'Monospace')), + const SizedBox(width: 15), + SizedBox( + height: 40, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + elevation: 1, + primary: orangeC, // 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', + height: 25, + ), + const SizedBox(width: 7), + Text('Copier', + style: TextStyle( + fontSize: 15, color: Colors.grey[50])) + ]))), + ]))), + SizedBox(height: 10 * ratio), + InkWell( + key: const Key('displayHistory'), + onTap: () { + _historyProvider.nPage = 1; + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return HistoryScreen( + pubkey: _walletOptions.pubkey.text); + }), + ); + }, + child: SizedBox( + height: 50, + child: Row(children: <Widget>[ + const SizedBox(width: 30), + Image.asset( + 'assets/walletOptions/clock.png', + height: 45, + ), + const SizedBox(width: 22), + const Text('Historique des transactions', + style: + TextStyle(fontSize: 20, color: Colors.black)), + ]))), + SizedBox(height: 7 * ratio), + InkWell( + key: const Key('changePin'), + onTap: () async { + // await _chestProvider.changePin(context, cesiumWallet); + String newPin = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return ChangePinScreen( + walletName: cesiumWallet.name, + walletProvider: _myWalletProvider, + ); + }, + ), + ); + + if (newPin != null) _myWalletProvider.pinCode = newPin; + }, + child: SizedBox( height: 50, child: Row(children: <Widget>[ - const SizedBox(width: 30), + const SizedBox(width: 31), Image.asset( - 'assets/walletOptions/key.png', + 'assets/chests/secret_code.png', + height: 24, ), const SizedBox(width: 20), - Text("${shortPubkey.split(':')[0]}:", - style: const TextStyle( - fontSize: 22, - fontWeight: FontWeight.w800, - fontFamily: 'Monospace', - color: Colors.black)), - Text(shortPubkey.split(':')[1], - style: const TextStyle( - fontSize: 22, - fontWeight: FontWeight.w800, - fontFamily: 'Monospace')), - const SizedBox(width: 15), - SizedBox( - height: 40, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - elevation: 1, - primary: orangeC, // 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', - ), - const SizedBox(width: 7), - Text('Copier', - style: TextStyle( - fontSize: 15, color: Colors.grey[50])) - ]))), - ]))), - SizedBox(height: 10 * ratio), - InkWell( - key: const Key('displayHistory'), - onTap: () { - _historyProvider.isPubkey(ctx, _walletOptions.pubkey.text, - goHistory: true); - }, - child: SizedBox( + const Text('Changer mon code secret', + style: TextStyle(fontSize: 20, color: Colors.black)), + ]), + ), + ), + SizedBox(height: 7 * ratio), + InkWell( + key: const Key('deleteWallet'), + onTap: () async { + await _chestProvider.deleteChest(context, cesiumWallet); + }, + child: SizedBox( height: 50, child: Row(children: <Widget>[ - const SizedBox(width: 30), + const SizedBox(width: 33), Image.asset( - 'assets/walletOptions/clock.png', + 'assets/walletOptions/trash.png', + height: 45, ), - const SizedBox(width: 22), - const Text('Historique des transactions', - style: TextStyle(fontSize: 20, color: Colors.black)), - ]))), - SizedBox(height: 7 * ratio), - InkWell( - key: const Key('changePin'), - onTap: () async { - // await _chestProvider.changePin(context, cesiumWallet); - _myWalletProvider.pinCode = await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return ChangePinScreen( - walletName: cesiumWallet.name, - walletProvider: _myWalletProvider, - ); - }, - ), - ); - }, - child: SizedBox( - height: 50, - child: Row(children: <Widget>[ - const SizedBox(width: 28), - Image.asset( - 'assets/chests/secret_code.png', - ), - const SizedBox(width: 18), - const Text('Changer mon code secret', - style: TextStyle(fontSize: 20, color: Colors.black)), - ])), - ), - SizedBox(height: 7 * ratio), - InkWell( - key: const Key('deleteWallet'), - onTap: () async { - await _chestProvider.deleteChest(context, cesiumWallet); - }, - child: SizedBox( - height: 50, - child: Row(children: <Widget>[ - const SizedBox(width: 33), - Image.asset( - 'assets/walletOptions/trash.png', - ), - const SizedBox(width: 25), - const Text( - 'Supprimer ce coffre', - style: TextStyle( - fontSize: 20, - color: Color(0xffD80000), - ), + const SizedBox(width: 21), + const Text( + 'Supprimer ce coffre', + style: TextStyle( + fontSize: 20, + color: Color(0xffD80000), + ), + ), + ]), ), - ]), - ), + ), + ]), ), - ]), + ), ), - ), - ); + ); + } } diff --git a/lib/screens/myWallets/change_pin.dart b/lib/screens/myWallets/change_pin.dart index 25ba873f5e9c93b29331f870064d49ed4a2d17ed..5abfca404b5191672f1476001c965089b2273d86 100644 --- a/lib/screens/myWallets/change_pin.dart +++ b/lib/screens/myWallets/change_pin.dart @@ -34,20 +34,28 @@ class ChangePinScreen extends StatelessWidget with ChangeNotifier { child: Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar( - toolbarHeight: 60 * ratio, - leading: IconButton( - icon: const Icon(Icons.arrow_back, color: Colors.black), - onPressed: () { - _changePin.newPin.text = ''; - Navigator.of(context).pop(); - }), - title: SizedBox( - height: 22, - child: Text(walletName), - )), + toolbarHeight: 60 * ratio, + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.black), + onPressed: () { + _changePin.newPin.text = ''; + Navigator.of(context).pop(); + }), + title: SizedBox( + height: 22, + child: Text(walletName), + ), + ), body: Center( child: SafeArea( child: Column(children: <Widget>[ + StatefulWrapper( + onInit: () async { + _newWalletFile = + await _changePin.changePin(walletProvider.pinCode); + }, + child: Container(), + ), const SizedBox(height: 80), Text( 'Choisissez un code secret autogénéré :', @@ -111,3 +119,27 @@ class ChangePinScreen extends StatelessWidget with ChangeNotifier { ); } } + +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() { + if (widget.onInit != null) { + widget.onInit(); + } + super.initState(); + } + + @override + Widget build(BuildContext context) { + return widget.child; + } +} diff --git a/lib/screens/myWallets/chest_options.dart b/lib/screens/myWallets/chest_options.dart index dfae9b70d293fcebc416c1054fe858be3fe5e562..cc7eb68e9fa2cd27b76f0165eb36fbdaff917877 100644 --- a/lib/screens/myWallets/chest_options.dart +++ b/lib/screens/myWallets/chest_options.dart @@ -46,7 +46,7 @@ class ChestOptions extends StatelessWidget { key: const Key('changePin'), onTap: () async { // await _chestProvider.changePin(context, cesiumWallet); - walletProvider.pinCode = await Navigator.push( + String pinResult = await Navigator.push( context, MaterialPageRoute( builder: (context) { @@ -57,6 +57,10 @@ class ChestOptions extends StatelessWidget { }, ), ); + + if (pinResult != null) { + walletProvider.pinCode = pinResult; + } }, child: SizedBox( height: 50, @@ -64,6 +68,7 @@ class ChestOptions extends StatelessWidget { const SizedBox(width: 28), Image.asset( 'assets/chests/secret_code.png', + height: 25, ), const SizedBox(width: 18), const Text('Changer mon code secret', @@ -79,11 +84,12 @@ class ChestOptions extends StatelessWidget { child: SizedBox( height: 50, child: Row(children: <Widget>[ - const SizedBox(width: 33), + const SizedBox(width: 30), Image.asset( 'assets/walletOptions/trash.png', + height: 45, ), - const SizedBox(width: 24), + const SizedBox(width: 20), const Text( 'Supprimer ce coffre', style: TextStyle( diff --git a/lib/screens/myWallets/choose_chest.dart b/lib/screens/myWallets/choose_chest.dart index 6377d2e8947c1933e5e1eac6c919a4169bca91ca..7179903d187a71ec0a5aa201892d802fc7095145 100644 --- a/lib/screens/myWallets/choose_chest.dart +++ b/lib/screens/myWallets/choose_chest.dart @@ -9,7 +9,8 @@ import 'package:carousel_slider/carousel_slider.dart'; import 'package:provider/provider.dart'; class ChooseChest extends StatefulWidget { - const ChooseChest({Key key}) : super(key: key); + const ChooseChest({this.action, Key key}) : super(key: key); + final String action; @override State<StatefulWidget> createState() { @@ -29,6 +30,8 @@ class _ChooseChestState extends State<ChooseChest> { MyWalletsProvider _myWalletProvider = Provider.of<MyWalletsProvider>(context); + log.d(widget.action); + return Scaffold( appBar: AppBar( toolbarHeight: 60 * ratio, @@ -113,13 +116,16 @@ class _ChooseChestState extends State<ChooseChest> { WalletData defaultWallet = _myWalletProvider.getDefaultWallet(currentChest); _myWalletProvider.rebuildWidget(); - Navigator.pushAndRemoveUntil(context, - MaterialPageRoute(builder: (context) { - return UnlockingWallet( - wallet: defaultWallet, - action: "mywallets", - ); - }), ModalRoute.withName('/')); + Navigator.pushAndRemoveUntil( + context, + MaterialPageRoute(builder: (context) { + return UnlockingWallet( + wallet: defaultWallet, + action: widget.action ?? "mywallets", + ); + }), + ModalRoute.withName('/'), + ); }, child: Text( 'Ouvrir ce coffre', diff --git a/lib/screens/myWallets/confirm_wallet_storage.dart b/lib/screens/myWallets/confirm_wallet_storage.dart index e822f19bd4374e4f908340f16e5aa785b1f10357..fe7dc15d217888648f88311a01e98e256cdbd285 100644 --- a/lib/screens/myWallets/confirm_wallet_storage.dart +++ b/lib/screens/myWallets/confirm_wallet_storage.dart @@ -6,7 +6,7 @@ import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/models/generate_wallets.dart'; import 'package:gecko/models/my_wallets.dart'; -import 'package:gecko/models/wallet_options.dart'; +import 'package:gecko/screens/myWallets/unlocking_wallet.dart'; import 'package:provider/provider.dart'; // ignore: must_be_immutable @@ -32,8 +32,6 @@ class ConfirmStoreWallet extends StatelessWidget with ChangeNotifier { Provider.of<GenerateWalletsProvider>(context); MyWalletsProvider _myWalletProvider = Provider.of<MyWalletsProvider>(context); - WalletOptionsProvider _walletOptions = - Provider.of<WalletOptionsProvider>(context); final int _currentChest = _myWalletProvider.getCurrentChest(); _mnemonicController.text = generatedMnemonic; @@ -136,7 +134,7 @@ class ConfirmStoreWallet extends StatelessWidget with ChangeNotifier { onPressed: (_generateWalletProvider .isAskedWordValid && walletName.text != '') - ? () { + ? () async { _generateWalletProvider.storeHDWChest( generatedWallet, walletName.text, @@ -148,12 +146,19 @@ class ConfirmStoreWallet extends StatelessWidget with ChangeNotifier { _myWalletProvider.listWallets = _myWalletProvider .readAllWallets(_currentChest); - scheduleMicrotask(() { - _walletOptions.reloadBuild(); - _myWalletProvider.rebuildWidget(); - }); - Navigator.popUntil( - context, ModalRoute.withName('/')); + await Future.delayed( + const Duration(milliseconds: 50)); + _myWalletProvider.rebuildWidget(); + Navigator.pushAndRemoveUntil(context, + MaterialPageRoute(builder: (context) { + return UnlockingWallet( + wallet: + _myWalletProvider.getDefaultWallet( + configBox.get('currentChest'), + ), + action: "mywallets", + ); + }), ModalRoute.withName('/')); } : null, child: const Text('Confirmer', diff --git a/lib/screens/myWallets/generate_wallets.dart b/lib/screens/myWallets/generate_wallets.dart index b85fdbae0c52b56a6ab8ce82330dd9e116432bf5..0adf9d507482e8c42a3e5090e77c89934499674c 100644 --- a/lib/screens/myWallets/generate_wallets.dart +++ b/lib/screens/myWallets/generate_wallets.dart @@ -1,10 +1,8 @@ import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/models/generate_wallets.dart'; -import 'package:gecko/models/my_wallets.dart'; import 'package:gecko/screens/myWallets/confirm_wallet_storage.dart'; import 'package:flutter/material.dart'; -import 'package:gecko/screens/myWallets/unlocking_wallet.dart'; import 'package:printing/printing.dart'; import 'package:provider/provider.dart'; import 'package:super_tooltip/super_tooltip.dart'; @@ -29,8 +27,6 @@ class GenerateFastChestScreen extends StatelessWidget { Provider.of<GenerateWalletsProvider>(context); _generateWalletProvider.generateMnemonic(); - MyWalletsProvider _myWalletClass = MyWalletsProvider(); - return Scaffold( appBar: AppBar( toolbarHeight: 60 * ratio, @@ -122,14 +118,17 @@ class GenerateFastChestScreen extends StatelessWidget { ); await Future.delayed( const Duration(milliseconds: 20)); - await Navigator.pushAndRemoveUntil(context, - MaterialPageRoute(builder: (context) { - return UnlockingWallet( - wallet: _myWalletClass.getDefaultWallet( - configBox.get('currentChest')), - action: "mywallets", - ); - }), ModalRoute.withName('/')); + // if (_generateWalletProvider.hasBeenStored) { + // _generateWalletProvider.hasBeenStored = false; + // await Navigator.pushAndRemoveUntil(context, + // MaterialPageRoute(builder: (context) { + // return UnlockingWallet( + // wallet: _myWalletClass.getDefaultWallet( + // configBox.get('currentChest')), + // action: "mywallets", + // ); + // }), ModalRoute.withName('/')); + // } } : null, child: const Text('Enregistrer ce trousseau', diff --git a/lib/screens/myWallets/import_wallet.dart b/lib/screens/myWallets/import_cesium_wallet.dart similarity index 97% rename from lib/screens/myWallets/import_wallet.dart rename to lib/screens/myWallets/import_cesium_wallet.dart index 5a86fc82d4ef2f906f92653b1bf079fe2672822a..84e2fea97990f0610b2c9e995d262e933bb77179 100644 --- a/lib/screens/myWallets/import_wallet.dart +++ b/lib/screens/myWallets/import_cesium_wallet.dart @@ -24,7 +24,7 @@ class ImportWalletScreen extends StatelessWidget { return WillPopScope( onWillPop: () { - _generateWalletProvider.resetImportView(); + _generateWalletProvider.resetCesiumImportView(); return Future<bool>.value(true); }, child: Scaffold( @@ -33,7 +33,7 @@ class ImportWalletScreen extends StatelessWidget { leading: IconButton( icon: const Icon(Icons.arrow_back, color: Colors.black), onPressed: () { - _generateWalletProvider.resetImportView(); + _generateWalletProvider.resetCesiumImportView(); Navigator.of(context).pop(); }), title: const SizedBox( @@ -171,7 +171,8 @@ class ImportWalletScreen extends StatelessWidget { .importCesiumWallet() .then((value) { _myWalletProvider.rebuildWidget(); - _generateWalletProvider.resetImportView(); + _generateWalletProvider + .resetCesiumImportView(); Navigator.popUntil( context, ModalRoute.withName('/'), diff --git a/lib/screens/myWallets/restore_chest.dart b/lib/screens/myWallets/restore_chest.dart new file mode 100644 index 0000000000000000000000000000000000000000..9f8b1ebb9b42672bead0380be8b2d0785f4a5350 --- /dev/null +++ b/lib/screens/myWallets/restore_chest.dart @@ -0,0 +1,184 @@ +import 'package:bubble/bubble.dart'; +import 'package:flutter/services.dart'; +import 'package:gecko/globals.dart'; +import 'package:flutter/material.dart'; +import 'package:gecko/models/generate_wallets.dart'; +import 'package:gecko/screens/common_elements.dart'; +import 'package:gecko/screens/onBoarding/11.dart'; +import 'package:provider/provider.dart'; +// import 'package:gecko/models/home.dart'; +// import 'package:provider/provider.dart'; + +class RestoreChest extends StatelessWidget { + const RestoreChest({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); + GenerateWalletsProvider generateWalletProvider = + Provider.of<GenerateWalletsProvider>(context, listen: false); + + generateWalletProvider.actualWallet = null; + + return WillPopScope( + onWillPop: () { + generateWalletProvider.resetImportView(); + return Future<bool>.value(true); + }, + child: Scaffold( + appBar: AppBar( + toolbarHeight: 60 * ratio, + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.black), + onPressed: () { + generateWalletProvider.resetImportView(); + Navigator.of(context).pop(); + }), + title: const SizedBox( + height: 22, + child: Text('Restaurer un coffre'), + )), + body: SafeArea( + child: Column(children: <Widget>[ + SizedBox(height: isTall ? 30 : 15), + bubbleSpeak( + 'Pour restaurer vos portefeuilles Gecko, rentrez dans les champs ci-dessous les 12 mots qui constituent votre phrase de restauration :'), + SizedBox(height: isTall ? 30 : 15), + Column(children: <Widget>[ + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: <Widget>[ + arrayCell(context, generateWalletProvider.cellController0), + arrayCell(context, generateWalletProvider.cellController1), + arrayCell(context, generateWalletProvider.cellController2), + arrayCell(context, generateWalletProvider.cellController3), + ]), + const SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: <Widget>[ + arrayCell(context, generateWalletProvider.cellController4), + arrayCell(context, generateWalletProvider.cellController5), + arrayCell(context, generateWalletProvider.cellController6), + arrayCell(context, generateWalletProvider.cellController7), + ]), + const SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: <Widget>[ + arrayCell(context, generateWalletProvider.cellController8), + arrayCell(context, generateWalletProvider.cellController9), + arrayCell(context, generateWalletProvider.cellController10), + arrayCell(context, generateWalletProvider.cellController11), + ]), + ]), + // const Spacer(), + if (generateWalletProvider.isSentenceComplete(context)) + Expanded( + child: Align( + alignment: Alignment.center, + child: SizedBox( + width: 410, + height: 70, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 4, + primary: orangeC, // background + onPrimary: Colors.white, // foreground + ), + onPressed: () async { + if (await generateWalletProvider.isSentenceValid()) { + generateWalletProvider.resetImportView(); + await Navigator.push( + context, + FaderTransition( + page: OnboardingStepThirteen(), isFast: true), + ); + } else { + await badMnemonicPopup(context); + } + }, + child: const Text( + 'Restaurer ce coffre', + style: + TextStyle(fontSize: 24, fontWeight: FontWeight.w600), + ), + ), + ), + // SizedBox(height: isTall ? 80 : 80), + )) + ]), + ), + ), + ); + } + + Widget bubbleSpeak(String text) { + return Bubble( + margin: const BubbleEdges.symmetric(horizontal: 20), + padding: BubbleEdges.all(isTall ? 25 : 15), + borderWidth: 1, + borderColor: Colors.black, + radius: Radius.zero, + color: Colors.white, + child: Text( + text, + key: const Key('importText'), + textAlign: TextAlign.justify, + style: const TextStyle( + color: Colors.black, fontSize: 19, fontWeight: FontWeight.w400), + ), + ); + } + + Widget arrayCell(BuildContext context, TextEditingController cellCtl) { + GenerateWalletsProvider generateWalletProvider = + Provider.of<GenerateWalletsProvider>(context); + + return Container( + width: 102, + height: 40 * ratio, + child: TextField( + autofocus: true, + controller: cellCtl, + textInputAction: TextInputAction.next, + onChanged: (v) { + bool isValid = generateWalletProvider.isBipWord(v); + + if (isValid && generateWalletProvider.cellController11.text.isEmpty) { + FocusScope.of(context).nextFocus(); + } + }, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 20), + ), + decoration: BoxDecoration( + border: Border.all(color: Colors.grey), + color: Colors.white, + borderRadius: BorderRadius.circular(3), + ), + ); + } + + Future<bool> badMnemonicPopup(BuildContext context) async { + return showDialog<bool>( + context: context, + barrierDismissible: true, // user must tap button! + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Phrase incorrecte'), + content: const Text( + 'Votre phrase de restauration semble incorrecte, veuillez la corriger.'), + actions: <Widget>[ + TextButton( + child: const Text("OK"), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + }, + ); + } +} diff --git a/lib/screens/myWallets/unlocking_wallet.dart b/lib/screens/myWallets/unlocking_wallet.dart index 65c1b5fccb5b8378b24efea21d58c81899420bd9..77701afa56332815af1bf0cfedb6c9a26de35c40 100644 --- a/lib/screens/myWallets/unlocking_wallet.dart +++ b/lib/screens/myWallets/unlocking_wallet.dart @@ -2,11 +2,12 @@ import 'dart:async'; import 'package:dubp/dubp.dart'; import 'package:flutter/services.dart'; import 'package:gecko/models/chest_data.dart'; -import 'package:gecko/models/history.dart'; +import 'package:gecko/models/wallets_profiles.dart'; import 'package:gecko/models/my_wallets.dart'; import 'package:gecko/models/wallet_data.dart'; import 'package:gecko/models/wallet_options.dart'; import 'package:flutter/material.dart'; +import 'package:gecko/screens/myWallets/cesium_wallet_options.dart'; import 'package:gecko/screens/myWallets/choose_chest.dart'; import 'package:pin_code_fields/pin_code_fields.dart'; import 'package:provider/provider.dart'; @@ -36,7 +37,6 @@ class UnlockingWallet extends StatelessWidget { Provider.of<WalletOptionsProvider>(context); int _pinLenght; - ChestData currentChest = chestBox.get(configBox.get('currentChest')); if (currentChest.isCesium) { @@ -53,7 +53,7 @@ class UnlockingWallet extends StatelessWidget { child: Column(children: <Widget>[ Expanded( child: Column(children: <Widget>[ - SizedBox(height: isTall ? 80 : 20), + SizedBox(height: isTall ? 100 : 20), Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ currentChest.imageFile == null ? Image.asset( @@ -87,7 +87,7 @@ class UnlockingWallet extends StatelessWidget { fontWeight: FontWeight.w400), )), SizedBox(height: 40 * ratio), - pinForm(context, _pinLenght), + pinForm(context, _pinLenght, currentChest), SizedBox(height: 3 * ratio), InkWell( key: const Key('chooseChest'), @@ -95,7 +95,7 @@ class UnlockingWallet extends StatelessWidget { Navigator.push( context, MaterialPageRoute(builder: (context) { - return const ChooseChest(); + return ChooseChest(action: action); }), ); }, @@ -103,11 +103,14 @@ class UnlockingWallet extends StatelessWidget { width: 400, height: 70, child: Center( - child: Text('Changer de coffre', - style: TextStyle( - fontSize: 22, - color: orangeC, - fontWeight: FontWeight.w600))), + child: Text( + 'Changer de coffre', + style: TextStyle( + fontSize: 22, + color: orangeC, + fontWeight: FontWeight.w600), + ), + ), )), ]), ), @@ -115,7 +118,7 @@ class UnlockingWallet extends StatelessWidget { )); } - Widget pinForm(context, _pinLenght) { + Widget pinForm(context, _pinLenght, ChestData currentChest) { // var _walletPin = ''; // ignore: close_sinks StreamController<ErrorAnimationType> errorController = @@ -125,7 +128,8 @@ class UnlockingWallet extends StatelessWidget { Provider.of<WalletOptionsProvider>(context); MyWalletsProvider _myWalletProvider = Provider.of<MyWalletsProvider>(context); - HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context); + WalletsProfilesProvider _historyProvider = + Provider.of<WalletsProfilesProvider>(context); FocusNode pinFocus = FocusNode(); @@ -194,7 +198,16 @@ class UnlockingWallet extends StatelessWidget { pinColor = Colors.green[400]; // await Future.delayed(Duration(milliseconds: 50)); if (action == "mywallets") { - Navigator.pushNamed(formKey.currentContext, '/mywallets'); + currentChest.isCesium + ? Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return CesiumWalletOptions( + cesiumWallet: currentChest); + }), + ) + : Navigator.pushNamed( + formKey.currentContext, '/mywallets'); } else if (action == "pay") { resultPay = await _historyProvider.pay(context, _pin.toUpperCase()); @@ -212,6 +225,7 @@ class UnlockingWallet extends StatelessWidget { } Future<bool> _paymentsResult(context) { + if (resultPay != "Success") log.i(resultPay); return showDialog<bool>( context: context, barrierDismissible: true, // user must tap button! diff --git a/lib/screens/myWallets/wallet_options.dart b/lib/screens/myWallets/wallet_options.dart index 1e0ca916c0271c679abd9172881d845bde807788..ca8d7ed8d89eb5d97d869edca1b3038c98fec64b 100644 --- a/lib/screens/myWallets/wallet_options.dart +++ b/lib/screens/myWallets/wallet_options.dart @@ -3,11 +3,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/my_wallets.dart'; import 'package:gecko/models/queries.dart'; import 'package:gecko/models/wallet_data.dart'; import 'package:gecko/models/wallet_options.dart'; +import 'package:gecko/models/wallets_profiles.dart'; +import 'package:gecko/screens/history.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:provider/provider.dart'; import 'package:flutter/services.dart'; @@ -24,10 +25,12 @@ class WalletOptions extends StatelessWidget { Widget build(BuildContext context) { SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); WalletOptionsProvider _walletOptions = - Provider.of<WalletOptionsProvider>(context); + Provider.of<WalletOptionsProvider>(context, listen: false); + WalletsProfilesProvider _historyProvider = + Provider.of<WalletsProfilesProvider>(context, listen: false); + MyWalletsProvider _myWalletProvider = Provider.of<MyWalletsProvider>(context); - HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context); final int _currentChest = _myWalletProvider.getCurrentChest(); final String shortPubkey = @@ -57,299 +60,340 @@ class WalletOptions extends StatelessWidget { log.d("Wallet options: $currentChest:${wallet.number}"); return WillPopScope( - onWillPop: () { - _walletOptions.isEditing = false; - _walletOptions.isBalanceBlur = true; - Navigator.popUntil( - context, - ModalRoute.withName('/mywallets'), - ); - return Future<bool>.value(true); - }, - child: Scaffold( - resizeToAvoidBottomInset: false, - appBar: AppBar( - toolbarHeight: 60 * ratio, - leading: IconButton( - icon: const Icon(Icons.arrow_back, color: Colors.black), - onPressed: () { - _walletOptions.isEditing = false; - _walletOptions.isBalanceBlur = true; - Navigator.popUntil( - context, - ModalRoute.withName('/mywallets'), - ); - }), - title: SizedBox( - height: 22, - child: Text(_walletOptions.nameController.text), - )), - body: Builder( - builder: (ctx) => SafeArea( - child: Column(children: <Widget>[ - Container( - height: isTall ? 15 : 0, - color: yellowC, - ), - Container( - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - yellowC, - const Color(0xfffafafa), - ], - )), - child: Row(children: <Widget>[ - const SizedBox(width: 25), - InkWell( - onTap: () async { - File newAvatar = - await _walletOptions.changeAvatar(); - if (newAvatar != null) { - wallet.imageFile = newAvatar; - } - _walletOptions.reloadBuild(); - }, - child: wallet.imageFile == null - ? Image.asset( - 'assets/avatars/${wallet.imageName}', - width: 110, - ) - : Image.file( - wallet.imageFile, - width: 110, - )), - InkWell( - onTap: () async { - File newAvatar = - await _walletOptions.changeAvatar(); - if (newAvatar != null) { - wallet.imageFile = newAvatar; - } - _walletOptions.reloadBuild(); - }, - child: Column(children: <Widget>[ - Image.asset( - 'assets/walletOptions/camera.png', + onWillPop: () { + _walletOptions.isEditing = false; + _walletOptions.isBalanceBlur = true; + Navigator.popUntil( + context, + ModalRoute.withName('/mywallets'), + ); + return Future<bool>.value(true); + }, + child: Scaffold( + resizeToAvoidBottomInset: false, + appBar: AppBar( + toolbarHeight: 60 * ratio, + elevation: 0, + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.black), + onPressed: () { + _walletOptions.isEditing = false; + _walletOptions.isBalanceBlur = true; + Navigator.popUntil( + context, + ModalRoute.withName('/mywallets'), + ); + }), + title: SizedBox( + height: 22, + child: Consumer<WalletOptionsProvider>( + builder: (context, walletProvider, _) { + return Text(_walletOptions.nameController.text); + }), + ), + ), + body: Builder( + builder: (ctx) => SafeArea( + child: Column(children: <Widget>[ + Container( + height: isTall ? 5 : 0, + color: yellowC, + ), + Consumer<WalletOptionsProvider>( + builder: (context, walletProvider, _) { + return Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + yellowC, + const Color(0xfffafafa), + ], + )), + child: Row(children: <Widget>[ + const SizedBox(width: 25), + InkWell( + onTap: () async { + File newAvatar = await walletProvider.changeAvatar(); + if (newAvatar != null) { + wallet.imageFile = newAvatar; + } + walletProvider.reloadBuild(); + }, + child: wallet.imageFile == null + ? Image.asset( + 'assets/avatars/${wallet.imageName}', + width: 110, + ) + : Image.file( + wallet.imageFile, + width: 110, ), - const SizedBox(height: 100) - ])), - Column(children: <Widget>[ - Row(children: <Widget>[ - Column(children: <Widget>[ - SizedBox( - width: 260, - child: TextField( - key: const Key('walletName'), - autofocus: false, - focusNode: _walletOptions.walletNameFocus, - enabled: _walletOptions.isEditing, - controller: _walletOptions.nameController, - maxLines: _nbrLinesName, - 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 ? 27 : 23, - color: Colors.black, - fontWeight: FontWeight.w400, - fontFamily: 'Monospace')), + ), + InkWell( + onTap: () async { + File newAvatar = await walletProvider.changeAvatar(); + if (newAvatar != null) { + wallet.imageFile = newAvatar; + } + walletProvider.reloadBuild(); + }, + child: Column(children: <Widget>[ + Image.asset( + 'assets/walletOptions/camera.png', + height: 40, + ), + const SizedBox(height: 80) + ])), + Column(children: <Widget>[ + Row(children: <Widget>[ + Column(children: <Widget>[ + SizedBox( + width: 260, + child: TextField( + key: const Key('walletName'), + autofocus: false, + focusNode: walletProvider.walletNameFocus, + enabled: walletProvider.isEditing, + controller: walletProvider.nameController, + maxLines: _nbrLinesName, + 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 ? 27 : 23, + color: Colors.black, + fontWeight: FontWeight.w400, + fontFamily: 'Monospace')), + ), + SizedBox(height: isTall ? 5 : 0), + Query( + options: QueryOptions( + document: gql(getBalance), + variables: { + 'pubkey': walletProvider.pubkey.text, + }, + // pollInterval: Duration(seconds: 1), ), - 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()); - } + builder: (QueryResult result, + {VoidCallback refetch, FetchMore fetchMore}) { + if (result.hasException) { + return Text(result.exception.toString()); + } - if (result.isLoading) { - return const Text('Loading'); - } + if (result.isLoading) { + return const 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)), + // List repositories = result.data['viewer']['repositories']['nodes']; + String wBalanceUD; + if (result.data['balance'] == null) { + wBalanceUD = '0.0'; + } else if (result.hasException) { + wBalanceUD = '?'; + } 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: + walletProvider.isBalanceBlur ? 6 : 0, + sigmaY: + walletProvider.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(' DU', + style: TextStyle( + fontSize: isTall ? 20 : 18, + color: Colors.black)) + ]); - // Text( - // '$wBalanceUD DU', - // style: TextStyle( - // fontSize: 20, color: Colors.black), - // ); - }, + // Text( + // '$wBalanceUD DU', + // style: TextStyle( + // fontSize: 20, color: Colors.black), + // ); + }, + ), + const SizedBox(height: 5), + InkWell( + key: const Key('displayBalance'), + onTap: () { + walletProvider.bluringBalance(); + }, + child: Image.asset( + walletProvider.isBalanceBlur + ? 'assets/walletOptions/icon_oeuil.png' + : 'assets/walletOptions/icon_oeuil_close.png', + height: 35, ), - const SizedBox(height: 5), - InkWell( - key: const Key('displayBalance'), - onTap: () { - _walletOptions.bluringBalance(); - }, - child: Image.asset( - _walletOptions.isBalanceBlur - ? 'assets/walletOptions/icon_oeuil.png' - : 'assets/walletOptions/icon_oeuil_close.png', - )), - ]), - const SizedBox(width: 0), - Column(children: <Widget>[ - InkWell( - key: const Key('renameWallet'), - onTap: () async { - _isNewNameValid = - _walletOptions.editWalletName(wallet.id(), - isCesium: false); - await Future.delayed( - const Duration(milliseconds: 30)); - _walletOptions.walletNameFocus.requestFocus(); - }, - child: ClipRRect( - child: Image.asset( - _walletOptions.isEditing - ? 'assets/walletOptions/android-checkmark.png' - : 'assets/walletOptions/edit.png', - width: 20, - height: 20), - )), - const SizedBox( - height: 60, - ) - ]) + ), ]), + const SizedBox(width: 0), + Column(children: <Widget>[ + InkWell( + key: const Key('renameWallet'), + onTap: () async { + _isNewNameValid = walletProvider.editWalletName( + wallet.id(), + isCesium: false); + await Future.delayed( + const Duration(milliseconds: 30)); + walletProvider.walletNameFocus.requestFocus(); + }, + child: ClipRRect( + child: Image.asset( + walletProvider.isEditing + ? 'assets/walletOptions/android-checkmark.png' + : 'assets/walletOptions/edit.png', + width: 20, + height: 20), + )), + const 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) - : const Text('-', style: TextStyle(fontSize: 20)); - }), - SizedBox(height: 15 * ratio), - GestureDetector( + ]), + ]), + ); + }), + 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) + : const Text('-', style: TextStyle(fontSize: 20)); + }, + ), + SizedBox(height: 15 * ratio), + Consumer<WalletOptionsProvider>( + builder: (context, walletProvider, _) { + return Column(children: [ + GestureDetector( key: const Key('copyPubkey'), onTap: () { Clipboard.setData( - ClipboardData(text: _walletOptions.pubkey.text)); - _walletOptions.snackCopyKey(ctx); + ClipboardData(text: walletProvider.pubkey.text)); + walletProvider.snackCopyKey(ctx); }, child: SizedBox( - height: 50, - child: Row(children: <Widget>[ - const SizedBox(width: 30), - Image.asset( - 'assets/walletOptions/key.png', + height: 50, + child: Row(children: <Widget>[ + const SizedBox(width: 30), + Image.asset( + 'assets/walletOptions/key.png', + height: 45, + ), + const SizedBox(width: 20), + Text("${shortPubkey.split(':')[0]}:", + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.w800, + fontFamily: 'Monospace', + color: Colors.black)), + Text(shortPubkey.split(':')[1], + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.w800, + fontFamily: 'Monospace')), + const SizedBox(width: 15), + SizedBox( + height: 40, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + elevation: 1, + primary: orangeC, // background + onPrimary: Colors.black, // foreground + ), + onPressed: () { + Clipboard.setData(ClipboardData( + text: walletProvider.pubkey.text)); + walletProvider.snackCopyKey(ctx); + }, + child: Row(children: <Widget>[ + Image.asset( + 'assets/walletOptions/copy-white.png', + height: 25, + ), + const SizedBox(width: 7), + Text( + 'Copier', + style: TextStyle( + fontSize: 15, color: Colors.grey[50]), + ) + ]), ), - const SizedBox(width: 20), - Text("${shortPubkey.split(':')[0]}:", - style: const TextStyle( - fontSize: 22, - fontWeight: FontWeight.w800, - fontFamily: 'Monospace', - color: Colors.black)), - Text(shortPubkey.split(':')[1], - style: const TextStyle( - fontSize: 22, - fontWeight: FontWeight.w800, - fontFamily: 'Monospace')), - const SizedBox(width: 15), - SizedBox( - height: 40, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - elevation: 1, - primary: orangeC, // 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', - ), - const SizedBox(width: 7), - Text('Copier', - style: TextStyle( - fontSize: 15, - color: Colors.grey[50])) - ]))), - ]))), - SizedBox(height: 10 * ratio), - InkWell( + ), + ]), + ), + ), + SizedBox(height: 10 * ratio), + InkWell( key: const Key('displayHistory'), onTap: () { - _historyProvider.isPubkey(ctx, _walletOptions.pubkey.text, - goHistory: true); + _historyProvider.nPage = 1; + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return HistoryScreen( + pubkey: walletProvider.pubkey.text, + avatar: wallet.imageFile == null + ? Image.asset( + 'assets/avatars/${wallet.imageName}', + width: 110, + ) + : Image.file( + wallet.imageFile, + width: 110, + )); + }), + ); }, child: SizedBox( - height: 50, - child: Row(children: <Widget>[ - const SizedBox(width: 30), - Image.asset( - 'assets/walletOptions/clock.png', - ), - const SizedBox(width: 22), - const Text('Historique des transactions', - style: - TextStyle(fontSize: 20, color: Colors.black)), - ]))), - SizedBox(height: 12 * ratio), - InkWell( + height: 50, + child: Row(children: <Widget>[ + const SizedBox(width: 30), + Image.asset( + 'assets/walletOptions/clock.png', + height: 45, + ), + const SizedBox(width: 22), + const Text('Historique des transactions', + style: + TextStyle(fontSize: 20, color: Colors.black)), + ]), + ), + ), + SizedBox(height: 12 * ratio), + InkWell( key: const Key('setDefaultWallet'), - onTap: !_walletOptions.isDefaultWallet + onTap: !walletProvider.isDefaultWallet ? () { defaultWallet = wallet; chestBox.get(currentChest).defaultWallet = @@ -359,33 +403,37 @@ class WalletOptions extends StatelessWidget { } : null, child: SizedBox( - height: 50, - child: Row(children: <Widget>[ - const SizedBox(width: 31), - CircleAvatar( - backgroundColor: Colors.grey[ - _walletOptions.isDefaultWallet ? 300 : 500], - child: Image.asset( - 'assets/walletOptions/android-checkmark.png', - )), - const SizedBox(width: 22), - Text( - _walletOptions.isDefaultWallet - ? 'Ce portefeuille est celui par defaut' - : 'Définir comme portefeuille par défaut', - style: TextStyle( - fontSize: 20, - color: _walletOptions.isDefaultWallet - ? Colors.grey[500] - : Colors.black)), - ]))), - SizedBox(height: 17 * ratio), - if (!_walletOptions.isDefaultWallet) - InkWell( + height: 50, + child: Row(children: <Widget>[ + const SizedBox(width: 31), + CircleAvatar( + backgroundColor: Colors + .grey[walletProvider.isDefaultWallet ? 300 : 500], + child: Image.asset( + 'assets/walletOptions/android-checkmark.png', + height: 25, + ), + ), + const SizedBox(width: 22), + Text( + walletProvider.isDefaultWallet + ? 'Ce portefeuille est celui par defaut' + : 'Définir comme portefeuille par défaut', + style: TextStyle( + fontSize: 20, + color: walletProvider.isDefaultWallet + ? Colors.grey[500] + : Colors.black)), + ]), + ), + ), + SizedBox(height: 17 * ratio), + if (!walletProvider.isDefaultWallet) + InkWell( key: const Key('deleteWallet'), - onTap: !_walletOptions.isDefaultWallet + onTap: !walletProvider.isDefaultWallet ? () async { - await _walletOptions.deleteWallet( + await walletProvider.deleteWallet( context, wallet); WidgetsBinding.instance.addPostFrameCallback((_) { _myWalletProvider.listWallets = @@ -396,18 +444,23 @@ class WalletOptions extends StatelessWidget { } : null, child: Row(children: <Widget>[ - const SizedBox(width: 33), + const SizedBox(width: 30), Image.asset( 'assets/walletOptions/trash.png', + height: 45, ), - const SizedBox(width: 24), + const SizedBox(width: 19), const Text('Supprimer ce portefeuille', style: TextStyle( fontSize: 20, color: Color(0xffD80000))), - ])), - ]), - ), + ]), + ), + ]); + }), + ]), ), - )); + ), + ), + ); } } diff --git a/lib/screens/myWallets/wallets_home.dart b/lib/screens/myWallets/wallets_home.dart index c323c1e5bf542d23ec5f04e02518fd9b96f3ee87..4d180273be50d6903b8f0e244ab2fc58db338a98 100644 --- a/lib/screens/myWallets/wallets_home.dart +++ b/lib/screens/myWallets/wallets_home.dart @@ -7,11 +7,9 @@ import 'package:gecko/models/wallet_data.dart'; import 'package:gecko/models/wallet_options.dart'; import 'package:flutter/material.dart'; import 'package:gecko/screens/common_elements.dart'; -import 'package:gecko/screens/myWallets/cesium_wallet_options.dart'; import 'package:gecko/screens/myWallets/chest_options.dart'; import 'package:gecko/screens/myWallets/choose_chest.dart'; import 'package:gecko/screens/myWallets/wallet_options.dart'; -import 'package:gecko/screens/onBoarding/0_no_keychain_found.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:provider/provider.dart'; @@ -28,13 +26,8 @@ class WalletsHome extends StatelessWidget { final int _currentChestNumber = myWalletProvider.getCurrentChest(); final ChestData _currentChest = chestBox.get(_currentChestNumber); - bool isWalletsExists; - - if (!_currentChest.isCesium) { - myWalletProvider.listWallets = - myWalletProvider.readAllWallets(_currentChestNumber); - } - isWalletsExists = myWalletProvider.checkIfWalletExist(); + myWalletProvider.listWallets = + myWalletProvider.readAllWallets(_currentChestNumber); return WillPopScope( onWillPop: () { @@ -61,12 +54,7 @@ class WalletsHome extends StatelessWidget { backgroundColor: const Color(0xffFFD58D), ), body: SafeArea( - child: !isWalletsExists - ? const NoKeyChainScreen() - : _currentChest.isCesium - ? cesiumWalletOptions( - context, _currentChest, myWalletProvider) - : myWalletsTiles(context), + child: myWalletsTiles(context), ), ), ); diff --git a/lib/screens/old_history_pay.dart b/lib/screens/old_history_pay.dart new file mode 100644 index 0000000000000000000000000000000000000000..12571bf87afdb10d52e5572d7dc0ad4a73435b96 --- /dev/null +++ b/lib/screens/old_history_pay.dart @@ -0,0 +1,485 @@ +import 'package:flutter/services.dart'; +import 'package:gecko/globals.dart'; +import 'package:gecko/models/cesium_plus.dart'; +import 'package:gecko/models/home.dart'; +import 'package:gecko/models/my_wallets.dart'; +import 'package:gecko/models/queries.dart'; +import 'package:gecko/models/wallets_profiles.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; +import 'package:gecko/models/wallet_data.dart'; +import 'package:gecko/screens/myWallets/unlocking_wallet.dart'; +import 'package:gecko/screens/wallet_view.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 OldHistoryScreen extends StatelessWidget with ChangeNotifier { + final TextEditingController _outputPubkey = TextEditingController(); + ScrollController scrollController = ScrollController(); + final nRepositories = 20; + // HistoryProvider _historyProvider; + final _formKey = GlobalKey<FormState>(); + final FocusNode _pubkeyFocus = FocusNode(); + final double avatarsSize = 80; + + FetchMore fetchMore; + FetchMoreOptions opts; + final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); + + OldHistoryScreen({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); + WalletsProfilesProvider _historyProvider = + Provider.of<WalletsProfilesProvider>(context); + HomeProvider _homeProvider = Provider.of<HomeProvider>(context); + _outputPubkey.text = _historyProvider.pubkey; + log.i('Build pubkey : ' + _historyProvider.pubkey); + WidgetsBinding.instance.addPostFrameCallback((_) {}); + + return Scaffold( + key: _scaffoldKey, + appBar: AppBar( + toolbarHeight: 60 * ratio, + title: _homeProvider.appBarExplorer, + actions: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: IconButton( + icon: _homeProvider.searchIcon, + color: Colors.grey[850], + onPressed: () { + if (_homeProvider.searchIcon.icon == Icons.search) { + _homeProvider.searchIcon = Icon( + Icons.close, + color: Colors.grey[850], + ); + _homeProvider.appBarExplorer = TextField( + autofocus: true, + controller: _homeProvider.searchQuery, + onChanged: (text) { + log.d("Clé tappé: $text"); + if (_historyProvider.isPubkey(text)) { + _homeProvider.currentIndex = 0; + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return WalletViewScreen(pubkey: text); + }), + ); + } + }, + style: TextStyle( + color: Colors.grey[850], + ), + decoration: InputDecoration( + prefixIcon: + Icon(Icons.search, color: Colors.grey[850]), + hintText: "Rechercher ...", + hintStyle: TextStyle(color: Colors.grey[850])), + ); + _homeProvider.handleSearchStart(); + } else { + _homeProvider.handleSearchEnd(); + } + })) + ], + backgroundColor: const Color(0xffFFD58D), + ), + floatingActionButton: SizedBox( + height: 80.0, + width: 80.0, + child: FittedBox( + child: FloatingActionButton( + heroTag: "buttonScan", + onPressed: () async { + await _historyProvider.scan(context); + }, + child: SizedBox( + height: 40.0, + width: 40.0, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 3), + child: Image.asset('assets/qrcode-scan.png'))), + backgroundColor: + floattingYellow, //smoothYellow, //Color.fromARGB(500, 204, 255, 255), + ), + ), + ), + body: Column(children: <Widget>[ + const SizedBox(height: 0), + if (_historyProvider.pubkey != '') + historyQuery(context, _historyProvider), + ])); + } + + Widget historyQuery(context, WalletsProfilesProvider _historyProvider) { + _pubkeyFocus.unfocus(); + // HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context); + CesiumPlusProvider _cesiumPlusProvider = + Provider.of<CesiumPlusProvider>(context); + bool _isFirstExec = true; + return Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: <Widget>[ + Query( + options: QueryOptions( + document: gql(getHistory), + variables: <String, dynamic>{ + 'pubkey': _historyProvider.pubkey, + 'number': nRepositories, + 'cursor': null + }, + ), + builder: (QueryResult result, {fetchMore, refetch}) { + if (result.isLoading && result.data == null) { + return const Center( + child: CircularProgressIndicator(), + ); + } + + if (result.hasException) { + log.e('Error GVA: ' + result.exception.toString()); + return Column(children: const <Widget>[ + SizedBox(height: 50), + Text( + "Aucun noeud GVA valide n'a pu être trouvé.\nVeuillez réessayer ultérieurement.", + style: TextStyle(fontSize: 17.0), + ) + ]); + } + + if (result.data == null && result.exception.toString() == null) { + return const Text('Aucune donnée à afficher.'); + } + + 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); + + // _historyProvider.transBC = null; + + // Build history list + return NotificationListener( + child: Builder( + builder: (context) => Expanded( + child: ListView( + key: const Key('listTransactions'), + controller: scrollController, + children: <Widget>[ + const SizedBox(height: 20), + if (_historyProvider.pubkey != '') + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (_isFirstExec) + Container( + padding: const EdgeInsets.fromLTRB( + 20, 0, 30, 0), + child: FutureBuilder( + future: + _cesiumPlusProvider.getAvatar( + _historyProvider.pubkey, + 55), + builder: (BuildContext context, + AsyncSnapshot<Image> _avatar) { + if (_avatar.connectionState != + ConnectionState.done || + _avatar.hasError) { + return Stack(children: [ + _cesiumPlusProvider + .defaultAvatar(55), + Positioned( + top: 8, + right: 0, + width: 12, + height: 12, + child: + CircularProgressIndicator( + strokeWidth: 1, + color: orangeC, + ), + ), + ]); + } + if (_avatar.hasData) { + return ClipOval( + child: _avatar.data, + ); + } + return _cesiumPlusProvider + .defaultAvatar(55); + }), + ), + GestureDetector( + key: const Key('copyPubkey'), + onTap: () { + Clipboard.setData(ClipboardData( + text: _historyProvider.pubkey)); + _historyProvider.snackCopyKey(context); + }, + child: Text( + _historyProvider.getShortPubkey( + _historyProvider.pubkey), + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.w800, + fontFamily: 'Monospace')), + ), + Container( + padding: const EdgeInsets.fromLTRB( + 30, 0, 5, 0), // .only(right: 15), + child: Card( + child: Column( + children: <Widget>[ + SvgPicture.string( + _historyProvider + .generateIdenticon( + _historyProvider + .pubkey), + fit: BoxFit.contain, + height: 64, + width: 64, + ), + ], + ), + )), + const SizedBox(width: 0) + ]), + if (_isFirstExec) + Row( + mainAxisAlignment: + MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + padding: + const EdgeInsets.fromLTRB(0, 0, 0, 0), + // padding: const EdgeInsets., + child: FutureBuilder( + future: _cesiumPlusProvider + .getName(_historyProvider.pubkey), + initialData: '...', + builder: (context, snapshot) { + return Text(snapshot.data ?? '-', + style: const TextStyle( + fontSize: 20)); + }), + ) + ]), + const SizedBox(height: 18), + if (_isFirstExec) + Container( + padding: + const EdgeInsets.fromLTRB(0, 0, 0, 0), + child: Text(balance.toString() + ' Äž1', + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 18.0))), + const SizedBox(height: 20), + ElevatedButton( + key: const Key('switchPayHistory'), + style: ElevatedButton.styleFrom( + elevation: 1, + primary: Colors.grey[50], // background + onPrimary: Colors.black, // foreground + ), + onPressed: () { + _historyProvider.switchProfileView(); + }, + child: Text( + _historyProvider.historySwitchButtun, + style: TextStyle( + fontSize: 15, color: orangeC))), + // const Divider( + // color: Colors.grey, + // height: 5, + // thickness: 0.5, + // indent: 0, + // endIndent: 0, + // ), + _historyProvider.isHistoryScreen + ? historyView(context, result) + : payView(context, _historyProvider), + ], + ))), + onNotification: (t) { + if (t is ScrollEndNotification && + scrollController.position.pixels >= + scrollController.position.maxScrollExtent * 0.7) { + fetchMore(opts); + } + return true; + }); + }, + ), + ], + )); + } + + Widget payView(context, WalletsProfilesProvider _historyProvider) { + MyWalletsProvider _myWalletProvider = MyWalletsProvider(); + WalletData defaultWallet = + _myWalletProvider.getDefaultWallet(configBox.get('currentChest')); + + return Stack( + clipBehavior: Clip.hardEdge, + children: <Widget>[ + Form( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: <Widget>[ + const SizedBox(height: 20), + const Text('Commentaire:', style: TextStyle(fontSize: 20.0)), + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + controller: _historyProvider.payComment, + maxLines: 2, + textAlign: TextAlign.center, + decoration: const InputDecoration(), + style: const TextStyle( + fontSize: 22, + color: Colors.black, + fontWeight: FontWeight.bold))), + const SizedBox(height: 20), + const Text('Montant (DU/Äž1):', style: TextStyle(fontSize: 20.0)), + Padding( + padding: const EdgeInsets.all(8.0), + child: TextFormField( + style: const TextStyle(fontSize: 22), + controller: _historyProvider.payAmount, + textAlign: TextAlign.center, + maxLines: 1, + keyboardType: TextInputType.number, + decoration: InputDecoration( + contentPadding: const EdgeInsets.symmetric( + vertical: 25.0, horizontal: 10.0), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0)), + ), + inputFormatters: <TextInputFormatter>[ + FilteringTextInputFormatter.allow(RegExp(r'(^\d*\.?\d*)')) + ], + ), + ), + Padding( + padding: const EdgeInsets.only(top: 15), + child: OutlinedButton( + style: OutlinedButton.styleFrom( + side: BorderSide(width: 2, color: orangeC)), + onPressed: () { + // if (_formKey.currentState.validate()) { + // _formKey.currentState.save(); + // } + // _historyProvider.pay(payAmount.text, payComment.text); + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return UnlockingWallet( + wallet: defaultWallet, action: "pay"); + }), + ); + }, + child: Padding( + padding: const EdgeInsets.all(12), + child: Text( + "PAYER", + style: TextStyle(fontSize: 25, color: Colors.grey[850]), + ), + ), + ), + ) + ], + ), + ), + ], + ); + } + + Widget historyView(context, result) { + WalletsProfilesProvider _historyProvider = + Provider.of<WalletsProfilesProvider>(context); + HomeProvider _homeProvider = + Provider.of<HomeProvider>(context, listen: false); + int keyID = 0; + + return _historyProvider.transBC == null + ? const Text('Aucune transaction à afficher.') + : Column(children: <Widget>[ + for (var repository in _historyProvider.transBC) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 5.0), + child: ListTile( + key: Key('transaction${keyID++}'), + contentPadding: const EdgeInsets.all(5.0), + leading: Text(repository[1].toString(), + style: TextStyle( + fontSize: 12, + color: Colors.grey[800], + fontWeight: FontWeight.w700), + textAlign: TextAlign.center), + title: Text(repository[3], + style: const TextStyle( + fontSize: 15.0, fontFamily: 'Monospace'), + textAlign: TextAlign.center), + subtitle: Text(repository[6] != '' ? repository[6] : '-', + style: const TextStyle(fontSize: 12.0), + textAlign: TextAlign.center), + trailing: Text("${repository[4]} Äž1", + style: const TextStyle(fontSize: 14.0), + textAlign: TextAlign.justify), + dense: true, + isThreeLine: false, + onTap: () { + if (_historyProvider.isPubkey(repository[2])) { + _homeProvider.currentIndex = 0; + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return WalletViewScreen(pubkey: repository[2]); + }), + ); + } + Navigator.pop(context); + }), + ), + if (result.isLoading && + _historyProvider.pageInfo['hasPreviousPage']) + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: const <Widget>[ + CircularProgressIndicator(), + ], + ), + // if (_historyProvider.isTheEnd) // What I did before ... + if (!_historyProvider.pageInfo['hasPreviousPage']) + Column( + children: const <Widget>[ + SizedBox(height: 15), + Text("Début de l'historique.", + textAlign: TextAlign.center, + style: TextStyle(fontSize: 20)), + SizedBox(height: 15) + ], + ) + ]); + } +} diff --git a/lib/screens/onBoarding/0_no_keychain_found.dart b/lib/screens/onBoarding/0_no_keychain_found.dart index 31745a7ffc3429cb5d4e72215e2e7a0a479b7ffb..685bc27b19691fa2e1bf6b59802cf490eda54a43 100644 --- a/lib/screens/onBoarding/0_no_keychain_found.dart +++ b/lib/screens/onBoarding/0_no_keychain_found.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/screens/common_elements.dart'; -import 'package:gecko/screens/myWallets/import_wallet.dart'; +import 'package:gecko/screens/myWallets/import_cesium_wallet.dart'; import 'package:gecko/screens/onBoarding/1.dart'; class NoKeyChainScreen extends StatelessWidget { diff --git a/lib/screens/onBoarding/13_congratulations.dart b/lib/screens/onBoarding/13_congratulations.dart index ac737432c7bd15c170d054b2b3cecd275ac5639d..ae2ec6f8fd9c0f7e31ef7e5e649b18aa71bf805f 100644 --- a/lib/screens/onBoarding/13_congratulations.dart +++ b/lib/screens/onBoarding/13_congratulations.dart @@ -46,13 +46,10 @@ class OnboardingStepFiveteen extends StatelessWidget { onPrimary: Colors.white, // foreground ), onPressed: () { - Navigator.popUntil( - context, - ModalRoute.withName('/'), - ); - Navigator.pushNamed( + Navigator.pushNamedAndRemoveUntil( context, '/mywallets', + ModalRoute.withName('/'), ); }, child: const Text("Accéder à mes portefeuilles", diff --git a/lib/screens/onBoarding/7.dart b/lib/screens/onBoarding/7.dart index 756f046d068b4a50e82b0eeef22af982ccf884f7..6604b5b0daa96e3e5ffcb56a889822de3368b9be 100644 --- a/lib/screens/onBoarding/7.dart +++ b/lib/screens/onBoarding/7.dart @@ -51,6 +51,7 @@ class OnboardingStepNine extends StatelessWidget { }, child: Image.asset( 'assets/printer.png', + height: 35, ), ), Expanded( @@ -133,55 +134,63 @@ Widget sentanceArray(BuildContext context) { builder: (context, formatedArray) { // print(formatedArray.data); return Container( - padding: const EdgeInsets.symmetric(horizontal: 12), - child: Container( - decoration: BoxDecoration( - border: Border.all(color: Colors.black), - color: Colors.grey[300], - borderRadius: const BorderRadius.all( - Radius.circular(10), - )), - // color: Colors.grey[300], - padding: const EdgeInsets.all(20), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.center, - children: <Widget>[ - Row(children: <Widget>[ - arrayCell(formatedArray.data[0]), - arrayCell(formatedArray.data[1]), - arrayCell(formatedArray.data[2]), - arrayCell(formatedArray.data[3]), - ]), - const SizedBox(height: 15), - Row(children: <Widget>[ - arrayCell(formatedArray.data[4]), - arrayCell(formatedArray.data[5]), - arrayCell(formatedArray.data[6]), - arrayCell(formatedArray.data[7]), - ]), - const SizedBox(height: 15), - Row(children: <Widget>[ - arrayCell(formatedArray.data[8]), - arrayCell(formatedArray.data[9]), - arrayCell(formatedArray.data[10]), - arrayCell(formatedArray.data[11]), - ]), - ]))); + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.black), + color: Colors.grey[300], + borderRadius: const BorderRadius.all( + Radius.circular(10), + )), + // color: Colors.grey[300], + padding: const EdgeInsets.all(20), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + children: <Widget>[ + Row(children: <Widget>[ + arrayCell(formatedArray.data[0]), + arrayCell(formatedArray.data[1]), + arrayCell(formatedArray.data[2]), + arrayCell(formatedArray.data[3]), + ]), + const SizedBox(height: 15), + Row(children: <Widget>[ + arrayCell(formatedArray.data[4]), + arrayCell(formatedArray.data[5]), + arrayCell(formatedArray.data[6]), + arrayCell(formatedArray.data[7]), + ]), + const SizedBox(height: 15), + Row(children: <Widget>[ + arrayCell(formatedArray.data[8]), + arrayCell(formatedArray.data[9]), + arrayCell(formatedArray.data[10]), + arrayCell(formatedArray.data[11]), + ]), + ]), + ), + ); }); } Widget arrayCell(dataWord) { return SizedBox( - width: 102, - child: Column(children: <Widget>[ - Text(dataWord.split(':')[0], style: const TextStyle(fontSize: 14)), - const SizedBox(height: 2), - Text(dataWord.split(':')[1], - key: Key('word${dataWord.split(':')[0]}'), - style: const TextStyle(fontSize: 19, color: Colors.black)), - ])); + width: 102, + child: Column(children: <Widget>[ + Text( + dataWord.split(':')[0], + style: const TextStyle(fontSize: 14), + ), + const SizedBox(height: 2), + Text( + dataWord.split(':')[1], + key: Key('word${dataWord.split(':')[0]}'), + style: const TextStyle(fontSize: 19, color: Colors.black), + ), + ]), + ); } // ignore: must_be_immutable diff --git a/lib/screens/search.dart b/lib/screens/search.dart new file mode 100644 index 0000000000000000000000000000000000000000..39e43374f2f75bccfb1849a3c95ea9fe70d5fc60 --- /dev/null +++ b/lib/screens/search.dart @@ -0,0 +1,109 @@ +import 'package:flutter/services.dart'; +import 'package:gecko/globals.dart'; +import 'package:flutter/material.dart'; +import 'package:gecko/models/search.dart'; +import 'package:gecko/screens/search_result.dart'; +import 'package:provider/provider.dart'; +// import 'package:gecko/models/home.dart'; +// import 'package:provider/provider.dart'; + +class SearchScreen extends StatelessWidget { + const SearchScreen({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); + SearchProvider _searchProvider = Provider.of<SearchProvider>(context); + + return WillPopScope( + onWillPop: () { + _searchProvider.searchController.text = ''; + return Future<bool>.value(true); + }, + child: Scaffold( + appBar: AppBar( + toolbarHeight: 60 * ratio, + title: const SizedBox( + height: 22, + child: Text('Rechercher'), + ), + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.black), + onPressed: () { + _searchProvider.searchController.text = ''; + Navigator.of(context).pop(); + }), + ), + body: SafeArea( + child: Column(children: <Widget>[ + SizedBox(height: isTall ? 200 : 100), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 17), + child: TextField( + controller: _searchProvider.searchController, + autofocus: true, + maxLines: 1, + textAlign: TextAlign.left, + onChanged: (v) => _searchProvider.rebuildWidget(), + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + prefixIconConstraints: const BoxConstraints( + minHeight: 32, + ), + prefixIcon: const Padding( + padding: EdgeInsets.symmetric(horizontal: 17), + child: Image( + image: AssetImage('assets/loupe-noire.png'), + height: 35), + ), + border: OutlineInputBorder( + borderSide: + BorderSide(color: Colors.grey[500], width: 2), + borderRadius: BorderRadius.circular(8)), + focusedBorder: OutlineInputBorder( + borderSide: + BorderSide(color: Colors.grey[500], width: 2.5), + borderRadius: BorderRadius.circular(8), + ), + contentPadding: const EdgeInsets.all(20), + ), + style: const TextStyle( + fontSize: 20, + color: Colors.black, + fontWeight: FontWeight.w400, + ), + ), + ), + const Spacer(flex: 1), + SizedBox( + width: 410, + height: 70, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 4, + primary: orangeC, // background + onPrimary: Colors.white, // foreground + ), + onPressed: _searchProvider.searchController.text.length >= 2 + ? () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return const SearchResultScreen(); + }), + ); + } + : null, + child: const Text( + 'Rechercher', + style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600), + ), + ), + ), + const Spacer(flex: 1), + ]), + ), + )); + } +} diff --git a/lib/screens/search_result.dart b/lib/screens/search_result.dart new file mode 100644 index 0000000000000000000000000000000000000000..bcaa6c07670ec686e72c029edb0adea50a4099a1 --- /dev/null +++ b/lib/screens/search_result.dart @@ -0,0 +1,184 @@ +import 'package:flutter/services.dart'; +import 'package:gecko/globals.dart'; +import 'package:flutter/material.dart'; +import 'package:gecko/models/cesium_plus.dart'; +import 'package:gecko/models/g1_wallets_list.dart'; +import 'package:gecko/models/wallets_profiles.dart'; +import 'package:gecko/models/search.dart'; +import 'package:gecko/screens/wallet_view.dart'; +import 'package:provider/provider.dart'; + +class SearchResultScreen extends StatelessWidget { + const SearchResultScreen({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); + SearchProvider _searchProvider = + Provider.of<SearchProvider>(context, listen: false); + CesiumPlusProvider _cesiumPlusProvider = + Provider.of<CesiumPlusProvider>(context, listen: false); + WalletsProfilesProvider _walletsProfilesClass = + Provider.of<WalletsProfilesProvider>(context, listen: false); + + int keyID = 0; + double _avatarSize = 55; + + return Scaffold( + appBar: AppBar( + toolbarHeight: 60 * ratio, + title: const SizedBox( + height: 22, + child: Text('Résultats de votre recherche'), + ), + ), + body: SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: + Column(crossAxisAlignment: CrossAxisAlignment.start, children: < + Widget>[ + const SizedBox(height: 30), + RichText( + text: TextSpan( + style: TextStyle( + fontSize: 18, + color: Colors.grey[700], + ), + children: <TextSpan>[ + const TextSpan( + text: "Résultats pour ", + ), + TextSpan( + text: '"${_searchProvider.searchController.text}"', + style: const TextStyle(fontStyle: FontStyle.italic), + ), + ], + ), + ), + const SizedBox(height: 40), + const Text( + 'Dans la blockchain Äž1', + style: TextStyle(fontSize: 20), + ), + const SizedBox(height: 20), + FutureBuilder( + future: _searchProvider.searchBlockchain(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return Expanded( + child: ListView(children: <Widget>[ + for (G1WalletsList g1Wallet in snapshot.data) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 5), + child: ListTile( + key: Key('searchResult${keyID++}'), + horizontalTitleGap: 40, + contentPadding: const EdgeInsets.all(5), + leading: g1WalletsBox + .get(g1Wallet.pubkey) + ?.avatar != + null + ? ClipOval( + child: g1WalletsBox + .get(g1Wallet.pubkey) + .avatar) + : FutureBuilder( + future: _cesiumPlusProvider.getAvatar( + g1Wallet.pubkey, _avatarSize), + builder: (BuildContext context, + AsyncSnapshot<Image> _avatar) { + if (_avatar.connectionState != + ConnectionState.done || + _avatar.hasError) { + return Stack(children: [ + _cesiumPlusProvider + .defaultAvatar(_avatarSize), + Positioned( + top: 8, + right: 0, + width: 12, + height: 12, + child: CircularProgressIndicator( + strokeWidth: 1, + color: orangeC, + ), + ), + ]); + } + if (_avatar.hasData) { + g1WalletsBox + .get(g1Wallet.pubkey) + .avatar = _avatar.data; + return ClipOval(child: _avatar.data); + } else { + g1WalletsBox + .get(g1Wallet.pubkey) + .avatar = + _cesiumPlusProvider + .defaultAvatar(_avatarSize); + return _cesiumPlusProvider + .defaultAvatar(_avatarSize); + } + }), + title: Row(children: <Widget>[ + Text( + _walletsProfilesClass + .getShortPubkey(g1Wallet.pubkey), + style: const TextStyle( + fontSize: 18, + fontFamily: 'Monospace', + fontWeight: FontWeight.w500), + textAlign: TextAlign.center), + ]), + subtitle: Row(children: <Widget>[ + Text(g1Wallet?.id?.username ?? '', + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500), + textAlign: TextAlign.center), + ]), + dense: false, + isThreeLine: false, + onTap: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + _walletsProfilesClass.pubkey = + g1Wallet.pubkey; + return WalletViewScreen( + pubkey: g1Wallet.pubkey, + username: g1WalletsBox + .get(g1Wallet.pubkey) + ?.id + ?.username, + avatar: g1WalletsBox + .get(g1Wallet.pubkey) + ?.avatar, + ); + }), + ); + }), + ), + ]), + ); + } + return Center( + heightFactor: 5, + child: CircularProgressIndicator( + strokeWidth: 3, + backgroundColor: yellowC, + color: orangeC, + ), + ); + }, + ), + // Text( + // _searchProvider.searchResult.toString(), + // ) + ]), + ), + ), + ); + } +} diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart index 9494df2f429fdd8dce7f6aa902c3cebe73fd4b47..821106299d997088ee0443d601dee62aade21b59 100644 --- a/lib/screens/settings.dart +++ b/lib/screens/settings.dart @@ -1,13 +1,11 @@ import 'package:flutter/material.dart'; import 'package:dubp/dubp.dart'; import 'package:flutter/services.dart'; -import 'package:gecko/models/home.dart'; import 'package:gecko/models/my_wallets.dart'; import 'package:gecko/screens/myWallets/generate_wallets.dart'; import 'dart:io'; -import 'package:gecko/screens/myWallets/import_wallet.dart'; +import 'package:gecko/screens/myWallets/import_cesium_wallet.dart'; import 'package:gecko/globals.dart'; -import 'package:provider/provider.dart'; // ignore: must_be_immutable class SettingsScreen extends StatelessWidget { @@ -29,7 +27,6 @@ class SettingsScreen extends StatelessWidget { @override Widget build(BuildContext context) { SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); - HomeProvider _homeProvider = Provider.of<HomeProvider>(context); // getAppDirectory(); return Scaffold( @@ -94,9 +91,7 @@ class SettingsScreen extends StatelessWidget { ), onPressed: () async => { log.i('Suppression de tous les wallets'), - await _myWallets - .deleteAllWallet(context) - .then((v) => _homeProvider.rebuildWidget()) + await _myWallets.deleteAllWallet(context) }, child: const Text("EFFACER TOUS MES PORTEFEUILLES", style: TextStyle(fontSize: 20)))))), diff --git a/lib/screens/template_screen.dart b/lib/screens/template_screen.dart index 4759e7baceec8589e6dd1da6732165580989bc15..e8858c78b21cfad4b1e97f012fb9faf46d506ba1 100644 --- a/lib/screens/template_screen.dart +++ b/lib/screens/template_screen.dart @@ -1,6 +1,5 @@ import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; -import 'package:gecko/screens/home.dart'; import 'package:flutter/material.dart'; // import 'package:gecko/models/home.dart'; // import 'package:provider/provider.dart'; @@ -22,26 +21,6 @@ class TemplateScreen extends StatelessWidget { height: 22, child: Text('Template screen'), )), - floatingActionButton: SizedBox( - height: 80.0, - width: 80.0, - child: FittedBox( - child: FloatingActionButton( - heroTag: "tplButton", - onPressed: () => Navigator.push( - context, - MaterialPageRoute(builder: (context) { - return const HomeScreen(); - }), - ), - child: SizedBox( - height: 40.0, - width: 40.0, - child: Icon(Icons.home, color: Colors.grey[850]), - ), - backgroundColor: - floattingYellow, //smoothYellow, //Color.fromARGB(500, 204, 255, 255), - ))), body: SafeArea( child: Column(children: <Widget>[ const SizedBox(height: 20), @@ -58,30 +37,6 @@ class TemplateScreen extends StatelessWidget { color: Colors.black, fontWeight: FontWeight.w400)), const SizedBox(height: 20), - ElevatedButton( - style: ElevatedButton.styleFrom( - primary: yellowC, // background - onPrimary: Colors.black, // foreground - ), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute(builder: (context) { - return const HomeScreen(); - }), - ); - }, - child: const Text('Retour Accueil', - style: TextStyle(fontSize: 20))), - const SizedBox(height: 20), - GestureDetector( - onTap: () { - Navigator.popUntil( - context, - ModalRoute.withName('/'), - ); - }, - child: const Icon(Icons.home)) ]), )); } diff --git a/lib/screens/wallet_view.dart b/lib/screens/wallet_view.dart new file mode 100644 index 0000000000000000000000000000000000000000..c765b64eece1a550b502a5c1d418d4a261f19784 --- /dev/null +++ b/lib/screens/wallet_view.dart @@ -0,0 +1,502 @@ +import 'dart:ui'; +import 'package:flutter/services.dart'; +import 'package:gecko/globals.dart'; +import 'package:flutter/material.dart'; +import 'package:gecko/models/cesium_plus.dart'; +import 'package:gecko/models/my_wallets.dart'; +import 'package:gecko/models/wallet_data.dart'; +import 'package:gecko/models/wallets_profiles.dart'; +import 'package:gecko/models/queries.dart'; +import 'package:gecko/screens/avatar_fullscreen.dart'; +import 'package:gecko/screens/common_elements.dart'; +import 'package:gecko/screens/history.dart'; +import 'package:gecko/screens/myWallets/unlocking_wallet.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; +import 'package:provider/provider.dart'; + +class WalletViewScreen extends StatelessWidget { + const WalletViewScreen( + {@required this.pubkey, this.username, this.avatar, Key key}) + : super(key: key); + final String pubkey; + final String username; + final Image avatar; + final double buttonSize = 100; + final double buttonFontSize = 18; + + @override + Widget build(BuildContext context) { + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); + WalletsProfilesProvider _historyProvider = + Provider.of<WalletsProfilesProvider>(context, listen: false); + CesiumPlusProvider _cesiumPlusProvider = + Provider.of<CesiumPlusProvider>(context, listen: false); + + return Scaffold( + resizeToAvoidBottomInset: true, + appBar: AppBar( + elevation: 0, + toolbarHeight: 60 * ratio, + title: const SizedBox( + height: 22, + child: Text('Voir un portefeuille'), + ), + ), + body: SafeArea( + child: Column(children: <Widget>[ + headerProfileView(context, _historyProvider, _cesiumPlusProvider), + SizedBox(height: isTall ? 120 : 70), + Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ + Column(children: <Widget>[ + SizedBox( + height: buttonSize, + child: ClipOval( + child: Material( + color: const Color(0xffFFD58D), // button color + child: InkWell( + key: const Key('viewHistory'), + splashColor: orangeC, // inkwell color + child: const Padding( + padding: EdgeInsets.all(13), + child: Image( + image: AssetImage( + 'assets/walletOptions/clock.png'), + height: 90)), + onTap: () { + _historyProvider.nPage = 1; + Navigator.push( + context, + FaderTransition( + page: HistoryScreen( + pubkey: pubkey, + username: username ?? + g1WalletsBox.get(pubkey)?.username, + avatar: avatar ?? + g1WalletsBox.get(pubkey)?.avatar, + ), + isFast: false), + ); + }), + ), + ), + ), + const SizedBox(height: 9), + Text( + "Voir\nl'historique", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: buttonFontSize, fontWeight: FontWeight.w500), + ), + ]), + Column(children: <Widget>[ + SizedBox( + height: buttonSize, + child: ClipOval( + child: Material( + color: const Color(0xffFFD58D), // button color + child: InkWell( + key: const Key('copyKey'), + splashColor: orangeC, // inkwell color + child: const Padding( + padding: EdgeInsets.all(20), + child: Image( + image: AssetImage('assets/copy_key.png'), + height: 90)), + onTap: () { + Clipboard.setData(ClipboardData(text: pubkey)); + _historyProvider.snackCopyKey(context); + }), + ), + ), + ), + const SizedBox(height: 9), + Text( + "Copier\nla clef", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: buttonFontSize, fontWeight: FontWeight.w500), + ), + ]), + ]), + // FutureBuilder( + // future: _walletOptions.generateQRcode(_historyProvider.pubkey), + // builder: (context, snapshot) { + // return snapshot.data != null + // ? GestureDetector( + // key: const Key('openQrcode'), + // onTap: () { + // Navigator.push( + // context, + // MaterialPageRoute(builder: (context) { + // return AvatarFullscreen( + // Image.memory(snapshot.data), + // title: 'QrCode du profil', + // color: Colors.white, + // ); + // }), + // ); + // }, + // child: Image.memory(snapshot.data, height: 60 * ratio), + // ) + // : const Text('-', style: TextStyle(fontSize: 20)); + // }, + // ), + const Spacer(), + Container( + height: buttonSize, + decoration: BoxDecoration( + color: const Color(0xff7c94b6), + borderRadius: const BorderRadius.all(Radius.circular(100)), + border: Border.all( + color: const Color(0xFF6c4204), + width: 4, + ), + ), + child: ClipOval( + child: Material( + color: orangeC, // button color + child: InkWell( + key: const Key('pay'), + splashColor: yellowC, // inkwell color + child: const Padding( + padding: EdgeInsets.all(14), + child: Image( + image: AssetImage('assets/vector_white.png'), + )), + onTap: () { + paymentPopup(context, _historyProvider); + }), + ), + ), + ), + const SizedBox(height: 9), + Text( + "Faire un\nvirement", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: buttonFontSize, fontWeight: FontWeight.w500), + ), + SizedBox(height: isTall ? 120 : 70) + ]), + )); + } + + void paymentPopup( + BuildContext context, WalletsProfilesProvider _walletViewProvider) { + // WalletsProfilesProvider _walletViewProvider = + // Provider.of<WalletsProfilesProvider>(context); + const double shapeSize = 20; + MyWalletsProvider _myWalletProvider = MyWalletsProvider(); + WalletData defaultWallet = + _myWalletProvider.getDefaultWallet(configBox.get('currentChest')); + + showModalBottomSheet<void>( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topRight: Radius.circular(shapeSize), + topLeft: Radius.circular(shapeSize), + ), + ), + isScrollControlled: true, + context: context, + builder: (BuildContext context) { + return Padding( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).viewInsets.bottom), + child: Container( + height: 400, + decoration: const ShapeDecoration( + color: Color(0xffffeed1), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topRight: Radius.circular(shapeSize), + topLeft: Radius.circular(shapeSize), + ), + ), + ), + child: Padding( + padding: const EdgeInsets.all(24), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: <Widget>[ + const Text( + 'Effectuer un virement', + style: TextStyle( + fontSize: 26, fontWeight: FontWeight.w700), + ), + const SizedBox(height: 20), + Text( + 'Saisissez dans le champ ci-dessous le montant à virer de ... vers ...', + style: TextStyle( + fontSize: 19, + fontWeight: FontWeight.w500, + color: Colors.grey[600]), + ), + const Spacer(), + Center( + child: Column(children: <Widget>[ + TextField( + controller: _walletViewProvider.payAmount, + autofocus: true, + maxLines: 1, + textAlign: TextAlign.center, + keyboardType: TextInputType.number, + inputFormatters: <TextInputFormatter>[ + FilteringTextInputFormatter.allow( + RegExp(r'^\d+\.?\d{0,2}')), + ], + // onChanged: (v) => _searchProvider.rebuildWidget(), + decoration: InputDecoration( + hintText: '0.00', + suffix: const Text('DU/Äž1'), + filled: true, + fillColor: Colors.transparent, + // border: OutlineInputBorder( + // borderSide: + // BorderSide(color: Colors.grey[500], width: 2), + // borderRadius: BorderRadius.circular(8)), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.grey[500], width: 2), + borderRadius: BorderRadius.circular(8), + ), + contentPadding: const EdgeInsets.all(20), + ), + style: const TextStyle( + fontSize: 40, + color: Colors.black, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 40), + // const Spacer(), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 4, + primary: orangeC, // background + onPrimary: Colors.white, // foreground + ), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return UnlockingWallet( + wallet: defaultWallet, action: "pay"); + }, + ), + ); + }, + child: const Text( + 'Effectuer le virement', + style: TextStyle( + fontSize: 20, fontWeight: FontWeight.w600), + ), + ), + ), + const SizedBox(height: 20), + ]), + ), + ]), + ), + ), + ); + }).then((value) => _walletViewProvider.payAmount.text = ''); + } + + Widget headerProfileView( + BuildContext context, + WalletsProfilesProvider _historyProvider, + CesiumPlusProvider _cesiumPlusProvider) { + const double _avatarSize = 140; + + return Column(children: <Widget>[ + Container( + height: 10, + color: yellowC, + ), + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + yellowC, + const Color(0xFFE7811A), + ], + )), + child: Padding( + padding: const EdgeInsets.only(left: 30, right: 40), + child: Row(children: <Widget>[ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: <Widget>[ + Row(children: [ + GestureDetector( + key: const Key('copyPubkey'), + onTap: () { + Clipboard.setData(ClipboardData(text: pubkey)); + _historyProvider.snackCopyKey(context); + }, + child: Text( + _historyProvider.getShortPubkey(pubkey), + style: const TextStyle( + fontSize: 30, + fontWeight: FontWeight.w800, + ), + ), + ), + ]), + const SizedBox(height: 10), + if (username == null && + g1WalletsBox.get(pubkey)?.username == null) + Query( + options: QueryOptions( + document: gql(getId), + variables: { + 'pubkey': pubkey, + }, + ), + builder: (QueryResult result, + {VoidCallback refetch, FetchMore fetchMore}) { + if (result.isLoading || result.hasException) { + return const Text('...'); + } else if (result.data['idty'] == null || + result.data['idty']['username'] == null) { + g1WalletsBox.get(pubkey)?.username = ''; + return const Text(''); + } else { + g1WalletsBox.get(pubkey)?.username = + result?.data['idty']['username'] ?? ''; + return SizedBox( + width: 230, + child: Text( + result?.data['idty']['username'] ?? '', + style: const TextStyle( + fontSize: 27, + color: Color(0xff814C00), + ), + ), + ); + } + }, + ), + if (username == null && + g1WalletsBox.get(pubkey)?.username != null) + SizedBox( + width: 230, + child: Text( + g1WalletsBox.get(pubkey)?.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: 25), + FutureBuilder( + future: _cesiumPlusProvider.getName(pubkey), + initialData: '...', + builder: (context, snapshot) { + return SizedBox( + width: 230, + child: Text( + snapshot.data ?? '-', + style: const TextStyle( + fontSize: 18, color: Colors.black), + ), + ); + }), + const SizedBox(height: 30), + ]), + const Spacer(), + Column(children: <Widget>[ + if (avatar == null) + FutureBuilder( + future: _cesiumPlusProvider.getAvatar(pubkey, _avatarSize), + builder: + (BuildContext context, AsyncSnapshot<Image> _avatar) { + if (_avatar.connectionState != ConnectionState.done) { + return Stack(children: [ + ClipOval( + child: + _cesiumPlusProvider.defaultAvatar(_avatarSize), + ), + Positioned( + top: 15, + right: 45, + width: 51, + height: 51, + child: CircularProgressIndicator( + strokeWidth: 5, + color: orangeC, + ), + ), + ]); + } + if (_avatar.hasData) { + return GestureDetector( + key: const Key('openAvatar'), + onTap: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return AvatarFullscreen(_avatar.data); + }), + ); + }, + child: ClipOval( + child: Image( + image: _avatar.data.image, + height: _avatarSize, + fit: BoxFit.cover, + ), + ), + ); + } + return ClipOval( + child: _cesiumPlusProvider.defaultAvatar(_avatarSize), + ); + }), + if (avatar != null) + GestureDetector( + key: const Key('openAvatar'), + onTap: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return AvatarFullscreen(avatar); + }), + ); + }, + child: ClipOval( + child: Image( + image: avatar.image, + height: _avatarSize, + fit: BoxFit.cover, + ), + ), + ), + const SizedBox(height: 25), + ]), + ]), + ), + ), + ]); + } +} diff --git a/pubspec.lock b/pubspec.lock index 49c04fe7f4f05580795277d43d1a4d55e53d6bd5..d5101a8202f4eecce23ea7e2d37a6f58342b6dd6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -267,6 +267,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.5.6" + dio: + dependency: "direct main" + description: + name: dio + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.4" dubp: dependency: "direct main" description: @@ -544,6 +551,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.4.1" + infinite_scroll_pagination: + dependency: "direct main" + description: + name: infinite_scroll_pagination + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" integration_test: dependency: "direct dev" description: flutter @@ -878,6 +892,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" + pull_to_refresh: + dependency: "direct main" + description: + name: pull_to_refresh + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" qr: dependency: transitive description: @@ -1002,6 +1023,13 @@ packages: description: flutter source: sdk version: "0.0.99" + sliver_tools: + dependency: transitive + description: + name: sliver_tools + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.5" source_gen: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 7de7c38df86700c3d8f300b794fa738de609f2f2..b5124f7e0740feb179b8e3a98787b6229e939c67 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,13 +5,16 @@ 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.2+11 +version: 0.0.3+13 environment: sdk: ">=2.7.0 <3.0.0" dependencies: + assorted_layout_widgets: ^5.2.1 bubble: ^1.2.1 + carousel_slider: ^4.0.0 + confirm_dialog: ^1.0.0 crypto: ^3.0.1 dubp: path: packages/dubp_rs @@ -21,14 +24,16 @@ dependencies: flutter_driver: sdk: flutter flutter_launcher_icons: ^0.9.2 + flutter_lints: ^1.0.4 flutter_logs: ^2.1.4 flutter_svg: ^0.22.0 graphql_flutter: ^5.0.0 hive: ^2.0.4 hive_flutter: ^1.1.0 - http: ^0.13.0 + http: ^0.13.4 image_gallery_saver: ^1.6.9 image_picker: ^0.8.4 + infinite_scroll_pagination: ^3.1.0 intl: ^0.17.0 jdenticon_dart: ^2.0.0 logger: ^1.1.0 @@ -38,7 +43,7 @@ dependencies: permission_handler: 8.1.6 pin_code_fields: ^7.3.0 printing: ^5.6.0 - provider: ^6.0.0 + provider: ^6.0.1 qrscan: ^0.3.2 responsive_builder: ^0.4.1 responsive_framework: ^0.1.4 @@ -48,15 +53,11 @@ dependencies: super_tooltip: ^1.0.1 sync_http: ^0.3.0 test: ^1.17.10 - # test_api: ^0.4.7 - # test: ^1.19.3 truncate: ^3.0.1 unorm_dart: ^0.2.0 xml: ^5.3.0 - assorted_layout_widgets: ^5.2.1 - carousel_slider: ^4.0.0 - flutter_lints: ^1.0.4 - confirm_dialog: ^1.0.0 + pull_to_refresh: ^2.0.0 + dio: ^4.0.4 flutter_icons: android: "ic_launcher" @@ -65,12 +66,12 @@ flutter_icons: cupertino_icons: ^1.0.0 dev_dependencies: + build_runner: ^2.1.2 flutter_test: sdk: flutter + hive_generator: ^1.1.1 integration_test: sdk: flutter - hive_generator: ^1.1.1 - build_runner: ^2.1.2 # The following section is specific to Flutter. flutter: @@ -80,6 +81,7 @@ flutter: - images/ - config/gva_endpoints.json - assets/ + - assets/home/ - assets/customs/ - assets/avatars/ - assets/chests/ diff --git a/scripts/build-apk.sh b/scripts/build-apk.sh index 86a0ac3b6fde253f524e09a8ae3f78616fbfdca5..368a81e14220acabc0b8536c7cdbe42301485f72 100755 --- a/scripts/build-apk.sh +++ b/scripts/build-apk.sh @@ -20,8 +20,8 @@ if [[ $1 == "bundle" ]]; then flutter build appbundle --release --target-platform android-arm,android-arm64 --build-name $VERSION --build-number $BUILD else # flutter build apk --release --split-per-abi --target-platform android-arm,android-arm64 --build-name $VERSION --build-number $BUILD -# flutter build apk --release --split-per-abi --build-name $VERSION --build-number $BUILD - flutter build apk --release --build-name $VERSION --build-number $BUILD + flutter build apk --release --split-per-abi --build-name $VERSION --build-number $BUILD +# flutter build apk --release --build-name $VERSION --build-number $BUILD fi if [[ -d $HOME/Téléchargements ]]; then diff --git a/test_driver/app_test.dart b/test_driver/app_test.dart index e9f4c7618e9e1c203a478b9c753e056aeb2a5d6d..0efe479d18f4ece1df5f424eec3b32a27170cc64 100644 --- a/test_driver/app_test.dart +++ b/test_driver/app_test.dart @@ -217,8 +217,9 @@ void main() { {timeout = Timeout.none}) async { expect(await getText('step9'), "Super !\n\nJe vais maintenant créer votre code secret. \n\nVotre code secret chiffre votre trousseau de clefs, ce qui le rend inutilisable par d’autres, par exemple si vous perdez votre téléphone ou si on vous le vole."); - + await sleep(800); await tapOn('goStep10'); + await sleep(50); await tapOn('goStep11'); while (await getText('generatedPin') == '') {