Fix: Changing account does not update budget & wallet pnl

This commit is contained in:
Hanh 2023-03-13 12:33:34 +10:00
parent aeee969542
commit a6580baa48
4 changed files with 174 additions and 134 deletions

View File

@ -24,8 +24,7 @@ class AccountManagerState extends State<AccountManagerPage> {
super.initState();
Future.microtask(() async {
await _accounts.updateTBalance();
if (mounted)
setState(() {});
if (mounted) setState(() {});
});
}
@ -38,7 +37,6 @@ class AccountManagerState extends State<AccountManagerPage> {
compute(_markAsSynced, null);
Future.microtask(() async {
nav.pushNamedAndRemoveUntil('/welcome', (route) => false);
});
return SizedBox();
}
@ -54,73 +52,76 @@ class AccountManagerState extends State<AccountManagerPage> {
],
onSelected: _onMenu)
]),
body: Padding(padding: EdgeInsets.all(8), child: ListView.builder(
itemCount: _accounts.list.length,
itemBuilder: (BuildContext context, int index) {
final a = _accounts.list[index];
final weight = a.active ? FontWeight.bold : FontWeight.normal;
final zbal = a.balance / ZECUNIT;
final tbal = a.tbalance / ZECUNIT;
final balance = zbal + tbal;
return Card(
child: Dismissible(
key: Key(a.name),
child: ListTile(
leading: CircleAvatar(backgroundImage: settings.coins[a.coin].def.image),
title: Text(a.name,
style: theme.textTheme.headlineSmall
?.merge(TextStyle(fontWeight: weight))
.apply(color: a.coin == 0 ? theme.colorScheme.primary : theme.colorScheme.secondary,
)),
subtitle: Text("${decimalFormat(zbal, 3)} + ${decimalFormat(tbal, 3)}"),
trailing: Text(decimalFormat(balance, 3)),
onTap: () {
_selectAccount(a);
},
onLongPress: () {
_editAccount(a);
},
),
confirmDismiss: (d) => _onAccountDelete(a),
onDismissed: (d) =>
_onDismissed(index, a),
));
}),
),
floatingActionButton: SpeedDial(
icon: Icons.add,
onPress: _onAddAccount,
children: [
SpeedDialChild(
child: Icon(Icons.download),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
label: 'Restore Batch',
onTap: _onFullRestore,
),
SpeedDialChild(
child: Icon(Icons.upload),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
label: 'Save Batch',
onTap: _onFullBackup,
),
SpeedDialChild(
child: Icon(Icons.subdirectory_arrow_right),
backgroundColor: Colors.green,
foregroundColor: Colors.white,
label: 'New Sub-account',
onTap: _onNewSubaccount,
),
SpeedDialChild(
child: Icon(Icons.scanner),
backgroundColor: Colors.yellow,
foregroundColor: Colors.white,
label: 'Scan Accounts',
onTap: _onScanSubAccounts,
),
]
));
body: Padding(
padding: EdgeInsets.all(8),
child: ListView.builder(
itemCount: _accounts.list.length,
itemBuilder: (BuildContext context, int index) {
final a = _accounts.list[index];
final weight = a.active ? FontWeight.bold : FontWeight.normal;
final zbal = a.balance / ZECUNIT;
final tbal = a.tbalance / ZECUNIT;
final balance = zbal + tbal;
return Card(
child: Dismissible(
key: Key(a.name),
child: ListTile(
leading: CircleAvatar(
backgroundImage: settings.coins[a.coin].def.image),
title: Text(a.name,
style: theme.textTheme.headlineSmall
?.merge(TextStyle(fontWeight: weight))
.apply(
color: a.coin == 0
? theme.colorScheme.primary
: theme.colorScheme.secondary,
)),
subtitle: Text(
"${decimalFormat(zbal, 3)} + ${decimalFormat(tbal, 3)}"),
trailing: Text(decimalFormat(balance, 3)),
onTap: () {
_selectAccount(a);
},
onLongPress: () {
_editAccount(a);
},
),
confirmDismiss: (d) => _onAccountDelete(a),
onDismissed: (d) => _onDismissed(index, a),
));
}),
),
floatingActionButton:
SpeedDial(icon: Icons.add, onPress: _onAddAccount, children: [
SpeedDialChild(
child: Icon(Icons.download),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
label: 'Restore Batch',
onTap: _onFullRestore,
),
SpeedDialChild(
child: Icon(Icons.upload),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
label: 'Save Batch',
onTap: _onFullBackup,
),
SpeedDialChild(
child: Icon(Icons.subdirectory_arrow_right),
backgroundColor: Colors.green,
foregroundColor: Colors.white,
label: 'New Sub-account',
onTap: _onNewSubaccount,
),
SpeedDialChild(
child: Icon(Icons.scanner),
backgroundColor: Colors.yellow,
foregroundColor: Colors.white,
label: 'Scan Accounts',
onTap: _onScanSubAccounts,
),
]));
}
Future<bool> _onAccountDelete(Account account) async {
@ -145,7 +146,8 @@ class AccountManagerState extends State<AccountManagerPage> {
barrierDismissible: false,
builder: (context) => AlertDialog(
title: Text(S.of(context).deleteAccount),
content: Text(S.of(context).accountHasSomeBalanceAreYouSureYouWantTo),
content:
Text(S.of(context).accountHasSomeBalanceAreYouSureYouWantTo),
actions: confirmButtons(context, () {
Navigator.of(context).pop(true);
}, okLabel: S.of(context).delete, cancelValue: false)),
@ -163,13 +165,12 @@ class AccountManagerState extends State<AccountManagerPage> {
_selectAccount(Account account) async {
active.setActiveAccount(account.coin, account.id);
await syncStatus.update();
if (syncStatus.accountRestored) {
syncStatus.setAccountRestored(false);
final height = await rescanDialog(context);
if (height != null)
syncStatus.rescan(height);
if (height != null) syncStatus.rescan(height);
}
await syncStatus.update();
final navigator = Navigator.of(context);
navigator.pushNamedAndRemoveUntil('/account', (route) => false);
@ -182,11 +183,14 @@ class AccountManagerState extends State<AccountManagerPage> {
builder: (context) => AlertDialog(
title: Text(S.of(context).changeAccountName),
content: TextField(controller: _accountNameController),
actions: confirmButtons(context, () { _changeAccountName(account); })));
actions: confirmButtons(context, () {
_changeAccountName(account);
})));
}
_changeAccountName(Account account) {
_accounts.changeAccountName(account.coin, account.id, _accountNameController.text);
_accounts.changeAccountName(
account.coin, account.id, _accountNameController.text);
Navigator.of(context).pop();
setState(() {});
}
@ -233,8 +237,8 @@ class AccountManagerState extends State<AccountManagerPage> {
title: Text(s.newSubAccount),
content: Column(mainAxisSize: MainAxisSize.min, children: [
TextField(
decoration: InputDecoration(label: Text(s.accountName)),
controller: _accountNameController),
decoration: InputDecoration(label: Text(s.accountName)),
controller: _accountNameController),
TextField(
decoration: InputDecoration(label: Text(s.count)),
controller: _countController,
@ -245,8 +249,8 @@ class AccountManagerState extends State<AccountManagerPage> {
Navigator.of(context).pop(true);
})));
if (confirmed == true) {
WarpApi.newSubAccount(_accountNameController.text, -1,
int.parse(_countController.text));
WarpApi.newSubAccount(
_accountNameController.text, -1, int.parse(_countController.text));
}
await _refresh();
}
@ -255,7 +259,7 @@ class AccountManagerState extends State<AccountManagerPage> {
_accounts.refresh();
setState(() {});
}
_onFullBackup() {
Navigator.of(context).pushNamed('/fullBackup');
}

View File

@ -1,5 +1,5 @@
import 'package:shared_preferences/shared_preferences.dart';
import 'package:warp_api/data_fb_generated.dart' ;
import 'package:warp_api/data_fb_generated.dart';
import 'coin/coins.dart';
import 'package:mobx/mobx.dart';
import 'db.dart';
@ -22,7 +22,9 @@ class Account {
Account(this.coin, this.id, this.name, this.balance, this.tbalance);
String get address {
return id != 0 ? WarpApi.getAddress(this.coin, this.id, settings.uaType) : "";
return id != 0
? WarpApi.getAddress(this.coin, this.id, settings.uaType)
: "";
}
}
@ -38,9 +40,9 @@ class AccountList {
void refresh() {
List<Account> _list = [];
for (var coin in coins) {
var accounts = WarpApi.getAccountList(coin.coin).map((a) => Account(
coin.coin, a.id, a.name!, a.balance, 0)
).toList();
var accounts = WarpApi.getAccountList(coin.coin)
.map((a) => Account(coin.coin, a.id, a.name!, a.balance, 0))
.toList();
final id = WarpApi.getActiveAccountId(coin.coin);
if (id != 0) {
accounts.firstWhere((a) => a.id == id).active = true;
@ -50,7 +52,9 @@ class AccountList {
list = _list;
}
bool get isEmpty { return list.isEmpty; }
bool get isEmpty {
return list.isEmpty;
}
Future<void> updateTBalance() async {
for (var a in list) {
@ -69,14 +73,16 @@ class AccountList {
refresh();
}
Account get(int coin, int id) => list.firstWhere((e) => e.coin == coin && e.id == id, orElse: () => emptyAccount);
Account get(int coin, int id) =>
list.firstWhere((e) => e.coin == coin && e.id == id,
orElse: () => emptyAccount);
}
AccountId? getAvailableId(int coin) {
final nid = getActiveAccountId(coin);
if (nid.id != 0) return nid;
for (var coinData in settings.coins) {
// look for an account in any other coin
// look for an account in any other coin
if (coinData.coin != coin) {
final nid = getActiveAccountId(coinData.coin);
if (nid.id != 0) return nid;
@ -94,27 +100,39 @@ AccountId getActiveAccountId(int coin) {
class ActiveAccount = _ActiveAccount with _$ActiveAccount;
abstract class _ActiveAccount with Store {
@observable int dataEpoch = 0;
@observable
int dataEpoch = 0;
@observable int coin = 0;
@observable int id = 0;
@observable
int coin = 0;
@observable
int id = 0;
Account account = emptyAccount;
CoinBase coinDef = zcash;
bool canPay = false;
Balances balances = Balances();
@observable String taddress = "";
@observable
String taddress = "";
int tbalance = 0;
PoolBalances poolBalances = PoolBalances();
@observable List<Note> notes = [];
@observable List<Tx> txs = [];
@observable List<Spending> spendings = [];
@observable List<TimeSeriesPoint<double>> accountBalances = [];
@observable List<PnL> pnls = [];
@observable ObservableList<ZMessage> messages = ObservableList();
@observable int unread = 0;
@observable String banner = "";
@observable
List<Note> notes = [];
@observable
List<Tx> txs = [];
@observable
List<Spending> spendings = [];
@observable
List<TimeSeriesPoint<double>> accountBalances = [];
@observable
List<PnL> pnls = [];
@observable
ObservableList<ZMessage> messages = ObservableList();
@observable
int unread = 0;
@observable
String banner = "";
@observable
bool showTAddr = false;
@ -134,15 +152,16 @@ abstract class _ActiveAccount with Store {
@observable
Recipient? draftRecipient;
AccountId toId() { return AccountId(coin, id); }
AccountId toId() {
return AccountId(coin, id);
}
@action
Future<void> restore() async {
final prefs = await SharedPreferences.getInstance();
final coin = prefs.getInt('coin') ?? 0;
var id = prefs.getInt('account') ?? 0;
if (WarpApi.checkAccount(coin, id))
setActiveAccount(coin, id);
if (WarpApi.checkAccount(coin, id)) setActiveAccount(coin, id);
checkAndUpdate();
}
@ -150,25 +169,27 @@ abstract class _ActiveAccount with Store {
final aid = getAvailableId(active.coin);
if (aid == null) {
setActiveAccount(0, 0);
}
else if (aid != active.toId()) {
} else if (aid != active.toId()) {
setActiveAccount(aid.coin, aid.id);
}
}
@action
void setActiveAccount(int coin, int id) {
WarpApi.setActiveAccount(coin, id);
Future.microtask(() async {
final prefs = await SharedPreferences.getInstance();
prefs.setInt('coin', coin);
prefs.setInt('account', id);
});
if (coin != this.coin || id != this.id) {
this.coin = coin;
this.id = id;
_refreshAccount();
}
Future(Action(() async {
final prefs = await SharedPreferences.getInstance();
prefs.setInt('coin', coin);
prefs.setInt('account', id);
await priceStore.updateChart(force: true);
dataEpoch += 1;
}));
Future(() => priceStore.fetchCoinPrice(active.coin));
dataEpoch += 1;
}
void _refreshAccount() {
@ -205,8 +226,7 @@ abstract class _ActiveAccount with Store {
void updateTBalance() {
try {
tbalance = WarpApi.getTBalance();
}
on String {}
} on String {}
}
@action
@ -214,7 +234,8 @@ abstract class _ActiveAccount with Store {
final initialized = balances.initialized;
final prevBalance = balances.balance;
final b = WarpApi.getBalance(coin, id, syncStatus.confirmHeight);
balances.update(b.balance, b.shielded, b.unconfirmedSpent, b.underConfirmed, b.excluded);
balances.update(b.balance, b.shielded, b.unconfirmedSpent, b.underConfirmed,
b.excluded);
if (initialized && prevBalance != balances.balance) {
showBalanceNotification(prevBalance, balances.balance);
}
@ -283,8 +304,8 @@ abstract class _ActiveAccount with Store {
case "txid":
return _sort(txs2, (Tx tx) => tx.txid, txSortConfig.order);
case "address":
return _sort(
txs2, (Tx tx) => tx.contact ?? tx.address ?? '', txSortConfig.order);
return _sort(txs2, (Tx tx) => tx.contact ?? tx.address ?? '',
txSortConfig.order);
case "memo":
return _sort(txs2, (Tx tx) => tx.memo ?? '', txSortConfig.order);
}
@ -352,7 +373,8 @@ abstract class _ActiveAccount with Store {
final dbr = active.dbReader;
pnls = dbr.getPNL(active.id);
spendings = dbr.getSpending(active.id);
accountBalances = dbr.getAccountBalanceTimeSeries(active.id, active.balances.balance);
accountBalances =
dbr.getAccountBalanceTimeSeries(active.id, active.balances.balance);
}
@action
@ -375,13 +397,15 @@ abstract class _ActiveAccount with Store {
int prevInThread(int index) {
final message = messages[index];
final pn = WarpApi.getPrevNextMessage(coin, id, message.subject, message.height);
final pn =
WarpApi.getPrevNextMessage(coin, id, message.subject, message.height);
return pn.prev;
}
int nextInThread(int index) {
final message = messages[index];
final pn = WarpApi.getPrevNextMessage(coin, id, message.subject, message.height);
final pn =
WarpApi.getPrevNextMessage(coin, id, message.subject, message.height);
return pn.next;
}
@ -401,14 +425,20 @@ class Balances = _Balances with _$Balances;
abstract class _Balances with Store {
bool initialized = false;
@observable int balance = 0;
@observable int shieldedBalance = 0;
@observable int unconfirmedSpentBalance = 0;
@observable int underConfirmedBalance = 0;
@observable int excludedBalance = 0;
@observable
int balance = 0;
@observable
int shieldedBalance = 0;
@observable
int unconfirmedSpentBalance = 0;
@observable
int underConfirmedBalance = 0;
@observable
int excludedBalance = 0;
@action
void update(int balance, int shieldedBalance, int unconfirmedSpentBalance, int underConfirmedBalance, int excludedBalance) {
void update(int balance, int shieldedBalance, int unconfirmedSpentBalance,
int underConfirmedBalance, int excludedBalance) {
this.balance = balance;
this.shieldedBalance = shieldedBalance;
this.unconfirmedSpentBalance = unconfirmedSpentBalance;
@ -427,12 +457,16 @@ class AccountId {
class PoolBalances = _PoolBalances with _$PoolBalances;
abstract class _PoolBalances with Store {
@observable int transparent = 0;
@observable int sapling = 0;
@observable int orchard = 0;
@observable
int transparent = 0;
@observable
int sapling = 0;
@observable
int orchard = 0;
void update() {
final b = WarpApi.getBalance(active.coin, active.id, syncStatus.confirmHeight);
final b =
WarpApi.getBalance(active.coin, active.id, syncStatus.confirmHeight);
_update(active.tbalance, b.sapling, b.orchard);
}

View File

@ -646,12 +646,14 @@ abstract class _PriceStore with Store {
coinPrice = await getFxRate(c.currency, settings.currency) ?? 0.0;
}
Future<void> updateChart() async {
Future<void> updateChart({bool? force}) async {
final f = force ?? false;
try {
final _lastChartUpdateTime = lastChartUpdateTime;
final now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
await fetchCoinPrice(active.coin);
if (_lastChartUpdateTime == null || now > _lastChartUpdateTime + 5 * 60) {
if (f ||
_lastChartUpdateTime == null ||
now > _lastChartUpdateTime + 5 * 60) {
await WarpApi.syncHistoricalPrices(settings.currency);
active.fetchChartData();
lastChartUpdateTime = now;

View File

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.3.6+407
version: 1.3.6+408
environment:
sdk: ">=2.12.0 <3.0.0"