262 lines
8.6 KiB
Dart
262 lines
8.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import 'package:go_router/go_router.dart';
|
|
|
|
import 'package:provider/provider.dart';
|
|
|
|
import 'package:sxchnge_wallet/sxchnge_wallet.dart';
|
|
|
|
import 'activity.dart';
|
|
import 'copy.dart';
|
|
|
|
import '../generated/i18n/app_localizations.dart';
|
|
|
|
class AccountsPage extends StatelessWidget {
|
|
const AccountsPage({super.key, this.accountId});
|
|
final String? accountId;
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final p = context.watch<WalletProvider>();
|
|
final l = AppLocalizations.of(context)!;
|
|
final t = Theme.of(context);
|
|
final c = t.colorScheme;
|
|
final accounts = p.data?.accounts ?? [];
|
|
final selected = accountId == null
|
|
? accounts.firstOrNull
|
|
: p.account(accountId);
|
|
return SingleChildScrollView(
|
|
padding: const EdgeInsets.all(24),
|
|
child: Center(
|
|
child: ConstrainedBox(
|
|
constraints: const BoxConstraints(maxWidth: 1120),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Expanded(
|
|
child: Text(l.wHeadline, style: t.textTheme.displayMedium),
|
|
),
|
|
IconButton(
|
|
tooltip: l.wRefresh,
|
|
onPressed: p.loading ? null : p.refresh,
|
|
icon: const Icon(Icons.refresh),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 12),
|
|
Text(l.wSubheading, style: t.textTheme.bodyLarge),
|
|
const SizedBox(height: 16),
|
|
Card(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(l.kycIntro),
|
|
TextButton(
|
|
onPressed: () => context.push('/identity'),
|
|
child: Text(l.wIdentity),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 28),
|
|
if (p.loading) const LinearProgressIndicator(),
|
|
if (p.failed)
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
|
child: Text(l.wUnavailable, style: TextStyle(color: c.error)),
|
|
),
|
|
if (!p.loading && accounts.isEmpty) Text(l.wEmptyAccounts),
|
|
if (accountId != null &&
|
|
p.account(accountId) == null &&
|
|
!p.loading)
|
|
Text(l.wNotFound),
|
|
if (selected != null) ...[
|
|
const SizedBox(height: 24),
|
|
Text(selected.currency, style: t.textTheme.titleLarge),
|
|
const SizedBox(height: 12),
|
|
Wrap(
|
|
spacing: 12,
|
|
runSpacing: 12,
|
|
children: [
|
|
for (final kind in TransferKind.values)
|
|
_action(
|
|
context,
|
|
kind,
|
|
selected,
|
|
primary:
|
|
kind ==
|
|
(selected.available == 0
|
|
? TransferKind.topUp
|
|
: TransferKind.exchange),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
const SizedBox(height: 24),
|
|
LayoutBuilder(
|
|
builder: (context, box) {
|
|
final width = box.maxWidth >= 660
|
|
? (box.maxWidth - 32) / 3
|
|
: box.maxWidth;
|
|
return Wrap(
|
|
spacing: 16,
|
|
runSpacing: 16,
|
|
children: [
|
|
for (var i = 0; i < accounts.length; i++)
|
|
SizedBox(
|
|
width: width,
|
|
child: _AccountCard(
|
|
account: accounts[i],
|
|
selected: accounts[i].id == selected?.id,
|
|
index: i,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
const SizedBox(height: 36),
|
|
Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(l.wRecent, style: t.textTheme.headlineSmall),
|
|
TextButton(
|
|
onPressed: () => context.go('/activity'),
|
|
child: Text(l.wSeeAll),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 12),
|
|
...?p.data?.operations
|
|
.where(
|
|
(o) =>
|
|
accountId == null ||
|
|
o.quote.request.source == accountId ||
|
|
o.quote.request.destination == accountId,
|
|
)
|
|
.take(5)
|
|
.map((o) => OperationTile(operation: o)),
|
|
if (!(p.data?.operations.any(
|
|
(o) =>
|
|
accountId == null ||
|
|
o.quote.request.source == accountId ||
|
|
o.quote.request.destination == accountId,
|
|
) ??
|
|
false))
|
|
Container(
|
|
padding: const EdgeInsets.all(28),
|
|
decoration: BoxDecoration(
|
|
color: c.surfaceContainer,
|
|
borderRadius: BorderRadius.circular(24),
|
|
),
|
|
child: Text(l.wEmptyActivity),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _action(
|
|
BuildContext context,
|
|
TransferKind kind,
|
|
CurrencyAccount a, {
|
|
required bool primary,
|
|
}) {
|
|
final l = AppLocalizations.of(context)!;
|
|
void go() => context.go(
|
|
'/${kind.name == "topUp" ? "top-up" : kind.name}',
|
|
extra: a.id,
|
|
);
|
|
return primary
|
|
? FilledButton.icon(
|
|
key: ValueKey('${kind.name}-${a.id}'),
|
|
onPressed: go,
|
|
icon: Icon(kindIcon(kind)),
|
|
label: Text(kindText(l, kind)),
|
|
)
|
|
: FilledButton.tonalIcon(
|
|
key: ValueKey('${kind.name}-${a.id}'),
|
|
onPressed: go,
|
|
icon: Icon(kindIcon(kind)),
|
|
label: Text(kindText(l, kind)),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _AccountCard extends StatelessWidget {
|
|
const _AccountCard({
|
|
required this.account,
|
|
required this.selected,
|
|
required this.index,
|
|
});
|
|
final CurrencyAccount account;
|
|
final bool selected;
|
|
final int index;
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l = AppLocalizations.of(context)!;
|
|
final t = Theme.of(context);
|
|
final c = t.colorScheme;
|
|
final bg = selected
|
|
? c.primary
|
|
: index == 1
|
|
? c.tertiaryContainer
|
|
: c.primaryContainer;
|
|
final fg = selected
|
|
? c.onPrimary
|
|
: index == 1
|
|
? c.onTertiaryContainer
|
|
: c.onPrimaryContainer;
|
|
return Semantics(
|
|
selected: selected,
|
|
child: Material(
|
|
color: bg,
|
|
borderRadius: BorderRadius.circular(28),
|
|
child: InkWell(
|
|
key: ValueKey('account-${account.id}'),
|
|
borderRadius: BorderRadius.circular(28),
|
|
onTap: () => context.go('/accounts/${account.id}'),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(24),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Text(
|
|
account.currency,
|
|
style: t.textTheme.titleLarge?.copyWith(color: fg),
|
|
),
|
|
const Spacer(),
|
|
Icon(
|
|
selected
|
|
? Icons.check_circle_outline
|
|
: Icons.arrow_outward_rounded,
|
|
color: fg,
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 32),
|
|
Text(l.wAvailable, style: TextStyle(color: fg)),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
money(context, account, account.available),
|
|
key: ValueKey('balance-${account.id}'),
|
|
style: t.textTheme.headlineMedium?.copyWith(color: fg),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|