Skip to content
This repository has been archived by the owner on Oct 21, 2020. It is now read-only.

Commit

Permalink
feat(wallet): poll for account balance changes within a wallet
Browse files Browse the repository at this point in the history
  • Loading branch information
devinus committed Feb 16, 2018
1 parent c0a63e6 commit 58e3f34
Show file tree
Hide file tree
Showing 12 changed files with 123 additions and 22 deletions.
2 changes: 2 additions & 0 deletions app/account/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export default DS.Model.extend({
@attr('big-number') balance: null,
@attr('big-number') pending: null,

@attr('date') modifiedTimestamp: null,

toString() {
const id = this.get('id');
const wallet = this.get('wallet.id');
Expand Down
6 changes: 6 additions & 0 deletions app/account/serializer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import DS from 'ember-data';

import { underscore } from '@ember/string';

export default DS.JSONSerializer.extend({
primaryKey: 'account',

keyForAttribute(attr) {
return underscore(attr);
},
});
2 changes: 1 addition & 1 deletion app/components/download-progress/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default Component.extend({
},

updateProgress(value) {
if (!this.isDestroyed) {
if (!this.isDestroying) {
this.set('value', value);
}
},
Expand Down
10 changes: 10 additions & 0 deletions app/rpc/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const actions = {
WALLET_CREATE: 'wallet_create',
WALLET_LOCKED: 'wallet_locked',
WALLET_BALANCE_TOTAL: 'wallet_balance_total',
WALLET_BALANCES: 'wallet_balances',
WALLET_CHANGE_SEED: 'wallet_change_seed',
ACCOUNT_CREATE: 'account_create',
ACCOUNT_INFO: 'account_info',
Expand Down Expand Up @@ -96,6 +97,11 @@ export default Service.extend({
return this.call(actions.WALLET_BALANCE_TOTAL, { wallet });
},

async walletBalances(wallet) {
const { balances } = await this.call(actions.WALLET_BALANCES, { wallet });
return balances;
},

async walletChangeSeed(wallet, seed) {
const { success } = await this.call(actions.WALLET_CHANGE_SEED, { wallet, seed });
return success === '';
Expand Down Expand Up @@ -126,6 +132,10 @@ export default Service.extend({
}
}

if (!info.modified_timestamp) {
info.modified_timestamp = String(Date.now());
}

return info;
},

Expand Down
4 changes: 2 additions & 2 deletions app/status/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { on } from 'ember-decorators/object/evented';

import { hash } from 'rsvp';

const DEFAULT_INTERVAL = 10000; // 10s
const STATUS_POLL_INTERVAL = 10000; // 10s

const Service = ObjectProxy.extend(PromiseProxyMixin, {
@service pollboy: null,
Expand All @@ -18,7 +18,7 @@ const Service = ObjectProxy.extend(PromiseProxyMixin, {
@on('init')
setupPoller() {
const pollboy = this.get('pollboy');
this.poller = pollboy.add(this, this.onPoll, DEFAULT_INTERVAL);
this.poller = pollboy.add(this, this.onPoll, STATUS_POLL_INTERVAL);
this.poller.pause();
},

Expand Down
11 changes: 11 additions & 0 deletions app/utils/sum-amounts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { A } from '@ember/array';
import { isPresent } from '@ember/utils';

import BigNumber from 'npm:bignumber.js';

export default function sumAmounts(amounts) {
return A(amounts)
.filter(isPresent)
.map(x => new BigNumber(String(x)))
.reduce((x, y) => y.plus(x), 0);
}
17 changes: 2 additions & 15 deletions app/wallet/adapter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import DS from 'ember-data';

import { all } from 'rsvp';
import { service } from 'ember-decorators/service';

export default DS.Adapter.extend({
Expand All @@ -13,20 +12,8 @@ export default DS.Adapter.extend({
async findRecord(store, type, id, snapshot) {
const rpc = this.get('rpc');
const { wallet } = this.serialize(snapshot, { includeId: true });
const [
{ balance, pending },
{ accounts },
] = await all([
rpc.walletBalanceTotal(id),
rpc.accountList(id),
]);

return {
wallet,
balance,
pending,
accounts,
};
const { accounts } = await rpc.accountList(id);
return { wallet, accounts };
},

async createRecord(store, type, snapshot) {
Expand Down
19 changes: 16 additions & 3 deletions app/wallet/model.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
import DS from 'ember-data';
import { get } from '@ember/object';
import { A } from '@ember/array';
import { get, computed } from '@ember/object';

import { service } from 'ember-decorators/service';
import { attr, hasMany } from 'ember-decorators/data';

import sumAmounts from '../utils/sum-amounts';

const sumAccountsProperty = dependentKey =>
computed(`accounts.@each.${dependentKey}`, {
get() {
const accounts = this.get('accounts');
const amounts = A(accounts).mapBy(dependentKey);
return sumAmounts(amounts);
},
});

export default DS.Model.extend({
@service settings: null,

@hasMany('account', { async: true }) accounts: null,

@attr('big-number') balance: null,
@attr('big-number') pending: null,
@attr seed: null,

balance: sumAccountsProperty('balance'),
pending: sumAccountsProperty('pending'),

toString() {
const id = this.get('id');
const settings = this.get('settings');
Expand Down
48 changes: 48 additions & 0 deletions app/wallets/controller.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,55 @@
import Controller from '@ember/controller';
import { get } from '@ember/object';

import { service } from 'ember-decorators/service';
import { on } from 'ember-decorators/object/evented';

const WALLET_POLL_INTERVAL = 5000; // 5s

export default Controller.extend({
@service pollboy: null,
@service flashMessages: null,
@service rpc: null,

poller: null,

@on('init')
setupPoller() {
const pollboy = this.get('pollboy');
this.poller = pollboy.add(this, this.onPoll, WALLET_POLL_INTERVAL);
this.poller.pause();
},

willDestroy(...args) {
this._super(...args);

const poller = this.get('poller');
if (poller) {
this.get('pollboy').remove(poller);
}
},

async onPoll() {
if (!this.isDestroying) {
const model = this.get('model');
if (model) {
const isNew = get(model, 'isNew');
if (!isNew) {
const wallet = get(model, 'id');
this.updateBalances(wallet);
}
}
}
},

async updateBalances(wallet) {
const balances = await this.get('rpc').walletBalances(wallet);
const data = Object.entries(balances).map(([id, attributes]) => ({
id,
attributes,
type: 'account',
}));

this.store.push({ data });
},
});
8 changes: 8 additions & 0 deletions app/wallets/route.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Route from '@ember/routing/route';
import { get } from '@ember/object';
import { tryInvoke } from '@ember/utils';

import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
import { service } from 'ember-decorators/service';
Expand Down Expand Up @@ -35,4 +36,11 @@ export default Route.extend(AuthenticatedRouteMixin, {
});
}
},

setupController(controller, model) {
this._super(controller, model);

const poller = get(controller, 'poller');
tryInvoke(poller, 'resume');
},
});
12 changes: 12 additions & 0 deletions tests/unit/utils/sum-amounts-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { expect } from 'chai';
import { describe, it } from 'mocha';
import sumAmounts from '@nanocurrency/nano-desktop/utils/sum-amounts';

describe('Unit | Utility | sum-amounts', () => {
// Replace this with your real tests.
it('works', () => {
const result = sumAmounts([1, 2]);
expect(result).to.be.ok;
expect(result.toFixed(0)).to.equal('3');
});
});
6 changes: 5 additions & 1 deletion tests/unit/wallets/controller-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { setupTest } from 'ember-mocha';
describe('Unit | Controller | wallets', () => {
setupTest('controller:wallets', {
// Specify the other units that are required for this test.
needs: ['service:flashMessages'],
needs: [
'service:pollboy',
'service:flashMessages',
'service:rpc',
],
});

// Replace this with your real tests.
Expand Down

0 comments on commit 58e3f34

Please sign in to comment.