handle refund address support

This commit is contained in:
julian 2023-02-06 13:45:22 -06:00
parent b19a3dbbf8
commit 8061f0811d
2 changed files with 285 additions and 250 deletions

View file

@ -8,6 +8,7 @@ import 'package:stackwallet/pages/exchange_view/choose_from_stack_view.dart';
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_3_view.dart';
import 'package:stackwallet/pages/exchange_view/sub_widgets/step_row.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/exchange/majestic_bank/majestic_bank_exchange.dart';
import 'package:stackwallet/utilities/address_utils.dart';
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
@ -123,6 +124,10 @@ class _Step2ViewState extends ConsumerState<Step2View> {
@override
Widget build(BuildContext context) {
final supportsRefund =
ref.watch(currentExchangeNameStateProvider.state).state !=
MajesticBankExchange.exchangeName;
return Background(
child: Scaffold(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
@ -217,8 +222,9 @@ class _Step2ViewState extends ConsumerState<Step2View> {
setState(() {
enableNext =
_toController.text.isNotEmpty &&
_refundController
.text.isNotEmpty;
(_refundController
.text.isNotEmpty ||
!supportsRefund);
});
}
});
@ -291,8 +297,9 @@ class _Step2ViewState extends ConsumerState<Step2View> {
setState(() {
enableNext = _toController
.text.isNotEmpty &&
_refundController
.text.isNotEmpty;
(_refundController.text
.isNotEmpty ||
!supportsRefund);
});
},
child: const XIcon(),
@ -318,8 +325,10 @@ class _Step2ViewState extends ConsumerState<Step2View> {
enableNext = _toController
.text
.isNotEmpty &&
_refundController
.text.isNotEmpty;
(_refundController
.text
.isNotEmpty ||
!supportsRefund);
});
}
},
@ -367,8 +376,9 @@ class _Step2ViewState extends ConsumerState<Step2View> {
setState(() {
enableNext = _toController
.text.isNotEmpty &&
_refundController
.text.isNotEmpty;
(_refundController.text
.isNotEmpty ||
!supportsRefund);
});
});
},
@ -396,8 +406,9 @@ class _Step2ViewState extends ConsumerState<Step2View> {
setState(() {
enableNext = _toController
.text.isNotEmpty &&
_refundController
.text.isNotEmpty;
(_refundController.text
.isNotEmpty ||
!supportsRefund);
});
} else {
_toController.text =
@ -408,8 +419,9 @@ class _Step2ViewState extends ConsumerState<Step2View> {
setState(() {
enableNext = _toController
.text.isNotEmpty &&
_refundController
.text.isNotEmpty;
(_refundController.text
.isNotEmpty ||
!supportsRefund);
});
}
} on PlatformException catch (e, s) {
@ -440,133 +452,230 @@ class _Step2ViewState extends ConsumerState<Step2View> {
const SizedBox(
height: 24,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Refund Wallet (required)",
style: STextStyles.smallMed12(context),
),
if (isStackCoin(model.sendTicker))
CustomTextButton(
text: "Choose from Stack",
onTap: () {
try {
final coin =
coinFromTickerCaseInsensitive(
model.sendTicker,
);
Navigator.of(context)
.pushNamed(
ChooseFromStackView.routeName,
arguments: coin,
)
.then((value) async {
if (value is String) {
final manager = ref
.read(
walletsChangeNotifierProvider)
.getManager(value);
if (supportsRefund)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Refund Wallet (required)",
style: STextStyles.smallMed12(context),
),
if (isStackCoin(model.sendTicker))
CustomTextButton(
text: "Choose from Stack",
onTap: () {
try {
final coin =
coinFromTickerCaseInsensitive(
model.sendTicker,
);
Navigator.of(context)
.pushNamed(
ChooseFromStackView.routeName,
arguments: coin,
)
.then((value) async {
if (value is String) {
final manager = ref
.read(
walletsChangeNotifierProvider)
.getManager(value);
_refundController.text =
manager.walletName;
model.refundAddress = await manager
.currentReceivingAddress;
}
setState(() {
enableNext = _toController
.text.isNotEmpty &&
_refundController.text.isNotEmpty;
_refundController.text =
manager.walletName;
model.refundAddress = await manager
.currentReceivingAddress;
}
setState(() {
enableNext =
_toController.text.isNotEmpty &&
_refundController
.text.isNotEmpty;
});
});
});
} catch (e, s) {
Logging.instance
.log("$e\n$s", level: LogLevel.Info);
}
},
),
],
),
const SizedBox(
height: 4,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
} catch (e, s) {
Logging.instance.log("$e\n$s",
level: LogLevel.Info);
}
},
),
],
),
child: TextField(
key: const Key(
"refundExchangeStep2ViewAddressFieldKey"),
controller: _refundController,
readOnly: false,
autocorrect: false,
enableSuggestions: false,
// inputFormatters: <TextInputFormatter>[
// FilteringTextInputFormatter.allow(RegExp("[a-zA-Z0-9]{34}")),
// ],
toolbarOptions: const ToolbarOptions(
copy: false,
cut: false,
paste: true,
selectAll: false,
if (supportsRefund)
const SizedBox(
height: 4,
),
if (supportsRefund)
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
focusNode: _refundFocusNode,
style: STextStyles.field(context),
onChanged: (value) {
setState(() {});
},
decoration: standardInputDecoration(
"Enter ${model.sendTicker.toUpperCase()} refund address",
_refundFocusNode,
context,
).copyWith(
contentPadding: const EdgeInsets.only(
left: 16,
top: 6,
bottom: 8,
right: 5,
child: TextField(
key: const Key(
"refundExchangeStep2ViewAddressFieldKey"),
controller: _refundController,
readOnly: false,
autocorrect: false,
enableSuggestions: false,
// inputFormatters: <TextInputFormatter>[
// FilteringTextInputFormatter.allow(RegExp("[a-zA-Z0-9]{34}")),
// ],
toolbarOptions: const ToolbarOptions(
copy: false,
cut: false,
paste: true,
selectAll: false,
),
suffixIcon: Padding(
padding: _refundController.text.isEmpty
? const EdgeInsets.only(right: 16)
: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
_refundController.text.isNotEmpty
? TextFieldIconButton(
key: const Key(
"sendViewClearAddressFieldButtonKey"),
onTap: () {
_refundController.text = "";
model.refundAddress =
_refundController.text;
focusNode: _refundFocusNode,
style: STextStyles.field(context),
onChanged: (value) {
setState(() {});
},
decoration: standardInputDecoration(
"Enter ${model.sendTicker.toUpperCase()} refund address",
_refundFocusNode,
context,
).copyWith(
contentPadding: const EdgeInsets.only(
left: 16,
top: 6,
bottom: 8,
right: 5,
),
suffixIcon: Padding(
padding: _refundController.text.isEmpty
? const EdgeInsets.only(right: 16)
: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
_refundController.text.isNotEmpty
? TextFieldIconButton(
key: const Key(
"sendViewClearAddressFieldButtonKey"),
onTap: () {
_refundController.text = "";
model.refundAddress =
_refundController.text;
setState(() {
enableNext = _toController
.text
.isNotEmpty &&
_refundController
.text.isNotEmpty;
});
},
child: const XIcon(),
)
: TextFieldIconButton(
key: const Key(
"sendViewPasteAddressFieldButtonKey"),
onTap: () async {
final ClipboardData? data =
await clipboard.getData(
Clipboard
.kTextPlain);
if (data?.text != null &&
data!
.text!.isNotEmpty) {
final content =
data.text!.trim();
_refundController.text =
content;
model.refundAddress =
_refundController
.text;
setState(() {
enableNext = _toController
.text
.isNotEmpty &&
_refundController
.text
.isNotEmpty;
});
}
},
child: _refundController
.text.isEmpty
? const ClipboardIcon()
: const XIcon(),
),
if (_refundController.text.isEmpty)
TextFieldIconButton(
key: const Key(
"sendViewAddressBookButtonKey"),
onTap: () {
ref
.read(
exchangeFlowIsActiveStateProvider
.state)
.state = true;
Navigator.of(context)
.pushNamed(
AddressBookView.routeName,
)
.then((_) {
ref
.read(
exchangeFlowIsActiveStateProvider
.state)
.state = false;
final address = ref
.read(
exchangeFromAddressBookAddressStateProvider
.state)
.state;
if (address.isNotEmpty) {
_refundController.text =
address;
model.refundAddress =
_refundController.text;
}
setState(() {
enableNext = _toController
.text.isNotEmpty &&
_refundController
.text.isNotEmpty;
});
},
child: const XIcon(),
)
: TextFieldIconButton(
key: const Key(
"sendViewPasteAddressFieldButtonKey"),
onTap: () async {
final ClipboardData? data =
await clipboard.getData(
Clipboard.kTextPlain);
if (data?.text != null &&
data!.text!.isNotEmpty) {
final content =
data.text!.trim();
});
},
child: const AddressBookIcon(),
),
if (_refundController.text.isEmpty)
TextFieldIconButton(
key: const Key(
"sendViewScanQrButtonKey"),
onTap: () async {
try {
final qrResult =
await scanner.scan();
final results =
AddressUtils.parseUri(
qrResult.rawContent);
if (results.isNotEmpty) {
// auto fill address
_refundController.text =
content;
results["address"] ??
"";
model.refundAddress =
_refundController.text;
setState(() {
enableNext = _toController
.text
.isNotEmpty &&
_refundController
.text.isNotEmpty;
});
} else {
_refundController.text =
qrResult.rawContent;
model.refundAddress =
_refundController.text;
@ -578,116 +687,33 @@ class _Step2ViewState extends ConsumerState<Step2View> {
.text.isNotEmpty;
});
}
},
child: _refundController
.text.isEmpty
? const ClipboardIcon()
: const XIcon(),
),
if (_refundController.text.isEmpty)
TextFieldIconButton(
key: const Key(
"sendViewAddressBookButtonKey"),
onTap: () {
ref
.read(
exchangeFlowIsActiveStateProvider
.state)
.state = true;
Navigator.of(context)
.pushNamed(
AddressBookView.routeName,
)
.then((_) {
ref
.read(
exchangeFlowIsActiveStateProvider
.state)
.state = false;
final address = ref
.read(
exchangeFromAddressBookAddressStateProvider
.state)
.state;
if (address.isNotEmpty) {
_refundController.text =
address;
model.refundAddress =
_refundController.text;
} on PlatformException catch (e, s) {
Logging.instance.log(
"Failed to get camera permissions while trying to scan qr code in SendView: $e\n$s",
level: LogLevel.Warning,
);
}
setState(() {
enableNext = _toController
.text.isNotEmpty &&
_refundController
.text.isNotEmpty;
});
});
},
child: const AddressBookIcon(),
),
if (_refundController.text.isEmpty)
TextFieldIconButton(
key: const Key(
"sendViewScanQrButtonKey"),
onTap: () async {
try {
final qrResult =
await scanner.scan();
final results =
AddressUtils.parseUri(
qrResult.rawContent);
if (results.isNotEmpty) {
// auto fill address
_refundController.text =
results["address"] ?? "";
model.refundAddress =
_refundController.text;
setState(() {
enableNext = _toController
.text.isNotEmpty &&
_refundController
.text.isNotEmpty;
});
} else {
_refundController.text =
qrResult.rawContent;
model.refundAddress =
_refundController.text;
setState(() {
enableNext = _toController
.text.isNotEmpty &&
_refundController
.text.isNotEmpty;
});
}
} on PlatformException catch (e, s) {
Logging.instance.log(
"Failed to get camera permissions while trying to scan qr code in SendView: $e\n$s",
level: LogLevel.Warning,
);
}
},
child: const QrCodeIcon(),
),
],
},
child: const QrCodeIcon(),
),
],
),
),
),
),
),
),
),
const SizedBox(
height: 6,
),
RoundedWhiteContainer(
child: Text(
"In case something goes wrong during the exchange, we might need a refund address so we can return your coins back to you.",
style: STextStyles.label(context),
if (supportsRefund)
const SizedBox(
height: 6,
),
if (supportsRefund)
RoundedWhiteContainer(
child: Text(
"In case something goes wrong during the exchange, we might need a refund address so we can return your coins back to you.",
style: STextStyles.label(context),
),
),
),
const Spacer(),
Row(
children: [

View file

@ -7,9 +7,11 @@ import 'package:stackwallet/models/exchange/response_objects/trade.dart';
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_4_view.dart';
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
import 'package:stackwallet/pages/exchange_view/sub_widgets/step_row.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/exchange/majestic_bank/majestic_bank_exchange.dart';
import 'package:stackwallet/services/notifications_api.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
@ -51,6 +53,10 @@ class _Step3ViewState extends ConsumerState<Step3View> {
@override
Widget build(BuildContext context) {
final supportsRefund =
ref.watch(currentExchangeNameStateProvider.state).state !=
MajesticBankExchange.exchangeName;
return Background(
child: Scaffold(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
@ -174,27 +180,29 @@ class _Step3ViewState extends ConsumerState<Step3View> {
],
),
),
const SizedBox(
height: 8,
),
RoundedWhiteContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Refund ${model.sendTicker.toUpperCase()} address",
style: STextStyles.itemSubtitle(context),
),
const SizedBox(
height: 4,
),
Text(
model.refundAddress!,
style: STextStyles.itemSubtitle12(context),
)
],
if (supportsRefund)
const SizedBox(
height: 8,
),
if (supportsRefund)
RoundedWhiteContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Refund ${model.sendTicker.toUpperCase()} address",
style: STextStyles.itemSubtitle(context),
),
const SizedBox(
height: 4,
),
Text(
model.refundAddress!,
style: STextStyles.itemSubtitle12(context),
)
],
),
),
),
const SizedBox(
height: 8,
),
@ -259,8 +267,9 @@ class _Step3ViewState extends ConsumerState<Step3View> {
addressTo:
model.recipientAddress!,
extraId: null,
addressRefund:
model.refundAddress!,
addressRefund: supportsRefund
? model.refundAddress!
: "",
refundExtraId: "",
rateId: model.rateId,
reversed: model.reversed,