2024-07-14 15:34:24 +00:00
import ' package:flutter/material.dart ' ;
import ' package:provider/provider.dart ' ;
import ' package:haveno_flutter_app/providers/offers_provider.dart ' ;
import ' package:haveno_flutter_app/proto/compiled/pb.pb.dart ' ;
import ' package:fixnum/fixnum.dart ' as fixnum ;
import ' dart:convert ' ; // Import the dart:convert library
class NewTradeOfferForm extends StatefulWidget {
final GlobalKey < FormState > formKey ;
final List < PaymentAccount > paymentAccounts ;
2024-07-15 19:14:30 +00:00
final String direction ; // New argument for direction ('BUY' or 'SELL')
2024-07-14 15:34:24 +00:00
const NewTradeOfferForm ( {
required this . formKey ,
required this . paymentAccounts ,
2024-07-15 19:14:30 +00:00
required this . direction ,
2024-07-14 15:34:24 +00:00
} ) ;
@ override
__NewTradeOfferFormState createState ( ) = > __NewTradeOfferFormState ( ) ;
}
class __NewTradeOfferFormState extends State < NewTradeOfferForm > {
PaymentAccount ? _selectedPaymentAccount ;
TradeCurrency ? _selectedTradeCurrency ;
int _selectedPricingTypeIndex = 0 ; // 0 for Fixed, 1 for Dynamic
2024-07-15 19:14:30 +00:00
bool _reserveExactAmount = false ;
2024-07-14 15:34:24 +00:00
final TextEditingController _priceController = TextEditingController ( ) ;
final TextEditingController _depositController =
TextEditingController ( text: ' 15 ' ) ;
final TextEditingController _marginController =
TextEditingController ( text: ' 0 ' ) ;
final TextEditingController _amountController = TextEditingController ( ) ;
final TextEditingController _minAmountController = TextEditingController ( ) ;
final TextEditingController _triggerPriceController = TextEditingController ( ) ;
@ override
Widget build ( BuildContext context ) {
2024-07-15 19:14:30 +00:00
final isBuy = widget . direction = = ' BUY ' ;
2024-07-14 15:34:24 +00:00
return Padding (
padding: MediaQuery . of ( context ) . viewInsets ,
child: Container (
padding: const EdgeInsets . all ( 16.0 ) ,
child: Form (
key: widget . formKey ,
child: SingleChildScrollView (
child: Column (
mainAxisSize: MainAxisSize . min ,
children: < Widget > [
2024-07-15 19:14:30 +00:00
Text (
' Open a New XMR ${ isBuy ? ' Buy ' : ' Sell ' } Offer ' ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
2024-07-14 15:34:24 +00:00
const SizedBox ( height: 16.0 ) ,
ToggleButtons (
isSelected: [
_selectedPricingTypeIndex = = 0 ,
_selectedPricingTypeIndex = = 1
] ,
onPressed: ( index ) {
setState ( ( ) {
_selectedPricingTypeIndex = index ;
} ) ;
} ,
children: const [
Padding (
padding: EdgeInsets . symmetric ( horizontal: 16.0 ) ,
child: Text ( ' Fixed ' ) ,
) ,
Padding (
padding: EdgeInsets . symmetric ( horizontal: 16.0 ) ,
child: Text ( ' Dynamic ' ) ,
) ,
] ,
) ,
const SizedBox ( height: 16.0 ) ,
DropdownButtonFormField < PaymentAccount > (
decoration: const InputDecoration (
2024-07-15 19:14:30 +00:00
labelText: ' Your Sender Account ' ,
2024-07-14 15:34:24 +00:00
border: OutlineInputBorder ( ) ,
) ,
value: _selectedPaymentAccount ,
items: widget . paymentAccounts . map ( ( account ) {
return DropdownMenuItem < PaymentAccount > (
value: account ,
child: Text ( account . accountName ) ,
) ;
} ) . toList ( ) ,
onChanged: ( account ) {
setState ( ( ) {
_selectedPaymentAccount = account ;
_selectedTradeCurrency = null ; // Reset currency code
} ) ;
} ,
validator: ( value ) {
if ( value = = null ) {
return ' Please select a payment account ' ;
}
return null ;
} ,
) ,
const SizedBox ( height: 16.0 ) ,
DropdownButtonFormField < TradeCurrency > (
decoration: const InputDecoration (
labelText: ' Currency Code ' ,
border: OutlineInputBorder ( ) ,
) ,
value: _selectedTradeCurrency ,
items: _selectedPaymentAccount ? . tradeCurrencies
. map ( ( tradeCurrency ) {
return DropdownMenuItem < TradeCurrency > (
value: tradeCurrency ,
child: Text ( tradeCurrency . name ) ,
) ;
} ) . toList ( ) ? ?
[ ] ,
onChanged: ( value ) {
setState ( ( ) {
_selectedTradeCurrency = value ;
} ) ;
} ,
validator: ( value ) {
if ( value = = null ) {
return ' Please select a currency code ' ;
}
return null ;
} ,
) ,
const SizedBox ( height: 16.0 ) ,
if ( _selectedPricingTypeIndex = = 0 )
TextFormField (
controller: _priceController ,
decoration: const InputDecoration (
labelText: ' Price ' ,
border: OutlineInputBorder ( ) ,
) ,
validator: ( value ) {
if ( value = = null | | value . isEmpty ) {
return ' Please enter the price ' ;
}
return null ;
} ,
) ,
if ( _selectedPricingTypeIndex = = 1 )
TextFormField (
controller: _marginController ,
2024-07-15 19:14:30 +00:00
decoration: InputDecoration (
labelText: isBuy
? ' Market Price Below Margin (%) '
: ' Market Price Above Margin (%) ' ,
border: const OutlineInputBorder ( ) ,
2024-07-14 15:34:24 +00:00
) ,
validator: ( value ) {
if ( value = = null | | value . isEmpty ) {
return ' Please enter the market price margin ' ;
}
final margin = double . tryParse ( value ) ;
if ( margin = = null | | margin < 1 | | margin > 90 ) {
return ' Please enter a value between 1 and 90 ' ;
}
return null ;
} ,
) ,
const SizedBox ( height: 16.0 ) ,
TextFormField (
controller: _amountController ,
2024-07-15 19:14:30 +00:00
decoration: InputDecoration (
labelText: ' Amount of XMR to ${ isBuy ? ' Buy ' : ' Sell ' } ' ,
border: const OutlineInputBorder ( ) ,
2024-07-14 15:34:24 +00:00
) ,
validator: ( value ) {
if ( value = = null | | value . isEmpty ) {
2024-07-15 19:14:30 +00:00
return ' Please enter the maximum amount you wish to ${ isBuy ? ' buy ' : ' sell ' } ' ;
2024-07-14 15:34:24 +00:00
}
return null ;
} ,
) ,
const SizedBox ( height: 16.0 ) ,
TextFormField (
controller: _minAmountController ,
decoration: const InputDecoration (
2024-07-15 19:14:30 +00:00
labelText: ' Minimum Transaction Amount (XMR) ' ,
2024-07-14 15:34:24 +00:00
border: OutlineInputBorder ( ) ,
) ,
validator: ( value ) {
if ( value = = null | | value . isEmpty ) {
return ' Please enter the minimum transaction amount in XMR ' ;
}
return null ;
} ,
) ,
const SizedBox ( height: 16.0 ) ,
TextFormField (
controller: _depositController ,
decoration: const InputDecoration (
2024-07-15 19:14:30 +00:00
labelText: ' Mutual Security Deposit (%) ' ,
2024-07-14 15:34:24 +00:00
border: OutlineInputBorder ( ) ,
) ,
validator: ( value ) {
if ( value = = null | | value . isEmpty ) {
2024-07-15 19:14:30 +00:00
return ' Please enter the mutual security deposit ' ;
2024-07-14 15:34:24 +00:00
}
final deposit = double . tryParse ( value ) ;
if ( deposit = = null | | deposit < 0 | | deposit > 50 ) {
return ' Please enter a value between 0 and 50 ' ;
}
return null ;
} ,
) ,
if ( _selectedPricingTypeIndex = = 1 )
const SizedBox ( height: 16.0 ) ,
if ( _selectedPricingTypeIndex = = 1 )
TextFormField (
controller: _triggerPriceController ,
2024-07-15 19:14:30 +00:00
decoration: InputDecoration (
labelText:
' Delist If Market Price Goes Above ( ${ _selectedTradeCurrency ? . code ? ? ' ' } ) ' ,
border: const OutlineInputBorder ( ) ,
2024-07-14 15:34:24 +00:00
) ,
validator: ( value ) {
if ( value = = null | | value . isEmpty ) {
return ' Please enter the trigger price to suspend your offer ' ;
}
return null ;
} ,
) ,
const SizedBox ( height: 16.0 ) ,
2024-07-15 19:14:30 +00:00
Row (
children: [
Expanded (
child: CheckboxListTile (
title: const Row (
children: [
Text ( ' Reserve only the funds needed ' ) ,
SizedBox ( width: 4 ) ,
Tooltip (
message:
' If selected, only the exact amount of funds needed for this trade will be reserved. This may also incur a mining fee and will require 10 confirmations therefore it will take ~20 minutes longer to post your trade. ' ,
child: Icon (
Icons . info_outline ,
color: Colors . white ,
) ,
) ,
] ,
) ,
value: _reserveExactAmount ,
onChanged: ( value ) {
setState ( ( ) {
_reserveExactAmount = value ? ? false ;
} ) ;
} ,
controlAffinity: ListTileControlAffinity . leading ,
) ,
) ,
] ,
) ,
const SizedBox ( height: 16.0 ) ,
2024-07-14 15:34:24 +00:00
ElevatedButton (
onPressed: ( ) async {
if ( widget . formKey . currentState ? . validate ( ) ? ? false ) {
// Prepare the data to be sent
final offerData = {
' currencyCode ' : _selectedTradeCurrency ? . code ? ? ' ' ,
2024-07-15 19:14:30 +00:00
' direction ' : widget . direction ,
2024-07-14 15:34:24 +00:00
' price ' : _selectedPricingTypeIndex = = 0
? _priceController . text
: ' ' ,
' useMarketBasedPrice ' : _selectedPricingTypeIndex = = 1 ,
' marketPriceMarginPct ' : double . parse (
_marginController . text . isNotEmpty
? _marginController . text
: ' 0 ' ) ,
' amount ' : fixnum . Int64 (
( ( double . tryParse ( _amountController . text ) ? ?
0 ) *
1000000000000 )
. toInt ( ) )
. toString ( ) ,
' minAmount ' : fixnum . Int64 (
( ( double . tryParse ( _minAmountController . text ) ? ?
0 ) *
1000000000000 )
. toInt ( ) )
. toString ( ) ,
' buyerSecurityDepositPct ' :
double . parse ( _depositController . text ) / 10 ,
' triggerPrice ' : _selectedPricingTypeIndex = = 1
? _triggerPriceController . text
: ' ' ,
2024-07-15 19:14:30 +00:00
' reserveExactAmount ' : _reserveExactAmount ,
2024-07-14 15:34:24 +00:00
' paymentAccountId ' : _selectedPaymentAccount ? . id ? ? ' ' ,
} ;
// Print the JSON representation of the offer
debugPrint ( jsonEncode ( offerData ) ) ;
// Call the postOffer method
try {
final offersProvider =
Provider . of < OffersProvider > ( context , listen: false ) ;
offersProvider . postOffer (
currencyCode: _selectedTradeCurrency ? . code ? ? ' ' ,
2024-07-15 19:14:30 +00:00
direction: widget . direction ,
2024-07-14 15:34:24 +00:00
price: _selectedPricingTypeIndex = = 0
? _priceController . text
: ' ' , // Use the price from the controller
useMarketBasedPrice: _selectedPricingTypeIndex = = 1 ,
marketPriceMarginPct: double . parse (
_marginController . text . isNotEmpty
? _marginController . text
: ' 0 ' ) ,
amount: fixnum . Int64 (
( ( double . tryParse ( _amountController . text ) ? ? 0 ) *
1000000000000 )
. toInt ( ) ) ,
minAmount: fixnum . Int64 (
( ( double . tryParse ( _minAmountController . text ) ? ?
0 ) *
1000000000000 )
. toInt ( ) ) ,
buyerSecurityDepositPct:
double . parse ( _depositController . text ) / 100 ,
triggerPrice: _selectedPricingTypeIndex = = 1
? _triggerPriceController . text
: ' ' , // Use the trigger price from the controller
2024-07-15 19:14:30 +00:00
reserveExactAmount: _reserveExactAmount ,
2024-07-14 15:34:24 +00:00
paymentAccountId: _selectedPaymentAccount ? . id ? ? ' ' ,
) ;
Navigator . pop ( context ) ;
} catch ( e ) {
ScaffoldMessenger . of ( context ) . showSnackBar (
const SnackBar (
content: Text (
' Failed to post offer (likely cooldown limit) ' ) ) ,
) ;
}
}
} ,
child: const Text ( ' Post Offer ' ) ,
) ,
] ,
) ,
) ,
) ,
) ,
) ;
}
}
2024-07-15 19:14:30 +00:00