197 lines
6.6 KiB
Dart
197 lines
6.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import 'package:go_router/go_router.dart';
|
|
|
|
import 'package:intl/intl.dart';
|
|
|
|
import 'package:provider/provider.dart';
|
|
|
|
import 'package:sxchnge_wallet/sxchnge_wallet.dart';
|
|
|
|
import 'copy.dart';
|
|
|
|
import '../generated/i18n/app_localizations.dart';
|
|
|
|
class ActivityPage extends StatefulWidget {
|
|
const ActivityPage({super.key});
|
|
@override
|
|
State<ActivityPage> createState() => _ActivityPageState();
|
|
}
|
|
|
|
class _ActivityPageState extends State<ActivityPage> {
|
|
TransferKind? _kind;
|
|
OperationStatus? _status;
|
|
String _search = '';
|
|
String? _currency;
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final p = context.watch<WalletProvider>();
|
|
final l = AppLocalizations.of(context)!;
|
|
final list =
|
|
p.data?.operations
|
|
.where(
|
|
(o) =>
|
|
(_kind == null || o.quote.request.kind == _kind) &&
|
|
(_status == null || o.status == _status) &&
|
|
(_currency == null ||
|
|
o.quote.request.source == _currency ||
|
|
o.quote.request.destination == _currency) &&
|
|
o.id.toLowerCase().contains(_search.toLowerCase()),
|
|
)
|
|
.toList() ??
|
|
[];
|
|
return SingleChildScrollView(
|
|
padding: const EdgeInsets.all(24),
|
|
child: Center(
|
|
child: ConstrainedBox(
|
|
constraints: const BoxConstraints(maxWidth: 1000),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
l.wActivity,
|
|
style: Theme.of(context).textTheme.displaySmall,
|
|
),
|
|
),
|
|
IconButton(
|
|
tooltip: l.wRefresh,
|
|
onPressed: p.loading ? null : p.refresh,
|
|
icon: const Icon(Icons.refresh),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 24),
|
|
TextField(
|
|
decoration: InputDecoration(
|
|
labelText: l.wSearch,
|
|
prefixIcon: const Icon(Icons.search),
|
|
),
|
|
onChanged: (value) => setState(() => _search = value),
|
|
),
|
|
const SizedBox(height: 16),
|
|
Wrap(
|
|
spacing: 8,
|
|
runSpacing: 8,
|
|
children: [
|
|
ChoiceChip(
|
|
label: Text(l.wAll),
|
|
selected: _kind == null,
|
|
onSelected: (_) => setState(() => _kind = null),
|
|
),
|
|
for (final k in TransferKind.values)
|
|
ChoiceChip(
|
|
label: Text(kindText(l, k)),
|
|
selected: _kind == k,
|
|
onSelected: (_) => setState(() => _kind = k),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 12),
|
|
Wrap(
|
|
spacing: 8,
|
|
runSpacing: 8,
|
|
children: [
|
|
for (final s in OperationStatus.values)
|
|
FilterChip(
|
|
label: Text(statusText(l, s)),
|
|
selected: _status == s,
|
|
onSelected: (value) =>
|
|
setState(() => _status = value ? s : null),
|
|
),
|
|
for (final a in p.data?.accounts ?? <CurrencyAccount>[])
|
|
FilterChip(
|
|
label: Text(a.currency),
|
|
selected: _currency == a.id,
|
|
onSelected: (value) =>
|
|
setState(() => _currency = value ? a.id : null),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 20),
|
|
if (p.loading) const LinearProgressIndicator(),
|
|
if (p.failed) Text(l.wUnavailable),
|
|
if (list.isEmpty && !p.loading)
|
|
Padding(
|
|
padding: const EdgeInsets.all(24),
|
|
child: Text(
|
|
(p.data?.operations.isEmpty ?? true)
|
|
? l.wEmptyActivity
|
|
: l.wNoMatches,
|
|
),
|
|
),
|
|
for (final o in list) OperationTile(operation: o),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class OperationTile extends StatelessWidget {
|
|
const OperationTile({super.key, required this.operation});
|
|
final WalletOperation operation;
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final p = context.read<WalletProvider>();
|
|
final l = AppLocalizations.of(context)!;
|
|
final o = operation;
|
|
final a = p.account(o.quote.request.source);
|
|
final to = p.account(o.quote.request.destination);
|
|
final t = Theme.of(context);
|
|
return Card(
|
|
child: InkWell(
|
|
borderRadius: BorderRadius.circular(20),
|
|
onTap: () => context.push('/activity/${o.id}'),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(20),
|
|
child: Row(
|
|
children: [
|
|
CircleAvatar(
|
|
backgroundColor: t.colorScheme.primaryContainer,
|
|
child: Icon(
|
|
kindIcon(o.quote.request.kind),
|
|
color: t.colorScheme.onPrimaryContainer,
|
|
),
|
|
),
|
|
const SizedBox(width: 16),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
kindText(l, o.quote.request.kind),
|
|
style: t.textTheme.titleMedium,
|
|
),
|
|
Text(
|
|
'${a?.currency ?? ''}${to == null ? '' : ' → ${to.currency}'} · ${statusText(l, o.status)}',
|
|
),
|
|
Text(
|
|
DateFormat.yMMMd(
|
|
Localizations.localeOf(context).toLanguageTag(),
|
|
).add_Hm().format(o.createdAt),
|
|
style: t.textTheme.bodySmall,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Flexible(
|
|
child: Text(
|
|
a == null ? '' : money(context, a, o.quote.request.amount),
|
|
textAlign: TextAlign.end,
|
|
style: t.textTheme.titleMedium,
|
|
),
|
|
),
|
|
const Icon(Icons.chevron_right),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|