wallet card redesign
This commit is contained in:
@@ -1,44 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:pshared/provider/ledger.dart';
|
||||
|
||||
import 'package:pweb/controllers/dashboard/balance/source_actions.dart';
|
||||
import 'package:pweb/pages/dashboard/buttons/balance/actions/bar.dart';
|
||||
|
||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||
|
||||
|
||||
class LedgerSourceActions extends StatelessWidget {
|
||||
final String ledgerAccountRef;
|
||||
final VoidCallback onAddFunds;
|
||||
final VoidCallback onWalletDetails;
|
||||
|
||||
const LedgerSourceActions({
|
||||
super.key,
|
||||
required this.ledgerAccountRef,
|
||||
required this.onAddFunds,
|
||||
required this.onWalletDetails,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ledgerProvider = context.watch<LedgerAccountsProvider>();
|
||||
final loc = AppLocalizations.of(context)!;
|
||||
final isBusy =
|
||||
ledgerProvider.isWalletRefreshing(ledgerAccountRef) ||
|
||||
ledgerProvider.isLoading;
|
||||
final hasTarget = ledgerProvider.accounts.any(
|
||||
(a) => a.ledgerAccountRef == ledgerAccountRef,
|
||||
const controller = BalanceSourceActionsController();
|
||||
final state = controller.ledger(
|
||||
context: context,
|
||||
ledgerAccountRef: ledgerAccountRef,
|
||||
onAddFunds: onAddFunds,
|
||||
onWalletDetails: onWalletDetails,
|
||||
);
|
||||
|
||||
return BalanceActionsBar(
|
||||
isRefreshBusy: isBusy,
|
||||
canRefresh: hasTarget,
|
||||
onRefresh: () {
|
||||
context.read<LedgerAccountsProvider>().refreshBalance(ledgerAccountRef);
|
||||
},
|
||||
onAddFunds: onAddFunds,
|
||||
refreshLabel: loc.refreshBalance,
|
||||
addFundsLabel: loc.addFunds,
|
||||
);
|
||||
return BalanceActionsBar(state: state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:pshared/provider/payment/wallets.dart';
|
||||
|
||||
import 'package:pweb/controllers/dashboard/balance/source_actions.dart';
|
||||
import 'package:pweb/pages/dashboard/buttons/balance/actions/bar.dart';
|
||||
|
||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||
|
||||
|
||||
class WalletSourceActions extends StatelessWidget {
|
||||
final String walletRef;
|
||||
@@ -21,22 +16,13 @@ class WalletSourceActions extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final walletsProvider = context.watch<WalletsProvider>();
|
||||
final loc = AppLocalizations.of(context)!;
|
||||
final isBusy =
|
||||
walletsProvider.isWalletRefreshing(walletRef) ||
|
||||
walletsProvider.isLoading;
|
||||
final hasTarget = walletsProvider.wallets.any((w) => w.id == walletRef);
|
||||
|
||||
return BalanceActionsBar(
|
||||
isRefreshBusy: isBusy,
|
||||
canRefresh: hasTarget,
|
||||
onRefresh: () {
|
||||
context.read<WalletsProvider>().refreshBalance(walletRef);
|
||||
},
|
||||
const controller = BalanceSourceActionsController();
|
||||
final state = controller.wallet(
|
||||
context: context,
|
||||
walletRef: walletRef,
|
||||
onAddFunds: onAddFunds,
|
||||
refreshLabel: loc.refreshBalance,
|
||||
addFundsLabel: loc.addFunds,
|
||||
);
|
||||
|
||||
return BalanceActionsBar(state: state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,15 @@ import 'package:pshared/models/payment/source_type.dart';
|
||||
import 'package:pshared/models/payment/wallet.dart';
|
||||
import 'package:pshared/utils/l10n/chain.dart';
|
||||
|
||||
import 'package:pweb/controllers/dashboard/balance/source_copy.dart';
|
||||
import 'package:pweb/models/state/visibility.dart';
|
||||
import 'package:pweb/pages/dashboard/buttons/balance/amount.dart';
|
||||
import 'package:pweb/pages/dashboard/buttons/balance/ledger_amount.dart';
|
||||
import 'package:pweb/pages/dashboard/buttons/balance/source/actions/ledger.dart';
|
||||
import 'package:pweb/pages/dashboard/buttons/balance/source/actions/wallet.dart';
|
||||
import 'package:pweb/pages/dashboard/buttons/balance/source/card_layout.dart';
|
||||
import 'package:pweb/widgets/refresh_balance/ledger.dart';
|
||||
import 'package:pweb/widgets/refresh_balance/wallet.dart';
|
||||
|
||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||
|
||||
@@ -24,6 +28,8 @@ class BalanceSourceCard extends StatelessWidget {
|
||||
final LedgerAccount? _ledgerAccount;
|
||||
final VoidCallback onTap;
|
||||
final VoidCallback onAddFunds;
|
||||
static const BalanceSourceCopyController _copyController =
|
||||
BalanceSourceCopyController();
|
||||
|
||||
const BalanceSourceCard.wallet({
|
||||
super.key,
|
||||
@@ -55,12 +61,29 @@ class BalanceSourceCard extends StatelessWidget {
|
||||
? null
|
||||
: wallet.network!.localizedName(context);
|
||||
final symbol = wallet.tokenSymbol?.trim();
|
||||
final copyState = _copyController.wallet(wallet.depositAddress);
|
||||
|
||||
return BalanceSourceCardLayout(
|
||||
title: wallet.name,
|
||||
subtitle: networkLabel,
|
||||
badge: (symbol == null || symbol.isEmpty) ? null : symbol,
|
||||
onTap: onTap,
|
||||
onTap: null,
|
||||
copyLabel: copyState.label,
|
||||
canCopy: copyState.canCopy,
|
||||
onCopy: copyState.canCopy
|
||||
? () async {
|
||||
final copied = await _copyController.copy(copyState);
|
||||
if (!copied || !context.mounted) return;
|
||||
final loc = AppLocalizations.of(context)!;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(loc.addressCopied)));
|
||||
}
|
||||
: null,
|
||||
refreshButton: WalletBalanceRefreshButton(
|
||||
walletRef: wallet.id,
|
||||
iconOnly: VisibilityState.hidden,
|
||||
),
|
||||
actions: WalletSourceActions(
|
||||
walletRef: wallet.id,
|
||||
onAddFunds: onAddFunds,
|
||||
@@ -79,19 +102,35 @@ class BalanceSourceCard extends StatelessWidget {
|
||||
final accountName = account.name.trim();
|
||||
final accountCode = account.accountCode.trim();
|
||||
final title = accountName.isNotEmpty ? accountName : loc.paymentTypeLedger;
|
||||
final subtitle = accountCode.isNotEmpty ? accountCode : null;
|
||||
final badge = account.currency.trim().isEmpty
|
||||
? null
|
||||
: account.currency.toUpperCase();
|
||||
final copyState = _copyController.ledger(accountCode);
|
||||
|
||||
return BalanceSourceCardLayout(
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
subtitle: null,
|
||||
badge: badge,
|
||||
onTap: onTap,
|
||||
copyLabel: copyState.label,
|
||||
canCopy: copyState.canCopy,
|
||||
onCopy: copyState.canCopy
|
||||
? () async {
|
||||
final copied = await _copyController.copy(copyState);
|
||||
if (!copied || !context.mounted) return;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(loc.addressCopied)));
|
||||
}
|
||||
: null,
|
||||
refreshButton: LedgerBalanceRefreshButton(
|
||||
ledgerAccountRef: account.ledgerAccountRef,
|
||||
iconOnly: VisibilityState.hidden,
|
||||
),
|
||||
actions: LedgerSourceActions(
|
||||
ledgerAccountRef: account.ledgerAccountRef,
|
||||
onAddFunds: onAddFunds,
|
||||
onWalletDetails: onTap,
|
||||
),
|
||||
amount: LedgerBalanceAmount(account: account),
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:pweb/pages/dashboard/buttons/balance/config.dart';
|
||||
import 'package:pweb/pages/dashboard/buttons/balance/header.dart';
|
||||
import 'package:pweb/pages/dashboard/buttons/balance/source/layout/wide_body.dart';
|
||||
|
||||
|
||||
class BalanceSourceCardLayout extends StatelessWidget {
|
||||
@@ -9,8 +9,12 @@ class BalanceSourceCardLayout extends StatelessWidget {
|
||||
final String? subtitle;
|
||||
final String? badge;
|
||||
final Widget amount;
|
||||
final Widget refreshButton;
|
||||
final Widget actions;
|
||||
final VoidCallback onTap;
|
||||
final VoidCallback? onTap;
|
||||
final String copyLabel;
|
||||
final bool canCopy;
|
||||
final VoidCallback? onCopy;
|
||||
|
||||
const BalanceSourceCardLayout({
|
||||
super.key,
|
||||
@@ -18,40 +22,39 @@ class BalanceSourceCardLayout extends StatelessWidget {
|
||||
required this.subtitle,
|
||||
required this.badge,
|
||||
required this.amount,
|
||||
required this.refreshButton,
|
||||
required this.actions,
|
||||
required this.onTap,
|
||||
required this.copyLabel,
|
||||
required this.canCopy,
|
||||
required this.onCopy,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final borderRadius = BorderRadius.circular(WalletCardConfig.borderRadius);
|
||||
|
||||
return Card(
|
||||
color: colorScheme.onSecondary,
|
||||
elevation: WalletCardConfig.elevation,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(WalletCardConfig.borderRadius),
|
||||
),
|
||||
shape: RoundedRectangleBorder(borderRadius: borderRadius),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(WalletCardConfig.borderRadius),
|
||||
borderRadius: borderRadius,
|
||||
onTap: onTap,
|
||||
child: SizedBox.expand(
|
||||
child: Padding(
|
||||
padding: WalletCardConfig.contentPadding,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
BalanceHeader(title: title, subtitle: subtitle, badge: badge),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Expanded(child: amount),
|
||||
const SizedBox(width: 12),
|
||||
actions,
|
||||
],
|
||||
),
|
||||
],
|
||||
child: BalanceSourceBody(
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
badge: badge,
|
||||
amount: amount,
|
||||
refreshButton: refreshButton,
|
||||
actions: actions,
|
||||
copyLabel: copyLabel,
|
||||
canCopy: canCopy,
|
||||
onCopy: onCopy,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:pshared/models/ledger/account.dart';
|
||||
import 'package:pweb/pages/dashboard/buttons/balance/source/card.dart';
|
||||
|
||||
|
||||
class LedgerAccountCard extends StatelessWidget {
|
||||
final LedgerAccount account;
|
||||
final VoidCallback onAddFunds;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
const LedgerAccountCard({
|
||||
super.key,
|
||||
required this.account,
|
||||
required this.onAddFunds,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BalanceSourceCard.ledger(
|
||||
account: account,
|
||||
onTap: onTap ?? () {},
|
||||
onAddFunds: onAddFunds,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:pshared/models/payment/wallet.dart';
|
||||
|
||||
import 'package:pweb/pages/dashboard/buttons/balance/source/card.dart';
|
||||
|
||||
|
||||
class WalletCard extends StatelessWidget {
|
||||
final Wallet wallet;
|
||||
final VoidCallback onTopUp;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const WalletCard({
|
||||
super.key,
|
||||
required this.wallet,
|
||||
required this.onTopUp,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BalanceSourceCard.wallet(
|
||||
wallet: wallet,
|
||||
onTap: onTap,
|
||||
onAddFunds: onTopUp,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
class BalanceAmountWithRefresh extends StatelessWidget {
|
||||
final Widget amount;
|
||||
final Widget refreshButton;
|
||||
|
||||
const BalanceAmountWithRefresh({
|
||||
super.key,
|
||||
required this.amount,
|
||||
required this.refreshButton,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButtonTheme(
|
||||
data: IconButtonThemeData(
|
||||
style: IconButton.styleFrom(
|
||||
minimumSize: const Size(30, 30),
|
||||
maximumSize: const Size(40, 40),
|
||||
padding: EdgeInsets.zero,
|
||||
foregroundColor: colorScheme.primary,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
),
|
||||
child: refreshButton,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
amount,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
class BalanceCopyableField extends StatelessWidget {
|
||||
final String label;
|
||||
final bool canCopy;
|
||||
final VoidCallback? onCopy;
|
||||
|
||||
const BalanceCopyableField({
|
||||
super.key,
|
||||
required this.label,
|
||||
required this.canCopy,
|
||||
required this.onCopy,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final colorScheme = theme.colorScheme;
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.onSecondary,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: colorScheme.primaryFixed, width: 0.6),
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: canCopy ? onCopy : null,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.copy_rounded,
|
||||
size: 16,
|
||||
color: canCopy
|
||||
? colorScheme.primaryFixed
|
||||
: colorScheme.primary.withValues(alpha: 0.35),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Flexible(
|
||||
child: Text(
|
||||
label,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: theme.textTheme.labelMedium?.copyWith(
|
||||
color: canCopy
|
||||
? colorScheme.primaryFixed
|
||||
: colorScheme.primary.withValues(alpha: 0.45),
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:pweb/pages/dashboard/buttons/balance/header.dart';
|
||||
import 'package:pweb/pages/dashboard/buttons/balance/source/layout/amount_with_refresh.dart';
|
||||
import 'package:pweb/pages/dashboard/buttons/balance/source/layout/copyable_field.dart';
|
||||
|
||||
|
||||
class BalanceSourceBody extends StatelessWidget {
|
||||
final String title;
|
||||
final String? subtitle;
|
||||
final String? badge;
|
||||
final Widget amount;
|
||||
final Widget refreshButton;
|
||||
final Widget actions;
|
||||
final String copyLabel;
|
||||
final bool canCopy;
|
||||
final VoidCallback? onCopy;
|
||||
|
||||
const BalanceSourceBody({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.badge,
|
||||
required this.amount,
|
||||
required this.refreshButton,
|
||||
required this.actions,
|
||||
required this.copyLabel,
|
||||
required this.canCopy,
|
||||
required this.onCopy,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final sideMaxWidth = constraints.maxWidth * 0.30;
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Flexible(
|
||||
fit: FlexFit.loose,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: sideMaxWidth),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
BalanceHeader(
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
badge: badge,
|
||||
),
|
||||
SizedBox(height: constraints.maxHeight * 0.06),
|
||||
BalanceCopyableField(
|
||||
label: copyLabel,
|
||||
canCopy: canCopy,
|
||||
onCopy: onCopy,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.center,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: BalanceAmountWithRefresh(
|
||||
amount: amount,
|
||||
refreshButton: refreshButton,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
fit: FlexFit.loose,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: sideMaxWidth),
|
||||
child: SizedBox(height: constraints.maxHeight, child: actions),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user