quotation rate display

This commit is contained in:
Stephan D
2025-12-12 13:45:58 +01:00
parent d64d7dab58
commit 00045c1e65
35 changed files with 530 additions and 199 deletions

3
.gitignore vendored
View File

@@ -9,4 +9,5 @@ untranslated.txt
generate_protos.sh
update_dep.sh
.vscode/
.gocache/
.gocache/
.cache/

View File

@@ -0,0 +1,18 @@
import 'package:pshared/models/currency.dart';
import 'package:pshared/utils/currency.dart';
class Asset {
final Currency currency;
final double amount;
const Asset({
required this.currency,
required this.amount,
});
}
Asset createAsset(String currencyCode, String amount) => Asset(
currency: currencyStringToCode(currencyCode),
amount: double.parse(amount),
);

View File

@@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
class PaymentAmountProvider with ChangeNotifier {
double _amount = 10.0;
bool _payerCoversFee = true;
double get amount => _amount;
bool get payerCoversFee => _payerCoversFee;
void setAmount(double value) {
_amount = value;
notifyListeners();
}
void setPayerCoversFee(bool value) {
_payerCoversFee = value;
notifyListeners();
}
}

View File

@@ -1,5 +1,17 @@
import 'package:flutter/material.dart';
import 'package:pshared/models/asset.dart';
import 'package:uuid/uuid.dart';
import 'package:pshared/models/payment/currency_pair.dart';
import 'package:pshared/models/payment/fx/intent.dart';
import 'package:pshared/models/payment/fx/side.dart';
import 'package:pshared/models/payment/kind.dart';
import 'package:pshared/models/payment/methods/card.dart';
import 'package:pshared/models/payment/methods/managed_wallet.dart';
import 'package:pshared/models/payment/money.dart';
import 'package:pshared/models/payment/settlement_mode.dart';
import 'package:pshared/provider/payment/amount.dart';
import 'package:pshared/api/requests/payment/quote.dart';
import 'package:pshared/data/mapper/payment/intent/payment.dart';
import 'package:pshared/models/payment/intent.dart';
@@ -7,7 +19,6 @@ import 'package:pshared/models/payment/quote.dart';
import 'package:pshared/provider/organizations.dart';
import 'package:pshared/provider/resource.dart';
import 'package:pshared/service/payment/quotation.dart';
import 'package:uuid/uuid.dart';
class QuotationProvider extends ChangeNotifier {
@@ -15,14 +26,46 @@ class QuotationProvider extends ChangeNotifier {
late OrganizationsProvider _organizations;
bool _isLoaded = false;
void update(OrganizationsProvider venue) {
void update(OrganizationsProvider venue, PaymentAmountProvider payment) {
_organizations = venue;
getQuotation(PaymentIntent(
kind: PaymentKind.payout,
amount: Money(
amount: payment.amount.toString(),
currency: 'USDT',
),
destination: CardPaymentMethod(
pan: '4000000000000077',
firstName: 'John',
lastName: 'Doe',
),
source: ManagedWalletPaymentMethod(
managedWalletRef: '',
),
fx: FxIntent(
pair: CurrencyPair(
base: 'USDT',
quote: 'RUB',
),
side: FxSide.sellBaseBuyQuote,
),
settlementMode: payment.payerCoversFee ? SettlementMode.fixReceived : SettlementMode.fixSource,
));
}
PaymentQuote? get quotation => _quotation.data;
bool get isReady => _isLoaded && !_quotation.isLoading && _quotation.error == null;
Asset? get fee => quotation == null ? null : createAsset(quotation!.expectedFeeTotal!.currency, quotation!.expectedFeeTotal!.amount);
Asset? get total => quotation == null ? null : createAsset(quotation!.debitAmount!.currency, quotation!.debitAmount!.amount);
Asset? get recipientGets => quotation == null ? null : createAsset(quotation!.expectedSettlementAmount!.currency, quotation!.expectedSettlementAmount!.amount);
void _setResource(Resource<PaymentQuote> quotation) {
_quotation = quotation;
notifyListeners();
}
Future<PaymentQuote?> getQuotation(PaymentIntent intent) async {
if (!_organizations.isOrganizationSet) throw StateError('Organization is not set');
try {
@@ -35,19 +78,20 @@ class QuotationProvider extends ChangeNotifier {
),
);
_isLoaded = true;
_quotation = _quotation.copyWith(data: response, isLoading: false);
_setResource(_quotation.copyWith(data: response, isLoading: false, error: null));
} catch (e) {
_quotation = _quotation.copyWith(
_setResource(_quotation.copyWith(
data: null,
error: e is Exception ? e : Exception(e.toString()),
isLoading: false,
);
));
}
notifyListeners();
return _quotation.data;
}
void reset() {
_quotation = Resource(data: null, isLoading: false, error: null);
_setResource(Resource(data: null, isLoading: false, error: null));
_isLoaded = false;
notifyListeners();
}

View File

@@ -4,8 +4,8 @@ import 'package:pshared/api/requests/payment/quote.dart';
import 'package:pshared/api/responses/payment/quotation.dart';
import 'package:pshared/data/mapper/payment/payment_quote.dart';
import 'package:pshared/models/payment/quote.dart';
import 'package:pshared/service/authorization/service.dart';
import 'package:pshared/service/services.dart';
import 'package:pshared/utils/http/requests.dart';
class QuotationService {
@@ -14,7 +14,11 @@ class QuotationService {
static Future<PaymentQuote> getQuotation(String organizationRef, QuotePaymentRequest request) async {
_logger.fine('Quoting payment for organization $organizationRef');
final response = await getPOSTResponse(_objectType, '/quote/$organizationRef', request.toJson());
final response = await AuthorizationService.getPOSTResponse(
_objectType,
'/quote/$organizationRef',
request.toJson(),
);
return PaymentQuoteResponse.fromJson(response).quote.toDomain();
}
}

View File

@@ -1,22 +1,64 @@
String currencyCodeToSymbol(String currencyCode) {
import 'package:flutter/material.dart';
import 'package:pshared/models/asset.dart';
import 'package:pshared/models/currency.dart';
String currencyCodeToSymbol(Currency currencyCode) {
switch (currencyCode) {
case 'USD':
case Currency.usd:
return '\$';
case 'PLN':
return '';
case 'EUR':
return '';
case 'GBP':
return '£';
case 'HUF':
return 'Ft';
case 'RUB':
case Currency.usdt:
return '';
case Currency.usdc:
return '\$';
case Currency.rub:
return '';
default:
return currencyCode;
case Currency.eur:
return '';
}
}
String currencyToString(String currencyCode, double amount) {
return '${currencyCodeToSymbol(currencyCode)}\u00A0${amount.toStringAsFixed(2)}';
String amountToString(double amount) {
return amount.toStringAsFixed(2);
}
String currencyToString(Currency currencyCode, double amount) {
return '${currencyCodeToSymbol(currencyCode)}\u00A0${amountToString(amount)}';
}
String assetToString(Asset asset) {
return currencyToString(asset.currency, asset.amount);
}
Currency currencyStringToCode(String currencyCode) {
switch (currencyCode) {
case 'USD':
return Currency.usd;
case 'USDT':
return Currency.usdt;
case 'USDC':
return Currency.usdc;
case 'RUB':
return Currency.rub;
case 'EUR':
return Currency.eur;
default:
throw ArgumentError('Unknown currency code: $currencyCode');
}
}
IconData iconForCurrencyType(Currency currencyCode) {
switch (currencyCode) {
case Currency.usd:
return Icons.currency_exchange;
case Currency.eur:
return Icons.currency_exchange;
case Currency.rub:
return Icons.currency_ruble;
case Currency.usdt:
return Icons.currency_exchange;
case Currency.usdc:
return Icons.money;
}
}

View File

@@ -1,6 +1,7 @@
import 'package:pshared/models/wallet/wallet.dart' as domain;
import 'package:pweb/models/currency.dart';
import 'package:pshared/models/currency.dart';
import 'package:pweb/models/wallet.dart';

View File

@@ -378,7 +378,7 @@
"send": "Send Payout",
"recipientPaysFee": "Recipient pays the fee",
"sentAmount": "Sent amount: ${amount}",
"sentAmount": "Sent amount: {amount}",
"@sentAmount": {
"description": "Label showing the amount sent",
"placeholders": {
@@ -388,7 +388,7 @@
}
},
"fee": "Fee: ${fee}",
"fee": "Fee: {fee}",
"@fee": {
"description": "Label showing the transaction fee",
"placeholders": {
@@ -398,7 +398,7 @@
}
},
"recipientWillReceive": "Recipient will receive: ${amount}",
"recipientWillReceive": "Recipient will receive: {amount}",
"@recipientWillReceive": {
"description": "Label showing how much the recipient will receive",
"placeholders": {
@@ -408,7 +408,7 @@
}
},
"total": "Total: ${total}",
"total": "Total: {total}",
"@total": {
"description": "Label showing the total amount of the transaction",
"placeholders": {

View File

@@ -378,7 +378,7 @@
"send": "Отправить выплату",
"recipientPaysFee": "Получатель оплачивает комиссию",
"sentAmount": "Отправленная сумма: ${amount}",
"sentAmount": "Отправленная сумма: {amount}",
"@sentAmount": {
"description": "Метка, показывающая отправленную сумму",
"placeholders": {
@@ -388,7 +388,7 @@
}
},
"fee": "Комиссия: ${fee}",
"fee": "Комиссия: {fee}",
"@fee": {
"description": "Метка, показывающая комиссию за транзакцию",
"placeholders": {
@@ -398,7 +398,7 @@
}
},
"recipientWillReceive": "Получатель получит: ${amount}",
"recipientWillReceive": "Получатель получит: {amount}",
"@recipientWillReceive": {
"description": "Метка, показывающая, сколько получит получатель",
"placeholders": {
@@ -408,7 +408,7 @@
}
},
"total": "Итого: ${total}",
"total": "Итого: {total}",
"@total": {
"description": "Метка, показывающая общую сумму транзакции",
"placeholders": {

View File

@@ -12,7 +12,6 @@ import 'package:pshared/provider/locale.dart';
import 'package:pshared/provider/permissions.dart';
import 'package:pshared/provider/account.dart';
import 'package:pshared/provider/organizations.dart';
import 'package:pshared/provider/payment/quotation.dart';
import 'package:pshared/provider/recipient/provider.dart';
import 'package:pshared/provider/recipient/pmethods.dart';
@@ -94,10 +93,6 @@ void main() async {
ChangeNotifierProvider(
create: (_) => OperationProvider(OperationService())..loadOperations(),
),
ChangeNotifierProxyProvider<OrganizationsProvider, QuotationProvider>(
create: (_) => QuotationProvider(),
update: (context, orgnization, provider) => provider!..update(orgnization),
),
],
child: const PayApp(),
),

View File

@@ -1,4 +1,4 @@
import 'package:pweb/models/currency.dart';
import 'package:pshared/models/currency.dart';
class Wallet {

View File

@@ -2,7 +2,7 @@ import 'package:flutter/widgets.dart';
import 'package:pshared/models/payment/status.dart';
import 'package:pweb/models/currency.dart';
import 'package:pshared/models/currency.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';

View File

@@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:pshared/utils/currency.dart';
import 'package:pweb/models/wallet.dart';
import 'package:pweb/utils/currency.dart';
class BalanceAmount extends StatelessWidget {
@@ -25,7 +27,7 @@ class BalanceAmount extends StatelessWidget {
return Row(
children: [
Text(
wallet.isHidden ? '•••• $currencyBalance' : '${wallet.balance.toStringAsFixed(2)} $currencyBalance',
wallet.isHidden ? '•••• $currencyBalance' : '${amountToString(wallet.balance)} $currencyBalance',
style: textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,

View File

@@ -0,0 +1,74 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pshared/provider/payment/amount.dart';
import 'package:pshared/utils/currency.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
class PaymentAmountWidget extends StatefulWidget {
const PaymentAmountWidget({super.key});
@override
State<PaymentAmountWidget> createState() => _PaymentAmountWidgetState();
}
class _PaymentAmountWidgetState extends State<PaymentAmountWidget> {
late final TextEditingController _controller;
bool _isSyncingText = false;
@override
void initState() {
super.initState();
final initialAmount = context.read<PaymentAmountProvider>().amount;
_controller = TextEditingController(text: amountToString(initialAmount));
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
double? _parseAmount(String value) => double.tryParse(value.replaceAll(',', '.'));
void _syncTextWithAmount(double amount) {
final parsedText = _parseAmount(_controller.text);
if (parsedText != null && parsedText == amount) return;
final nextText = amountToString(amount);
_isSyncingText = true;
_controller.value = TextEditingValue(
text: nextText,
selection: TextSelection.collapsed(offset: nextText.length),
);
_isSyncingText = false;
}
void _onChanged(String value) {
if (_isSyncingText) return;
final parsed = _parseAmount(value);
if (parsed != null) {
context.read<PaymentAmountProvider>().setAmount(parsed);
}
}
@override
Widget build(BuildContext context) {
final amount = context.select<PaymentAmountProvider, double>((provider) => provider.amount);
_syncTextWithAmount(amount);
return TextField(
controller: _controller,
keyboardType: const TextInputType.numberWithOptions(decimal: true),
decoration: InputDecoration(
labelText: AppLocalizations.of(context)!.amount,
border: const OutlineInputBorder(),
),
onChanged: _onChanged,
);
}
}

View File

@@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pshared/provider/payment/amount.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
class FeePayerSwitch extends StatelessWidget {
final double spacing;
final TextStyle? style;
const FeePayerSwitch({super.key, required this.spacing, TextStyle? this.style});
@override
Widget build(BuildContext context) => Consumer<PaymentAmountProvider>(
builder: (context, provider, _) => Row(
spacing: spacing,
children: [
Text(AppLocalizations.of(context)!.recipientPaysFee, style: style),
Switch(
value: !provider.payerCoversFee,
onChanged: (val) => provider.setPayerCoversFee(!val),
),
],
),
);
}

View File

@@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
import 'package:pweb/pages/dashboard/payouts/amount.dart';
import 'package:pweb/pages/dashboard/payouts/fee_payer.dart';
import 'package:pweb/pages/dashboard/payouts/summary/widget.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
class PaymentFormWidget extends StatelessWidget {
const PaymentFormWidget({super.key});
static const double _smallSpacing = 5;
static const double _mediumSpacing = 10;
static const double _largeSpacing = 16;
static const double _extraSpacing = 15;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final loc = AppLocalizations.of(context)!;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(loc.details, style: theme.textTheme.titleMedium),
const SizedBox(height: _smallSpacing),
const PaymentAmountWidget(),
const SizedBox(height: _mediumSpacing),
FeePayerSwitch(spacing: _mediumSpacing, style: theme.textTheme.titleMedium),
const SizedBox(height: _largeSpacing),
const PaymentSummary(spacing: _extraSpacing),
],
);
}
}

View File

@@ -1,92 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pweb/providers/mock_payment.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
class PaymentFormWidget extends StatelessWidget {
const PaymentFormWidget({super.key});
static const double _smallSpacing = 5;
static const double _mediumSpacing = 10;
static const double _largeSpacing = 16;
static const double _extraSpacing = 15;
String _formatAmount(double amount) => amount.toStringAsFixed(2);
@override
Widget build(BuildContext context) {
final provider = Provider.of<MockPaymentProvider>(context);
final theme = Theme.of(context);
final loc = AppLocalizations.of(context)!;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(loc.details, style: theme.textTheme.titleMedium),
const SizedBox(height: _smallSpacing),
TextField(
keyboardType: const TextInputType.numberWithOptions(decimal: true),
decoration: InputDecoration(
labelText: loc.amount,
border: const OutlineInputBorder(),
),
onChanged: (val) {
final parsed = double.tryParse(val.replaceAll(',', '.')) ?? 0.0;
provider.setAmount(parsed);
},
),
const SizedBox(height: _mediumSpacing),
Row(
spacing: _mediumSpacing,
children: [
Text(loc.recipientPaysFee, style: theme.textTheme.titleMedium),
Switch(
value: !provider.payerCoversFee,
onChanged: (val) => provider.setPayerCoversFee(!val),
),
],
),
const SizedBox(height: _largeSpacing),
Align(
alignment: Alignment.center,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_SummaryRow(label: loc.sentAmount(_formatAmount(provider.amount)), style: theme.textTheme.titleMedium),
_SummaryRow(label: loc.fee(_formatAmount(provider.fee)), style: theme.textTheme.titleMedium),
_SummaryRow(label: loc.recipientWillReceive(_formatAmount(provider.recipientGets)), style: theme.textTheme.titleMedium),
const SizedBox(height: _extraSpacing),
_SummaryRow(
label: loc.total(_formatAmount(provider.total)),
style: theme.textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w600),
),
],
),
),
],
);
}
}
class _SummaryRow extends StatelessWidget {
final String label;
final TextStyle? style;
const _SummaryRow({required this.label, this.style});
@override
Widget build(BuildContext context) {
return Text(label, style: style);
}
}

View File

@@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pshared/provider/payment/quotation.dart';
import 'package:pweb/pages/dashboard/payouts/summary/row.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
class PaymentFeeRow extends StatelessWidget {
const PaymentFeeRow({super.key});
@override
Widget build(BuildContext context) => Consumer<QuotationProvider>(
builder: (context, provider, _) => PaymentSummaryRow(
labelFactory: AppLocalizations.of(context)!.fee,
asset: provider.fee,
style: Theme.of(context).textTheme.titleMedium,
),
);
}

View File

@@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pshared/provider/payment/quotation.dart';
import 'package:pweb/pages/dashboard/payouts/summary/row.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
class PaymentRecipientReceivesRow extends StatelessWidget {
const PaymentRecipientReceivesRow({super.key});
@override
Widget build(BuildContext context) => Consumer<QuotationProvider>(
builder: (context, provider, _) => PaymentSummaryRow(
labelFactory: AppLocalizations.of(context)!.recipientWillReceive,
asset: provider.recipientGets,
style: Theme.of(context).textTheme.titleMedium,
),
);
}

View File

@@ -0,0 +1,24 @@
import 'package:flutter/material.dart';
import 'package:pshared/models/asset.dart';
import 'package:pshared/utils/currency.dart';
class PaymentSummaryRow extends StatelessWidget {
final String Function(String) labelFactory;
final Asset? asset;
final TextStyle? style;
const PaymentSummaryRow({
super.key,
required this.labelFactory,
required this.asset,
this.style,
});
@override
Widget build(BuildContext context) => Text(
labelFactory(asset == null ? 'N/A' : assetToString(asset!)),
style: style,
);
}

View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pshared/models/asset.dart';
import 'package:pshared/models/currency.dart';
import 'package:pshared/provider/payment/amount.dart';
import 'package:pweb/pages/dashboard/payouts/summary/row.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
class PaymentSentAmountRow extends StatelessWidget {
final Currency currency;
const PaymentSentAmountRow({super.key, required this.currency});
@override
Widget build(BuildContext context) => Consumer<PaymentAmountProvider>(
builder: (context, provider, _) => PaymentSummaryRow(
labelFactory: AppLocalizations.of(context)!.sentAmount,
asset: Asset(currency: currency, amount: provider.amount),
style: Theme.of(context).textTheme.titleMedium,
),
);
}

View File

@@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pshared/provider/payment/quotation.dart';
import 'package:pweb/pages/dashboard/payouts/summary/row.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
class PaymentTotalRow extends StatelessWidget {
const PaymentTotalRow({super.key});
@override
Widget build(BuildContext context) => Consumer<QuotationProvider>(
builder: (context, provider, _) => PaymentSummaryRow(
labelFactory: AppLocalizations.of(context)!.total,
asset: provider.total,
style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w600),
),
);
}

View File

@@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pshared/utils/currency.dart';
import 'package:pweb/pages/dashboard/payouts/summary/fee.dart';
import 'package:pweb/pages/dashboard/payouts/summary/recipient_receives.dart';
import 'package:pweb/pages/dashboard/payouts/summary/sent_amount.dart';
import 'package:pweb/pages/dashboard/payouts/summary/total.dart';
import 'package:pweb/providers/wallets.dart';
class PaymentSummary extends StatelessWidget {
final double spacing;
const PaymentSummary({super.key, required this.spacing});
@override
Widget build(BuildContext context) => Align(
alignment: Alignment.center,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
PaymentSentAmountRow(currency: currencyStringToCode(context.read<WalletsProvider>().selectedWallet?.tokenSymbol ?? 'USDT')),
const PaymentFeeRow(),
const PaymentRecipientReceivesRow(),
SizedBox(height: spacing),
const PaymentTotalRow(),
],
),
);
}

View File

@@ -0,0 +1,28 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pshared/provider/organizations.dart';
import 'package:pshared/provider/payment/amount.dart';
import 'package:pshared/provider/payment/quotation.dart';
import 'package:pweb/pages/dashboard/payouts/form.dart';
class PaymentFromWrappingWidget extends StatelessWidget {
const PaymentFromWrappingWidget({super.key});
@override
Widget build(BuildContext context) => MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => PaymentAmountProvider(),
),
ChangeNotifierProxyProvider2<OrganizationsProvider, PaymentAmountProvider, QuotationProvider>(
create: (_) => QuotationProvider(),
update: (context, orgnization, payment, provider) => provider!..update(orgnization, payment),
),
],
child: const PaymentFormWidget(),
);
}

View File

@@ -11,7 +11,7 @@ import 'package:pweb/pages/payment_methods/payment_page/back_button.dart';
import 'package:pweb/pages/payment_methods/payment_page/header.dart';
import 'package:pweb/pages/payment_methods/payment_page/method_selector.dart';
import 'package:pweb/pages/payment_methods/payment_page/send_button.dart';
import 'package:pweb/pages/dashboard/payouts/payment_form.dart';
import 'package:pweb/pages/dashboard/payouts/form.dart';
import 'package:pweb/pages/payment_methods/widgets/payment_info_section.dart';
import 'package:pweb/pages/payment_methods/widgets/recipient_section.dart';
import 'package:pweb/pages/payment_methods/widgets/section_title.dart';

View File

@@ -2,14 +2,14 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pshared/models/payment/methods/type.dart';
import 'package:pshared/provider/recipient/pmethods.dart';
import 'package:pweb/models/wallet.dart';
import 'package:pweb/providers/wallets.dart';
import 'package:pweb/utils/payment/dropdown.dart';
class PaymentMethodSelector extends StatelessWidget {
final ValueChanged<PaymentMethod> onMethodChanged;
final ValueChanged<Wallet> onMethodChanged;
const PaymentMethodSelector({
super.key,
@@ -17,9 +17,9 @@ class PaymentMethodSelector extends StatelessWidget {
});
@override
Widget build(BuildContext context) => Consumer<PaymentMethodsProvider>(builder:(context, provider, _) => PaymentMethodDropdown(
methods: provider.methods,
initialValue: provider.currentObject,
Widget build(BuildContext context) => Consumer<WalletsProvider>(builder:(context, provider, _) => PaymentMethodDropdown(
methods: provider.wallets,
initialValue: provider.selectedWallet,
onChanged: onMethodChanged,
));
}

View File

@@ -7,7 +7,7 @@ import 'package:pshared/models/recipient/recipient.dart';
import 'package:pshared/provider/recipient/pmethods.dart';
import 'package:pshared/provider/recipient/provider.dart';
import 'package:pweb/pages/dashboard/payouts/payment_form.dart';
import 'package:pweb/pages/dashboard/payouts/widget.dart';
import 'package:pweb/pages/payment_methods/payment_page/back_button.dart';
import 'package:pweb/pages/payment_methods/payment_page/header.dart';
import 'package:pweb/pages/payment_methods/payment_page/method_selector.dart';
@@ -105,7 +105,7 @@ class PaymentPageContent extends StatelessWidget {
availableTypes: availablePaymentTypes,
),
SizedBox(height: dimensions.paddingLarge),
const PaymentFormWidget(),
const PaymentFromWrappingWidget(),
SizedBox(height: dimensions.paddingXXXLarge),
SendButton(onPressed: onSend),
SizedBox(height: dimensions.paddingLarge),

View File

@@ -1,10 +1,12 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pshared/utils/currency.dart';
import 'package:pweb/models/wallet.dart';
import 'package:pweb/pages/dashboard/buttons/balance/amount.dart';
import 'package:pweb/providers/wallets.dart';
import 'package:pweb/utils/currency.dart';
class WalletCard extends StatelessWidget {

View File

@@ -1,9 +1,10 @@
import 'package:flutter/material.dart';
import 'package:pshared/utils/currency.dart';
import 'package:pweb/models/wallet_transaction.dart';
import 'package:pweb/pages/payout_page/wallet/history/chip.dart';
import 'package:pweb/pages/report/table/badge.dart';
import 'package:pweb/utils/currency.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
@@ -66,11 +67,11 @@ class WalletTransactionsTable extends StatelessWidget {
DataCell(OperationStatusBadge(status: tx.status)),
DataCell(TypeChip(type: tx.type)),
DataCell(Text(
'${tx.type.sign}${tx.amount.toStringAsFixed(2)} ${currencyCodeToSymbol(tx.currency)}')),
'${tx.type.sign}${amountToString(tx.amount)} ${currencyCodeToSymbol(tx.currency)}')),
DataCell(Text(
tx.balanceAfter == null
? '-'
: '${tx.balanceAfter!.toStringAsFixed(2)} ${currencyCodeToSymbol(tx.currency)}',
: '${amountToString(tx.balanceAfter!)} ${currencyCodeToSymbol(tx.currency)}',
)),
DataCell(Text(tx.counterparty ?? '-')),
DataCell(Text(

View File

@@ -1,15 +1,18 @@
// operation_row.dart
import 'package:flutter/material.dart';
import 'package:pshared/models/payment/operation.dart';
import 'package:pshared/utils/currency.dart';
import 'package:pweb/pages/report/table/badge.dart';
class OperationRow {
static DataRow build(OperationItem op, BuildContext context) {
return DataRow(cells: [
DataCell(OperationStatusBadge(status: op.status)),
DataCell(Text(op.fileName ?? '')),
DataCell(Text('${op.amount.toStringAsFixed(2)} ${op.currency}')),
DataCell(Text('${op.toAmount.toStringAsFixed(2)} ${op.toCurrency}')),
DataCell(Text('${amountToString(op.amount)} ${op.currency}')),
DataCell(Text('${amountToString(op.toAmount)} ${op.toCurrency}')),
DataCell(Text(op.payId)),
DataCell(Text(op.cardNumber ?? '-')),
DataCell(Text(op.name)),

View File

@@ -1,10 +1,11 @@
import 'package:flutter/material.dart';
import 'package:pshared/utils/currency.dart';
import 'package:pweb/models/wallet.dart';
import 'package:pweb/pages/wallet_top_up/details.dart';
import 'package:pweb/pages/wallet_top_up/header.dart';
import 'package:pweb/pages/wallet_top_up/meta.dart';
import 'package:pweb/utils/currency.dart';
import 'package:pweb/utils/dimensions.dart';

View File

@@ -1,6 +1,6 @@
import 'package:pshared/models/payment/status.dart';
import 'package:pshared/models/currency.dart';
import 'package:pweb/models/currency.dart';
import 'package:pweb/models/wallet_transaction.dart';

View File

@@ -1,38 +0,0 @@
import 'package:flutter/material.dart';
import 'package:pweb/models/currency.dart';
String currencyCodeToSymbol(Currency currencyCode) {
switch (currencyCode) {
case Currency.usd:
return '\$';
case Currency.eur:
return '';
case Currency.rub:
return '';
case Currency.usdt:
return 'USDT';
case Currency.usdc:
return 'USDC';
}
}
String currencyToString(Currency currencyCode, double amount) {
return '${amount.toStringAsFixed(2)} ${currencyCodeToSymbol(currencyCode)}';
}
IconData iconForCurrencyType(Currency currencyCode) {
switch (currencyCode) {
case Currency.usd:
return Icons.currency_exchange;
case Currency.eur:
return Icons.currency_exchange;
case Currency.rub:
return Icons.currency_ruble;
case Currency.usdt:
return Icons.currency_exchange;
case Currency.usdc:
return Icons.money;
}
}

View File

@@ -1,16 +1,17 @@
import 'package:flutter/material.dart';
import 'package:pshared/models/payment/methods/type.dart';
import 'package:pshared/models/payment/type.dart';
import 'package:pweb/models/wallet.dart';
import 'package:pweb/pages/payment_methods/icon.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
class PaymentMethodDropdown extends StatefulWidget {
final List<PaymentMethod> methods;
final ValueChanged<PaymentMethod> onChanged;
final PaymentMethod? initialValue;
final List<Wallet> methods;
final ValueChanged<Wallet> onChanged;
final Wallet? initialValue;
const PaymentMethodDropdown({
super.key,
@@ -24,7 +25,7 @@ class PaymentMethodDropdown extends StatefulWidget {
}
class _PaymentMethodDropdownState extends State<PaymentMethodDropdown> {
late PaymentMethod _selectedMethod;
late Wallet _selectedMethod;
@override
void initState() {
@@ -34,7 +35,7 @@ class _PaymentMethodDropdownState extends State<PaymentMethodDropdown> {
@override
Widget build(BuildContext context) {
return DropdownButtonFormField<PaymentMethod>(
return DropdownButtonFormField<Wallet>(
dropdownColor: Theme.of(context).colorScheme.onSecondary,
initialValue: _selectedMethod,
decoration: InputDecoration(
@@ -42,13 +43,13 @@ class _PaymentMethodDropdownState extends State<PaymentMethodDropdown> {
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
),
items: widget.methods.map((method) {
return DropdownMenuItem<PaymentMethod>(
return DropdownMenuItem<Wallet>(
value: method,
child: Row(
children: [
Icon(iconForPaymentType(method.type), size: 20),
Icon(iconForPaymentType(PaymentType.managedWallet), size: 20),
const SizedBox(width: 8),
Text('${method.name}' + (method.description == null ? '' : ' (${method.description!})')),
Text(method.name),
],
),
);