Merge pull request #323 from cypherstack/simplex
Buy: validation, error handling, wallet view and color fixes, and remove non-Stack coins from buy list
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 336.41 337.42"><defs><style>.cls-1{fill:#f0b90b;stroke:#f0b90b;}</style></defs><title>Asset 1</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M168.2.71l41.5,42.5L105.2,147.71l-41.5-41.5Z"/><path class="cls-1" d="M231.2,63.71l41.5,42.5L105.2,273.71l-41.5-41.5Z"/><path class="cls-1" d="M42.2,126.71l41.5,42.5-41.5,41.5L.7,169.21Z"/><path class="cls-1" d="M294.2,126.71l41.5,42.5L168.2,336.71l-41.5-41.5Z"/></g></g></svg>
|
|
Before Width: | Height: | Size: 528 B |
|
@ -1 +0,0 @@
|
||||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2500 2500"><title>cosmos-atom-logo</title><circle cx="1250" cy="1250" r="1250" style="fill:#2e3148"/><circle cx="1250" cy="1250" r="725.31" style="fill:#1b1e36"/><path d="M1252.57,159.47c-134.93,0-244.34,489.4-244.34,1093.11s109.41,1093.11,244.34,1093.11,244.34-489.4,244.34-1093.11S1387.5,159.47,1252.57,159.47ZM1269.44,2284c-15.43,20.58-30.86,5.14-30.86,5.14-62.14-72-93.21-205.76-93.21-205.76-108.69-349.79-82.82-1100.82-82.82-1100.82,51.08-596.24,144-737.09,175.62-768.36a19.29,19.29,0,0,1,24.74-2c45.88,32.51,84.36,168.47,84.36,168.47,113.63,421.81,103.34,817.9,103.34,817.9,10.29,344.65-56.94,730.45-56.94,730.45C1341.92,2222.22,1269.44,2284,1269.44,2284Z" style="fill:#6f7390"/><path d="M2200.72,708.59c-67.18-117.08-546.09,31.58-1070,332s-893.47,638.89-826.34,755.92,546.09-31.58,1070-332,893.47-638.89,826.34-755.92h0ZM366.36,1780.45c-25.72-3.24-19.91-24.38-19.91-24.38C378,1666.36,478.4,1572.84,478.4,1572.84c249.43-268.36,913.79-619.65,913.79-619.65,542.54-252.42,711.06-241.77,753.81-230a19.29,19.29,0,0,1,14,20.58c-5.14,56-104.17,157-104.17,157C1746.71,1209.36,1398,1397.58,1398,1397.58c-293.83,180.5-661.93,314.09-661.93,314.09-280.09,100.93-369.7,68.78-369.7,68.78h0Z" style="fill:#6f7390"/><path d="M2198.35,1800.41c67.7-116.77-300.93-456.79-823-759.47S374.43,587.76,306.79,704.73s300.93,456.79,823.3,759.47S2130.71,1917.39,2198.35,1800.41ZM351.65,749.85c-10-23.71,11.11-29.42,11.11-29.42C456.22,702.78,587.5,743,587.5,743c357.15,81.33,994,480.25,994,480.25,490.33,343.11,565.53,494.24,576.8,537.14a19.29,19.29,0,0,1-10.7,22.43c-51.13,23.41-188.07-11.47-188.07-11.47-422.07-113.17-759.62-320.52-759.62-320.52-303.29-163.58-603.19-415.28-603.19-415.28-227.88-191.87-245-285.44-245-285.44Z" style="fill:#6f7390"/><circle cx="1250" cy="1250" r="128.6" style="fill:#b7b9c8"/><ellipse cx="1777.26" cy="756.17" rx="74.59" ry="77.16" style="fill:#b7b9c8"/><ellipse cx="552.98" cy="1018.52" rx="74.59" ry="77.16" style="fill:#b7b9c8"/><ellipse cx="1098.25" cy="1965.02" rx="74.59" ry="77.16" style="fill:#b7b9c8"/></svg>
|
|
Before Width: | Height: | Size: 2.1 KiB |
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<!-- Creator: CorelDRAW 2019 (64-Bit) -->
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="100%" height="100%" version="1.1" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 444.44 444.44" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
|
|
||||||
<g id="Layer_x0020_1">
|
|
||||||
<metadata id="CorelCorpID_0Corel-Layer"/>
|
|
||||||
<path fill="#F5AC37" fill-rule="nonzero" d="M222.22 0c122.74,0 222.22,99.5 222.22,222.22 0,122.74 -99.48,222.22 -222.22,222.22 -122.72,0 -222.22,-99.49 -222.22,-222.22 0,-122.72 99.5,-222.22 222.22,-222.22z"/>
|
|
||||||
<path fill="#FEFEFD" fill-rule="nonzero" d="M230.41 237.91l84.44 0c1.8,0 2.65,0 2.78,-2.36 0.69,-8.59 0.69,-17.23 0,-25.83 0,-1.67 -0.83,-2.36 -2.64,-2.36l-168.05 0c-2.08,0 -2.64,0.69 -2.64,2.64l0 24.72c0,3.19 0,3.19 3.33,3.19l82.78 0zm77.79 -59.44c0.24,-0.63 0.24,-1.32 0,-1.94 -1.41,-3.07 -3.08,-6 -5.02,-8.75 -2.92,-4.7 -6.36,-9.03 -10.28,-12.92 -1.85,-2.35 -3.99,-4.46 -6.39,-6.25 -12.02,-10.23 -26.31,-17.47 -41.67,-21.11 -7.75,-1.74 -15.67,-2.57 -23.61,-2.5l-74.58 0c-2.08,0 -2.36,0.83 -2.36,2.64l0 49.3c0,2.08 0,2.64 2.64,2.64l160.27 0c0,0 1.39,-0.28 1.67,-1.11l-0.68 0zm0 88.33c-2.36,-0.26 -4.74,-0.26 -7.1,0l-154.02 0c-2.08,0 -2.78,0 -2.78,2.78l0 48.2c0,2.22 0,2.78 2.78,2.78l71.11 0c3.4,0.26 6.8,0.02 10.13,-0.69 10.32,-0.74 20.47,-2.98 30.15,-6.67 3.52,-1.22 6.92,-2.81 10.13,-4.72l0.97 0c16.67,-8.67 30.21,-22.29 38.75,-39.01 0,0 0.97,-2.1 -0.12,-2.65zm-191.81 78.75l0 -0.83 0 -32.36 0 -10.97 0 -32.64c0,-1.81 0,-2.08 -2.22,-2.08l-30.14 0c-1.67,0 -2.36,0 -2.36,-2.22l0 -26.39 32.22 0c1.8,0 2.5,0 2.5,-2.36l0 -26.11c0,-1.67 0,-2.08 -2.22,-2.08l-30.14 0c-1.67,0 -2.36,0 -2.36,-2.22l0 -24.44c0,-1.53 0,-1.94 2.22,-1.94l29.86 0c2.08,0 2.64,0 2.64,-2.64l0 -74.86c0,-2.22 0,-2.78 2.78,-2.78l104.16 0c7.56,0.3 15.07,1.13 22.5,2.5 15.31,2.83 30.02,8.3 43.47,16.11 8.92,5.25 17.13,11.59 24.44,18.89 5.5,5.71 10.46,11.89 14.86,18.47 4.37,6.67 8,13.8 10.85,21.25 0.35,1.94 2.21,3.25 4.15,2.92l24.86 0c3.19,0 3.19,0 3.33,3.06l0 22.78c0,2.22 -0.83,2.78 -3.06,2.78l-19.17 0c-1.94,0 -2.5,0 -2.36,2.5 0.76,8.46 0.76,16.95 0,25.41 0,2.36 0,2.64 2.65,2.64l21.93 0c0.97,1.25 0,2.5 0,3.76 0.14,1.61 0.14,3.24 0,4.85l0 16.81c0,2.36 -0.69,3.06 -2.78,3.06l-26.25 0c-1.83,-0.35 -3.61,0.82 -4.03,2.64 -6.25,16.25 -16.25,30.82 -29.17,42.5 -4.72,4.25 -9.68,8.25 -14.86,11.94 -5.56,3.2 -10.97,6.53 -16.67,9.17 -10.49,4.72 -21.49,8.2 -32.78,10.41 -10.72,1.92 -21.59,2.79 -32.5,2.64l-96.39 0 0 -0.14z"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.7 KiB |
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 513.4 416.8"><defs><style>.cls-1{fill:#008de4;}</style></defs><title>d</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M336.25,0H149.35l-15.5,86.6,168.7.2c83.1,0,107.6,30.2,106.9,80.2-.4,25.6-11.5,69-16.3,83.1-12.8,37.5-39.1,80.2-137.7,80.1l-164-.1L76,416.8h186.5c65.8,0,93.7-7.7,123.4-21.3,65.7-30.5,104.8-95.3,120.5-179.9C529.65,89.6,500.65,0,336.25,0"/><path class="cls-1" d="M68.7,164.9c-49,0-56,31.9-60.6,51.2C2,241.3,0,251.6,0,251.6H191.4c49,0,56-31.9,60.6-51.2,6.1-25.2,8.1-35.5,8.1-35.5Z"/></g></g></svg>
|
|
Before Width: | Height: | Size: 621 B |
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 33.2 50"><title>Asset 1</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path d="M16.6,0,4.9,16.1,0,39.9,16.6,50,33.2,39.9,28.2,16ZM2.7,38.8,6.4,20.7l8.4,25.5ZM7.6,16.6l9-12.4,9,12.4-9,27.2ZM18.3,46.2l8.4-25.5,3.7,18.1Z"/></g></g></svg>
|
|
Before Width: | Height: | Size: 322 B |
|
@ -1,18 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<!-- Creator: CorelDRAW 2019 (64-Bit) -->
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="100%" height="100%" version="1.1" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 784.37 1277.39" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
|
|
||||||
<g id="Layer_x0020_1">
|
|
||||||
<metadata id="CorelCorpID_0Corel-Layer"/>
|
|
||||||
<g id="_1421394342400">
|
|
||||||
<g>
|
|
||||||
<polygon fill="#343434" fill-rule="nonzero" points="392.07,0 383.5,29.11 383.5,873.74 392.07,882.29 784.13,650.54 "/>
|
|
||||||
<polygon fill="#8C8C8C" fill-rule="nonzero" points="392.07,0 -0,650.54 392.07,882.29 392.07,472.33 "/>
|
|
||||||
<polygon fill="#3C3C3B" fill-rule="nonzero" points="392.07,956.52 387.24,962.41 387.24,1263.28 392.07,1277.38 784.37,724.89 "/>
|
|
||||||
<polygon fill="#8C8C8C" fill-rule="nonzero" points="392.07,1277.38 392.07,956.52 -0,724.89 "/>
|
|
||||||
<polygon fill="#141414" fill-rule="nonzero" points="392.07,882.29 784.13,650.54 392.07,472.33 "/>
|
|
||||||
<polygon fill="#393939" fill-rule="nonzero" points="0,650.54 392.07,882.29 392.07,472.33 "/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.3 KiB |
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 424"><defs><style>.cls-1{fill:#23292f;}</style></defs><title>x</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M437,0h74L357,152.48c-55.77,55.19-146.19,55.19-202,0L.94,0H75L192,115.83a91.11,91.11,0,0,0,127.91,0Z"/><path class="cls-1" d="M74.05,424H0L155,270.58c55.77-55.19,146.19-55.19,202,0L512,424H438L320,307.23a91.11,91.11,0,0,0-127.91,0Z"/></g></g></svg>
|
|
Before Width: | Height: | Size: 472 B |
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 236.36 200"><title>Asset 1</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path d="M203,26.16l-28.46,14.5-137.43,70a82.49,82.49,0,0,1-.7-10.69A81.87,81.87,0,0,1,158.2,28.6l16.29-8.3,2.43-1.24A100,100,0,0,0,18.18,100q0,3.82.29,7.61a18.19,18.19,0,0,1-9.88,17.58L0,129.57V150l25.29-12.89,0,0,8.19-4.18,8.07-4.11v0L186.43,55l16.28-8.29,33.65-17.15V9.14Z"/><path d="M236.36,50,49.78,145,33.5,153.31,0,170.38v20.41l33.27-16.95,28.46-14.5L199.3,89.24A83.45,83.45,0,0,1,200,100,81.87,81.87,0,0,1,78.09,171.36l-1,.53-17.66,9A100,100,0,0,0,218.18,100c0-2.57-.1-5.14-.29-7.68a18.2,18.2,0,0,1,9.87-17.58l8.6-4.38Z"/></g></g></svg>
|
|
Before Width: | Height: | Size: 705 B |
|
@ -1 +0,0 @@
|
||||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 339.43 295.27"><title>tether-usdt-logo</title><path d="M62.15,1.45l-61.89,130a2.52,2.52,0,0,0,.54,2.94L167.95,294.56a2.55,2.55,0,0,0,3.53,0L338.63,134.4a2.52,2.52,0,0,0,.54-2.94l-61.89-130A2.5,2.5,0,0,0,275,0H64.45a2.5,2.5,0,0,0-2.3,1.45h0Z" style="fill:#50af95;fill-rule:evenodd"/><path d="M191.19,144.8v0c-1.2.09-7.4,0.46-21.23,0.46-11,0-18.81-.33-21.55-0.46v0c-42.51-1.87-74.24-9.27-74.24-18.13s31.73-16.25,74.24-18.15v28.91c2.78,0.2,10.74.67,21.74,0.67,13.2,0,19.81-.55,21-0.66v-28.9c42.42,1.89,74.08,9.29,74.08,18.13s-31.65,16.24-74.08,18.12h0Zm0-39.25V79.68h59.2V40.23H89.21V79.68H148.4v25.86c-48.11,2.21-84.29,11.74-84.29,23.16s36.18,20.94,84.29,23.16v82.9h42.78V151.83c48-2.21,84.12-11.73,84.12-23.14s-36.09-20.93-84.12-23.15h0Zm0,0h0Z" style="fill:#fff;fill-rule:evenodd"/></svg>
|
|
Before Width: | Height: | Size: 874 B |
|
@ -1 +0,0 @@
|
||||||
<svg id="Calque_1" data-name="Calque 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#ff060a;}</style></defs><title>tron</title><g id="tron"><path class="cls-1" d="M61.55,19.28c-3-2.77-7.15-7-10.53-10l-.2-.14a3.82,3.82,0,0,0-1.11-.62l0,0C41.56,7,3.63-.09,2.89,0a1.4,1.4,0,0,0-.58.22L2.12.37a2.23,2.23,0,0,0-.52.84l-.05.13v.71l0,.11C5.82,14.05,22.68,53,26,62.14c.2.62.58,1.8,1.29,1.86h.16c.38,0,2-2.14,2-2.14S58.41,26.74,61.34,23a9.46,9.46,0,0,0,1-1.48A2.41,2.41,0,0,0,61.55,19.28ZM36.88,23.37,49.24,13.12l7.25,6.68Zm-4.8-.67L10.8,5.26l34.43,6.35ZM34,27.27l21.78-3.51-24.9,30ZM7.91,7,30.3,26,27.06,53.78Z"/></g></svg>
|
|
Before Width: | Height: | Size: 651 B |
|
@ -17,6 +17,7 @@ import 'package:stackwallet/pages/buy_view/sub_widgets/fiat_selection_view.dart'
|
||||||
import 'package:stackwallet/pages/exchange_view/choose_from_stack_view.dart';
|
import 'package:stackwallet/pages/exchange_view/choose_from_stack_view.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart';
|
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart';
|
||||||
import 'package:stackwallet/providers/providers.dart';
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
|
import 'package:stackwallet/services/buy/buy_response.dart';
|
||||||
import 'package:stackwallet/services/buy/simplex/simplex_api.dart';
|
import 'package:stackwallet/services/buy/simplex/simplex_api.dart';
|
||||||
import 'package:stackwallet/utilities/address_utils.dart';
|
import 'package:stackwallet/utilities/address_utils.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
|
@ -47,10 +48,13 @@ import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||||
class BuyForm extends ConsumerStatefulWidget {
|
class BuyForm extends ConsumerStatefulWidget {
|
||||||
const BuyForm({
|
const BuyForm({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
this.coin,
|
||||||
this.clipboard = const ClipboardWrapper(),
|
this.clipboard = const ClipboardWrapper(),
|
||||||
this.scanner = const BarcodeScannerWrapper(),
|
this.scanner = const BarcodeScannerWrapper(),
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final Coin? coin;
|
||||||
|
|
||||||
final ClipboardInterface clipboard;
|
final ClipboardInterface clipboard;
|
||||||
final BarcodeScannerInterface scanner;
|
final BarcodeScannerInterface scanner;
|
||||||
|
|
||||||
|
@ -59,6 +63,8 @@ class BuyForm extends ConsumerStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BuyFormState extends ConsumerState<BuyForm> {
|
class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
|
late final Coin? coin;
|
||||||
|
|
||||||
late final ClipboardInterface clipboard;
|
late final ClipboardInterface clipboard;
|
||||||
late final BarcodeScannerInterface scanner;
|
late final BarcodeScannerInterface scanner;
|
||||||
|
|
||||||
|
@ -75,8 +81,8 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
List<Fiat>? fiats;
|
List<Fiat>? fiats;
|
||||||
String? _address;
|
String? _address;
|
||||||
|
|
||||||
Fiat? selectedFiat;
|
static Fiat? selectedFiat;
|
||||||
Crypto? selectedCrypto;
|
static Crypto? selectedCrypto;
|
||||||
SimplexQuote quote = SimplexQuote(
|
SimplexQuote quote = SimplexQuote(
|
||||||
crypto: Crypto.fromJson({'ticker': 'BTC', 'name': 'Bitcoin'}),
|
crypto: Crypto.fromJson({'ticker': 'BTC', 'name': 'Bitcoin'}),
|
||||||
fiat: Fiat.fromJson({'ticker': 'USD', 'name': 'United States Dollar'}),
|
fiat: Fiat.fromJson({'ticker': 'USD', 'name': 'United States Dollar'}),
|
||||||
|
@ -87,13 +93,20 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
buyWithFiat: true,
|
buyWithFiat: true,
|
||||||
); // TODO enum this or something
|
); // TODO enum this or something
|
||||||
|
|
||||||
bool buyWithFiat = true;
|
static bool buyWithFiat = true;
|
||||||
bool _addressToggleFlag = false;
|
bool _addressToggleFlag = false;
|
||||||
bool _hovering1 = false;
|
bool _hovering1 = false;
|
||||||
bool _hovering2 = false;
|
bool _hovering2 = false;
|
||||||
|
|
||||||
Decimal minFiat = Decimal.fromInt(50);
|
// TODO actually check USD min and max, these could get updated by Simplex
|
||||||
Decimal maxFiat = Decimal.fromInt(20000);
|
static Decimal minFiat = Decimal.fromInt(50);
|
||||||
|
static Decimal maxFiat = Decimal.fromInt(20000);
|
||||||
|
|
||||||
|
// We can't get crypto min and max without asking for a quote
|
||||||
|
static Decimal minCrypto = Decimal.parse((0.00000001)
|
||||||
|
.toString()); // lol how to go from double->Decimal more easily?
|
||||||
|
static Decimal maxCrypto = Decimal.parse((10000.00000000).toString());
|
||||||
|
static String boundedCryptoTicker = '';
|
||||||
|
|
||||||
void fiatFieldOnChanged(String value) async {}
|
void fiatFieldOnChanged(String value) async {}
|
||||||
|
|
||||||
|
@ -125,6 +138,13 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
coins: ref.read(simplexProvider).supportedCryptos,
|
coins: ref.read(simplexProvider).supportedCryptos,
|
||||||
onSelected: (crypto) {
|
onSelected: (crypto) {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
if (selectedCrypto?.ticker != _BuyFormState.boundedCryptoTicker) {
|
||||||
|
// Reset crypto mins and maxes ... we don't know these bounds until we request a quote
|
||||||
|
_BuyFormState.minCrypto = Decimal.parse((0.00000001)
|
||||||
|
.toString()); // lol how to go from double->Decimal more easily?
|
||||||
|
_BuyFormState.maxCrypto =
|
||||||
|
Decimal.parse((10000.00000000).toString());
|
||||||
|
}
|
||||||
selectedCrypto = crypto;
|
selectedCrypto = crypto;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -357,9 +377,10 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget? getIconForTicker(String ticker) {
|
Widget? getIconForTicker(String ticker) {
|
||||||
String? iconAsset = isStackCoin(ticker)
|
String? iconAsset = /*isStackCoin(ticker)
|
||||||
? Assets.svg.iconFor(coin: coinFromTickerCaseInsensitive(ticker))
|
?*/
|
||||||
: Assets.svg.buyIconFor(ticker);
|
Assets.svg.iconFor(coin: coinFromTickerCaseInsensitive(ticker));
|
||||||
|
// : Assets.svg.buyIconFor(ticker);
|
||||||
return (iconAsset != null)
|
return (iconAsset != null)
|
||||||
? SvgPicture.asset(iconAsset, height: 20, width: 20)
|
? SvgPicture.asset(iconAsset, height: 20, width: 20)
|
||||||
: null;
|
: null;
|
||||||
|
@ -394,22 +415,111 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
buyWithFiat: buyWithFiat,
|
buyWithFiat: buyWithFiat,
|
||||||
);
|
);
|
||||||
|
|
||||||
await _loadQuote(quote);
|
BuyResponse<SimplexQuote> quoteResponse = await _loadQuote(quote);
|
||||||
shouldPop = true;
|
shouldPop = true;
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
Navigator.of(context, rootNavigator: isDesktop).pop();
|
Navigator.of(context, rootNavigator: isDesktop).pop();
|
||||||
}
|
}
|
||||||
quote = ref.read(simplexProvider).quote;
|
if (quoteResponse.exception == null) {
|
||||||
|
quote = quoteResponse.value as SimplexQuote;
|
||||||
|
|
||||||
if (quote.id != 'id' && quote.id != 'someID') {
|
if (quote.id != 'id' && quote.id != 'someID') {
|
||||||
// TODO detect default quote better
|
// TODO detect default quote better
|
||||||
await _showFloatingBuyQuotePreviewSheet(
|
await _showFloatingBuyQuotePreviewSheet(
|
||||||
quote: ref.read(simplexProvider).quote,
|
quote: ref.read(simplexProvider).quote,
|
||||||
onSelected: (quote) {
|
onSelected: (quote) {
|
||||||
// TODO launch URL
|
// TODO launch URL
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
await showDialog<dynamic>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: true,
|
||||||
|
builder: (context) {
|
||||||
|
if (isDesktop) {
|
||||||
|
return DesktopDialog(
|
||||||
|
maxWidth: 450,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(32),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Simplex API unresponsive",
|
||||||
|
style: STextStyles.desktopH3(context),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Simplex API unresponsive, please try again later",
|
||||||
|
style: STextStyles.smallMed14(context),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 56,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Spacer(),
|
||||||
|
Expanded(
|
||||||
|
child: PrimaryButton(
|
||||||
|
buttonHeight: ButtonHeight.l,
|
||||||
|
label: "Ok",
|
||||||
|
onPressed: Navigator.of(context).pop,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return StackDialog(
|
||||||
|
title: "Simplex API error",
|
||||||
|
message: "${quoteResponse.exception?.errorMessage}",
|
||||||
|
rightButton: TextButton(
|
||||||
|
style: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.getSecondaryEnabledButtonStyle(context),
|
||||||
|
child: Text(
|
||||||
|
"Ok",
|
||||||
|
style: STextStyles.button(context).copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.accentColorDark),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Error; probably amount out of bounds
|
||||||
|
String errorMessage = "${quoteResponse.exception?.errorMessage}";
|
||||||
|
if (errorMessage.contains('must be between')) {
|
||||||
|
errorMessage = errorMessage.substring(
|
||||||
|
(errorMessage.indexOf('getQuote exception: ') ?? 19) + 20,
|
||||||
|
errorMessage.indexOf(", value: null"));
|
||||||
|
_BuyFormState.boundedCryptoTicker = errorMessage.substring(
|
||||||
|
errorMessage.indexOf('The ') + 4,
|
||||||
|
errorMessage.indexOf(' amount must be between'));
|
||||||
|
_BuyFormState.minCrypto = Decimal.parse(errorMessage.substring(
|
||||||
|
errorMessage.indexOf('must be between ') + 16,
|
||||||
|
errorMessage.indexOf(' and ')));
|
||||||
|
_BuyFormState.maxCrypto = Decimal.parse(errorMessage.substring(
|
||||||
|
errorMessage.indexOf("$minCrypto and ") + "$minCrypto and ".length,
|
||||||
|
errorMessage.length));
|
||||||
|
if (Decimal.parse(_buyAmountController.text) >
|
||||||
|
_BuyFormState.maxCrypto) {
|
||||||
|
_buyAmountController.text = _BuyFormState.maxCrypto.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
await showDialog<dynamic>(
|
await showDialog<dynamic>(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: true,
|
barrierDismissible: true,
|
||||||
|
@ -424,14 +534,14 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Simplex API unresponsive",
|
"Simplex API error",
|
||||||
style: STextStyles.desktopH3(context),
|
style: STextStyles.desktopH3(context),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 24,
|
height: 24,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"Simplex API unresponsive, please try again later",
|
errorMessage,
|
||||||
style: STextStyles.smallMed14(context),
|
style: STextStyles.smallMed14(context),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
|
@ -455,9 +565,9 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return StackDialog(
|
return StackDialog(
|
||||||
title: "Simplex API unresponsive",
|
title: "Simplex API error",
|
||||||
message:
|
message: "${quoteResponse.exception?.errorMessage}",
|
||||||
"Unexpected response from Simplex API, please try again later",
|
// "${quoteResponse.exception?.errorMessage.substring(8, (quoteResponse.exception?.errorMessage?.length ?? 109) - (8 + 6))}",
|
||||||
rightButton: TextButton(
|
rightButton: TextButton(
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
|
@ -480,16 +590,24 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadQuote(SimplexQuote quote) async {
|
Future<BuyResponse<SimplexQuote>> _loadQuote(SimplexQuote quote) async {
|
||||||
final response = await SimplexAPI.instance.getQuote(quote);
|
final response = await SimplexAPI.instance.getQuote(quote);
|
||||||
|
|
||||||
if (response.value != null) {
|
if (response.value != null) {
|
||||||
|
// TODO check for error key
|
||||||
ref.read(simplexProvider).updateQuote(response.value!);
|
ref.read(simplexProvider).updateQuote(response.value!);
|
||||||
|
return BuyResponse(value: response.value!);
|
||||||
} else {
|
} else {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"_loadQuote: $response",
|
"_loadQuote: $response",
|
||||||
level: LogLevel.Warning,
|
level: LogLevel.Warning,
|
||||||
);
|
);
|
||||||
|
return BuyResponse(
|
||||||
|
exception: BuyException(
|
||||||
|
response.toString(),
|
||||||
|
BuyExceptionType.generic,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -578,10 +696,8 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
// quote = ref.read(simplexProvider).quote;
|
// quote = ref.read(simplexProvider).quote;
|
||||||
|
|
||||||
quote = SimplexQuote(
|
quote = SimplexQuote(
|
||||||
crypto:
|
crypto: Crypto.fromJson({'ticker': 'BTC', 'name': 'Bitcoin'}),
|
||||||
Crypto.fromJson({'ticker': 'BTC', 'name': 'Bitcoin', 'image': ''}),
|
fiat: Fiat.fromJson({'ticker': 'USD', 'name': 'United States Dollar'}),
|
||||||
fiat: Fiat.fromJson(
|
|
||||||
{'ticker': 'USD', 'name': 'United States Dollar', 'image': ''}),
|
|
||||||
youPayFiatPrice: Decimal.parse("100"),
|
youPayFiatPrice: Decimal.parse("100"),
|
||||||
youReceiveCryptoAmount: Decimal.parse("1.0238917"),
|
youReceiveCryptoAmount: Decimal.parse("1.0238917"),
|
||||||
id: "someID",
|
id: "someID",
|
||||||
|
@ -590,10 +706,12 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
); // TODO enum this or something
|
); // TODO enum this or something
|
||||||
|
|
||||||
// TODO set defaults better; should probably explicitly enumerate the coins & fiats used and pull the specific ones we need rather than generating them as defaults here
|
// TODO set defaults better; should probably explicitly enumerate the coins & fiats used and pull the specific ones we need rather than generating them as defaults here
|
||||||
selectedFiat = Fiat.fromJson(
|
selectedFiat =
|
||||||
{'ticker': 'USD', 'name': 'United States Dollar', 'image': ''});
|
Fiat.fromJson({'ticker': 'USD', 'name': 'United States Dollar'});
|
||||||
selectedCrypto =
|
selectedCrypto = Crypto.fromJson({
|
||||||
Crypto.fromJson({'ticker': 'BTC', 'name': 'Bitcoin', 'image': ''});
|
'ticker': widget.coin?.ticker ?? 'BTC',
|
||||||
|
'name': widget.coin?.prettyName ?? 'Bitcoin'
|
||||||
|
});
|
||||||
|
|
||||||
// TODO set initial crypto to open wallet if a wallet is open
|
// TODO set initial crypto to open wallet if a wallet is open
|
||||||
|
|
||||||
|
@ -660,7 +778,7 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
color: _hovering1
|
color: _hovering1
|
||||||
? Theme.of(context)
|
? Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.highlight
|
.currencyListItemBG
|
||||||
.withOpacity(_hovering1 ? 0.3 : 0)
|
.withOpacity(_hovering1 ? 0.3 : 0)
|
||||||
: Theme.of(context)
|
: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
|
@ -727,7 +845,7 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
color: _hovering2
|
color: _hovering2
|
||||||
? Theme.of(context)
|
? Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.highlight
|
.currencyListItemBG
|
||||||
.withOpacity(_hovering2 ? 0.3 : 0)
|
.withOpacity(_hovering2 ? 0.3 : 0)
|
||||||
: Theme.of(context)
|
: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
|
@ -743,7 +861,7 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.highlight,
|
.currencyListItemBG,
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
@ -820,7 +938,10 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
color: Theme.of(context).extension<StackColors>()!.textDark,
|
color: Theme.of(context).extension<StackColors>()!.textDark,
|
||||||
),
|
),
|
||||||
key: const Key("buyAmountInputFieldTextFieldKey"),
|
key: const Key("buyAmountInputFieldTextFieldKey"),
|
||||||
controller: _buyAmountController,
|
controller: _buyAmountController
|
||||||
|
..text = _BuyFormState.buyWithFiat
|
||||||
|
? _BuyFormState.minFiat.toStringAsFixed(2) ?? '50.00'
|
||||||
|
: _BuyFormState.minCrypto.toStringAsFixed(8),
|
||||||
focusNode: _buyAmountFocusNode,
|
focusNode: _buyAmountFocusNode,
|
||||||
keyboardType: Util.isDesktop
|
keyboardType: Util.isDesktop
|
||||||
? null
|
? null
|
||||||
|
@ -829,12 +950,7 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
decimal: true,
|
decimal: true,
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
inputFormatters: [
|
inputFormatters: [NumericalRangeFormatter()],
|
||||||
// regex to validate a crypto amount with 8 decimal places or
|
|
||||||
// 2 if fiat
|
|
||||||
NumericalRangeFormatter(
|
|
||||||
min: minFiat, max: maxFiat, buyWithFiat: buyWithFiat)
|
|
||||||
],
|
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
contentPadding: const EdgeInsets.only(
|
contentPadding: const EdgeInsets.only(
|
||||||
// top: 22,
|
// top: 22,
|
||||||
|
@ -864,7 +980,7 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.highlight,
|
.currencyListItemBG,
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
@ -905,11 +1021,20 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
_buyAmountController.text.isNotEmpty
|
_buyAmountController.text.isNotEmpty
|
||||||
? TextFieldIconButton(
|
? TextFieldIconButton(
|
||||||
key: const Key(
|
key: const Key(
|
||||||
"buyViewClearAddressFieldButtonKey"),
|
"buyViewClearAmountFieldButtonKey"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_buyAmountController.text = "";
|
if (_BuyFormState.buyWithFiat) {
|
||||||
// _receiveAddress = "";
|
_buyAmountController.text = _BuyFormState
|
||||||
setState(() {});
|
.minFiat
|
||||||
|
.toStringAsFixed(2);
|
||||||
|
} else {
|
||||||
|
if (selectedCrypto?.ticker ==
|
||||||
|
_BuyFormState.boundedCryptoTicker) {
|
||||||
|
_buyAmountController.text = _BuyFormState
|
||||||
|
.minCrypto
|
||||||
|
.toStringAsFixed(8);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: const XIcon(),
|
child: const XIcon(),
|
||||||
)
|
)
|
||||||
|
@ -1248,36 +1373,49 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
|
|
||||||
// See https://stackoverflow.com/a/68072967
|
// See https://stackoverflow.com/a/68072967
|
||||||
class NumericalRangeFormatter extends TextInputFormatter {
|
class NumericalRangeFormatter extends TextInputFormatter {
|
||||||
final Decimal min;
|
NumericalRangeFormatter();
|
||||||
final Decimal max;
|
|
||||||
final bool buyWithFiat;
|
|
||||||
|
|
||||||
NumericalRangeFormatter(
|
|
||||||
{required this.min, required this.max, required this.buyWithFiat});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TextEditingValue formatEditUpdate(
|
TextEditingValue formatEditUpdate(
|
||||||
TextEditingValue oldValue,
|
TextEditingValue oldValue,
|
||||||
TextEditingValue newValue,
|
TextEditingValue newValue,
|
||||||
) {
|
) {
|
||||||
|
TextSelection newSelection = newValue.selection;
|
||||||
|
String newVal = _BuyFormState.buyWithFiat
|
||||||
|
? Decimal.parse(newValue.text).toStringAsFixed(2)
|
||||||
|
: Decimal.parse(newValue.text).toStringAsFixed(8);
|
||||||
if (newValue.text == '') {
|
if (newValue.text == '') {
|
||||||
return newValue;
|
return newValue;
|
||||||
} else {
|
} else {
|
||||||
if (buyWithFiat) {
|
if (_BuyFormState.buyWithFiat) {
|
||||||
if (Decimal.parse(newValue.text) < min) {
|
if (Decimal.parse(newValue.text) < _BuyFormState.minFiat) {
|
||||||
newValue =
|
newVal = _BuyFormState.minFiat.toStringAsFixed(2);
|
||||||
const TextEditingValue().copyWith(text: min.toStringAsFixed(2));
|
// _BuyFormState._buyAmountController.selection =
|
||||||
} else {
|
// TextSelection.collapsed(
|
||||||
newValue = Decimal.parse(newValue.text) > max ? oldValue : newValue;
|
// offset: _BuyFormState.buyWithFiat
|
||||||
|
// ? _BuyFormState._buyAmountController.text.length - 2
|
||||||
|
// : _BuyFormState._buyAmountController.text.length - 8);
|
||||||
|
} else if (Decimal.parse(newValue.text) > _BuyFormState.maxFiat) {
|
||||||
|
newVal = _BuyFormState.maxFiat.toStringAsFixed(2);
|
||||||
|
}
|
||||||
|
} else if (!_BuyFormState.buyWithFiat &&
|
||||||
|
_BuyFormState.selectedCrypto?.ticker ==
|
||||||
|
_BuyFormState.boundedCryptoTicker) {
|
||||||
|
if (Decimal.parse(newValue.text) < _BuyFormState.minCrypto) {
|
||||||
|
newVal = _BuyFormState.minCrypto.toStringAsFixed(8);
|
||||||
|
} else if (Decimal.parse(newValue.text) > _BuyFormState.maxCrypto) {
|
||||||
|
newVal = _BuyFormState.maxCrypto.toStringAsFixed(8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final regexString = buyWithFiat
|
final regexString = _BuyFormState.buyWithFiat
|
||||||
? r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$'
|
? r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$'
|
||||||
: r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$';
|
: r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$';
|
||||||
|
|
||||||
// return RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
|
// return RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
|
||||||
return RegExp(regexString).hasMatch(newValue.text) ? newValue : oldValue;
|
return RegExp(regexString).hasMatch(newVal)
|
||||||
|
? TextEditingValue(text: newVal, selection: newSelection)
|
||||||
|
: oldValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
48
lib/pages/buy_view/buy_in_wallet_view.dart
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stackwallet/pages/buy_view/buy_view.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/widgets/background.dart';
|
||||||
|
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
|
|
||||||
|
class BuyInWalletView extends StatefulWidget {
|
||||||
|
const BuyInWalletView({
|
||||||
|
Key? key,
|
||||||
|
required this.coin,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
static const String routeName = "/stackBuyInWalletView";
|
||||||
|
|
||||||
|
final Coin? coin;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<BuyInWalletView> createState() => _BuyInWalletViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BuyInWalletViewState extends State<BuyInWalletView> {
|
||||||
|
late final Coin? coin;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
debugPrint("BUILD: $runtimeType");
|
||||||
|
|
||||||
|
return Background(
|
||||||
|
child: Scaffold(
|
||||||
|
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||||
|
appBar: AppBar(
|
||||||
|
leading: AppBarBackButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
"Buy ${widget.coin?.ticker}",
|
||||||
|
style: STextStyles.navBarTitle(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: BuyView(coin: widget.coin),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,28 +1,36 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stackwallet/pages/buy_view/buy_form.dart';
|
import 'package:stackwallet/pages/buy_view/buy_form.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
|
||||||
class BuyView extends StatefulWidget {
|
class BuyView extends StatefulWidget {
|
||||||
const BuyView({Key? key}) : super(key: key);
|
const BuyView({
|
||||||
|
Key? key,
|
||||||
|
this.coin,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
static const String routeName = "/stackBuyView";
|
static const String routeName = "/stackBuyView";
|
||||||
|
|
||||||
|
final Coin? coin;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<BuyView> createState() => _BuyViewState();
|
State<BuyView> createState() => _BuyViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BuyViewState extends State<BuyView> {
|
class _BuyViewState extends State<BuyView> {
|
||||||
|
late final Coin? coin;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
debugPrint("BUILD: $runtimeType");
|
debugPrint("BUILD: $runtimeType");
|
||||||
|
|
||||||
return const SafeArea(
|
return SafeArea(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
left: 16,
|
left: 16,
|
||||||
right: 16,
|
right: 16,
|
||||||
top: 16,
|
top: 16,
|
||||||
),
|
),
|
||||||
child: BuyForm(),
|
child: BuyForm(coin: widget.coin),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,18 +29,9 @@ class BuyWarningPopup extends StatelessWidget {
|
||||||
SimplexOrder? order;
|
SimplexOrder? order;
|
||||||
|
|
||||||
Future<BuyResponse<SimplexOrder>> newOrder(SimplexQuote quote) async {
|
Future<BuyResponse<SimplexOrder>> newOrder(SimplexQuote quote) async {
|
||||||
final response = await SimplexAPI.instance.newOrder(quote);
|
final orderResponse = await SimplexAPI.instance.newOrder(quote);
|
||||||
|
|
||||||
// if (response.value != null) {
|
return orderResponse;
|
||||||
// ref.read(simplexProvider).updateOrder(response.value!);
|
|
||||||
// } else {
|
|
||||||
// Logging.instance.log(
|
|
||||||
// "_loadQuote: $response",
|
|
||||||
// level: LogLevel.Warning,
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<BuyResponse<bool>> redirect(SimplexOrder order) async {
|
Future<BuyResponse<bool>> redirect(SimplexOrder order) async {
|
||||||
|
@ -122,13 +113,90 @@ class BuyWarningPopup extends StatelessWidget {
|
||||||
rightButton: PrimaryButton(
|
rightButton: PrimaryButton(
|
||||||
label: "Continue",
|
label: "Continue",
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
BuyResponse<SimplexOrder> order = await newOrder(quote);
|
BuyResponse<SimplexOrder> orderResponse = await newOrder(quote);
|
||||||
await redirect(order.value as SimplexOrder).then((_response) async {
|
if (orderResponse.exception == null) {
|
||||||
this.order = order.value as SimplexOrder;
|
await redirect(orderResponse.value as SimplexOrder)
|
||||||
Navigator.of(context, rootNavigator: isDesktop).pop();
|
.then((_response) async {
|
||||||
Navigator.of(context, rootNavigator: isDesktop).pop();
|
this.order = orderResponse.value as SimplexOrder;
|
||||||
await _buyInvoice();
|
Navigator.of(context, rootNavigator: isDesktop).pop();
|
||||||
});
|
Navigator.of(context, rootNavigator: isDesktop).pop();
|
||||||
|
await _buyInvoice();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await showDialog<dynamic>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: true,
|
||||||
|
builder: (context) {
|
||||||
|
if (isDesktop) {
|
||||||
|
return DesktopDialog(
|
||||||
|
maxWidth: 450,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(32),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Simplex API error",
|
||||||
|
style: STextStyles.desktopH3(context),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"${orderResponse.exception?.errorMessage}",
|
||||||
|
style: STextStyles.smallMed14(context),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 56,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Spacer(),
|
||||||
|
Expanded(
|
||||||
|
child: PrimaryButton(
|
||||||
|
buttonHeight: ButtonHeight.l,
|
||||||
|
label: "Ok",
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).pop(); // weee
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return StackDialog(
|
||||||
|
title: "Simplex API error",
|
||||||
|
message: "${orderResponse.exception?.errorMessage}",
|
||||||
|
// "${quoteResponse.exception?.errorMessage.substring(8, (quoteResponse.exception?.errorMessage?.length ?? 109) - (8 + 6))}",
|
||||||
|
rightButton: TextButton(
|
||||||
|
style: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.getSecondaryEnabledButtonStyle(context),
|
||||||
|
child: Text(
|
||||||
|
"Ok",
|
||||||
|
style: STextStyles.button(context).copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.accentColorDark),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).pop(); // weee
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
icon: SizedBox(
|
icon: SizedBox(
|
||||||
|
|
|
@ -253,9 +253,10 @@ bool isStackCoin(String? ticker) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget? getIconForTicker(String ticker) {
|
Widget? getIconForTicker(String ticker) {
|
||||||
String? iconAsset = isStackCoin(ticker)
|
String? iconAsset = /*isStackCoin(ticker)
|
||||||
? Assets.svg.iconFor(coin: coinFromTickerCaseInsensitive(ticker))
|
?*/
|
||||||
: Assets.svg.buyIconFor(ticker);
|
Assets.svg.iconFor(coin: coinFromTickerCaseInsensitive(ticker));
|
||||||
|
// : Assets.svg.buyIconFor(ticker);
|
||||||
return (iconAsset != null)
|
return (iconAsset != null)
|
||||||
? SvgPicture.asset(iconAsset, height: 20, width: 20)
|
? SvgPicture.asset(iconAsset, height: 20, width: 20)
|
||||||
: null;
|
: null;
|
||||||
|
|
|
@ -207,7 +207,7 @@ class _FiatSelectionViewState extends State<FiatSelectionView> {
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.highlight,
|
.currencyListItemBG,
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|
|
@ -412,47 +412,49 @@ class _WalletNavigationBarState extends State<WalletNavigationBar> {
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 12,
|
width: 12,
|
||||||
),
|
),
|
||||||
RawMaterialButton(
|
if (widget.coin.hasBuySupport)
|
||||||
constraints: const BoxConstraints(
|
RawMaterialButton(
|
||||||
minWidth: 66,
|
constraints: const BoxConstraints(
|
||||||
),
|
minWidth: 66,
|
||||||
onPressed: widget.onBuyPressed,
|
|
||||||
splashColor:
|
|
||||||
Theme.of(context).extension<StackColors>()!.highlight,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(
|
|
||||||
widget.height / 2.0,
|
|
||||||
),
|
),
|
||||||
),
|
onPressed: widget.onBuyPressed,
|
||||||
child: Container(
|
splashColor:
|
||||||
color: Colors.transparent,
|
Theme.of(context).extension<StackColors>()!.highlight,
|
||||||
child: Padding(
|
shape: RoundedRectangleBorder(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 2.0),
|
borderRadius: BorderRadius.circular(
|
||||||
child: Column(
|
widget.height / 2.0,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
),
|
||||||
children: [
|
),
|
||||||
const Spacer(),
|
child: Container(
|
||||||
SvgPicture.asset(
|
color: Colors.transparent,
|
||||||
Assets.svg.buyDesktop,
|
child: Padding(
|
||||||
width: 24,
|
padding: const EdgeInsets.symmetric(vertical: 2.0),
|
||||||
height: 24,
|
child: Column(
|
||||||
),
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
const SizedBox(
|
children: [
|
||||||
height: 4,
|
const Spacer(),
|
||||||
),
|
SvgPicture.asset(
|
||||||
Text(
|
Assets.svg.buy(context),
|
||||||
"Buy",
|
width: 24,
|
||||||
style: STextStyles.buttonSmall(context),
|
height: 24,
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const SizedBox(
|
||||||
],
|
height: 4,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Buy",
|
||||||
|
style: STextStyles.buttonSmall(context),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
if (widget.coin.hasBuySupport)
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 12,
|
width: 12,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||||
|
import 'package:stackwallet/pages/buy_view/buy_in_wallet_view.dart';
|
||||||
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
|
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
|
||||||
import 'package:stackwallet/pages/exchange_view/wallet_initiated_exchange_view.dart';
|
import 'package:stackwallet/pages/exchange_view/wallet_initiated_exchange_view.dart';
|
||||||
import 'package:stackwallet/pages/home_view/home_view.dart';
|
import 'package:stackwallet/pages/home_view/home_view.dart';
|
||||||
|
@ -772,7 +773,15 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onBuyPressed: () {},
|
onBuyPressed: () {
|
||||||
|
// TODO set default coin to currently open wallet here by passing it as an argument
|
||||||
|
// final coin = ref.read(managerProvider).coin;
|
||||||
|
|
||||||
|
unawaited(Navigator.of(context).pushNamed(
|
||||||
|
BuyInWalletView.routeName,
|
||||||
|
arguments: coin,
|
||||||
|
));
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -22,7 +22,9 @@ import 'package:stackwallet/pages/address_book_views/subviews/address_book_filte
|
||||||
import 'package:stackwallet/pages/address_book_views/subviews/contact_details_view.dart';
|
import 'package:stackwallet/pages/address_book_views/subviews/contact_details_view.dart';
|
||||||
import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_address_view.dart';
|
import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_address_view.dart';
|
||||||
import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_name_emoji_view.dart';
|
import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_name_emoji_view.dart';
|
||||||
|
import 'package:stackwallet/pages/buy_view/buy_in_wallet_view.dart';
|
||||||
import 'package:stackwallet/pages/buy_view/buy_quote_preview.dart';
|
import 'package:stackwallet/pages/buy_view/buy_quote_preview.dart';
|
||||||
|
import 'package:stackwallet/pages/buy_view/buy_view.dart';
|
||||||
import 'package:stackwallet/pages/exchange_view/choose_from_stack_view.dart';
|
import 'package:stackwallet/pages/exchange_view/choose_from_stack_view.dart';
|
||||||
import 'package:stackwallet/pages/exchange_view/edit_trade_note_view.dart';
|
import 'package:stackwallet/pages/exchange_view/edit_trade_note_view.dart';
|
||||||
import 'package:stackwallet/pages/exchange_view/exchange_loading_overlay.dart';
|
import 'package:stackwallet/pages/exchange_view/exchange_loading_overlay.dart';
|
||||||
|
@ -1125,6 +1127,24 @@ class RouteGenerator {
|
||||||
builder: (_) => const DesktopExchangeView(),
|
builder: (_) => const DesktopExchangeView(),
|
||||||
settings: RouteSettings(name: settings.name));
|
settings: RouteSettings(name: settings.name));
|
||||||
|
|
||||||
|
case BuyView.routeName:
|
||||||
|
return getRoute(
|
||||||
|
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||||
|
builder: (_) => const BuyView(),
|
||||||
|
settings: RouteSettings(name: settings.name));
|
||||||
|
|
||||||
|
case BuyInWalletView.routeName:
|
||||||
|
if (args is Coin) {
|
||||||
|
return getRoute(
|
||||||
|
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||||
|
builder: (_) => BuyInWalletView(coin: args),
|
||||||
|
settings: RouteSettings(
|
||||||
|
name: settings.name,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||||
|
|
||||||
case DesktopBuyView.routeName:
|
case DesktopBuyView.routeName:
|
||||||
return getRoute(
|
return getRoute(
|
||||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||||
|
|
|
@ -8,6 +8,7 @@ import 'package:stackwallet/models/buy/response_objects/fiat.dart';
|
||||||
import 'package:stackwallet/models/buy/response_objects/order.dart';
|
import 'package:stackwallet/models/buy/response_objects/order.dart';
|
||||||
import 'package:stackwallet/models/buy/response_objects/quote.dart';
|
import 'package:stackwallet/models/buy/response_objects/quote.dart';
|
||||||
import 'package:stackwallet/services/buy/buy_response.dart';
|
import 'package:stackwallet/services/buy/buy_response.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/enums/fiat_enum.dart';
|
import 'package:stackwallet/utilities/enums/fiat_enum.dart';
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
import 'package:stackwallet/utilities/prefs.dart';
|
import 'package:stackwallet/utilities/prefs.dart';
|
||||||
|
@ -15,7 +16,7 @@ import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class SimplexAPI {
|
class SimplexAPI {
|
||||||
static const String authority = "simplex-sandbox.stackwallet.com";
|
static const String authority = "simplex-sandbox.stackwallet.com";
|
||||||
// static const String authority = "localhost";
|
// static const String authority = "localhost"; // For development purposes
|
||||||
static const String scheme = authority == "localhost" ? "http" : "https";
|
static const String scheme = authority == "localhost" ? "http" : "https";
|
||||||
|
|
||||||
final _prefs = Prefs.instance;
|
final _prefs = Prefs.instance;
|
||||||
|
@ -71,13 +72,15 @@ class SimplexAPI {
|
||||||
|
|
||||||
for (final crypto in jsonArray as List) {
|
for (final crypto in jsonArray as List) {
|
||||||
// TODO validate jsonArray
|
// TODO validate jsonArray
|
||||||
cryptos.add(Crypto.fromJson({
|
if (isStackCoin("${crypto['ticker_symbol']}")) {
|
||||||
'ticker': "${crypto['ticker_symbol']}",
|
cryptos.add(Crypto.fromJson({
|
||||||
'name': crypto['name'],
|
'ticker': "${crypto['ticker_symbol']}",
|
||||||
'network': "${crypto['network']}",
|
'name': crypto['name'],
|
||||||
'contractAddress': "${crypto['contractAddress']}",
|
'network': "${crypto['network']}",
|
||||||
'image': "",
|
'contractAddress': "${crypto['contractAddress']}",
|
||||||
}));
|
'image': "",
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return BuyResponse(value: cryptos);
|
return BuyResponse(value: cryptos);
|
||||||
|
@ -184,6 +187,12 @@ class SimplexAPI {
|
||||||
throw Exception('getQuote exception: statusCode= ${res.statusCode}');
|
throw Exception('getQuote exception: statusCode= ${res.statusCode}');
|
||||||
}
|
}
|
||||||
final jsonArray = jsonDecode(res.body);
|
final jsonArray = jsonDecode(res.body);
|
||||||
|
if (jsonArray.containsKey('error') as bool) {
|
||||||
|
if (jsonArray['error'] == true || jsonArray['error'] == 'true') {
|
||||||
|
// jsonArray['error'] as bool == true?
|
||||||
|
throw Exception('getQuote exception: ${jsonArray['error']}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
jsonArray['quote'] = quote; // Add and pass this on
|
jsonArray['quote'] = quote; // Add and pass this on
|
||||||
|
|
||||||
|
@ -261,13 +270,17 @@ class SimplexAPI {
|
||||||
date.toIso8601String() + timeZoneFormatter(date.timeZoneOffset);
|
date.toIso8601String() + timeZoneFormatter(date.timeZoneOffset);
|
||||||
}
|
}
|
||||||
Uri url = _buildUri('api.php', data);
|
Uri url = _buildUri('api.php', data);
|
||||||
print(data);
|
|
||||||
|
|
||||||
var res = await http.get(url, headers: headers);
|
var res = await http.get(url, headers: headers);
|
||||||
if (res.statusCode != 200) {
|
if (res.statusCode != 200) {
|
||||||
throw Exception('newOrder exception: statusCode= ${res.statusCode}');
|
throw Exception('newOrder exception: statusCode= ${res.statusCode}');
|
||||||
}
|
}
|
||||||
final jsonArray = jsonDecode(res.body); // TODO check if valid json
|
final jsonArray = jsonDecode(res.body); // TODO check if valid json
|
||||||
|
if (jsonArray.containsKey('error') as bool) {
|
||||||
|
if (jsonArray['error'] == true || jsonArray['error'] == 'true') {
|
||||||
|
throw Exception(jsonArray['message']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SimplexOrder _order = SimplexOrder(
|
SimplexOrder _order = SimplexOrder(
|
||||||
quote: quote,
|
quote: quote,
|
||||||
|
@ -328,3 +341,14 @@ class SimplexAPI {
|
||||||
String timeZoneFormatter(Duration offset) =>
|
String timeZoneFormatter(Duration offset) =>
|
||||||
"${offset.isNegative ? "-" : "+"}${offset.inHours.abs().toString().padLeft(2, "0")}:${(offset.inMinutes - offset.inHours * 60).abs().toString().padLeft(2, "0")}";
|
"${offset.isNegative ? "-" : "+"}${offset.inHours.abs().toString().padLeft(2, "0")}:${(offset.inMinutes - offset.inHours * 60).abs().toString().padLeft(2, "0")}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isStackCoin(String? ticker) {
|
||||||
|
if (ticker == null) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
coinFromTickerCaseInsensitive(ticker);
|
||||||
|
return true;
|
||||||
|
} on ArgumentError catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -32,9 +32,8 @@ class _BUY {
|
||||||
const _BUY();
|
const _BUY();
|
||||||
|
|
||||||
// TODO: switch this to something like
|
// TODO: switch this to something like
|
||||||
// String buy(BuildContext context) =>
|
String buy(BuildContext context) =>
|
||||||
// "assets/svg/${Theme.of(context).extension<StackColors>()!.themeType.name}/buy.svg";
|
"assets/svg/${Theme.of(context).extension<StackColors>()!.themeType.name}/buy-coins-icon.svg";
|
||||||
String get buy => "assets/svg/light/buy-coins-icon.svg";
|
|
||||||
|
|
||||||
String simplexLogo(BuildContext context) {
|
String simplexLogo(BuildContext context) {
|
||||||
return (Theme.of(context).extension<StackColors>()!.themeType ==
|
return (Theme.of(context).extension<StackColors>()!.themeType ==
|
||||||
|
@ -210,17 +209,6 @@ class _SVG {
|
||||||
String get namecoin => "assets/svg/coin_icons/Namecoin.svg";
|
String get namecoin => "assets/svg/coin_icons/Namecoin.svg";
|
||||||
String get particl => "assets/svg/coin_icons/Particl.svg";
|
String get particl => "assets/svg/coin_icons/Particl.svg";
|
||||||
|
|
||||||
String get cosmos => "assets/svg/coin_icons/Cosmos.svg";
|
|
||||||
String get binanceusd => "assets/svg/coin_icons/BinanceUSD.svg";
|
|
||||||
String get dai => "assets/svg/coin_icons/Dai.svg";
|
|
||||||
String get dash => "assets/svg/coin_icons/Dash.svg";
|
|
||||||
String get eos => "assets/svg/coin_icons/EOS.svg";
|
|
||||||
String get ethereum => "assets/svg/coin_icons/Ethereum.svg";
|
|
||||||
String get tron => "assets/svg/coin_icons/Tron.svg";
|
|
||||||
String get tether => "assets/svg/coin_icons/Tether.svg";
|
|
||||||
String get stellar => "assets/svg/coin_icons/Stellar.svg";
|
|
||||||
String get ripple => "assets/svg/coin_icons/Ripple.svg";
|
|
||||||
|
|
||||||
String get chevronRight => "assets/svg/chevron-right.svg";
|
String get chevronRight => "assets/svg/chevron-right.svg";
|
||||||
String get minimize => "assets/svg/minimize.svg";
|
String get minimize => "assets/svg/minimize.svg";
|
||||||
String get walletFa => "assets/svg/wallet-fa.svg";
|
String get walletFa => "assets/svg/wallet-fa.svg";
|
||||||
|
@ -268,33 +256,6 @@ class _SVG {
|
||||||
return dogecoinTestnet;
|
return dogecoinTestnet;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String? buyIconFor(String ticker) {
|
|
||||||
switch (ticker.toLowerCase()) {
|
|
||||||
case 'atom':
|
|
||||||
return cosmos;
|
|
||||||
case 'busd':
|
|
||||||
return binanceusd;
|
|
||||||
case 'dai':
|
|
||||||
return dai;
|
|
||||||
case 'dash':
|
|
||||||
return dash;
|
|
||||||
case 'eos':
|
|
||||||
return eos;
|
|
||||||
case 'eth':
|
|
||||||
return ethereum;
|
|
||||||
case 'trx':
|
|
||||||
return tron;
|
|
||||||
case 'usdt':
|
|
||||||
return tether;
|
|
||||||
case 'xlm':
|
|
||||||
return stellar;
|
|
||||||
case 'xrp':
|
|
||||||
return ripple;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PNG {
|
class _PNG {
|
||||||
|
|
|
@ -195,6 +195,29 @@ extension CoinExt on Coin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get hasBuySupport {
|
||||||
|
switch (this) {
|
||||||
|
case Coin.bitcoin:
|
||||||
|
case Coin.litecoin:
|
||||||
|
case Coin.bitcoincash:
|
||||||
|
case Coin.dogecoin:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case Coin.firo:
|
||||||
|
case Coin.namecoin:
|
||||||
|
case Coin.particl:
|
||||||
|
case Coin.epicCash:
|
||||||
|
case Coin.monero:
|
||||||
|
case Coin.wownero:
|
||||||
|
case Coin.dogecoinTestNet:
|
||||||
|
case Coin.bitcoinTestNet:
|
||||||
|
case Coin.litecoinTestNet:
|
||||||
|
case Coin.bitcoincashTestnet:
|
||||||
|
case Coin.firoTestNet:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int get requiredConfirmations {
|
int get requiredConfirmations {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case Coin.bitcoin:
|
case Coin.bitcoin:
|
||||||
|
|
11
pubspec.yaml
|
@ -320,17 +320,6 @@ flutter:
|
||||||
- assets/svg/coin_icons/Wownero.svg
|
- assets/svg/coin_icons/Wownero.svg
|
||||||
- assets/svg/coin_icons/Namecoin.svg
|
- assets/svg/coin_icons/Namecoin.svg
|
||||||
- assets/svg/coin_icons/Particl.svg
|
- assets/svg/coin_icons/Particl.svg
|
||||||
# buy coin icons
|
|
||||||
- assets/svg/coin_icons/Cosmos.svg
|
|
||||||
- assets/svg/coin_icons/BinanceUSD.svg
|
|
||||||
- assets/svg/coin_icons/Dai.svg
|
|
||||||
- assets/svg/coin_icons/Dash.svg
|
|
||||||
- assets/svg/coin_icons/EOS.svg
|
|
||||||
- assets/svg/coin_icons/Ethereum.svg
|
|
||||||
- assets/svg/coin_icons/Tron.svg
|
|
||||||
- assets/svg/coin_icons/Tether.svg
|
|
||||||
- assets/svg/coin_icons/Stellar.svg
|
|
||||||
- assets/svg/coin_icons/Ripple.svg
|
|
||||||
# lottie animations
|
# lottie animations
|
||||||
- assets/lottie/test.json
|
- assets/lottie/test.json
|
||||||
- assets/lottie/test2.json
|
- assets/lottie/test2.json
|
||||||
|
|