temp build

This commit is contained in:
Stephan D
2025-12-05 01:32:41 +01:00
parent 082d782a80
commit f71cc76f64
50 changed files with 853 additions and 707 deletions

View File

@@ -311,6 +311,7 @@
"paymentTypeBankAccount": "Russian Bank Account",
"paymentTypeIban": "IBAN",
"paymentTypeWallet": "Wallet",
"paymentTypeCryptoAddress": "Crypto address",
"cardNumber": "Card Number",
"enterCardNumber": "Enter the card number",

View File

@@ -311,6 +311,7 @@
"paymentTypeBankAccount": "Российский банковский счет",
"paymentTypeIban": "IBAN",
"paymentTypeWallet": "Кошелек",
"paymentTypeCryptoAddress": "Крипто-адрес",
"cardNumber": "Номер карты",
"enterCardNumber": "Введите номер карты",

View File

@@ -12,6 +12,8 @@ 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/recipient/provider.dart';
import 'package:pshared/provider/recipient/pmethods.dart';
import 'package:pweb/app/app.dart';
import 'package:pweb/app/timeago.dart';
@@ -19,17 +21,13 @@ import 'package:pweb/providers/carousel.dart';
import 'package:pweb/providers/mock_payment.dart';
import 'package:pweb/providers/operatioins.dart';
import 'package:pweb/providers/page_selector.dart';
import 'package:pweb/providers/payment_methods.dart';
import 'package:pweb/providers/recipient.dart';
import 'package:pweb/providers/two_factor.dart';
import 'package:pweb/providers/upload_history.dart';
import 'package:pweb/providers/wallets.dart';
import 'package:pweb/providers/wallet_transactions.dart';
// import 'package:pweb/services/amplitude.dart';
import 'package:pweb/services/operations.dart';
import 'package:pweb/services/payments/payment_methods.dart';
import 'package:pweb/services/payments/upload_history.dart';
import 'package:pweb/services/recipient/recipient.dart';
import 'package:pweb/services/payments/history.dart';
import 'package:pweb/services/wallet_transactions.dart';
import 'package:pweb/services/wallets.dart';
@@ -77,8 +75,13 @@ void main() async {
ChangeNotifierProvider(
create: (_) => UploadHistoryProvider(service: MockUploadHistoryService())..load(),
),
ChangeNotifierProvider(
create: (_) => PaymentMethodsProvider(service: MockPaymentMethodsService())..loadMethods(),
ChangeNotifierProxyProvider<OrganizationsProvider, RecipientsProvider>(
create: (_) => RecipientsProvider(),
update: (context, organizations, provider) => provider!..updateProviders(organizations),
),
ChangeNotifierProxyProvider2<OrganizationsProvider, RecipientsProvider, PaymentMethodsProvider>(
create: (_) => PaymentMethodsProvider(),
update: (context, organizations, recipients, provider) => provider!..updateProviders(organizations, recipients),
),
ChangeNotifierProxyProvider<OrganizationsProvider, WalletsProvider>(
create: (_) => WalletsProvider(ApiWalletsService()),
@@ -90,18 +93,11 @@ void main() async {
ChangeNotifierProvider(
create: (_) => MockPaymentProvider(),
),
ChangeNotifierProvider(
create: (_) => RecipientProvider(RecipientService())..loadRecipients(),
),
ChangeNotifierProxyProvider3<RecipientProvider, WalletsProvider, PaymentMethodsProvider, PageSelectorProvider>(
ChangeNotifierProxyProvider3<RecipientsProvider, WalletsProvider, PaymentMethodsProvider, PageSelectorProvider>(
create: (context) => PageSelectorProvider(),
update: (context, recipientProv, walletsProv, methodsProv, previous) =>
previous ?? PageSelectorProvider(
recipientProvider: recipientProv,
walletsProvider: walletsProv,
methodsProvider: methodsProv,
)..update(recipientProv, walletsProv, methodsProv),
previous ?? PageSelectorProvider()..update(recipientProv, walletsProv, methodsProv),
),
ChangeNotifierProvider(

View File

@@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pshared/models/payment/methods/card.dart';
import 'package:pshared/models/payment/methods/crypto_address.dart';
import 'package:pshared/models/payment/methods/iban.dart';
@@ -9,6 +11,7 @@ import 'package:pshared/models/payment/type.dart';
import 'package:pshared/models/recipient/recipient.dart';
import 'package:pshared/models/recipient/status.dart';
import 'package:pshared/models/recipient/type.dart';
import 'package:pshared/provider/organizations.dart';
import 'package:pweb/pages/address_book/form/view.dart';
import 'package:pweb/services/amplitude.dart';
@@ -38,8 +41,8 @@ class _AdressBookRecipientFormState extends State<AdressBookRecipientForm> {
void initState() {
super.initState();
final r = widget.recipient;
_nameCtrl = TextEditingController(text: r?.name ?? "");
_emailCtrl = TextEditingController(text: r?.email ?? "");
_nameCtrl = TextEditingController(text: r?.name ?? '');
_emailCtrl = TextEditingController(text: r?.email ?? '');
_type = r?.type ?? RecipientType.internal;
_status = r?.status ?? RecipientStatus.ready;
@@ -50,7 +53,7 @@ class _AdressBookRecipientFormState extends State<AdressBookRecipientForm> {
if (r?.cryptoAddress != null) _methods[PaymentType.cryptoAddress] = r!.cryptoAddress;
}
//TODO Change when registration is ready
//TODO: Change when registration is ready
void _save() {
if (!_formKey.currentState!.validate() || _methods.isEmpty) {
AmplitudeService.recipientAddCompleted(
@@ -66,47 +69,41 @@ class _AdressBookRecipientFormState extends State<AdressBookRecipientForm> {
return;
}
final recipient = Recipient(
final recipient = newRecipient(
name: _nameCtrl.text,
email: _emailCtrl.text,
type: _type,
status: _status,
avatarUrl: null,
card: _methods[PaymentType.card] as CardPaymentMethod?,
iban: _methods[PaymentType.iban] as IbanPaymentMethod?,
wallet: _methods[PaymentType.wallet] as WalletPaymentMethod?,
bank: _methods[PaymentType.bankAccount] as RussianBankAccountPaymentMethod?,
cryptoAddress: _methods[PaymentType.cryptoAddress] as CryptoAddressPaymentMethod?,
organizationRef: context.read<OrganizationsProvider>().current.id
);
widget.onSaved?.call(recipient);
}
@override
Widget build(BuildContext context) {
return FormView(
formKey: _formKey,
nameCtrl: _nameCtrl,
emailCtrl: _emailCtrl,
type: _type,
status: _status,
methods: _methods,
onTypeChanged: (t) => setState(() => _type = t),
onStatusChanged: (s) => setState(() => _status = s),
onMethodsChanged: (type, data) {
setState(() {
if (data != null) {
_methods[type] = data;
} else {
_methods.remove(type);
}
});
},
onSave: _save,
isEditing: widget.recipient != null,
onBack: () {
widget.onSaved?.call(null);
},
);
}
Widget build(BuildContext context) => FormView(
formKey: _formKey,
nameCtrl: _nameCtrl,
emailCtrl: _emailCtrl,
type: _type,
status: _status,
methods: _methods,
onTypeChanged: (t) => setState(() => _type = t),
onStatusChanged: (s) => setState(() => _status = s),
onMethodsChanged: (type, data) {
setState(() {
if (data != null) {
_methods[type] = data;
} else {
_methods.remove(type);
}
});
},
onSave: _save,
isEditing: widget.recipient != null,
onBack: () {
widget.onSaved?.call(null);
},
);
}

View File

@@ -11,13 +11,11 @@ class PaymentInfoRow extends StatelessWidget {
});
@override
Widget build(BuildContext context) {
return Row(
children: [
Text(label, style: Theme.of(context).textTheme.bodySmall),
const SizedBox(width: 8),
Text(value, style: Theme.of(context).textTheme.bodySmall),
],
);
}
Widget build(BuildContext context) => Row(
children: [
Text(label, style: Theme.of(context).textTheme.bodySmall),
const SizedBox(width: 8),
Text(value, style: Theme.of(context).textTheme.bodySmall),
],
);
}

View File

@@ -1,36 +1,60 @@
import 'package:flutter/material.dart';
import 'package:pshared/models/payment/type.dart';
import 'package:provider/provider.dart';
import 'package:pshared/models/recipient/recipient.dart';
import 'package:pshared/provider/organizations.dart';
import 'package:pshared/provider/recipient/pmethods.dart';
import 'package:pshared/provider/recipient/provider.dart';
import 'package:pweb/pages/dashboard/payouts/single/adress_book/avatar.dart';
import 'package:pweb/pages/dashboard/payouts/single/adress_book/long_list/info_row.dart';
import 'package:pweb/utils/payment/label.dart';
class RecipientItem extends StatelessWidget {
final Recipient recipient;
final VoidCallback onTap;
class RecipientItem extends StatefulWidget {
static const double _horizontalPadding = 16.0;
static const double _verticalPadding = 8.0;
static const double _avatarRadius = 20;
static const double _spacingWidth = 12;
final Recipient recipient;
final VoidCallback onTap;
const RecipientItem({
super.key,
required this.recipient,
required this.onTap,
});
@override
State<RecipientItem> createState() => _RecipientItemState();
}
class _RecipientItemState extends State<RecipientItem> {
late PaymentMethodsProvider _methodsProvider;
@override
void initState() {
super.initState();
_methodsProvider = PaymentMethodsProvider();
_methodsProvider.updateProviders(
context.read<OrganizationsProvider>(),
context.read<RecipientsProvider>(),
);
}
@override
Widget build(BuildContext context) {
if (!_methodsProvider.isReady) return const Center(child: CircularProgressIndicator());
final recipient = widget.recipient;
return InkWell(
onTap: onTap,
onTap: widget.onTap,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: _horizontalPadding,
vertical: _verticalPadding,
horizontal: RecipientItem._horizontalPadding,
vertical: RecipientItem._verticalPadding,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -42,43 +66,20 @@ class RecipientItem extends StatelessWidget {
isVisible: false,
name: recipient.name,
avatarUrl: recipient.avatarUrl,
avatarRadius: _avatarRadius,
avatarRadius: RecipientItem._avatarRadius,
nameStyle: Theme.of(context).textTheme.bodyMedium,
),
title: Text(recipient.name),
subtitle: Text(recipient.email),
),
),
const SizedBox(width: _spacingWidth),
const SizedBox(width: RecipientItem._spacingWidth),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (recipient.bank?.accountNumber.isNotEmpty == true)
PaymentInfoRow(
label: getPaymentTypeLabel(context, PaymentType.bankAccount),
value: recipient.bank!.accountNumber,
),
if (recipient.card?.pan.isNotEmpty == true)
PaymentInfoRow(
label: getPaymentTypeLabel(context, PaymentType.card),
value: recipient.card!.pan,
),
if (recipient.iban?.iban.isNotEmpty == true)
PaymentInfoRow(
label: getPaymentTypeLabel(context, PaymentType.iban),
value: recipient.iban!.iban,
),
if (recipient.wallet?.walletId.isNotEmpty == true)
PaymentInfoRow(
label: getPaymentTypeLabel(context, PaymentType.wallet),
value: recipient.wallet!.walletId,
),
if (recipient.cryptoAddress?.address.isNotEmpty == true)
PaymentInfoRow(
label: getPaymentTypeLabel(context, PaymentType.cryptoAddress),
value: recipient.cryptoAddress!.address,
),
],
children: _methodsProvider.methods.map((m) => PaymentInfoRow(
label: getPaymentTypeLabel(context, m.type),
value: _displayString(m),
)).toList(),
),
],
),

View File

@@ -3,11 +3,11 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pshared/models/recipient/recipient.dart';
import 'package:pshared/provider/recipient/provider.dart';
import 'package:pweb/pages/address_book/page/search.dart';
import 'package:pweb/pages/dashboard/payouts/single/adress_book/long_list/long_list.dart';
import 'package:pweb/pages/dashboard/payouts/single/adress_book/long_list/widget.dart';
import 'package:pweb/pages/dashboard/payouts/single/adress_book/short_list.dart';
import 'package:pweb/providers/recipient.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
@@ -39,7 +39,7 @@ class _AdressBookPayoutState extends State<AdressBookPayout> {
@override
void initState() {
super.initState();
final provider = context.read<RecipientProvider>();
final provider = context.read<RecipientsProvider>();
_searchController = TextEditingController(text: provider.query);
_searchController.addListener(() {
@@ -57,7 +57,7 @@ class _AdressBookPayoutState extends State<AdressBookPayout> {
@override
Widget build(BuildContext context) {
final loc = AppLocalizations.of(context)!;
final provider = context.watch<RecipientProvider>();
final provider = context.watch<RecipientsProvider>();
if (provider.isLoading) {
return const Center(child: CircularProgressIndicator());
@@ -88,14 +88,14 @@ class _AdressBookPayoutState extends State<AdressBookPayout> {
const SizedBox(height: _spacingBetween),
Expanded(
child: _isExpanded
? LongListAdressBookPayout(
filteredRecipients: provider.filteredRecipients,
onSelected: widget.onSelected,
)
: ShortListAdressBookPayout(
recipients: provider.recipients,
onSelected: widget.onSelected,
),
? LongListAdressBookPayout(
filteredRecipients: provider.filteredRecipients,
onSelected: widget.onSelected,
)
: ShortListAdressBookPayout(
recipients: provider.recipients,
onSelected: widget.onSelected,
),
),
],
),

View File

@@ -1,27 +1,25 @@
import 'package:flutter/material.dart';
import 'package:pshared/models/payment/methods/type.dart';
import 'package:provider/provider.dart';
import 'package:pshared/models/payment/methods/type.dart';
import 'package:pshared/provider/recipient/pmethods.dart';
import 'package:pweb/providers/payment_methods.dart';
import 'package:pweb/utils/payment/dropdown.dart';
class PaymentMethodSelector extends StatelessWidget {
final PaymentMethodsProvider methodsProvider;
final ValueChanged<PaymentMethod> onMethodChanged;
const PaymentMethodSelector({
super.key,
required this.methodsProvider,
required this.onMethodChanged,
});
@override
Widget build(BuildContext context) {
return PaymentMethodDropdown(
methods: methodsProvider.methods,
initialValue: methodsProvider.selectedMethod,
onChanged: onMethodChanged,
);
}
Widget build(BuildContext context) => Consumer<PaymentMethodsProvider>(builder:(context, provider, _) => PaymentMethodDropdown(
methods: provider.methods,
initialValue: provider.currentObject,
onChanged: onMethodChanged,
));
}

View File

@@ -3,12 +3,12 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pshared/models/recipient/recipient.dart';
import 'package:pshared/provider/recipient/pmethods.dart';
import 'package:pshared/provider/recipient/provider.dart';
import 'package:pweb/providers/payment_flow_provider.dart';
import 'package:pweb/pages/payment_methods/widgets/payment_page_body.dart';
import 'package:pweb/providers/page_selector.dart';
import 'package:pweb/providers/payment_methods.dart';
import 'package:pweb/providers/recipient.dart';
class PaymentPage extends StatefulWidget {
@@ -49,30 +49,22 @@ class _PaymentPageState extends State<PaymentPage> {
void _initializePaymentPage() {
final pageSelector = context.read<PageSelectorProvider>();
final methodsProvider = context.read<PaymentMethodsProvider>();
final recipientProvider = context.read<RecipientProvider>();
final recipientProvider = context.read<RecipientsProvider>();
pageSelector.handleWalletAutoSelection();
if (methodsProvider.methods.isEmpty && !methodsProvider.isLoading) {
methodsProvider.loadMethods();
}
if (recipientProvider.recipients.isEmpty && !recipientProvider.isLoading) {
recipientProvider.loadRecipients();
}
_flowProvider.syncWithSelector(pageSelector);
}
void _handleSearchChanged(String query) {
context.read<RecipientProvider>().setQuery(query);
context.read<RecipientsProvider>().setQuery(query);
}
void _handleRecipientSelected(Recipient recipient) {
final pageSelector = context.read<PageSelectorProvider>();
final recipientProvider = context.read<RecipientProvider>();
final recipientProvider = context.read<RecipientsProvider>();
recipientProvider.selectRecipient(recipient);
recipientProvider.setCurrentObject(recipient.id);
pageSelector.selectRecipient(recipient);
_flowProvider.reset(pageSelector);
_clearSearchField();
@@ -80,9 +72,9 @@ class _PaymentPageState extends State<PaymentPage> {
void _handleRecipientCleared() {
final pageSelector = context.read<PageSelectorProvider>();
final recipientProvider = context.read<RecipientProvider>();
final recipientProvider = context.read<RecipientsProvider>();
recipientProvider.selectRecipient(null);
recipientProvider.setCurrentObject(null);
pageSelector.selectRecipient(null);
_flowProvider.reset(pageSelector);
_clearSearchField();
@@ -91,7 +83,7 @@ class _PaymentPageState extends State<PaymentPage> {
void _clearSearchField() {
_searchController.clear();
_searchFocusNode.unfocus();
context.read<RecipientProvider>().setQuery('');
context.read<RecipientsProvider>().setQuery('');
}
void _handleSendPayment() {

View File

@@ -31,7 +31,7 @@ class PaymentMethodTile extends StatelessWidget {
final theme = Theme.of(context);
return Opacity(
opacity: method.isEnabled ? 1 : 0.5,
opacity: method.isArchived ? 1 : 0.5,
child: Card(
margin: const EdgeInsets.symmetric(vertical: 4),
elevation: 0,
@@ -41,11 +41,12 @@ class PaymentMethodTile extends StatelessWidget {
onTap: makeMain,
title: Row(
children: [
Expanded(child: Text(method.label)),
Text(
method.details,
style: theme.textTheme.bodySmall,
),
Expanded(child: Text(method.name)),
if (method.description != null)
Text(
method.description!,
style: theme.textTheme.bodySmall,
),
],
),
trailing: Row(
@@ -73,12 +74,10 @@ class PaymentMethodTile extends StatelessWidget {
);
}
Widget _buildEnabledSwitch() {
return Switch.adaptive(
value: method.isEnabled,
onChanged: toggleEnabled,
);
}
Widget _buildEnabledSwitch() => Switch.adaptive(
value: method.isArchived,
onChanged: toggleEnabled,
);
Widget _buildPopupMenu(AppLocalizations l10n) {
return PopupMenuButton<String>(

View File

@@ -1,7 +1,10 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
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/payment_methods/header.dart';
import 'package:pweb/pages/payment_methods/method_selector.dart';
@@ -12,9 +15,8 @@ import 'package:pweb/pages/payment_methods/widgets/recipient_section.dart';
import 'package:pweb/pages/payment_methods/widgets/section_title.dart';
import 'package:pweb/providers/page_selector.dart';
import 'package:pweb/providers/payment_flow_provider.dart';
import 'package:pweb/providers/payment_methods.dart';
import 'package:pweb/providers/recipient.dart';
import 'package:pweb/utils/dimensions.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
@@ -43,7 +45,7 @@ class PaymentPageBody extends StatelessWidget {
final dimensions = AppDimensions();
final pageSelector = context.watch<PageSelectorProvider>();
final methodsProvider = context.watch<PaymentMethodsProvider>();
final recipientProvider = context.watch<RecipientProvider>();
final recipientProvider = context.watch<RecipientsProvider>();
final flowProvider = context.watch<PaymentFlowProvider>();
final recipient = pageSelector.selectedRecipient;
final loc = AppLocalizations.of(context)!;
@@ -79,8 +81,7 @@ class PaymentPageBody extends StatelessWidget {
SectionTitle(loc.sourceOfFunds),
SizedBox(height: dimensions.paddingSmall),
PaymentMethodSelector(
methodsProvider: methodsProvider,
onMethodChanged: methodsProvider.selectMethod,
onMethodChanged: (m) => methodsProvider.setCurrentObject(m.id),
),
SizedBox(height: dimensions.paddingXLarge),

View File

@@ -1,12 +1,12 @@
import 'package:flutter/material.dart';
import 'package:pshared/models/recipient/recipient.dart';
import 'package:pshared/provider/recipient/provider.dart';
import 'package:pweb/pages/address_book/page/search.dart';
import 'package:pweb/pages/payment_methods/widgets/card.dart';
import 'package:pweb/pages/payment_methods/widgets/search.dart';
import 'package:pweb/pages/payment_methods/widgets/section_title.dart';
import 'package:pweb/providers/recipient.dart';
import 'package:pweb/utils/dimensions.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
@@ -15,7 +15,7 @@ import 'package:pweb/generated/i18n/app_localizations.dart';
class RecipientSection extends StatelessWidget {
final Recipient? recipient;
final AppDimensions dimensions;
final RecipientProvider recipientProvider;
final RecipientsProvider recipientProvider;
final TextEditingController searchController;
final FocusNode searchFocusNode;
final ValueChanged<String> onSearchChanged;

View File

@@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:pshared/models/recipient/recipient.dart';
import 'package:pshared/provider/recipient/provider.dart';
import 'package:pweb/providers/recipient.dart';
import 'package:pweb/utils/dimensions.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
@@ -10,7 +10,7 @@ import 'package:pweb/generated/i18n/app_localizations.dart';
class RecipientSearchResults extends StatelessWidget {
final AppDimensions dimensions;
final RecipientProvider recipientProvider;
final RecipientsProvider recipientProvider;
final ValueChanged<Recipient> onRecipientSelected;
const RecipientSearchResults({

View File

@@ -4,8 +4,8 @@ import 'package:provider/provider.dart';
import 'package:pshared/models/payment/methods/data.dart';
import 'package:pshared/models/payment/methods/type.dart';
import 'package:pshared/provider/recipient/pmethods.dart';
import 'package:pweb/providers/payment_methods.dart';
import 'package:pweb/pages/payment_methods/add/widget.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
@@ -16,18 +16,10 @@ class PaymentConfigController {
PaymentConfigController(this.context);
void loadMethods() {
context.read<PaymentMethodsProvider>().loadMethods();
}
Future<void> addMethod() async {
final methodsProvider = context.read<PaymentMethodsProvider>();
await showDialog<PaymentMethodData>(
context: context,
builder: (_) => const AddPaymentMethodDialog(),
);
methodsProvider.loadMethods();
}
Future<void> addMethod() async => showDialog<PaymentMethodData>(
context: context,
builder: (_) => const AddPaymentMethodDialog(),
);
Future<void> editMethod(PaymentMethod method) async {
// TODO: implement edit functionality
@@ -55,12 +47,12 @@ class PaymentConfigController {
);
if (confirmed == true) {
methodsProvider.deleteMethod(method);
methodsProvider.delete(method.id);
}
}
void toggleEnabled(PaymentMethod method, bool value) {
context.read<PaymentMethodsProvider>().toggleEnabled(method, value);
context.read<PaymentMethodsProvider>().setArchivedMethod(method: method, newIsArchived: value);
}
void makeMain(PaymentMethod method) {
@@ -68,6 +60,7 @@ class PaymentConfigController {
}
void reorder(int oldIndex, int newIndex) {
context.read<PaymentMethodsProvider>().reorderMethods(oldIndex, newIndex);
// TODO: rimplement on top of Indexable
// context.read<PaymentMethodsProvider>().reorderMethods(oldIndex, newIndex);
}
}

View File

@@ -2,9 +2,10 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pshared/provider/recipient/pmethods.dart';
import 'package:pweb/pages/payment_methods/title.dart';
import 'package:pweb/pages/payout_page/methods/controller.dart';
import 'package:pweb/providers/payment_methods.dart';
class PaymentConfigList extends StatelessWidget {

View File

@@ -20,7 +20,6 @@ class _MethodsWidgetState extends State<MethodsWidget> {
void initState() {
super.initState();
controller = PaymentConfigController(context);
controller.loadMethods();
}
@override

View File

@@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pweb/models/wallet.dart';
import 'package:pshared/provider/recipient/pmethods.dart';
import 'package:pweb/models/wallet.dart';
import 'package:pweb/pages/payout_page/methods/widget.dart';
import 'package:pweb/pages/payout_page/wallet/wigets.dart';
import 'package:pweb/providers/payment_methods.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';

View File

@@ -1,41 +1,40 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:logging/logging.dart';
import 'package:pshared/models/payment/methods/type.dart';
import 'package:pshared/models/payment/type.dart';
import 'package:pshared/models/recipient/recipient.dart';
import 'package:pshared/provider/recipient/pmethods.dart';
import 'package:pshared/provider/recipient/provider.dart';
import 'package:pweb/models/wallet.dart';
import 'package:pweb/providers/payment_methods.dart';
import 'package:pweb/providers/wallets.dart';
import 'package:pweb/widgets/sidebar/destinations.dart';
import 'package:pweb/services/amplitude.dart';
import 'package:pweb/providers/recipient.dart';
import 'package:pweb/widgets/sidebar/destinations.dart';
class PageSelectorProvider extends ChangeNotifier {
static final _logger = Logger('provider.page_selector');
PayoutDestination _selected = PayoutDestination.dashboard;
PaymentType? _type;
bool _cameFromRecipientList = false;
PayoutDestination? _previousDestination;
RecipientProvider? recipientProvider;
WalletsProvider? walletsProvider;
PaymentMethodsProvider? methodsProvider;
late RecipientsProvider recipientProvider;
late WalletsProvider walletsProvider;
late PaymentMethodsProvider methodsProvider;
PayoutDestination get selected => _selected;
PaymentType? get type => _type;
bool get cameFromRecipientList => _cameFromRecipientList;
PageSelectorProvider({
this.recipientProvider,
this.walletsProvider,
this.methodsProvider,
});
PageSelectorProvider();
void update(
RecipientProvider recipientProv,
RecipientsProvider recipientProv,
WalletsProvider walletsProv,
PaymentMethodsProvider methodsProv,
) {
@@ -50,44 +49,30 @@ class PageSelectorProvider extends ChangeNotifier {
}
void selectRecipient(Recipient? recipient, {bool fromList = false}) {
if (recipientProvider != null) {
recipientProvider!.selectRecipient(recipient);
_cameFromRecipientList = fromList;
_setPreviousDestination();
_selected = PayoutDestination.payment;
notifyListeners();
} else {
debugPrint("RecipientProvider is null — cannot select recipient");
}
recipientProvider.setCurrentObject(recipient?.id);
_cameFromRecipientList = fromList;
_setPreviousDestination();
_selected = PayoutDestination.payment;
notifyListeners();
}
void editRecipient(Recipient? recipient, {bool fromList = false}) {
if (recipientProvider != null) {
recipientProvider!.selectRecipient(recipient);
_cameFromRecipientList = fromList;
_selected = PayoutDestination.addrecipient;
notifyListeners();
} else {
debugPrint("RecipientProvider is null — cannot select recipient");
}
recipientProvider.setCurrentObject(recipient?.id);
_cameFromRecipientList = fromList;
_selected = PayoutDestination.addrecipient;
notifyListeners();
}
void goToAddRecipient() {
if (recipientProvider != null) {
AmplitudeService.recipientAddStarted();
recipientProvider!.selectRecipient(null);
_selected = PayoutDestination.addrecipient;
_cameFromRecipientList = false;
notifyListeners();
} else {
debugPrint("RecipientProvider is null — cannot go to add recipient");
}
AmplitudeService.recipientAddStarted();
recipientProvider!.setCurrentObject(null);
_selected = PayoutDestination.addrecipient;
_cameFromRecipientList = false;
notifyListeners();
}
void startPaymentWithoutRecipient(PaymentType type) {
if (recipientProvider != null) {
recipientProvider!.selectRecipient(null);
}
recipientProvider.setCurrentObject(null);
_type = type;
_cameFromRecipientList = false;
_setPreviousDestination();
@@ -111,13 +96,9 @@ class PageSelectorProvider extends ChangeNotifier {
}
void selectWallet(Wallet wallet) {
if (walletsProvider != null) {
walletsProvider!.selectWallet(wallet);
_selected = PayoutDestination.editwallet;
notifyListeners();
} else {
debugPrint("WalletsProvider is null — cannot select wallet");
}
walletsProvider.selectWallet(wallet);
_selected = PayoutDestination.editwallet;
notifyListeners();
}
void startPaymentFromWallet(Wallet wallet) {
@@ -129,26 +110,26 @@ class PageSelectorProvider extends ChangeNotifier {
}
PaymentMethod? getPaymentMethodForWallet(Wallet wallet) {
if (methodsProvider == null || methodsProvider!.methods.isEmpty) {
if (methodsProvider.methods.isEmpty) {
return null;
}
return methodsProvider!.methods.firstWhereOrNull(
(method) => method.type == PaymentType.wallet &&
method.details.contains(wallet.walletUserID)
(method.description?.contains(wallet.walletUserID) ?? false),
);
}
Map<PaymentType, Object> getAvailablePaymentTypes() {
final recipient = selectedRecipient;
if (recipient == null) return {};
if ((recipient == null) || !methodsProvider.isReady) return {};
final methodsForRecipient = methodsProvider.methods.where(
(method) => !method.isArchived && method.recipientRef == recipient.id,
);
return {
if (recipient.card != null) PaymentType.card: recipient.card!,
if (recipient.iban != null) PaymentType.iban: recipient.iban!,
if (recipient.wallet != null) PaymentType.wallet: recipient.wallet!,
if (recipient.bank != null) PaymentType.bankAccount: recipient.bank!,
if (recipient.cryptoAddress != null) PaymentType.cryptoAddress: recipient.cryptoAddress!,
for (final method in methodsForRecipient) method.type: method.data,
};
}
@@ -158,11 +139,11 @@ class PageSelectorProvider extends ChangeNotifier {
if (availableTypes.containsKey(currentType)) {
return currentType;
} else if (availableTypes.isNotEmpty) {
return availableTypes.keys.first;
} else {
return PaymentType.bankAccount;
}
if (availableTypes.isNotEmpty) {
return availableTypes.keys.first;
}
return PaymentType.bankAccount;
}
bool shouldShowPaymentForm() {
@@ -170,11 +151,11 @@ class PageSelectorProvider extends ChangeNotifier {
}
void handleWalletAutoSelection() {
if (selectedWallet != null && methodsProvider != null) {
if (selectedWallet != null) {
final wallet = selectedWallet!;
final matchingMethod = getPaymentMethodForWallet(wallet);
if (matchingMethod != null) {
methodsProvider!.selectMethod(matchingMethod);
methodsProvider.setCurrentObject(matchingMethod.id);
}
}
}
@@ -185,6 +166,6 @@ class PageSelectorProvider extends ChangeNotifier {
}
}
Recipient? get selectedRecipient => recipientProvider?.selectedRecipient;
Wallet? get selectedWallet => walletsProvider?.selectedWallet;
Recipient? get selectedRecipient => recipientProvider.currentObject;
Wallet? get selectedWallet => walletsProvider.selectedWallet;
}

View File

@@ -1,68 +0,0 @@
import 'package:flutter/material.dart';
import 'package:pshared/models/payment/methods/type.dart';
import 'package:pweb/services/payments/payment_methods.dart';
class PaymentMethodsProvider extends ChangeNotifier {
final PaymentMethodsService service;
List<PaymentMethod> _methods = [];
PaymentMethod? _selectedMethod;
bool _isLoading = false;
String? _error;
PaymentMethodsProvider({required this.service});
List<PaymentMethod> get methods => _methods;
PaymentMethod? get selectedMethod => _selectedMethod;
bool get isLoading => _isLoading;
String? get error => _error;
Future<void> loadMethods() async {
_isLoading = true;
_error = null;
notifyListeners();
try {
_methods = await service.fetchMethods();
_selectedMethod = _methods.firstWhere((m) => m.isMain, orElse: () => _methods.first);
} catch (e) {
_error = e.toString();
}
_isLoading = false;
notifyListeners();
}
void selectMethod(PaymentMethod method) {
_selectedMethod = method;
notifyListeners();
}
void deleteMethod(PaymentMethod method) {
_methods.remove(method);
notifyListeners();
}
void reorderMethods(int oldIndex, int newIndex) {
if (newIndex > oldIndex) newIndex--;
final item = _methods.removeAt(oldIndex);
_methods.insert(newIndex, item);
notifyListeners();
}
void toggleEnabled(PaymentMethod method, bool value) {
method.isEnabled = value;
notifyListeners();
}
void makeMain(PaymentMethod method) {
for (final m in _methods) {
m.isMain = false;
}
method.isMain = true;
selectMethod(method);
}
}

View File

@@ -1,80 +0,0 @@
import 'package:flutter/foundation.dart';
import 'package:pshared/models/recipient/filter.dart';
import 'package:pshared/models/recipient/recipient.dart';
import 'package:pshared/models/recipient/status.dart';
import 'package:pweb/services/recipient/recipient.dart';
class RecipientProvider extends ChangeNotifier {
final RecipientService _service;
RecipientProvider(this._service);
List<Recipient> _recipients = [];
bool _isLoading = false;
String? _error;
RecipientFilter _selectedFilter = RecipientFilter.all;
String _query = '';
Recipient? _selectedRecipient;
List<Recipient> get recipients => _recipients;
bool get isLoading => _isLoading;
String? get error => _error;
RecipientFilter get selectedFilter => _selectedFilter;
String get query => _query;
Recipient? get selectedRecipient => _selectedRecipient;
List<Recipient> get filteredRecipients {
List<Recipient> filtered = _recipients.where((r) {
switch (_selectedFilter) {
case RecipientFilter.ready:
return r.status == RecipientStatus.ready;
case RecipientFilter.registered:
return r.status == RecipientStatus.registered;
case RecipientFilter.notRegistered:
return r.status == RecipientStatus.notRegistered;
case RecipientFilter.all:
return true;
}
}).toList();
if (_query.isNotEmpty) {
filtered = filtered.where((r) => r.matchesQuery(_query)).toList();
}
return filtered;
}
Future<void> loadRecipients() async {
_isLoading = true;
_error = null;
notifyListeners();
try {
_recipients = await _service.fetchRecipients();
} catch (e) {
_error = e.toString();
} finally {
_isLoading = false;
notifyListeners();
}
}
void setFilter(RecipientFilter filter) {
_selectedFilter = filter;
notifyListeners();
}
void setQuery(String query) {
_query = query.trim().toLowerCase();
notifyListeners();
}
void selectRecipient(Recipient? recipient) {
_selectedRecipient = recipient;
notifyListeners();
}
}

View File

@@ -1,10 +1,9 @@
import 'package:pshared/models/payment/upload_history_item.dart';
import 'package:pweb/providers/template.dart';
import 'package:pweb/services/payments/upload_history.dart';
import 'package:pweb/services/payments/history.dart';
class UploadHistoryProvider extends FutureProviderTemplate<List<UploadHistoryItem>> {
UploadHistoryProvider({required UploadHistoryService service})
: super(loader: service.fetchHistory);
UploadHistoryProvider({required UploadHistoryService service}) : super(loader: service.fetchHistory);
}

View File

@@ -0,0 +1,48 @@
// import 'package:pshared/models/payment/methods/type.dart';
// import 'package:pshared/models/payment/type.dart';
// abstract class PaymentMethodsService {
// Future<List<PaymentMethod>> fetchMethods();
// }
// class MockPaymentMethodsService implements PaymentMethodsService {
// @override
// Future<List<PaymentMethod>> fetchMethods() async {
// await Future.delayed(const Duration(milliseconds: 200));
// return [
// PaymentMethod(
// id: '1',
// label: 'My account',
// details: '•••4567',
// type: PaymentType.bankAccount,
// isMain: true,
// ),
// PaymentMethod(
// id: '2',
// label: 'Euro IBAN',
// details: 'DE•• •••8901',
// type: PaymentType.iban,
// ),
// PaymentMethod(
// id: '3',
// label: 'Wallet',
// details: 'WA12345667',
// type: PaymentType.wallet,
// ),
// PaymentMethod(
// id: '4',
// label: 'Wallet',
// details: 'WA-76654321',
// type: PaymentType.wallet,
// ),
// PaymentMethod(
// id: '5',
// label: 'Credit Card',
// details: '21•• •••• •••• 8901',
// type: PaymentType.card,
// ),
// ];
// }
// }

View File

@@ -1,48 +0,0 @@
import 'package:pshared/models/payment/methods/type.dart';
import 'package:pshared/models/payment/type.dart';
abstract class PaymentMethodsService {
Future<List<PaymentMethod>> fetchMethods();
}
class MockPaymentMethodsService implements PaymentMethodsService {
@override
Future<List<PaymentMethod>> fetchMethods() async {
await Future.delayed(const Duration(milliseconds: 200));
return [
PaymentMethod(
id: '1',
label: 'My account',
details: '•••4567',
type: PaymentType.bankAccount,
isMain: true,
),
PaymentMethod(
id: '2',
label: 'Euro IBAN',
details: 'DE•• •••8901',
type: PaymentType.iban,
),
PaymentMethod(
id: '3',
label: 'Wallet',
details: 'WA12345667',
type: PaymentType.wallet,
),
PaymentMethod(
id: '4',
label: 'Wallet',
details: 'WA-76654321',
type: PaymentType.wallet,
),
PaymentMethod(
id: '5',
label: 'Credit Card',
details: '21•• •••• •••• 8901',
type: PaymentType.card,
),
];
}
}

View File

@@ -1,88 +1,88 @@
import 'package:pshared/models/recipient/recipient.dart';
import 'package:pshared/models/payment/methods/card.dart';
import 'package:pshared/models/payment/methods/iban.dart';
import 'package:pshared/models/payment/methods/russian_bank.dart';
import 'package:pshared/models/payment/methods/wallet.dart';
import 'package:pshared/models/recipient/status.dart';
import 'package:pshared/models/recipient/type.dart';
// import 'package:pshared/models/recipient/recipient.dart';
// import 'package:pshared/models/payment/methods/card.dart';
// import 'package:pshared/models/payment/methods/iban.dart';
// import 'package:pshared/models/payment/methods/russian_bank.dart';
// import 'package:pshared/models/payment/methods/wallet.dart';
// import 'package:pshared/models/recipient/status.dart';
// import 'package:pshared/models/recipient/type.dart';
class RecipientService {
Future<List<Recipient>> fetchRecipients() async {
await Future.delayed(const Duration(milliseconds: 500));
return RecipientMockData.all;
}
}
// class RecipientService {
// Future<List<Recipient>> fetchRecipients() async {
// await Future.delayed(const Duration(milliseconds: 500));
// return RecipientMockData.all;
// }
// }
class RecipientMockData {
static List<Recipient> get all => [
Recipient.mock(
name: 'Alice Johnson',
email: 'alice@example.com',
status: RecipientStatus.ready,
type: RecipientType.internal,
card: CardPaymentMethod(
pan: '1213',
firstName: 'Alice',
lastName: 'Johnson',
),
),
Recipient.mock(
name: 'Bob & Co Ltd.',
email: 'payout@bobco.com',
status: RecipientStatus.registered,
type: RecipientType.external,
card: CardPaymentMethod(
pan: '4343',
firstName: 'Bob',
lastName: 'Co',
),
iban: IbanPaymentMethod(
iban: 'FR7630***890189',
accountHolder: 'Bob & Co Ltd.',
bic: 'AGRIFRPP',
bankName: 'Credit Agricole',
),
wallet: WalletPaymentMethod(walletId: '8932231'),
),
Recipient.mock(
name: 'Carlos Kline',
email: 'carlos@acme.org',
status: RecipientStatus.notRegistered,
type: RecipientType.internal,
wallet: WalletPaymentMethod(walletId: '7723490'),
),
Recipient.mock(
name: 'Delta Outsourcing GmbH',
email: 'finance@delta-os.de',
status: RecipientStatus.registered,
type: RecipientType.external,
card: CardPaymentMethod(
pan: '9988',
firstName: 'Delta',
lastName: 'GmbH',
),
iban: IbanPaymentMethod(
iban: 'DE4450***324931',
accountHolder: 'Delta Outsourcing GmbH',
bic: 'INGDDEFFXXX',
bankName: 'ING',
),
),
Recipient.mock(
name: 'Erin Patel',
email: 'erin@labster.io',
status: RecipientStatus.ready,
type: RecipientType.internal,
bank: RussianBankAccountPaymentMethod(
accountNumber: '4081***7654',
recipientName: 'Erin Patel',
inn: '7812012345',
kpp: '781201001',
bankName: 'Alfa-Bank',
bik: '044525593',
correspondentAccount: '30101810200000000593',
),
),
];
}
// class RecipientMockData {
// static List<Recipient> get all => [
// Recipient.mock(
// name: 'Alice Johnson',
// email: 'alice@example.com',
// status: RecipientStatus.ready,
// type: RecipientType.internal,
// card: CardPaymentMethod(
// pan: '1213',
// firstName: 'Alice',
// lastName: 'Johnson',
// ),
// ),
// Recipient.mock(
// name: 'Bob & Co Ltd.',
// email: 'payout@bobco.com',
// status: RecipientStatus.registered,
// type: RecipientType.external,
// card: CardPaymentMethod(
// pan: '4343',
// firstName: 'Bob',
// lastName: 'Co',
// ),
// iban: IbanPaymentMethod(
// iban: 'FR7630***890189',
// accountHolder: 'Bob & Co Ltd.',
// bic: 'AGRIFRPP',
// bankName: 'Credit Agricole',
// ),
// wallet: WalletPaymentMethod(walletId: '8932231'),
// ),
// Recipient.mock(
// name: 'Carlos Kline',
// email: 'carlos@acme.org',
// status: RecipientStatus.notRegistered,
// type: RecipientType.internal,
// wallet: WalletPaymentMethod(walletId: '7723490'),
// ),
// Recipient.mock(
// name: 'Delta Outsourcing GmbH',
// email: 'finance@delta-os.de',
// status: RecipientStatus.registered,
// type: RecipientType.external,
// card: CardPaymentMethod(
// pan: '9988',
// firstName: 'Delta',
// lastName: 'GmbH',
// ),
// iban: IbanPaymentMethod(
// iban: 'DE4450***324931',
// accountHolder: 'Delta Outsourcing GmbH',
// bic: 'INGDDEFFXXX',
// bankName: 'ING',
// ),
// ),
// Recipient.mock(
// name: 'Erin Patel',
// email: 'erin@labster.io',
// status: RecipientStatus.ready,
// type: RecipientType.internal,
// bank: RussianBankAccountPaymentMethod(
// accountNumber: '4081***7654',
// recipientName: 'Erin Patel',
// inn: '7812012345',
// kpp: '781201001',
// bankName: 'Alfa-Bank',
// bik: '044525593',
// correspondentAccount: '30101810200000000593',
// ),
// ),
// ];
// }

View File

@@ -48,7 +48,7 @@ class _PaymentMethodDropdownState extends State<PaymentMethodDropdown> {
children: [
Icon(iconForPaymentType(method.type), size: 20),
const SizedBox(width: 8),
Text('${method.label} (${method.details})'),
Text('${method.name}' + (method.description == null ? '' : ' (${method.description!})')),
],
),
);

View File

@@ -12,6 +12,6 @@ String getPaymentTypeLabel(BuildContext context, PaymentType type) {
PaymentType.bankAccount => l10n.paymentTypeBankAccount,
PaymentType.iban => l10n.paymentTypeIban,
PaymentType.wallet => l10n.paymentTypeWallet,
PaymentType.cryptoAddress => 'Crypto address',
PaymentType.cryptoAddress => l10n.paymentTypeCryptoAddress,
};
}

View File

@@ -72,7 +72,7 @@ class PageSelector extends StatelessWidget {
break;
case PayoutDestination.addrecipient:
final recipient = provider.recipientProvider?.selectedRecipient;
final recipient = provider.recipientProvider.currentObject;
content = AdressBookRecipientForm(
recipient: recipient,
onSaved: (_) => provider.selectPage(PayoutDestination.recipients),
@@ -100,7 +100,7 @@ class PageSelector extends StatelessWidget {
break;
case PayoutDestination.editwallet:
final wallet = provider.walletsProvider?.selectedWallet;
final wallet = provider.walletsProvider.selectedWallet;
content = wallet != null
? WalletEditPage(
onBack: provider.goBackFromWalletEdit,