Build exchange workspace and shared transaction services (#28)
This commit is contained in:
1 parent
52b7135eda
commit
c496da8a55
48 files changed
+4757
-120
No files matched your search
@@ -0,0 +1,196 @@
|
||||
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),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user