show pending outgoing balance in overview

This commit is contained in:
Florian Dold 2016-10-19 22:49:03 +02:00
parent 4fd1e07449
commit e6763f3fac
4 changed files with 101 additions and 35 deletions

View File

@ -327,6 +327,7 @@ export interface WalletBalance {
export interface WalletBalanceEntry { export interface WalletBalanceEntry {
available: AmountJson; available: AmountJson;
pendingIncoming: AmountJson; pendingIncoming: AmountJson;
pendingPayment: AmountJson;
} }
@ -432,26 +433,30 @@ export namespace Amounts {
} }
export function sub(a: AmountJson, b: AmountJson): Result { export function sub(a: AmountJson, ...rest: AmountJson[]): Result {
if (a.currency !== b.currency) {
throw Error(`Mismatched currency: ${a.currency} and ${b.currency}`);
}
let currency = a.currency; let currency = a.currency;
let value = a.value; let value = a.value;
let fraction = a.fraction; let fraction = a.fraction;
if (fraction < b.fraction) {
if (value < 1) { for (let b of rest) {
if (b.currency !== currency) {
throw Error(`Mismatched currency: ${b.currency} and ${currency}`);
}
if (fraction < b.fraction) {
if (value < 1) {
return { amount: { currency, value: 0, fraction: 0 }, saturated: true };
}
value--;
fraction += 1e6;
}
console.assert(fraction >= b.fraction);
fraction -= b.fraction;
if (value < b.value) {
return { amount: { currency, value: 0, fraction: 0 }, saturated: true }; return { amount: { currency, value: 0, fraction: 0 }, saturated: true };
} }
value--; value -= b.value;
fraction += 1e6;
} }
console.assert(fraction >= b.fraction);
fraction -= b.fraction;
if (value < b.value) {
return { amount: { currency, value: 0, fraction: 0 }, saturated: true };
}
value -= b.value;
return { amount: { currency, value, fraction }, saturated: false }; return { amount: { currency, value, fraction }, saturated: false };
} }

View File

@ -153,6 +153,12 @@ interface Transaction {
contract: Contract; contract: Contract;
payReq: PayReq; payReq: PayReq;
merchantSig: string; merchantSig: string;
/**
* The transaction isn't active anymore, it's either successfully paid
* or refunded/aborted.
*/
finished: boolean;
} }
export enum HistoryLevel { export enum HistoryLevel {
@ -461,7 +467,8 @@ export class Wallet {
let x: number; let x: number;
function storeExchangeCoin(mc: JoinResult<IExchangeInfo, Coin>, url: string) { function storeExchangeCoin(mc: JoinResult<IExchangeInfo, Coin>,
url: string) {
let exchange: IExchangeInfo = mc.left; let exchange: IExchangeInfo = mc.left;
console.log("got coin for exchange", url); console.log("got coin for exchange", url);
let coin: Coin = mc.right; let coin: Coin = mc.right;
@ -584,6 +591,7 @@ export class Wallet {
contract: offer.contract, contract: offer.contract,
payReq: payReq, payReq: payReq,
merchantSig: offer.merchant_sig, merchantSig: offer.merchant_sig,
finished: false,
}; };
let historyEntry: HistoryRecord = { let historyEntry: HistoryRecord = {
@ -835,6 +843,7 @@ export class Wallet {
.put(Stores.reserves, reserve) .put(Stores.reserves, reserve)
.put(Stores.history, historyEntry) .put(Stores.history, historyEntry)
.finish(); .finish();
this.notifier.notify();
this.processReserve(reserve); this.processReserve(reserve);
} }
@ -915,7 +924,7 @@ export class Wallet {
throw Error("can't withdraw from reserve when current amount is" + throw Error("can't withdraw from reserve when current amount is" +
" unknown"); " unknown");
} }
let x = Amounts.sub(currentAmount, preCoin.coinValue); let x = Amounts.sub(currentAmount, preCoin.coinValue, denom.fee_withdraw);
if (x.saturated) { if (x.saturated) {
aborted = true; aborted = true;
throw AbortTransaction; throw AbortTransaction;
@ -928,6 +937,7 @@ export class Wallet {
.put(Stores.precoins, preCoin) .put(Stores.precoins, preCoin)
.mutate(Stores.reserves, reserve.reserve_pub, mutateReserve) .mutate(Stores.reserves, reserve.reserve_pub, mutateReserve)
.finish(); .finish();
this.notifier.notify();
if (!aborted) { if (!aborted) {
await this.processPreCoin(preCoin); await this.processPreCoin(preCoin);
} }
@ -987,6 +997,7 @@ export class Wallet {
await this.q() await this.q()
.put(Stores.reserves, reserve) .put(Stores.reserves, reserve)
.finish(); .finish();
this.notifier.notify();
return reserve; return reserve;
} }
@ -1186,6 +1197,7 @@ export class Wallet {
balance[currency] = entry = { balance[currency] = entry = {
available: z, available: z,
pendingIncoming: z, pendingIncoming: z,
pendingPayment: z,
}; };
} }
return entry; return entry;
@ -1228,6 +1240,17 @@ export class Wallet {
return balance; return balance;
} }
function collectPayments(t: Transaction, balance: WalletBalance) {
if (t.finished) {
return balance;
}
let entry = ensureEntry(balance, t.contract.amount.currency);
entry.pendingPayment = Amounts.add(entry.pendingPayment,
t.contract.amount).amount;
return balance;
}
function collectSmallestWithdraw(e: IExchangeInfo, sw: any) { function collectSmallestWithdraw(e: IExchangeInfo, sw: any) {
let min: AmountJson|undefined; let min: AmountJson|undefined;
for (let d of e.active_denoms) { for (let d of e.active_denoms) {
@ -1253,8 +1276,7 @@ export class Wallet {
.iter(Stores.exchanges) .iter(Stores.exchanges)
.reduce(collectSmallestWithdraw, {})); .reduce(collectSmallestWithdraw, {}));
console.log("smallest withdraw", smallestWithdraw); console.log("smallest withdraws", smallestWithdraw)
await (this.q() await (this.q()
.iter(Stores.coins) .iter(Stores.coins)
.reduce(collectBalances, balance)); .reduce(collectBalances, balance));
@ -1262,13 +1284,12 @@ export class Wallet {
await (this.q() await (this.q()
.iter(Stores.refresh) .iter(Stores.refresh)
.reduce(collectPendingRefresh, balance)); .reduce(collectPendingRefresh, balance));
console.log("balances collected");
await (this.q() await (this.q()
.iter(Stores.reserves) .iter(Stores.reserves)
.reduce(collectPendingWithdraw, balance)); .reduce(collectPendingWithdraw, balance));
console.log("balance", balance); await (this.q()
.iter(Stores.transactions)
.reduce(collectPayments, balance));
return balance; return balance;
} }
@ -1583,6 +1604,8 @@ export class Wallet {
console.error("contract not found"); console.error("contract not found");
return; return;
} }
t.finished = true;
let modifiedCoins: Coin[] = [];
for (let pc of t.payReq.coins) { for (let pc of t.payReq.coins) {
let c = await this.q().get<Coin>(Stores.coins, pc.coin_pub); let c = await this.q().get<Coin>(Stores.coins, pc.coin_pub);
if (!c) { if (!c) {
@ -1590,8 +1613,13 @@ export class Wallet {
return; return;
} }
c.transactionPending = false; c.transactionPending = false;
await this.q().put(Stores.coins, c).finish(); modifiedCoins.push(c);
} }
await this.q()
.putAll(Stores.coins, modifiedCoins)
.put(Stores.transactions, t)
.finish();
for (let c of t.payReq.coins) { for (let c of t.payReq.coins) {
this.refresh(c.coin_pub); this.refresh(c.coin_pub);
} }

View File

@ -43,6 +43,9 @@ class ReserveView extends preact.Component<ReserveViewProps, void> {
<ul> <ul>
<li>Key: {r.reserve_pub}</li> <li>Key: {r.reserve_pub}</li>
<li>Created: {(new Date(r.created * 1000).toString())}</li> <li>Created: {(new Date(r.created * 1000).toString())}</li>
<li>Current: {r.current_amount ? prettyAmount(r.current_amount!) : "null"}</li>
<li>Requested: {prettyAmount(r.requested_amount)}</li>
<li>Confirmed: {r.confirmed}</li>
</ul> </ul>
</div> </div>
); );

View File

@ -221,12 +221,46 @@ class WalletBalanceView extends preact.Component<any, any> {
getting started?</div>; getting started?</div>;
} }
formatPending(amount: AmountJson) { formatPending(entry: WalletBalanceEntry): JSX.Element {
return ( let incoming: JSX.Element | undefined;
<span> let payment: JSX.Element | undefined;
(<span style="color: darkgreen">{prettyAmount(amount)}</span> pending)
</span> console.log("available: ", entry.pendingIncoming ? prettyAmount(entry.available) : null);
); console.log("incoming: ", entry.pendingIncoming ? prettyAmount(entry.pendingIncoming) : null);
if (Amounts.isNonZero(entry.pendingIncoming)) {
incoming = (
<span>
<span style="color: darkgreen">
{"+"}
{prettyAmount(entry.pendingIncoming)}
</span>
{" "}
incoming
</span>);
}
if (Amounts.isNonZero(entry.pendingPayment)) {
payment = (
<span>
<span style="color: darkblue">
{prettyAmount(entry.pendingPayment)}
</span>
{" "}
being spent
</span>);
}
let l = [incoming, payment].filter((x) => x !== undefined);
if (l.length == 0) {
return <span />;
}
if (l.length == 1) {
return <span>({l})</span>
}
return <span>({l[0]}, {l[1]})</span>;
} }
render(): JSX.Element { render(): JSX.Element {
@ -243,12 +277,8 @@ class WalletBalanceView extends preact.Component<any, any> {
return ( return (
<p> <p>
{prettyAmount(entry.available)} {prettyAmount(entry.available)}
{ " "} {" "}
{Amounts.isNonZero(entry.pendingIncoming) {this.formatPending(entry)}
? this.formatPending(entry.pendingIncoming)
: []
}
</p> </p>
); );
}); });