Skip to content

Commit

Permalink
Add some stats to the Trades view (#357)
Browse files Browse the repository at this point in the history
  • Loading branch information
sindresorhus committed Jun 13, 2018
1 parent 2761bc7 commit 7d996b4
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 32 deletions.
7 changes: 5 additions & 2 deletions app/renderer/containers/SuperContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,11 @@ const addLifeCycleHooks = (self, lifecycleHooks) => {
if (typeof originalMethod === 'function') {
originalMethod.call(this, ...args);
}
hook(...args);
hook.call(this, ...args);
};
}
};

// eslint-disable-next-line no-unused-vars
const withState = (FunctionalComponent, initialState = {}, lifecycleHooks = {}) => {
return toClassComponent(FunctionalComponent, self => {
self.state = initialState;
Expand Down Expand Up @@ -141,3 +140,7 @@ class SuperContainer extends Container {
}

export default SuperContainer;

export {
withState,
};
58 changes: 50 additions & 8 deletions app/renderer/swap-db.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import cryptoPouch from 'crypto-pouch';
import Emittery from 'emittery';
import PQueue from 'p-queue';
import roundTo from 'round-to';
import {subDays} from 'date-fns';
import appContainer from 'containers/App';
import swapTransactions from './swap-transactions';

PouchDB.plugin(pouchDBFind);
Expand Down Expand Up @@ -191,28 +193,68 @@ class SwapDB {
return swap;
}

async _getAllSwapData() {
async _getAllSwapData(options = {}) {
await this.ready;

options = {
since: true,
sort: 'desc',
...options,
};

const query = {
selector: {
timeStarted: {
$gt: options.since,
},
},
sort: [
{
timeStarted: options.sort,
},
],
};

// We need `timeStarted: {$gt: true}` so PouchDB can sort.
// https://github.com/pouchdb/pouchdb/issues/7206
const {docs} = await this.db.find({
selector: {timeStarted: {$gt: true}},
sort: [{timeStarted: 'desc'}],
});
const {docs} = await this.db.find(options.query || query);

return docs;
}

async getSwaps() {
const swapData = await this._getAllSwapData();

async getSwaps(options) {
const swapData = await this._getAllSwapData(options);
return swapData.map(this._formatSwap);
}

async destroy() {
await this.db.destroy();
}

async statsSince(timestamp) {
const swaps = await this.getSwaps({since: timestamp});
const successfulSwaps = swaps.filter(swap => swap.status === 'completed');

const quoteCurrencies = new Set();
for (const swap of successfulSwaps) {
quoteCurrencies.add(swap.quoteCurrency);
}

let totalSwapsWorthInUsd = 0;
for (const swap of successfulSwaps) {
totalSwapsWorthInUsd += swap.quoteCurrencyAmount * appContainer.getCurrency(swap.quoteCurrency).cmcPriceUsd;
}

return {
successfulSwapCount: successfulSwaps.length,
currencyCount: quoteCurrencies.size,
totalSwapsWorthInUsd,
};
}

statsSinceLastMonth() {
return this.statsSince(subDays(Date.now(), 30).getTime());
}
}

export default SwapDB;
66 changes: 44 additions & 22 deletions app/renderer/views/Trades.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import React from 'react';
import {classNames} from 'react-extras';
import {Subscribe} from 'unstated';
import {withState} from 'containers/SuperContainer';
import appContainer from 'containers/App';
import exchangeContainer from 'containers/Exchange'; // TODO(sindresorhus): Find a better place to have the SwapDB data, since both the Exchange and Trades view uses it
import tradesContainer from 'containers/Trades';
import View from 'components/View';
import SwapList from 'components/SwapList';
import {formatCurrency} from '../util';
import AppTabView from './TabView';
import './Exchange/Swaps.scss';
import './Trades.scss';
Expand Down Expand Up @@ -43,29 +46,48 @@ const SwapHistory = () => {
return <SwapList swaps={filteredData} showCancel/>;
};

const Trades = () => (
const Trades = props => (
<Subscribe to={[tradesContainer, /* Temp => */exchangeContainer]}>
{() => (
<AppTabView title="Trades" className="Trades">
<header>
<nav>
<TabButton
title="Open Orders"
component={OpenOrders}
/>
<TabButton
title="Swap History"
component={SwapHistory}
/>
</nav>
</header>
<main>
<TabView component={OpenOrders}/>
<TabView component={SwapHistory}/>
</main>
</AppTabView>
)}
{() => {
const {state} = props;
const {stats} = state;

return (
<AppTabView title="Trades" className="Trades">
<header>
<nav>
<TabButton
title="Open Orders"
component={OpenOrders}
/>
<TabButton
title="Swap History"
component={SwapHistory}
/>
</nav>
<div className="stats">
{stats &&
<p>In the last month you did {stats.successfulSwapCount} successful {stats.successfulSwapCount === 1 ? 'trade' : 'trades'} for {stats.currencyCount} {stats.currencyCount === 1 ? 'currency' : 'currencies'} worth {formatCurrency(stats.totalSwapsWorthInUsd)} in total</p>
}
</div>
</header>
<main>
<TabView component={OpenOrders}/>
<TabView component={SwapHistory}/>
</main>
</AppTabView>
);
}}
</Subscribe>
);

export default Trades;
export default withState(Trades, {}, {
async componentDidMount() {
/// TODO: This is only here temporarily until we move the swap stuff to the App container
exchangeContainer.setSwapHistory();

this.setState({
stats: await appContainer.swapDB.statsSinceLastMonth(),
});
},
});
16 changes: 16 additions & 0 deletions app/renderer/views/Trades.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import '../styles/variables';

.Trades {
display: flex;
height: 100%;
Expand All @@ -11,6 +13,20 @@

header {
padding: 20px;
position: relative;

.stats {
position: absolute;
top: 0;
right: 20px;
@include center-vertically;

p {
font-size: 12px;
margin: 0;
padding: 0;
}
}
}

main {
Expand Down

0 comments on commit 7d996b4

Please sign in to comment.