mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-03-22 15:19:11 +00:00
desktop trade steps flow fade transition and state management updates
This commit is contained in:
parent
7641539bf7
commit
779bf20cc4
10 changed files with 584 additions and 566 deletions
|
@ -1,8 +1,9 @@
|
||||||
import 'package:decimal/decimal.dart';
|
import 'package:decimal/decimal.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
|
import 'package:stackwallet/models/exchange/response_objects/trade.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';
|
||||||
|
|
||||||
class IncompleteExchangeModel {
|
class IncompleteExchangeModel extends ChangeNotifier {
|
||||||
final String sendTicker;
|
final String sendTicker;
|
||||||
final String receiveTicker;
|
final String receiveTicker;
|
||||||
|
|
||||||
|
@ -15,12 +16,49 @@ class IncompleteExchangeModel {
|
||||||
|
|
||||||
final bool reversed;
|
final bool reversed;
|
||||||
|
|
||||||
String? recipientAddress;
|
String? _recipientAddress;
|
||||||
String? refundAddress;
|
|
||||||
|
|
||||||
String? rateId;
|
String? get recipientAddress => _recipientAddress;
|
||||||
|
|
||||||
Trade? trade;
|
set recipientAddress(String? recipientAddress) {
|
||||||
|
if (_recipientAddress != recipientAddress) {
|
||||||
|
_recipientAddress = recipientAddress;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _refundAddress;
|
||||||
|
|
||||||
|
String? get refundAddress => _refundAddress;
|
||||||
|
|
||||||
|
set refundAddress(String? refundAddress) {
|
||||||
|
if (_refundAddress != refundAddress) {
|
||||||
|
_refundAddress = refundAddress;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _rateId;
|
||||||
|
|
||||||
|
String? get rateId => _rateId;
|
||||||
|
|
||||||
|
set rateId(String? rateId) {
|
||||||
|
if (_rateId != rateId) {
|
||||||
|
_rateId = rateId;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Trade? _trade;
|
||||||
|
|
||||||
|
Trade? get trade => _trade;
|
||||||
|
|
||||||
|
set trade(Trade? trade) {
|
||||||
|
if (_trade != trade) {
|
||||||
|
_trade = trade;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IncompleteExchangeModel({
|
IncompleteExchangeModel({
|
||||||
required this.sendTicker,
|
required this.sendTicker,
|
||||||
|
@ -30,6 +68,6 @@ class IncompleteExchangeModel {
|
||||||
required this.receiveAmount,
|
required this.receiveAmount,
|
||||||
required this.rateType,
|
required this.rateType,
|
||||||
required this.reversed,
|
required this.reversed,
|
||||||
this.rateId,
|
String? rateId,
|
||||||
});
|
}) : _rateId = rateId;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,6 @@ import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_provider_op
|
||||||
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/sub_widgets/rate_type_toggle.dart';
|
import 'package:stackwallet/pages/exchange_view/sub_widgets/rate_type_toggle.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart';
|
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_1.dart';
|
|
||||||
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.dart';
|
|
||||||
import 'package:stackwallet/providers/providers.dart';
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart';
|
import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart';
|
||||||
import 'package:stackwallet/services/exchange/simpleswap/simpleswap_exchange.dart';
|
import 'package:stackwallet/services/exchange/simpleswap/simpleswap_exchange.dart';
|
||||||
|
@ -1022,19 +1020,16 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
||||||
ref.read(exchangeSendFromWalletIdStateProvider.state).state =
|
ref.read(exchangeSendFromWalletIdStateProvider.state).state =
|
||||||
Tuple2(walletId!, coin!);
|
Tuple2(walletId!, coin!);
|
||||||
if (isDesktop) {
|
if (isDesktop) {
|
||||||
|
ref.read(ssss.state).state = model;
|
||||||
await showDialog<void>(
|
await showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return DesktopDialog(
|
return const DesktopDialog(
|
||||||
maxWidth: 720,
|
maxWidth: 720,
|
||||||
maxHeight: double.infinity,
|
maxHeight: double.infinity,
|
||||||
child: StepScaffold(
|
child: StepScaffold(
|
||||||
step: 2,
|
initialStep: 2,
|
||||||
model: model,
|
|
||||||
body: DesktopStep2(
|
|
||||||
model: model,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -1051,19 +1046,16 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
||||||
ref.read(exchangeSendFromWalletIdStateProvider.state).state = null;
|
ref.read(exchangeSendFromWalletIdStateProvider.state).state = null;
|
||||||
|
|
||||||
if (isDesktop) {
|
if (isDesktop) {
|
||||||
|
ref.read(ssss.state).state = model;
|
||||||
await showDialog<void>(
|
await showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return DesktopDialog(
|
return const DesktopDialog(
|
||||||
maxWidth: 720,
|
maxWidth: 720,
|
||||||
maxHeight: double.infinity,
|
maxHeight: double.infinity,
|
||||||
child: StepScaffold(
|
child: StepScaffold(
|
||||||
step: 1,
|
initialStep: 1,
|
||||||
model: model,
|
|
||||||
body: DesktopStep1(
|
|
||||||
model: model,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,39 +1,190 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'dart:async';
|
||||||
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
|
|
||||||
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/subwidgets/desktop_exchange_steps_indicator.dart';
|
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
|
||||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
|
||||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
|
||||||
|
|
||||||
class StepScaffold extends StatefulWidget {
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:qr_flutter/qr_flutter.dart';
|
||||||
|
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
|
||||||
|
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
|
||||||
|
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
|
||||||
|
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_1.dart';
|
||||||
|
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.dart';
|
||||||
|
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_3.dart';
|
||||||
|
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_4.dart';
|
||||||
|
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/subwidgets/desktop_exchange_steps_indicator.dart';
|
||||||
|
import 'package:stackwallet/providers/exchange/exchange_provider.dart';
|
||||||
|
import 'package:stackwallet/providers/global/trades_service_provider.dart';
|
||||||
|
import 'package:stackwallet/services/exchange/exchange_response.dart';
|
||||||
|
import 'package:stackwallet/services/notifications_api.dart';
|
||||||
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
|
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/simple_desktop_dialog.dart';
|
||||||
|
import 'package:stackwallet/widgets/fade_stack.dart';
|
||||||
|
|
||||||
|
final ssss = StateProvider<IncompleteExchangeModel?>((_) => null);
|
||||||
|
|
||||||
|
final desktopExchangeModelProvider =
|
||||||
|
ChangeNotifierProvider<IncompleteExchangeModel?>(
|
||||||
|
(ref) => ref.watch(ssss.state).state);
|
||||||
|
|
||||||
|
class StepScaffold extends ConsumerStatefulWidget {
|
||||||
const StepScaffold({
|
const StepScaffold({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.body,
|
required this.initialStep,
|
||||||
required this.step,
|
|
||||||
required this.model,
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final Widget body;
|
final int initialStep;
|
||||||
final int step;
|
|
||||||
final IncompleteExchangeModel model;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StepScaffold> createState() => _StepScaffoldState();
|
ConsumerState<StepScaffold> createState() => _StepScaffoldState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _StepScaffoldState extends State<StepScaffold> {
|
class _StepScaffoldState extends ConsumerState<StepScaffold> {
|
||||||
int currentStep = 0;
|
int currentStep = 1;
|
||||||
late final IncompleteExchangeModel model;
|
bool enableNext = false;
|
||||||
|
|
||||||
|
late final Duration duration;
|
||||||
|
|
||||||
|
void updateEnableNext(bool enableNext) {
|
||||||
|
if (enableNext != this.enableNext) {
|
||||||
|
setState(() => this.enableNext = enableNext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> createTrade() async {
|
||||||
|
unawaited(
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (_) => WillPopScope(
|
||||||
|
onWillPop: () async => false,
|
||||||
|
child: Container(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.overlay
|
||||||
|
.withOpacity(0.6),
|
||||||
|
child: const CustomLoadingOverlay(
|
||||||
|
message: "Creating a trade",
|
||||||
|
eventBus: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final ExchangeResponse<Trade> response = await ref
|
||||||
|
.read(exchangeProvider)
|
||||||
|
.createTrade(
|
||||||
|
from: ref.read(desktopExchangeModelProvider)!.sendTicker,
|
||||||
|
to: ref.read(desktopExchangeModelProvider)!.receiveTicker,
|
||||||
|
fixedRate: ref.read(desktopExchangeModelProvider)!.rateType !=
|
||||||
|
ExchangeRateType.estimated,
|
||||||
|
amount: ref.read(desktopExchangeModelProvider)!.reversed
|
||||||
|
? ref.read(desktopExchangeModelProvider)!.receiveAmount
|
||||||
|
: ref.read(desktopExchangeModelProvider)!.sendAmount,
|
||||||
|
addressTo: ref.read(desktopExchangeModelProvider)!.recipientAddress!,
|
||||||
|
extraId: null,
|
||||||
|
addressRefund: ref.read(desktopExchangeModelProvider)!.refundAddress!,
|
||||||
|
refundExtraId: "",
|
||||||
|
rateId: ref.read(desktopExchangeModelProvider)!.rateId,
|
||||||
|
reversed: ref.read(desktopExchangeModelProvider)!.reversed,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.value == null) {
|
||||||
|
if (mounted) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
unawaited(
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: true,
|
||||||
|
builder: (_) => SimpleDesktopDialog(
|
||||||
|
title: "Failed to create trade",
|
||||||
|
message: response.exception?.toString() ?? ""),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save trade to hive
|
||||||
|
await ref.read(tradesServiceProvider).add(
|
||||||
|
trade: response.value!,
|
||||||
|
shouldNotifyListeners: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
String status = response.value!.status;
|
||||||
|
|
||||||
|
ref.read(desktopExchangeModelProvider)!.trade = response.value!;
|
||||||
|
|
||||||
|
// extra info if status is waiting
|
||||||
|
if (status == "Waiting") {
|
||||||
|
status += " for deposit";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
unawaited(
|
||||||
|
NotificationApi.showNotification(
|
||||||
|
changeNowId: ref.read(desktopExchangeModelProvider)!.trade!.tradeId,
|
||||||
|
title: status,
|
||||||
|
body:
|
||||||
|
"Trade ID ${ref.read(desktopExchangeModelProvider)!.trade!.tradeId}",
|
||||||
|
walletId: "",
|
||||||
|
iconAssetName: Assets.svg.arrowRotate,
|
||||||
|
date: ref.read(desktopExchangeModelProvider)!.trade!.timestamp,
|
||||||
|
shouldWatchForUpdates: true,
|
||||||
|
coinName: "coinName",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
// if (mounted) {
|
||||||
|
// unawaited(
|
||||||
|
// showDialog<void>(
|
||||||
|
// context: context,
|
||||||
|
// barrierColor: Colors.transparent,
|
||||||
|
// barrierDismissible: false,
|
||||||
|
// builder: (context) {
|
||||||
|
// return DesktopDialog(
|
||||||
|
// maxWidth: 720,
|
||||||
|
// maxHeight: double.infinity,
|
||||||
|
// child: StepScaffold(
|
||||||
|
// initialStep: 4,
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
void onBack() {
|
||||||
|
if (currentStep > 1 && currentStep < 4) {
|
||||||
|
setState(() => currentStep = currentStep - 1);
|
||||||
|
} else if (currentStep == 1) {
|
||||||
|
Navigator.of(context, rootNavigator: true).pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
currentStep = widget.step;
|
duration = const Duration(milliseconds: 250);
|
||||||
model = widget.model;
|
currentStep = widget.initialStep;
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final model = ref.watch(desktopExchangeModelProvider);
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
@ -43,15 +194,16 @@ class _StepScaffoldState extends State<StepScaffold> {
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
currentStep != 4
|
currentStep != 4
|
||||||
? const AppBarBackButton(
|
? AppBarBackButton(
|
||||||
isCompact: true,
|
isCompact: true,
|
||||||
iconSize: 23,
|
iconSize: 23,
|
||||||
|
onPressed: onBack,
|
||||||
)
|
)
|
||||||
: const SizedBox(
|
: const SizedBox(
|
||||||
width: 32,
|
width: 32,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"Exchange ${model.sendTicker.toUpperCase()} to ${model.receiveTicker.toUpperCase()}",
|
"Exchange ${model?.sendTicker.toUpperCase()} to ${model?.receiveTicker.toUpperCase()}",
|
||||||
style: STextStyles.desktopH3(context),
|
style: STextStyles.desktopH3(context),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -60,9 +212,6 @@ class _StepScaffoldState extends State<StepScaffold> {
|
||||||
DesktopDialogCloseButton(
|
DesktopDialogCloseButton(
|
||||||
onPressedOverride: () {
|
onPressedOverride: () {
|
||||||
Navigator.of(context, rootNavigator: true).pop();
|
Navigator.of(context, rootNavigator: true).pop();
|
||||||
Navigator.of(context, rootNavigator: true).pop();
|
|
||||||
Navigator.of(context, rootNavigator: true).pop();
|
|
||||||
Navigator.of(context, rootNavigator: true).pop();
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -85,7 +234,139 @@ class _StepScaffoldState extends State<StepScaffold> {
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 32,
|
horizontal: 32,
|
||||||
),
|
),
|
||||||
child: widget.body,
|
child: FadeStack(
|
||||||
|
index: currentStep - 1,
|
||||||
|
children: [
|
||||||
|
const DesktopStep1(),
|
||||||
|
DesktopStep2(
|
||||||
|
enableNextChanged: updateEnableNext,
|
||||||
|
),
|
||||||
|
const DesktopStep3(),
|
||||||
|
const DesktopStep4(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 20,
|
||||||
|
left: 32,
|
||||||
|
right: 32,
|
||||||
|
bottom: 32,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AnimatedCrossFade(
|
||||||
|
duration: const Duration(milliseconds: 250),
|
||||||
|
crossFadeState: currentStep == 4
|
||||||
|
? CrossFadeState.showSecond
|
||||||
|
: CrossFadeState.showFirst,
|
||||||
|
firstChild: SecondaryButton(
|
||||||
|
label: "Back",
|
||||||
|
buttonHeight: ButtonHeight.l,
|
||||||
|
onPressed: onBack,
|
||||||
|
),
|
||||||
|
secondChild: SecondaryButton(
|
||||||
|
label: "Send from Stack Wallet",
|
||||||
|
buttonHeight: ButtonHeight.l,
|
||||||
|
onPressed: onBack,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 16,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: AnimatedCrossFade(
|
||||||
|
duration: const Duration(milliseconds: 250),
|
||||||
|
crossFadeState: currentStep == 4
|
||||||
|
? CrossFadeState.showSecond
|
||||||
|
: CrossFadeState.showFirst,
|
||||||
|
firstChild: AnimatedCrossFade(
|
||||||
|
duration: const Duration(milliseconds: 250),
|
||||||
|
crossFadeState: currentStep == 3
|
||||||
|
? CrossFadeState.showSecond
|
||||||
|
: CrossFadeState.showFirst,
|
||||||
|
firstChild: PrimaryButton(
|
||||||
|
label: "Next",
|
||||||
|
enabled: currentStep != 2 ? true : enableNext,
|
||||||
|
buttonHeight: ButtonHeight.l,
|
||||||
|
onPressed: () async {
|
||||||
|
setState(() => currentStep = currentStep + 1);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
secondChild: PrimaryButton(
|
||||||
|
label: "Confirm",
|
||||||
|
enabled: currentStep != 2 ? true : enableNext,
|
||||||
|
buttonHeight: ButtonHeight.l,
|
||||||
|
onPressed: () async {
|
||||||
|
if (currentStep == 3) {
|
||||||
|
final success = await createTrade();
|
||||||
|
if (!success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setState(() => currentStep = currentStep + 1);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secondChild: PrimaryButton(
|
||||||
|
label: "Show QR code",
|
||||||
|
enabled: currentStep != 2 ? true : enableNext,
|
||||||
|
buttonHeight: ButtonHeight.l,
|
||||||
|
onPressed: () {
|
||||||
|
showDialog<dynamic>(
|
||||||
|
context: context,
|
||||||
|
barrierColor: Colors.transparent,
|
||||||
|
barrierDismissible: true,
|
||||||
|
builder: (_) {
|
||||||
|
return DesktopDialog(
|
||||||
|
maxHeight: 720,
|
||||||
|
maxWidth: 720,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Send ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendAmount.toStringAsFixed(8)))} ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker))} to this address",
|
||||||
|
style: STextStyles.desktopH3(context),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 48,
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: QrImage(
|
||||||
|
// TODO: grab coin uri scheme from somewhere
|
||||||
|
// data: "${coin.uriScheme}:$receivingAddress",
|
||||||
|
data: ref.watch(desktopExchangeModelProvider
|
||||||
|
.select((value) =>
|
||||||
|
value!.trade!.payInAddress)),
|
||||||
|
size: 290,
|
||||||
|
foregroundColor: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.accentColorDark,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 48,
|
||||||
|
),
|
||||||
|
SecondaryButton(
|
||||||
|
label: "Cancel",
|
||||||
|
width: 310,
|
||||||
|
buttonHeight: ButtonHeight.l,
|
||||||
|
onPressed: Navigator.of(context).pop,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,26 +1,18 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:stackwallet/models/exchange/incomplete_exchange.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_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart';
|
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.dart';
|
|
||||||
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_item.dart';
|
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_item.dart';
|
||||||
import 'package:stackwallet/providers/providers.dart';
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
|
||||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
|
||||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
|
||||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||||
|
|
||||||
class DesktopStep1 extends ConsumerWidget {
|
class DesktopStep1 extends ConsumerWidget {
|
||||||
const DesktopStep1({
|
const DesktopStep1({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.model,
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final IncompleteExchangeModel model;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
return Column(
|
return Column(
|
||||||
|
@ -55,7 +47,7 @@ class DesktopStep1 extends ConsumerWidget {
|
||||||
DesktopStepItem(
|
DesktopStepItem(
|
||||||
label: "You send",
|
label: "You send",
|
||||||
value:
|
value:
|
||||||
"${model.sendAmount.toStringAsFixed(8)} ${model.sendTicker.toUpperCase()}",
|
"${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendAmount.toStringAsFixed(8)))} ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker.toUpperCase()))}",
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
height: 1,
|
height: 1,
|
||||||
|
@ -64,63 +56,20 @@ class DesktopStep1 extends ConsumerWidget {
|
||||||
DesktopStepItem(
|
DesktopStepItem(
|
||||||
label: "You receive",
|
label: "You receive",
|
||||||
value:
|
value:
|
||||||
"~${model.receiveAmount.toStringAsFixed(8)} ${model.receiveTicker.toUpperCase()}",
|
"~${ref.watch(desktopExchangeModelProvider.select((value) => value!.receiveAmount.toStringAsFixed(8)))} ${ref.watch(desktopExchangeModelProvider.select((value) => value!.receiveTicker.toUpperCase()))}",
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
height: 1,
|
height: 1,
|
||||||
color: Theme.of(context).extension<StackColors>()!.background,
|
color: Theme.of(context).extension<StackColors>()!.background,
|
||||||
),
|
),
|
||||||
DesktopStepItem(
|
DesktopStepItem(
|
||||||
label: model.rateType == ExchangeRateType.estimated
|
label: ref.watch(desktopExchangeModelProvider
|
||||||
|
.select((value) => value!.rateType)) ==
|
||||||
|
ExchangeRateType.estimated
|
||||||
? "Estimated rate"
|
? "Estimated rate"
|
||||||
: "Fixed rate",
|
: "Fixed rate",
|
||||||
value: model.rateInfo,
|
value: ref.watch(desktopExchangeModelProvider
|
||||||
),
|
.select((value) => value!.rateInfo)),
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 20,
|
|
||||||
bottom: 32,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: SecondaryButton(
|
|
||||||
label: "Back",
|
|
||||||
buttonHeight: ButtonHeight.l,
|
|
||||||
onPressed: Navigator.of(context).pop,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
width: 16,
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: PrimaryButton(
|
|
||||||
label: "Next",
|
|
||||||
buttonHeight: ButtonHeight.l,
|
|
||||||
onPressed: () async {
|
|
||||||
await showDialog<void>(
|
|
||||||
context: context,
|
|
||||||
barrierColor: Colors.transparent,
|
|
||||||
barrierDismissible: false,
|
|
||||||
builder: (context) {
|
|
||||||
return DesktopDialog(
|
|
||||||
maxWidth: 720,
|
|
||||||
maxHeight: double.infinity,
|
|
||||||
child: StepScaffold(
|
|
||||||
step: 2,
|
|
||||||
model: model,
|
|
||||||
body: DesktopStep2(
|
|
||||||
model: model,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -2,9 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:stackwallet/models/contact_address_entry.dart';
|
import 'package:stackwallet/models/contact_address_entry.dart';
|
||||||
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
|
|
||||||
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart';
|
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_3.dart';
|
|
||||||
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_from_stack.dart';
|
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_from_stack.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart';
|
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart';
|
||||||
import 'package:stackwallet/providers/exchange/exchange_send_from_wallet_id_provider.dart';
|
import 'package:stackwallet/providers/exchange/exchange_send_from_wallet_id_provider.dart';
|
||||||
|
@ -18,31 +16,29 @@ import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
|
||||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
|
||||||
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
|
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
|
||||||
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
|
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
|
||||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
class DesktopStep2 extends ConsumerStatefulWidget {
|
class DesktopStep2 extends ConsumerStatefulWidget {
|
||||||
const DesktopStep2({
|
const DesktopStep2({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.model,
|
required this.enableNextChanged,
|
||||||
this.clipboard = const ClipboardWrapper(),
|
this.clipboard = const ClipboardWrapper(),
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final IncompleteExchangeModel model;
|
|
||||||
final ClipboardInterface clipboard;
|
final ClipboardInterface clipboard;
|
||||||
|
final void Function(bool) enableNextChanged;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<DesktopStep2> createState() => _DesktopStep2State();
|
ConsumerState<DesktopStep2> createState() => _DesktopStep2State();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
late final IncompleteExchangeModel model;
|
|
||||||
late final ClipboardInterface clipboard;
|
late final ClipboardInterface clipboard;
|
||||||
|
|
||||||
late final TextEditingController _toController;
|
late final TextEditingController _toController;
|
||||||
|
@ -51,8 +47,6 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
late final FocusNode _toFocusNode;
|
late final FocusNode _toFocusNode;
|
||||||
late final FocusNode _refundFocusNode;
|
late final FocusNode _refundFocusNode;
|
||||||
|
|
||||||
bool enableNext = false;
|
|
||||||
|
|
||||||
bool isStackCoin(String ticker) {
|
bool isStackCoin(String ticker) {
|
||||||
try {
|
try {
|
||||||
coinFromTickerCaseInsensitive(ticker);
|
coinFromTickerCaseInsensitive(ticker);
|
||||||
|
@ -65,10 +59,10 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
void selectRecipientAddressFromStack() async {
|
void selectRecipientAddressFromStack() async {
|
||||||
try {
|
try {
|
||||||
final coin = coinFromTickerCaseInsensitive(
|
final coin = coinFromTickerCaseInsensitive(
|
||||||
model.receiveTicker,
|
ref.read(desktopExchangeModelProvider)!.receiveTicker,
|
||||||
);
|
);
|
||||||
|
|
||||||
final address = await showDialog<String?>(
|
final info = await showDialog<Tuple2<String, String>?>(
|
||||||
context: context,
|
context: context,
|
||||||
barrierColor: Colors.transparent,
|
barrierColor: Colors.transparent,
|
||||||
builder: (context) => DesktopDialog(
|
builder: (context) => DesktopDialog(
|
||||||
|
@ -83,29 +77,25 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (address is String) {
|
if (info is Tuple2<String, String>) {
|
||||||
final manager =
|
_toController.text = info.item1;
|
||||||
ref.read(walletsChangeNotifierProvider).getManager(address);
|
ref.read(desktopExchangeModelProvider)!.recipientAddress = info.item2;
|
||||||
|
|
||||||
_toController.text = manager.walletName;
|
|
||||||
model.recipientAddress = await manager.currentReceivingAddress;
|
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logging.instance.log("$e\n$s", level: LogLevel.Info);
|
Logging.instance.log("$e\n$s", level: LogLevel.Info);
|
||||||
}
|
}
|
||||||
setState(() {
|
|
||||||
enableNext =
|
widget.enableNextChanged.call(
|
||||||
_toController.text.isNotEmpty && _refundController.text.isNotEmpty;
|
_toController.text.isNotEmpty && _refundController.text.isNotEmpty);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectRefundAddressFromStack() async {
|
void selectRefundAddressFromStack() async {
|
||||||
try {
|
try {
|
||||||
final coin = coinFromTickerCaseInsensitive(
|
final coin = coinFromTickerCaseInsensitive(
|
||||||
model.sendTicker,
|
ref.read(desktopExchangeModelProvider)!.sendTicker,
|
||||||
);
|
);
|
||||||
|
|
||||||
final address = await showDialog<String?>(
|
final info = await showDialog<Tuple2<String, String>?>(
|
||||||
context: context,
|
context: context,
|
||||||
barrierColor: Colors.transparent,
|
barrierColor: Colors.transparent,
|
||||||
builder: (context) => DesktopDialog(
|
builder: (context) => DesktopDialog(
|
||||||
|
@ -119,25 +109,20 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (address is String) {
|
if (info is Tuple2<String, String>) {
|
||||||
final manager =
|
_refundController.text = info.item1;
|
||||||
ref.read(walletsChangeNotifierProvider).getManager(address);
|
ref.read(desktopExchangeModelProvider)!.refundAddress = info.item2;
|
||||||
|
|
||||||
_refundController.text = manager.walletName;
|
|
||||||
model.refundAddress = await manager.currentReceivingAddress;
|
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logging.instance.log("$e\n$s", level: LogLevel.Info);
|
Logging.instance.log("$e\n$s", level: LogLevel.Info);
|
||||||
}
|
}
|
||||||
setState(() {
|
widget.enableNextChanged.call(
|
||||||
enableNext =
|
_toController.text.isNotEmpty && _refundController.text.isNotEmpty);
|
||||||
_toController.text.isNotEmpty && _refundController.text.isNotEmpty;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectRecipientFromAddressBook() async {
|
void selectRecipientFromAddressBook() async {
|
||||||
final coin = coinFromTickerCaseInsensitive(
|
final coin = coinFromTickerCaseInsensitive(
|
||||||
model.receiveTicker,
|
ref.read(desktopExchangeModelProvider)!.receiveTicker,
|
||||||
);
|
);
|
||||||
|
|
||||||
final entry = await showDialog<ContactAddressEntry?>(
|
final entry = await showDialog<ContactAddressEntry?>(
|
||||||
|
@ -176,17 +161,15 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
|
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
_toController.text = entry.address;
|
_toController.text = entry.address;
|
||||||
model.recipientAddress = entry.address;
|
ref.read(desktopExchangeModelProvider)!.recipientAddress = entry.address;
|
||||||
setState(() {
|
widget.enableNextChanged.call(
|
||||||
enableNext =
|
_toController.text.isNotEmpty && _refundController.text.isNotEmpty);
|
||||||
_toController.text.isNotEmpty && _refundController.text.isNotEmpty;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectRefundFromAddressBook() async {
|
void selectRefundFromAddressBook() async {
|
||||||
final coin = coinFromTickerCaseInsensitive(
|
final coin = coinFromTickerCaseInsensitive(
|
||||||
model.sendTicker,
|
ref.read(desktopExchangeModelProvider)!.sendTicker,
|
||||||
);
|
);
|
||||||
|
|
||||||
final entry = await showDialog<ContactAddressEntry?>(
|
final entry = await showDialog<ContactAddressEntry?>(
|
||||||
|
@ -225,17 +208,14 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
|
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
_refundController.text = entry.address;
|
_refundController.text = entry.address;
|
||||||
model.refundAddress = entry.address;
|
ref.read(desktopExchangeModelProvider)!.refundAddress = entry.address;
|
||||||
setState(() {
|
widget.enableNextChanged.call(
|
||||||
enableNext =
|
_toController.text.isNotEmpty && _refundController.text.isNotEmpty);
|
||||||
_toController.text.isNotEmpty && _refundController.text.isNotEmpty;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
model = widget.model;
|
|
||||||
clipboard = widget.clipboard;
|
clipboard = widget.clipboard;
|
||||||
|
|
||||||
_toController = TextEditingController();
|
_toController = TextEditingController();
|
||||||
|
@ -246,7 +226,7 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
|
|
||||||
final tuple = ref.read(exchangeSendFromWalletIdStateProvider.state).state;
|
final tuple = ref.read(exchangeSendFromWalletIdStateProvider.state).state;
|
||||||
if (tuple != null) {
|
if (tuple != null) {
|
||||||
if (model.receiveTicker.toLowerCase() ==
|
if (ref.read(desktopExchangeModelProvider)!.receiveTicker.toLowerCase() ==
|
||||||
tuple.item2.ticker.toLowerCase()) {
|
tuple.item2.ticker.toLowerCase()) {
|
||||||
ref
|
ref
|
||||||
.read(walletsChangeNotifierProvider)
|
.read(walletsChangeNotifierProvider)
|
||||||
|
@ -254,10 +234,11 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
.currentReceivingAddress
|
.currentReceivingAddress
|
||||||
.then((value) {
|
.then((value) {
|
||||||
_toController.text = value;
|
_toController.text = value;
|
||||||
model.recipientAddress = _toController.text;
|
ref.read(desktopExchangeModelProvider)!.recipientAddress =
|
||||||
|
_toController.text;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (model.sendTicker.toUpperCase() ==
|
if (ref.read(desktopExchangeModelProvider)!.sendTicker.toUpperCase() ==
|
||||||
tuple.item2.ticker.toUpperCase()) {
|
tuple.item2.ticker.toUpperCase()) {
|
||||||
ref
|
ref
|
||||||
.read(walletsChangeNotifierProvider)
|
.read(walletsChangeNotifierProvider)
|
||||||
|
@ -265,7 +246,8 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
.currentReceivingAddress
|
.currentReceivingAddress
|
||||||
.then((value) {
|
.then((value) {
|
||||||
_refundController.text = value;
|
_refundController.text = value;
|
||||||
model.refundAddress = _refundController.text;
|
ref.read(desktopExchangeModelProvider)!.refundAddress =
|
||||||
|
_refundController.text;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -316,7 +298,8 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.textFieldActiveSearchIconRight),
|
.textFieldActiveSearchIconRight),
|
||||||
),
|
),
|
||||||
if (isStackCoin(model.receiveTicker))
|
if (isStackCoin(ref.watch(desktopExchangeModelProvider
|
||||||
|
.select((value) => value!.receiveTicker))))
|
||||||
BlueTextButton(
|
BlueTextButton(
|
||||||
text: "Choose from stack",
|
text: "Choose from stack",
|
||||||
onTap: selectRecipientAddressFromStack,
|
onTap: selectRecipientAddressFromStack,
|
||||||
|
@ -349,13 +332,11 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
focusNode: _toFocusNode,
|
focusNode: _toFocusNode,
|
||||||
style: STextStyles.field(context),
|
style: STextStyles.field(context),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
widget.enableNextChanged.call(_toController.text.isNotEmpty &&
|
||||||
enableNext = _toController.text.isNotEmpty &&
|
_refundController.text.isNotEmpty);
|
||||||
_refundController.text.isNotEmpty;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
decoration: standardInputDecoration(
|
decoration: standardInputDecoration(
|
||||||
"Enter the ${model.receiveTicker.toUpperCase()} payout address",
|
"Enter the ${ref.watch(desktopExchangeModelProvider.select((value) => value!.receiveTicker.toUpperCase()))} payout address",
|
||||||
_toFocusNode,
|
_toFocusNode,
|
||||||
context,
|
context,
|
||||||
desktopMed: true,
|
desktopMed: true,
|
||||||
|
@ -380,11 +361,12 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
"sendViewClearAddressFieldButtonKey"),
|
"sendViewClearAddressFieldButtonKey"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_toController.text = "";
|
_toController.text = "";
|
||||||
model.recipientAddress = _toController.text;
|
ref
|
||||||
setState(() {
|
.read(desktopExchangeModelProvider)!
|
||||||
enableNext = _toController.text.isNotEmpty &&
|
.recipientAddress = _toController.text;
|
||||||
_refundController.text.isNotEmpty;
|
widget.enableNextChanged.call(
|
||||||
});
|
_toController.text.isNotEmpty &&
|
||||||
|
_refundController.text.isNotEmpty);
|
||||||
},
|
},
|
||||||
child: const XIcon(),
|
child: const XIcon(),
|
||||||
)
|
)
|
||||||
|
@ -398,12 +380,12 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
data!.text!.isNotEmpty) {
|
data!.text!.isNotEmpty) {
|
||||||
final content = data.text!.trim();
|
final content = data.text!.trim();
|
||||||
_toController.text = content;
|
_toController.text = content;
|
||||||
model.recipientAddress = _toController.text;
|
ref
|
||||||
setState(() {
|
.read(desktopExchangeModelProvider)!
|
||||||
enableNext =
|
.recipientAddress = _toController.text;
|
||||||
_toController.text.isNotEmpty &&
|
widget.enableNextChanged.call(
|
||||||
_refundController.text.isNotEmpty;
|
_toController.text.isNotEmpty &&
|
||||||
});
|
_refundController.text.isNotEmpty);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: _toController.text.isEmpty
|
child: _toController.text.isEmpty
|
||||||
|
@ -411,7 +393,8 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
: const XIcon(),
|
: const XIcon(),
|
||||||
),
|
),
|
||||||
if (_toController.text.isEmpty &&
|
if (_toController.text.isEmpty &&
|
||||||
isStackCoin(model.receiveTicker))
|
isStackCoin(ref.watch(desktopExchangeModelProvider
|
||||||
|
.select((value) => value!.receiveTicker))))
|
||||||
TextFieldIconButton(
|
TextFieldIconButton(
|
||||||
key: const Key("sendViewAddressBookButtonKey"),
|
key: const Key("sendViewAddressBookButtonKey"),
|
||||||
onTap: selectRecipientFromAddressBook,
|
onTap: selectRecipientFromAddressBook,
|
||||||
|
@ -430,7 +413,7 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
RoundedWhiteContainer(
|
RoundedWhiteContainer(
|
||||||
borderColor: Theme.of(context).extension<StackColors>()!.background,
|
borderColor: Theme.of(context).extension<StackColors>()!.background,
|
||||||
child: Text(
|
child: Text(
|
||||||
"This is the wallet where your ${model.receiveTicker.toUpperCase()} will be sent to.",
|
"This is the wallet where your ${ref.watch(desktopExchangeModelProvider.select((value) => value!.receiveTicker.toUpperCase()))} will be sent to.",
|
||||||
style: STextStyles.desktopTextExtraExtraSmall(context),
|
style: STextStyles.desktopTextExtraExtraSmall(context),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -447,7 +430,8 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.textFieldActiveSearchIconRight),
|
.textFieldActiveSearchIconRight),
|
||||||
),
|
),
|
||||||
if (isStackCoin(model.sendTicker))
|
if (isStackCoin(ref.watch(desktopExchangeModelProvider
|
||||||
|
.select((value) => value!.sendTicker))))
|
||||||
BlueTextButton(
|
BlueTextButton(
|
||||||
text: "Choose from stack",
|
text: "Choose from stack",
|
||||||
onTap: selectRefundAddressFromStack,
|
onTap: selectRefundAddressFromStack,
|
||||||
|
@ -479,13 +463,11 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
focusNode: _refundFocusNode,
|
focusNode: _refundFocusNode,
|
||||||
style: STextStyles.field(context),
|
style: STextStyles.field(context),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
widget.enableNextChanged.call(_toController.text.isNotEmpty &&
|
||||||
enableNext = _toController.text.isNotEmpty &&
|
_refundController.text.isNotEmpty);
|
||||||
_refundController.text.isNotEmpty;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
decoration: standardInputDecoration(
|
decoration: standardInputDecoration(
|
||||||
"Enter ${model.sendTicker.toUpperCase()} refund address",
|
"Enter ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker.toUpperCase()))} refund address",
|
||||||
_refundFocusNode,
|
_refundFocusNode,
|
||||||
context,
|
context,
|
||||||
desktopMed: true,
|
desktopMed: true,
|
||||||
|
@ -510,12 +492,13 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
"sendViewClearAddressFieldButtonKey"),
|
"sendViewClearAddressFieldButtonKey"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_refundController.text = "";
|
_refundController.text = "";
|
||||||
model.refundAddress = _refundController.text;
|
ref
|
||||||
|
.read(desktopExchangeModelProvider)!
|
||||||
|
.refundAddress = _refundController.text;
|
||||||
|
|
||||||
setState(() {
|
widget.enableNextChanged.call(
|
||||||
enableNext = _toController.text.isNotEmpty &&
|
_toController.text.isNotEmpty &&
|
||||||
_refundController.text.isNotEmpty;
|
_refundController.text.isNotEmpty);
|
||||||
});
|
|
||||||
},
|
},
|
||||||
child: const XIcon(),
|
child: const XIcon(),
|
||||||
)
|
)
|
||||||
|
@ -530,13 +513,13 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
final content = data.text!.trim();
|
final content = data.text!.trim();
|
||||||
|
|
||||||
_refundController.text = content;
|
_refundController.text = content;
|
||||||
model.refundAddress = _refundController.text;
|
ref
|
||||||
|
.read(desktopExchangeModelProvider)!
|
||||||
|
.refundAddress = _refundController.text;
|
||||||
|
|
||||||
setState(() {
|
widget.enableNextChanged.call(
|
||||||
enableNext =
|
_toController.text.isNotEmpty &&
|
||||||
_toController.text.isNotEmpty &&
|
_refundController.text.isNotEmpty);
|
||||||
_refundController.text.isNotEmpty;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: _refundController.text.isEmpty
|
child: _refundController.text.isEmpty
|
||||||
|
@ -544,7 +527,8 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
: const XIcon(),
|
: const XIcon(),
|
||||||
),
|
),
|
||||||
if (_refundController.text.isEmpty &&
|
if (_refundController.text.isEmpty &&
|
||||||
isStackCoin(model.sendTicker))
|
isStackCoin(ref.watch(desktopExchangeModelProvider
|
||||||
|
.select((value) => value!.sendTicker))))
|
||||||
TextFieldIconButton(
|
TextFieldIconButton(
|
||||||
key: const Key("sendViewAddressBookButtonKey"),
|
key: const Key("sendViewAddressBookButtonKey"),
|
||||||
onTap: selectRefundFromAddressBook,
|
onTap: selectRefundFromAddressBook,
|
||||||
|
@ -567,53 +551,6 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
style: STextStyles.desktopTextExtraExtraSmall(context),
|
style: STextStyles.desktopTextExtraExtraSmall(context),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 20,
|
|
||||||
bottom: 32,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: SecondaryButton(
|
|
||||||
label: "Back",
|
|
||||||
buttonHeight: ButtonHeight.l,
|
|
||||||
onPressed: Navigator.of(context).pop,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
width: 16,
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: PrimaryButton(
|
|
||||||
label: "Next",
|
|
||||||
enabled: enableNext,
|
|
||||||
buttonHeight: ButtonHeight.l,
|
|
||||||
onPressed: () async {
|
|
||||||
await showDialog<void>(
|
|
||||||
context: context,
|
|
||||||
barrierColor: Colors.transparent,
|
|
||||||
barrierDismissible: false,
|
|
||||||
builder: (context) {
|
|
||||||
return DesktopDialog(
|
|
||||||
maxWidth: 720,
|
|
||||||
maxHeight: double.infinity,
|
|
||||||
child: StepScaffold(
|
|
||||||
step: 3,
|
|
||||||
model: model,
|
|
||||||
body: DesktopStep3(
|
|
||||||
model: model,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,157 +1,23 @@
|
||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
|
|
||||||
import 'package:stackwallet/models/exchange/response_objects/trade.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_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart';
|
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_4.dart';
|
|
||||||
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_item.dart';
|
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_item.dart';
|
||||||
import 'package:stackwallet/providers/exchange/current_exchange_name_state_provider.dart';
|
import 'package:stackwallet/providers/exchange/current_exchange_name_state_provider.dart';
|
||||||
import 'package:stackwallet/providers/exchange/exchange_provider.dart';
|
|
||||||
import 'package:stackwallet/providers/global/trades_service_provider.dart';
|
|
||||||
import 'package:stackwallet/services/exchange/exchange_response.dart';
|
|
||||||
import 'package:stackwallet/services/notifications_api.dart';
|
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
|
|
||||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
|
||||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
|
||||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
|
||||||
import 'package:stackwallet/widgets/desktop/simple_desktop_dialog.dart';
|
|
||||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||||
|
|
||||||
class DesktopStep3 extends ConsumerStatefulWidget {
|
class DesktopStep3 extends ConsumerStatefulWidget {
|
||||||
const DesktopStep3({
|
const DesktopStep3({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.model,
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final IncompleteExchangeModel model;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<DesktopStep3> createState() => _DesktopStep3State();
|
ConsumerState<DesktopStep3> createState() => _DesktopStep3State();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DesktopStep3State extends ConsumerState<DesktopStep3> {
|
class _DesktopStep3State extends ConsumerState<DesktopStep3> {
|
||||||
late final IncompleteExchangeModel model;
|
|
||||||
|
|
||||||
Future<void> createTrade() async {
|
|
||||||
unawaited(
|
|
||||||
showDialog<void>(
|
|
||||||
context: context,
|
|
||||||
barrierDismissible: false,
|
|
||||||
builder: (_) => WillPopScope(
|
|
||||||
onWillPop: () async => false,
|
|
||||||
child: Container(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.overlay
|
|
||||||
.withOpacity(0.6),
|
|
||||||
child: const CustomLoadingOverlay(
|
|
||||||
message: "Creating a trade",
|
|
||||||
eventBus: null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final ExchangeResponse<Trade> response =
|
|
||||||
await ref.read(exchangeProvider).createTrade(
|
|
||||||
from: model.sendTicker,
|
|
||||||
to: model.receiveTicker,
|
|
||||||
fixedRate: model.rateType != ExchangeRateType.estimated,
|
|
||||||
amount: model.reversed ? model.receiveAmount : model.sendAmount,
|
|
||||||
addressTo: model.recipientAddress!,
|
|
||||||
extraId: null,
|
|
||||||
addressRefund: model.refundAddress!,
|
|
||||||
refundExtraId: "",
|
|
||||||
rateId: model.rateId,
|
|
||||||
reversed: model.reversed,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.value == null) {
|
|
||||||
if (mounted) {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
unawaited(
|
|
||||||
showDialog<void>(
|
|
||||||
context: context,
|
|
||||||
barrierDismissible: true,
|
|
||||||
builder: (_) => SimpleDesktopDialog(
|
|
||||||
title: "Failed to create trade",
|
|
||||||
message: response.exception?.toString() ?? ""),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// save trade to hive
|
|
||||||
await ref.read(tradesServiceProvider).add(
|
|
||||||
trade: response.value!,
|
|
||||||
shouldNotifyListeners: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
String status = response.value!.status;
|
|
||||||
|
|
||||||
model.trade = response.value!;
|
|
||||||
|
|
||||||
// extra info if status is waiting
|
|
||||||
if (status == "Waiting") {
|
|
||||||
status += " for deposit";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mounted) {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
unawaited(
|
|
||||||
NotificationApi.showNotification(
|
|
||||||
changeNowId: model.trade!.tradeId,
|
|
||||||
title: status,
|
|
||||||
body: "Trade ID ${model.trade!.tradeId}",
|
|
||||||
walletId: "",
|
|
||||||
iconAssetName: Assets.svg.arrowRotate,
|
|
||||||
date: model.trade!.timestamp,
|
|
||||||
shouldWatchForUpdates: true,
|
|
||||||
coinName: "coinName",
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (mounted) {
|
|
||||||
unawaited(
|
|
||||||
showDialog<void>(
|
|
||||||
context: context,
|
|
||||||
barrierColor: Colors.transparent,
|
|
||||||
barrierDismissible: false,
|
|
||||||
builder: (context) {
|
|
||||||
return DesktopDialog(
|
|
||||||
maxWidth: 720,
|
|
||||||
maxHeight: double.infinity,
|
|
||||||
child: StepScaffold(
|
|
||||||
step: 4,
|
|
||||||
model: model,
|
|
||||||
body: DesktopStep4(
|
|
||||||
model: model,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
model = widget.model;
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
|
@ -179,7 +45,7 @@ class _DesktopStep3State extends ConsumerState<DesktopStep3> {
|
||||||
DesktopStepItem(
|
DesktopStepItem(
|
||||||
label: "You send",
|
label: "You send",
|
||||||
value:
|
value:
|
||||||
"${model.sendAmount.toStringAsFixed(8)} ${model.sendTicker.toUpperCase()}",
|
"${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendAmount.toStringAsFixed(8)))} ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker.toUpperCase()))}",
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
height: 1,
|
height: 1,
|
||||||
|
@ -188,17 +54,20 @@ class _DesktopStep3State extends ConsumerState<DesktopStep3> {
|
||||||
DesktopStepItem(
|
DesktopStepItem(
|
||||||
label: "You receive",
|
label: "You receive",
|
||||||
value:
|
value:
|
||||||
"~${model.receiveAmount.toStringAsFixed(8)} ${model.receiveTicker.toUpperCase()}",
|
"~${ref.watch(desktopExchangeModelProvider.select((value) => value!.receiveAmount.toStringAsFixed(8)))} ${ref.watch(desktopExchangeModelProvider.select((value) => value!.receiveTicker.toUpperCase()))}",
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
height: 1,
|
height: 1,
|
||||||
color: Theme.of(context).extension<StackColors>()!.background,
|
color: Theme.of(context).extension<StackColors>()!.background,
|
||||||
),
|
),
|
||||||
DesktopStepItem(
|
DesktopStepItem(
|
||||||
label: model.rateType == ExchangeRateType.estimated
|
label: ref.watch(desktopExchangeModelProvider
|
||||||
|
.select((value) => value!.rateType)) ==
|
||||||
|
ExchangeRateType.estimated
|
||||||
? "Estimated rate"
|
? "Estimated rate"
|
||||||
: "Fixed rate",
|
: "Fixed rate",
|
||||||
value: model.rateInfo,
|
value: ref.watch(desktopExchangeModelProvider
|
||||||
|
.select((value) => value!.rateInfo)),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
height: 1,
|
height: 1,
|
||||||
|
@ -206,8 +75,11 @@ class _DesktopStep3State extends ConsumerState<DesktopStep3> {
|
||||||
),
|
),
|
||||||
DesktopStepItem(
|
DesktopStepItem(
|
||||||
vertical: true,
|
vertical: true,
|
||||||
label: "Recipient ${model.receiveTicker.toUpperCase()} address",
|
label:
|
||||||
value: model.recipientAddress!,
|
"Recipient ${ref.watch(desktopExchangeModelProvider.select((value) => value!.receiveTicker.toUpperCase()))} address",
|
||||||
|
value: ref.watch(desktopExchangeModelProvider
|
||||||
|
.select((value) => value!.recipientAddress)) ??
|
||||||
|
"Error",
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
height: 1,
|
height: 1,
|
||||||
|
@ -215,35 +87,11 @@ class _DesktopStep3State extends ConsumerState<DesktopStep3> {
|
||||||
),
|
),
|
||||||
DesktopStepItem(
|
DesktopStepItem(
|
||||||
vertical: true,
|
vertical: true,
|
||||||
label: "Refund ${model.sendTicker.toUpperCase()} address",
|
label:
|
||||||
value: model.refundAddress!,
|
"Refund ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker.toUpperCase()))} address",
|
||||||
),
|
value: ref.watch(desktopExchangeModelProvider
|
||||||
],
|
.select((value) => value!.refundAddress)) ??
|
||||||
),
|
"Error",
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 20,
|
|
||||||
bottom: 32,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: SecondaryButton(
|
|
||||||
label: "Back",
|
|
||||||
buttonHeight: ButtonHeight.l,
|
|
||||||
onPressed: Navigator.of(context).pop,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
width: 16,
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: PrimaryButton(
|
|
||||||
label: "Confirm",
|
|
||||||
buttonHeight: ButtonHeight.l,
|
|
||||||
onPressed: createTrade,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,38 +1,26 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:decimal/decimal.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart';
|
||||||
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
|
|
||||||
import 'package:stackwallet/pages/exchange_view/send_from_view.dart';
|
|
||||||
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_item.dart';
|
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_item.dart';
|
||||||
import 'package:stackwallet/providers/providers.dart';
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
import 'package:stackwallet/route_generator.dart';
|
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
|
||||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
|
||||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
|
||||||
import 'package:stackwallet/widgets/rounded_container.dart';
|
import 'package:stackwallet/widgets/rounded_container.dart';
|
||||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||||
|
|
||||||
class DesktopStep4 extends ConsumerStatefulWidget {
|
class DesktopStep4 extends ConsumerStatefulWidget {
|
||||||
const DesktopStep4({
|
const DesktopStep4({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.model,
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final IncompleteExchangeModel model;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<DesktopStep4> createState() => _DesktopStep4State();
|
ConsumerState<DesktopStep4> createState() => _DesktopStep4State();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DesktopStep4State extends ConsumerState<DesktopStep4> {
|
class _DesktopStep4State extends ConsumerState<DesktopStep4> {
|
||||||
late final IncompleteExchangeModel model;
|
|
||||||
|
|
||||||
String _statusString = "New";
|
String _statusString = "New";
|
||||||
|
|
||||||
Timer? _statusTimer;
|
Timer? _statusTimer;
|
||||||
|
@ -51,8 +39,13 @@ class _DesktopStep4State extends ConsumerState<DesktopStep4> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _updateStatus() async {
|
Future<void> _updateStatus() async {
|
||||||
final statusResponse =
|
final trade = ref.read(desktopExchangeModelProvider)?.trade;
|
||||||
await ref.read(exchangeProvider).updateTrade(model.trade!);
|
|
||||||
|
if (trade == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final statusResponse = await ref.read(exchangeProvider).updateTrade(trade);
|
||||||
String status = "Waiting";
|
String status = "Waiting";
|
||||||
if (statusResponse.value != null) {
|
if (statusResponse.value != null) {
|
||||||
status = statusResponse.value!.status;
|
status = statusResponse.value!.status;
|
||||||
|
@ -72,8 +65,6 @@ class _DesktopStep4State extends ConsumerState<DesktopStep4> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
model = widget.model;
|
|
||||||
|
|
||||||
_statusTimer = Timer.periodic(const Duration(seconds: 60), (_) {
|
_statusTimer = Timer.periodic(const Duration(seconds: 60), (_) {
|
||||||
_updateStatus();
|
_updateStatus();
|
||||||
});
|
});
|
||||||
|
@ -93,14 +84,14 @@ class _DesktopStep4State extends ConsumerState<DesktopStep4> {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Send ${model.sendTicker.toUpperCase()} to the address below",
|
"Send ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker.toUpperCase()))} to the address below",
|
||||||
style: STextStyles.desktopTextMedium(context),
|
style: STextStyles.desktopTextMedium(context),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 8,
|
height: 8,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"Send ${model.sendTicker.toUpperCase()} to the address below. Once it is received, ${model.trade!.exchangeName} will send the ${model.receiveTicker.toUpperCase()} to the recipient address you provided. You can find this trade details and check its status in the list of trades.",
|
"Send ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker.toUpperCase()))} to the address below. Once it is received, ${ref.watch(desktopExchangeModelProvider.select((value) => value!.trade?.exchangeName))} will send the ${ref.watch(desktopExchangeModelProvider.select((value) => value!.receiveTicker.toUpperCase()))} to the recipient address you provided. You can find this trade details and check its status in the list of trades.",
|
||||||
style: STextStyles.desktopTextExtraExtraSmall(context),
|
style: STextStyles.desktopTextExtraExtraSmall(context),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
|
@ -111,7 +102,7 @@ class _DesktopStep4State extends ConsumerState<DesktopStep4> {
|
||||||
child: RichText(
|
child: RichText(
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
text:
|
text:
|
||||||
"You must send at least ${model.sendAmount.toString()} ${model.sendTicker}. ",
|
"You must send at least ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendAmount.toString()))} ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker))}. ",
|
||||||
style: STextStyles.label700(context).copyWith(
|
style: STextStyles.label700(context).copyWith(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
|
@ -121,7 +112,7 @@ class _DesktopStep4State extends ConsumerState<DesktopStep4> {
|
||||||
children: [
|
children: [
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text:
|
text:
|
||||||
"If you send less than ${model.sendAmount.toString()} ${model.sendTicker}, your transaction may not be converted and it may not be refunded.",
|
"If you send less than ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendAmount.toString()))} ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker))}, your transaction may not be converted and it may not be refunded.",
|
||||||
style: STextStyles.label(context).copyWith(
|
style: STextStyles.label(context).copyWith(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
|
@ -143,8 +134,11 @@ class _DesktopStep4State extends ConsumerState<DesktopStep4> {
|
||||||
children: [
|
children: [
|
||||||
DesktopStepItem(
|
DesktopStepItem(
|
||||||
vertical: true,
|
vertical: true,
|
||||||
label: "Send ${model.sendTicker.toUpperCase()} to this address",
|
label:
|
||||||
value: model.trade!.payInAddress,
|
"Send ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker.toUpperCase()))} to this address",
|
||||||
|
value: ref.watch(desktopExchangeModelProvider
|
||||||
|
.select((value) => value!.trade?.payInAddress)) ??
|
||||||
|
"Error",
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
height: 1,
|
height: 1,
|
||||||
|
@ -153,7 +147,7 @@ class _DesktopStep4State extends ConsumerState<DesktopStep4> {
|
||||||
DesktopStepItem(
|
DesktopStepItem(
|
||||||
label: "Amount",
|
label: "Amount",
|
||||||
value:
|
value:
|
||||||
"${model.sendAmount.toStringAsFixed(8)} ${model.sendTicker.toUpperCase()}",
|
"${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendAmount.toStringAsFixed(8)))} ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker.toUpperCase()))}",
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
height: 1,
|
height: 1,
|
||||||
|
@ -161,7 +155,9 @@ class _DesktopStep4State extends ConsumerState<DesktopStep4> {
|
||||||
),
|
),
|
||||||
DesktopStepItem(
|
DesktopStepItem(
|
||||||
label: "Trade ID",
|
label: "Trade ID",
|
||||||
value: model.trade!.tradeId,
|
value: ref.watch(desktopExchangeModelProvider
|
||||||
|
.select((value) => value!.trade?.tradeId)) ??
|
||||||
|
"Error",
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
height: 1,
|
height: 1,
|
||||||
|
@ -191,110 +187,6 @@ class _DesktopStep4State extends ConsumerState<DesktopStep4> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 20,
|
|
||||||
bottom: 32,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: SecondaryButton(
|
|
||||||
label: "Send from Stack Wallet",
|
|
||||||
buttonHeight: ButtonHeight.l,
|
|
||||||
onPressed: () {
|
|
||||||
final trade = model.trade!;
|
|
||||||
final amount = Decimal.parse(trade.payInAmount);
|
|
||||||
final address = trade.payInAddress;
|
|
||||||
|
|
||||||
final coin =
|
|
||||||
coinFromTickerCaseInsensitive(trade.payInCurrency);
|
|
||||||
|
|
||||||
showDialog<void>(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => Navigator(
|
|
||||||
initialRoute: SendFromView.routeName,
|
|
||||||
onGenerateRoute: RouteGenerator.generateRoute,
|
|
||||||
onGenerateInitialRoutes: (_, __) {
|
|
||||||
return [
|
|
||||||
FadePageRoute(
|
|
||||||
SendFromView(
|
|
||||||
coin: coin,
|
|
||||||
trade: trade,
|
|
||||||
amount: amount,
|
|
||||||
address: address,
|
|
||||||
shouldPopRoot: true,
|
|
||||||
fromDesktopStep4: true,
|
|
||||||
),
|
|
||||||
const RouteSettings(
|
|
||||||
name: SendFromView.routeName,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
width: 16,
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: PrimaryButton(
|
|
||||||
label: "Show QR code",
|
|
||||||
buttonHeight: ButtonHeight.l,
|
|
||||||
onPressed: () {
|
|
||||||
showDialog<dynamic>(
|
|
||||||
context: context,
|
|
||||||
barrierColor: Colors.transparent,
|
|
||||||
barrierDismissible: true,
|
|
||||||
builder: (_) {
|
|
||||||
return DesktopDialog(
|
|
||||||
maxHeight: 720,
|
|
||||||
maxWidth: 720,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Send ${model.sendAmount.toStringAsFixed(8)} ${model.sendTicker} to this address",
|
|
||||||
style: STextStyles.desktopH3(context),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 48,
|
|
||||||
),
|
|
||||||
Center(
|
|
||||||
child: QrImage(
|
|
||||||
// TODO: grab coin uri scheme from somewhere
|
|
||||||
// data: "${coin.uriScheme}:$receivingAddress",
|
|
||||||
data: model.trade!.payInAddress,
|
|
||||||
size: 290,
|
|
||||||
foregroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorDark,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 48,
|
|
||||||
),
|
|
||||||
SecondaryButton(
|
|
||||||
label: "Cancel",
|
|
||||||
width: 310,
|
|
||||||
buttonHeight: ButtonHeight.l,
|
|
||||||
onPressed: Navigator.of(context).pop,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||||
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_coin_icon.dart';
|
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_coin_icon.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
class DesktopChooseFromStack extends ConsumerStatefulWidget {
|
class DesktopChooseFromStack extends ConsumerStatefulWidget {
|
||||||
const DesktopChooseFromStack({
|
const DesktopChooseFromStack({
|
||||||
|
@ -222,8 +223,18 @@ class _DesktopChooseFromStackState
|
||||||
),
|
),
|
||||||
BlueTextButton(
|
BlueTextButton(
|
||||||
text: "Select wallet",
|
text: "Select wallet",
|
||||||
onTap: () {
|
onTap: () async {
|
||||||
Navigator.of(context).pop(manager.walletId);
|
final address =
|
||||||
|
await manager.currentReceivingAddress;
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
Navigator.of(context).pop(
|
||||||
|
Tuple2(
|
||||||
|
manager.walletName,
|
||||||
|
address,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -22,41 +22,54 @@ class SimpleDesktopDialog extends StatelessWidget {
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Padding(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
padding: const EdgeInsets.only(left: 32),
|
||||||
children: [
|
child: Row(
|
||||||
Text(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
title,
|
children: [
|
||||||
style: STextStyles.desktopH3(context),
|
Text(
|
||||||
),
|
title,
|
||||||
const DesktopDialogCloseButton(),
|
style: STextStyles.desktopH3(context),
|
||||||
],
|
),
|
||||||
|
const DesktopDialogCloseButton(),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Text(
|
Padding(
|
||||||
message,
|
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
style: STextStyles.desktopTextSmall(context),
|
child: Text(
|
||||||
|
message,
|
||||||
|
style: STextStyles.desktopTextSmall(context),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const Spacer(
|
const Spacer(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
),
|
),
|
||||||
Row(
|
Padding(
|
||||||
children: [
|
padding: const EdgeInsets.only(
|
||||||
const Spacer(),
|
left: 32,
|
||||||
const SizedBox(
|
right: 32,
|
||||||
width: 16,
|
bottom: 32,
|
||||||
),
|
),
|
||||||
Expanded(
|
child: Row(
|
||||||
child: PrimaryButton(
|
children: [
|
||||||
label: "Ok",
|
const Spacer(),
|
||||||
buttonHeight: ButtonHeight.l,
|
const SizedBox(
|
||||||
onPressed: Navigator.of(
|
width: 16,
|
||||||
context,
|
|
||||||
rootNavigator: true,
|
|
||||||
).pop,
|
|
||||||
),
|
),
|
||||||
),
|
Expanded(
|
||||||
],
|
child: PrimaryButton(
|
||||||
|
label: "Ok",
|
||||||
|
buttonHeight: ButtonHeight.l,
|
||||||
|
onPressed: Navigator.of(
|
||||||
|
context,
|
||||||
|
rootNavigator: true,
|
||||||
|
).pop,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
57
lib/widgets/fade_stack.dart
Normal file
57
lib/widgets/fade_stack.dart
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class FadeStack extends StatefulWidget {
|
||||||
|
final int index;
|
||||||
|
final List<Widget> children;
|
||||||
|
|
||||||
|
const FadeStack({
|
||||||
|
super.key,
|
||||||
|
required this.index,
|
||||||
|
required this.children,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
FadeStackState createState() => FadeStackState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class FadeStackState extends State<FadeStack>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
late final AnimationController animationController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(FadeStack oldWidget) {
|
||||||
|
if (widget.index != oldWidget.index) {
|
||||||
|
animationController.forward(from: 0.0);
|
||||||
|
}
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
animationController = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: const Duration(
|
||||||
|
milliseconds: 250,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
animationController.forward();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
animationController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: animationController,
|
||||||
|
child: IndexedStack(
|
||||||
|
index: widget.index,
|
||||||
|
children: widget.children,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue