2021-06-26 07:30:12 -07:00
|
|
|
import 'dart:async';
|
|
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter/rendering.dart';
|
|
|
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
|
|
|
import 'package:intl/intl.dart';
|
|
|
|
import 'package:local_auth/local_auth.dart';
|
|
|
|
import 'package:qr_flutter/qr_flutter.dart';
|
2021-07-07 08:40:05 -07:00
|
|
|
import 'package:warp/store.dart';
|
2021-06-26 07:30:12 -07:00
|
|
|
import 'package:warp_api/warp_api.dart';
|
|
|
|
|
|
|
|
import 'about.dart';
|
|
|
|
import 'main.dart';
|
|
|
|
|
|
|
|
class AccountPage extends StatefulWidget {
|
|
|
|
@override
|
|
|
|
State<StatefulWidget> createState() => _AccountPageState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _AccountPageState extends State<AccountPage>
|
|
|
|
with WidgetsBindingObserver, AutomaticKeepAliveClientMixin {
|
|
|
|
Timer _timerSync;
|
2021-07-07 08:40:05 -07:00
|
|
|
int _progress = 0;
|
2021-07-07 21:22:54 -07:00
|
|
|
bool _useSnapAddress = false;
|
|
|
|
String _snapAddress = "";
|
2021-07-09 06:33:39 -07:00
|
|
|
bool _showTAddr = false;
|
2021-06-26 07:30:12 -07:00
|
|
|
|
|
|
|
@override
|
|
|
|
bool get wantKeepAlive => true;
|
|
|
|
|
|
|
|
@override
|
|
|
|
initState() {
|
2021-07-07 08:40:05 -07:00
|
|
|
super.initState();
|
2021-06-26 07:30:12 -07:00
|
|
|
Future.microtask(() async {
|
2021-07-07 08:40:05 -07:00
|
|
|
await accountManager.updateUnconfirmedBalance();
|
|
|
|
await accountManager.fetchNotesAndHistory();
|
|
|
|
_setupTimer();
|
2021-06-26 07:30:12 -07:00
|
|
|
});
|
|
|
|
WidgetsBinding.instance.addObserver(this);
|
2021-07-07 08:40:05 -07:00
|
|
|
progressStream.listen((percent) {
|
|
|
|
setState(() {
|
|
|
|
_progress = percent;
|
|
|
|
});
|
|
|
|
});
|
2021-06-26 07:30:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void dispose() {
|
2021-07-07 08:40:05 -07:00
|
|
|
print("DISPOSE");
|
2021-06-26 07:30:12 -07:00
|
|
|
_timerSync?.cancel();
|
|
|
|
WidgetsBinding.instance.removeObserver(this);
|
|
|
|
super.dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
|
|
|
print("appstate $state");
|
|
|
|
switch (state) {
|
|
|
|
case AppLifecycleState.detached:
|
|
|
|
case AppLifecycleState.inactive:
|
|
|
|
case AppLifecycleState.paused:
|
|
|
|
_timerSync?.cancel();
|
|
|
|
break;
|
|
|
|
case AppLifecycleState.resumed:
|
|
|
|
_setupTimer();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
super.build(context);
|
|
|
|
if (!syncStatus.isSynced()) _trySync();
|
|
|
|
if (accountManager.active == null) return CircularProgressIndicator();
|
2021-07-07 21:22:54 -07:00
|
|
|
|
2021-06-26 07:30:12 -07:00
|
|
|
return DefaultTabController(
|
|
|
|
length: 3,
|
|
|
|
child: Scaffold(
|
|
|
|
appBar: AppBar(
|
|
|
|
title: Observer(
|
2021-07-09 06:33:39 -07:00
|
|
|
builder: (context) =>
|
|
|
|
Text("\u24E9 Wallet - ${accountManager.active.name}")),
|
2021-06-26 07:30:12 -07:00
|
|
|
bottom: TabBar(tabs: [
|
|
|
|
Tab(text: "Account"),
|
|
|
|
Tab(text: "Notes"),
|
|
|
|
Tab(text: "History"),
|
|
|
|
]),
|
|
|
|
actions: [
|
2021-07-07 08:40:05 -07:00
|
|
|
Observer(builder: (context) {
|
|
|
|
accountManager.canPay;
|
|
|
|
return PopupMenuButton<String>(
|
|
|
|
itemBuilder: (context) => [
|
|
|
|
PopupMenuItem(child: Text("Accounts"), value: "Accounts"),
|
|
|
|
PopupMenuItem(child: Text("Backup"), value: "Backup"),
|
|
|
|
PopupMenuItem(child: Text("Rescan"), value: "Rescan"),
|
|
|
|
if (accountManager.canPay)
|
|
|
|
PopupMenuItem(
|
|
|
|
child: Text("Cold Storage"), value: "Cold"),
|
|
|
|
PopupMenuItem(
|
|
|
|
child: Text(settings.nextMode()), value: "Theme"),
|
2021-07-09 22:44:34 -07:00
|
|
|
PopupMenuItem(child: Text('Settings'), value: "Settings"),
|
2021-07-07 08:40:05 -07:00
|
|
|
PopupMenuItem(child: Text("About"), value: "About"),
|
|
|
|
],
|
|
|
|
onSelected: _onMenu,
|
|
|
|
);
|
|
|
|
})
|
2021-06-26 07:30:12 -07:00
|
|
|
],
|
|
|
|
),
|
|
|
|
body: TabBarView(children: [
|
|
|
|
Container(
|
|
|
|
padding: EdgeInsets.all(20),
|
|
|
|
child: Center(
|
|
|
|
child: Column(children: [
|
|
|
|
Observer(
|
2021-07-07 08:40:05 -07:00
|
|
|
builder: (context) => syncStatus.syncedHeight <= 0
|
|
|
|
? Text('Synching')
|
|
|
|
: syncStatus.isSynced()
|
|
|
|
? Text('${syncStatus.syncedHeight}',
|
|
|
|
style: Theme.of(this.context)
|
|
|
|
.textTheme
|
|
|
|
.caption)
|
|
|
|
: Text(
|
|
|
|
'${syncStatus.syncedHeight} / ${syncStatus.latestHeight}')),
|
2021-06-26 07:30:12 -07:00
|
|
|
Padding(padding: EdgeInsets.symmetric(vertical: 8)),
|
2021-07-09 06:33:39 -07:00
|
|
|
Observer(builder: (context) {
|
|
|
|
final _ = accountManager.active.address;
|
|
|
|
final address = _showTAddr
|
|
|
|
? accountManager.taddress
|
|
|
|
: (_useSnapAddress
|
|
|
|
? _snapAddress
|
|
|
|
: accountManager.active.address);
|
|
|
|
return Column(children: [
|
|
|
|
Text(_showTAddr
|
|
|
|
? 'Tap QR Code for Shielded Address'
|
|
|
|
: 'Tap QR Code for Transparent Address'),
|
|
|
|
Padding(padding: EdgeInsets.symmetric(vertical: 4)),
|
|
|
|
GestureDetector(
|
|
|
|
onTap: _onQRTap,
|
|
|
|
child: QrImage(
|
|
|
|
data: address,
|
|
|
|
size: 200,
|
|
|
|
backgroundColor: Colors.white)),
|
2021-06-26 07:30:12 -07:00
|
|
|
Padding(padding: EdgeInsets.symmetric(vertical: 8)),
|
2021-07-07 21:22:54 -07:00
|
|
|
SelectableText('$address'),
|
2021-07-09 06:33:39 -07:00
|
|
|
if (!_showTAddr)
|
|
|
|
TextButton(
|
|
|
|
child: Text('New Snap Address'),
|
|
|
|
onPressed: _onSnapAddress),
|
|
|
|
if (_showTAddr)
|
|
|
|
TextButton(
|
|
|
|
child: Text('Shield Transp. Balance'),
|
|
|
|
onPressed: _onShieldTAddr,
|
|
|
|
)
|
|
|
|
]);
|
|
|
|
}),
|
|
|
|
Observer(builder: (context) {
|
|
|
|
final balance = _showTAddr
|
|
|
|
? accountManager.tbalance
|
|
|
|
: accountManager.balance;
|
|
|
|
return Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.baseline,
|
|
|
|
textBaseline: TextBaseline.ideographic,
|
|
|
|
children: <Widget>[
|
|
|
|
Text('\u24E9 ${_getBalance_hi(balance)}',
|
|
|
|
style: Theme.of(context).textTheme.headline2),
|
|
|
|
Text('${_getBalance_lo(balance)}'),
|
|
|
|
]);
|
|
|
|
}),
|
2021-06-26 07:30:12 -07:00
|
|
|
Observer(builder: (context) {
|
2021-07-09 06:33:39 -07:00
|
|
|
final balance = _showTAddr
|
|
|
|
? accountManager.tbalance
|
|
|
|
: accountManager.balance;
|
2021-06-26 07:30:12 -07:00
|
|
|
final zecPrice = priceStore.zecPrice;
|
2021-07-09 06:33:39 -07:00
|
|
|
final balanceUSD = balance * zecPrice / ZECUNIT;
|
2021-06-26 07:30:12 -07:00
|
|
|
return Column(children: [
|
|
|
|
if (zecPrice != 0.0)
|
|
|
|
Text("${balanceUSD.toStringAsFixed(2)} USDT",
|
|
|
|
style:
|
|
|
|
Theme.of(this.context).textTheme.headline6),
|
|
|
|
if (zecPrice != 0.0)
|
|
|
|
Text("1 ZEC = ${zecPrice.toStringAsFixed(2)} USDT"),
|
|
|
|
]);
|
|
|
|
}),
|
|
|
|
Padding(padding: EdgeInsets.symmetric(vertical: 8)),
|
2021-07-07 08:40:05 -07:00
|
|
|
Observer(
|
|
|
|
builder: (context) =>
|
|
|
|
(accountManager.unconfirmedBalance != 0)
|
|
|
|
? Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
crossAxisAlignment:
|
|
|
|
CrossAxisAlignment.baseline,
|
|
|
|
textBaseline: TextBaseline.ideographic,
|
|
|
|
children: <Widget>[
|
|
|
|
Text(
|
|
|
|
'${_sign(accountManager.unconfirmedBalance)} ${_getBalance_hi(accountManager.unconfirmedBalance)}',
|
|
|
|
style: Theme.of(context)
|
|
|
|
.textTheme
|
|
|
|
.headline4
|
|
|
|
?.merge(_unconfirmedStyle())),
|
|
|
|
Text(
|
|
|
|
'${_getBalance_lo(accountManager.unconfirmedBalance)}',
|
|
|
|
style: _unconfirmedStyle()),
|
|
|
|
])
|
|
|
|
: Container()),
|
|
|
|
if (_progress > 0)
|
|
|
|
LinearProgressIndicator(value: _progress / 100.0),
|
2021-06-26 07:30:12 -07:00
|
|
|
]))),
|
|
|
|
NoteWidget(),
|
|
|
|
HistoryWidget(),
|
|
|
|
]),
|
|
|
|
floatingActionButton: Observer(
|
|
|
|
builder: (context) => accountManager.canPay
|
|
|
|
? FloatingActionButton(
|
|
|
|
onPressed: _onSend,
|
|
|
|
tooltip: 'Send',
|
|
|
|
child: Icon(Icons.send),
|
|
|
|
)
|
|
|
|
: Container(), // This trailing comma makes auto-formatting nicer for build methods.
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
|
|
|
|
_sign(int b) {
|
|
|
|
return b < 0 ? '-' : '+';
|
|
|
|
}
|
|
|
|
|
2021-07-09 06:33:39 -07:00
|
|
|
_onQRTap() {
|
|
|
|
setState(() {
|
|
|
|
_showTAddr = !_showTAddr;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
_onShieldTAddr() {
|
|
|
|
showDialog(
|
|
|
|
context: this.context,
|
|
|
|
barrierDismissible: false,
|
|
|
|
builder: (context) => AlertDialog(
|
|
|
|
title: Text('Shield Transparent Balance'),
|
|
|
|
content: Text(
|
|
|
|
'Do you want to transfer your entire transparent balance to your shielded address?'),
|
|
|
|
actions: [
|
|
|
|
TextButton(
|
|
|
|
child: Text('Cancel'),
|
|
|
|
onPressed: () {
|
|
|
|
Navigator.of(this.context).pop();
|
|
|
|
},
|
|
|
|
),
|
|
|
|
TextButton(
|
|
|
|
child: Text('OK'),
|
|
|
|
onPressed: () async {
|
|
|
|
Navigator.of(this.context).pop();
|
|
|
|
final snackBar1 =
|
|
|
|
SnackBar(content: Text('Shielding in progress...'));
|
|
|
|
rootScaffoldMessengerKey.currentState.showSnackBar(snackBar1);
|
|
|
|
final txid =
|
|
|
|
await WarpApi.shieldTAddr(accountManager.active.id);
|
|
|
|
final snackBar2 = SnackBar(content: Text(txid));
|
|
|
|
rootScaffoldMessengerKey.currentState.showSnackBar(snackBar2);
|
|
|
|
},
|
|
|
|
)
|
|
|
|
]),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-06-26 07:30:12 -07:00
|
|
|
_unconfirmedStyle() {
|
|
|
|
return accountManager.unconfirmedBalance > 0
|
|
|
|
? TextStyle(color: Colors.green)
|
|
|
|
: TextStyle(color: Colors.red);
|
|
|
|
}
|
|
|
|
|
|
|
|
_getBalance_hi(int b) {
|
|
|
|
return ((b.abs() ~/ 100000) / 1000.0).toStringAsFixed(3);
|
|
|
|
}
|
|
|
|
|
|
|
|
_getBalance_lo(b) {
|
|
|
|
return (b.abs() % 100000).toString().padLeft(5, '0');
|
|
|
|
}
|
|
|
|
|
|
|
|
_setupTimer() {
|
2021-07-07 08:40:05 -07:00
|
|
|
_sync();
|
2021-06-26 07:30:12 -07:00
|
|
|
_timerSync = Timer.periodic(Duration(seconds: 15), (Timer t) {
|
|
|
|
_trySync();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
_sync() {
|
2021-07-09 22:44:34 -07:00
|
|
|
WarpApi.warpSync(settings.getTx, settings.anchorOffset, (int height) async {
|
2021-06-26 07:30:12 -07:00
|
|
|
setState(() {
|
|
|
|
syncStatus.setSyncHeight(height);
|
2021-07-07 08:40:05 -07:00
|
|
|
if (syncStatus.isSynced()) accountManager.fetchNotesAndHistory();
|
2021-06-26 07:30:12 -07:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
_trySync() async {
|
|
|
|
priceStore.fetchZecPrice();
|
|
|
|
if (syncStatus.syncedHeight < 0) return;
|
|
|
|
await syncStatus.update();
|
|
|
|
await accountManager.updateUnconfirmedBalance();
|
|
|
|
if (!syncStatus.isSynced()) {
|
2021-07-09 22:44:34 -07:00
|
|
|
final res =
|
|
|
|
await WarpApi.tryWarpSync(settings.getTx, settings.anchorOffset);
|
2021-06-26 07:30:12 -07:00
|
|
|
if (res == 1) {
|
|
|
|
// Reorg
|
|
|
|
final targetHeight = syncStatus.syncedHeight - 10;
|
|
|
|
WarpApi.rewindToHeight(targetHeight);
|
|
|
|
syncStatus.setSyncHeight(targetHeight);
|
|
|
|
} else if (res == 0) {
|
|
|
|
syncStatus.setSyncHeight(syncStatus.latestHeight);
|
|
|
|
}
|
|
|
|
}
|
2021-07-07 08:40:05 -07:00
|
|
|
await accountManager.fetchNotesAndHistory();
|
|
|
|
await accountManager.updateBalance();
|
2021-06-26 07:30:12 -07:00
|
|
|
await accountManager.updateUnconfirmedBalance();
|
2021-07-09 06:33:39 -07:00
|
|
|
accountManager.updateTBalance();
|
2021-06-26 07:30:12 -07:00
|
|
|
}
|
|
|
|
|
2021-07-07 21:22:54 -07:00
|
|
|
_onSnapAddress() {
|
|
|
|
final address = accountManager.newAddress();
|
|
|
|
setState(() {
|
|
|
|
_useSnapAddress = true;
|
|
|
|
_snapAddress = address;
|
|
|
|
});
|
|
|
|
Timer(Duration(seconds: 15), () {
|
|
|
|
setState(() {
|
|
|
|
_useSnapAddress = false;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2021-07-09 06:33:39 -07:00
|
|
|
|
2021-06-26 07:30:12 -07:00
|
|
|
_onSend() {
|
|
|
|
Navigator.of(this.context).pushNamed('/send');
|
|
|
|
}
|
|
|
|
|
|
|
|
_onMenu(String choice) {
|
|
|
|
switch (choice) {
|
|
|
|
case "Accounts":
|
|
|
|
Navigator.of(this.context).pushNamed('/accounts');
|
|
|
|
break;
|
|
|
|
case "Backup":
|
|
|
|
_backup();
|
|
|
|
break;
|
|
|
|
case "Rescan":
|
|
|
|
_rescan();
|
|
|
|
break;
|
2021-07-07 08:40:05 -07:00
|
|
|
case "Cold":
|
|
|
|
_cold();
|
|
|
|
break;
|
2021-06-26 07:30:12 -07:00
|
|
|
case "Theme":
|
|
|
|
settings.toggle();
|
|
|
|
break;
|
2021-07-09 06:33:39 -07:00
|
|
|
case "Settings":
|
|
|
|
_settings();
|
|
|
|
break;
|
2021-06-26 07:30:12 -07:00
|
|
|
case "About":
|
|
|
|
showAbout(this.context);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_backup() async {
|
|
|
|
final localAuth = LocalAuthentication();
|
|
|
|
final didAuthenticate = await localAuth.authenticate(
|
2021-07-07 08:40:05 -07:00
|
|
|
localizedReason: "Please authenticate to show account seed");
|
2021-06-26 07:30:12 -07:00
|
|
|
if (didAuthenticate) {
|
|
|
|
final seed = await accountManager.getBackup();
|
|
|
|
showDialog(
|
|
|
|
context: this.context,
|
|
|
|
barrierDismissible: false,
|
|
|
|
builder: (context) => AlertDialog(
|
|
|
|
title: Text('Account Backup'),
|
|
|
|
content: SelectableText(seed),
|
|
|
|
actions: [
|
|
|
|
TextButton(
|
|
|
|
child: Text('OK'),
|
|
|
|
onPressed: () {
|
|
|
|
Navigator.of(this.context).pop();
|
|
|
|
},
|
|
|
|
)
|
|
|
|
]),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_rescan() {
|
|
|
|
showDialog(
|
|
|
|
context: this.context,
|
|
|
|
barrierDismissible: false,
|
|
|
|
builder: (context) => AlertDialog(
|
|
|
|
title: Text('Rescan'),
|
|
|
|
content: Text('Rescan wallet from the first block?'),
|
|
|
|
actions: [
|
|
|
|
TextButton(
|
|
|
|
child: Text('Cancel'),
|
|
|
|
onPressed: () {
|
|
|
|
Navigator.of(this.context).pop();
|
|
|
|
},
|
|
|
|
),
|
|
|
|
TextButton(
|
|
|
|
child: Text('OK'),
|
|
|
|
onPressed: () {
|
|
|
|
Navigator.of(this.context).pop();
|
|
|
|
final snackBar = SnackBar(content: Text("Rescan Requested..."));
|
|
|
|
rootScaffoldMessengerKey.currentState.showSnackBar(snackBar);
|
|
|
|
syncStatus.setSyncHeight(0);
|
|
|
|
WarpApi.rewindToHeight(0);
|
|
|
|
_sync();
|
|
|
|
},
|
|
|
|
),
|
|
|
|
]),
|
|
|
|
);
|
|
|
|
}
|
2021-07-07 08:40:05 -07:00
|
|
|
|
|
|
|
_cold() {
|
|
|
|
showDialog(
|
|
|
|
context: this.context,
|
|
|
|
barrierDismissible: false,
|
|
|
|
builder: (context) => AlertDialog(
|
|
|
|
title: Text('Cold Storage'),
|
|
|
|
content: Text(
|
|
|
|
'Do you want to DELETE the secret key and convert this account to a watch-only account? '
|
|
|
|
'You will not be able to spend from this device anymore. This operation is NOT reversible.'),
|
|
|
|
actions: [
|
|
|
|
TextButton(
|
|
|
|
onPressed: () {
|
|
|
|
Navigator.of(context).pop();
|
|
|
|
},
|
|
|
|
child: Text('Cancel')),
|
|
|
|
TextButton(
|
|
|
|
onPressed: _convertToWatchOnly, child: Text('DELETE'))
|
|
|
|
]));
|
|
|
|
}
|
|
|
|
|
|
|
|
_convertToWatchOnly() {
|
|
|
|
accountManager.convertToWatchOnly();
|
|
|
|
Navigator.of(context).pop();
|
|
|
|
}
|
2021-07-09 06:33:39 -07:00
|
|
|
|
|
|
|
_settings() {
|
|
|
|
Navigator.of(this.context).pushNamed('/settings');
|
|
|
|
}
|
2021-06-26 07:30:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
class NoteWidget extends StatefulWidget {
|
|
|
|
@override
|
|
|
|
State<StatefulWidget> createState() => _NoteState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _NoteState extends State<NoteWidget> with AutomaticKeepAliveClientMixin {
|
|
|
|
final DateFormat dateFormat = DateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
|
|
|
|
|
@override
|
|
|
|
bool get wantKeepAlive => true; //Set to true
|
|
|
|
|
|
|
|
_notes() => accountManager.notes
|
|
|
|
.map((note) => DataRow(
|
|
|
|
cells: [
|
|
|
|
DataCell(Text("${note.height}",
|
|
|
|
style: !_confirmed(note.height)
|
|
|
|
? Theme.of(this.context).textTheme.overline
|
|
|
|
: null)),
|
|
|
|
DataCell(Text("${note.timestamp}")),
|
|
|
|
DataCell(Text("${note.value}")),
|
|
|
|
],
|
|
|
|
// messes with dark theme
|
|
|
|
// color: MaterialStateColor.resolveWith((states) => _color(note.height)),
|
|
|
|
))
|
|
|
|
.toList();
|
|
|
|
|
|
|
|
bool _confirmed(int height) {
|
2021-07-09 06:33:39 -07:00
|
|
|
return syncStatus.latestHeight - height >= settings.anchorOffset;
|
2021-06-26 07:30:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
super.build(context);
|
|
|
|
return SingleChildScrollView(
|
|
|
|
scrollDirection: Axis.vertical,
|
2021-07-09 06:33:39 -07:00
|
|
|
padding: EdgeInsets.only(bottom: 64),
|
2021-06-26 07:30:12 -07:00
|
|
|
child: Observer(
|
|
|
|
builder: (context) => DataTable(
|
|
|
|
columns: [
|
|
|
|
DataColumn(label: Text('Height')),
|
|
|
|
DataColumn(label: Text('Date/Time')),
|
|
|
|
DataColumn(label: Text('Amount')),
|
|
|
|
],
|
|
|
|
rows: _notes(),
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class HistoryWidget extends StatefulWidget {
|
|
|
|
@override
|
|
|
|
State<StatefulWidget> createState() => _HistoryState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _HistoryState extends State<HistoryWidget>
|
|
|
|
with AutomaticKeepAliveClientMixin {
|
|
|
|
final DateFormat dateFormat = DateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
|
|
|
|
|
@override
|
|
|
|
bool get wantKeepAlive => true; //Set to true
|
|
|
|
|
|
|
|
_txs() => accountManager.txs
|
2021-07-09 22:44:34 -07:00
|
|
|
.map((tx) => DataRow(
|
|
|
|
cells: [
|
|
|
|
DataCell(Text("${tx.height}")),
|
|
|
|
DataCell(Text("${tx.timestamp}")),
|
|
|
|
DataCell(Text("${tx.txid}")),
|
|
|
|
DataCell(Text("${tx.value}")),
|
|
|
|
],
|
|
|
|
onSelectChanged: (_) {
|
|
|
|
Navigator.of(this.context).pushNamed('/tx', arguments: tx);
|
|
|
|
}))
|
2021-06-26 07:30:12 -07:00
|
|
|
.toList();
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
super.build(context);
|
|
|
|
return SingleChildScrollView(
|
|
|
|
scrollDirection: Axis.vertical,
|
2021-07-07 08:40:05 -07:00
|
|
|
padding: EdgeInsets.only(bottom: 64),
|
2021-06-26 07:30:12 -07:00
|
|
|
child: Observer(
|
|
|
|
builder: (context) => DataTable(
|
|
|
|
columns: [
|
|
|
|
DataColumn(label: Text('Height')),
|
|
|
|
DataColumn(label: Text('Date/Time')),
|
|
|
|
DataColumn(label: Text('TXID')),
|
|
|
|
DataColumn(label: Text('Amount')),
|
|
|
|
],
|
|
|
|
columnSpacing: 32,
|
2021-07-09 22:44:34 -07:00
|
|
|
showCheckboxColumn: false,
|
2021-06-26 07:30:12 -07:00
|
|
|
rows: _txs(),
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Fix transaction / log panel
|
|
|
|
// transaction between account are counted properly
|