diff --git a/src/core/api/index.js b/src/core/api/index.js index d6cb0a01..4553d010 100644 --- a/src/core/api/index.js +++ b/src/core/api/index.js @@ -15,5 +15,11 @@ export { getNetworkStats, getWeight, getWeightHistory, - getDaily + getDaily, + get_blocks_confirmed_summary, + get_accounts_unconfirmed_summary, + get_blocks_unconfirmed_summary } from './sagas' + +export { api_reducer } from './reducer' +export { get_request_history } from './selectors' diff --git a/src/core/api/reducer.js b/src/core/api/reducer.js new file mode 100644 index 00000000..0494a751 --- /dev/null +++ b/src/core/api/reducer.js @@ -0,0 +1,41 @@ +import { Map } from 'immutable' + +import { nanodb_actions } from '@core/nanodb/actions' + +const initialState = new Map({ + request_history: new Map() +}) + +export function api_reducer(state = initialState, { payload, type }) { + switch (type) { + case nanodb_actions.GET_BLOCKS_CONFIRMED_SUMMARY_PENDING: + return state.setIn( + [ + 'request_history', + `GET_BLOCKS_CONFIRMED_SUMMARY_${payload.params.period}` + ], + true + ) + + case nanodb_actions.GET_BLOCKS_CONFIRMED_SUMMARY_FAILED: + return state.deleteIn([ + 'request_history', + `GET_BLOCKS_CONFIRMED_SUMMARY_${payload.params.period}` + ]) + + case nanodb_actions.GET_ACCOUNTS_UNCONFIRMED_SUMMARY_PENDING: + return state.setIn( + ['request_history', 'GET_ACCOUNTS_UNCONFIRMED_SUMMARY'], + true + ) + + case nanodb_actions.GET_ACCOUNTS_UNCONFIRMED_SUMMARY_FAILED: + return state.deleteIn([ + 'request_history', + 'GET_ACCOUNTS_UNCONFIRMED_SUMMARY' + ]) + + default: + return state + } +} diff --git a/src/core/api/sagas.js b/src/core/api/sagas.js index 11e40ef4..7358aa34 100644 --- a/src/core/api/sagas.js +++ b/src/core/api/sagas.js @@ -26,6 +26,11 @@ import { } from '@core/accounts/actions' import { blockRequestActions } from '@core/blocks/actions' import { dailyRequestActions } from '@core/ledger/actions' +import { + block_confirmed_summary_request_actions, + accounts_unconfirmed_summary_request_actions, + blocks_unconfirmed_summary_request_actions +} from '@core/nanodb/actions' function* fetchAPI(apiFunction, actions, opts = {}) { const { token } = yield select(getApp) @@ -120,3 +125,21 @@ export const getWeightHistory = fetch.bind( weightHistoryRequestActions ) export const getDaily = fetch.bind(null, api.getDaily, dailyRequestActions) + +export const get_blocks_confirmed_summary = fetch.bind( + null, + api.get_blocks_confirmed_summary, + block_confirmed_summary_request_actions +) + +export const get_accounts_unconfirmed_summary = fetch.bind( + null, + api.get_accounts_unconfirmed_summary, + accounts_unconfirmed_summary_request_actions +) + +export const get_blocks_unconfirmed_summary = fetch.bind( + null, + api.get_blocks_unconfirmed_summary, + blocks_unconfirmed_summary_request_actions +) diff --git a/src/core/api/selectors.js b/src/core/api/selectors.js new file mode 100644 index 00000000..83ed9cab --- /dev/null +++ b/src/core/api/selectors.js @@ -0,0 +1,2 @@ +export const get_request_history = (state) => + state.getIn(['api', 'request_history']) diff --git a/src/core/api/service.js b/src/core/api/service.js index 80ed44b4..4cef27ec 100644 --- a/src/core/api/service.js +++ b/src/core/api/service.js @@ -81,6 +81,18 @@ export const api = { getWeightHistory() { const url = `${API_URL}/weight/history` return { url } + }, + get_blocks_confirmed_summary({ period = '10m' }) { + const url = `${API_URL}/nanodb/blocks/confirmed/summary?period=${period}` + return { url } + }, + get_accounts_unconfirmed_summary() { + const url = `${API_URL}/nanodb/accounts/unconfirmed/summary` + return { url } + }, + get_blocks_unconfirmed_summary() { + const url = `${API_URL}/nanodb/blocks/unconfirmed/summary` + return { url } } } diff --git a/src/core/nanodb/actions.js b/src/core/nanodb/actions.js new file mode 100644 index 00000000..d40372af --- /dev/null +++ b/src/core/nanodb/actions.js @@ -0,0 +1,125 @@ +export const nanodb_actions = { + GET_BLOCKS_CONFIRMED_SUMMARY: 'GET_BLOCKS_CONFIRMED_SUMMARY', + + GET_BLOCKS_CONFIRMED_SUMMARY_FAILED: 'GET_BLOCKS_CONFIRMED_SUMMARY_FAILED', + GET_BLOCKS_CONFIRMED_SUMMARY_PENDING: 'GET_BLOCKS_CONFIRMED_SUMMARY_PENDING', + GET_BLOCKS_CONFIRMED_SUMMARY_FULFILLED: + 'GET_BLOCKS_CONFIRMED_SUMMARY_FULFILLED', + + GET_ACCOUNTS_UNCONFIRMED_SUMMARY: 'GET_ACCOUNTS_UNCONFIRMED_SUMMARY', + + GET_ACCOUNTS_UNCONFIRMED_SUMMARY_FAILED: + 'GET_ACCOUNTS_UNCONFIRMED_SUMMARY_FAILED', + GET_ACCOUNTS_UNCONFIRMED_SUMMARY_PENDING: + 'GET_ACCOUNTS_UNCONFIRMED_SUMMARY_PENDING', + GET_ACCOUNTS_UNCONFIRMED_SUMMARY_FULFILLED: + 'GET_ACCOUNTS_UNCONFIRMED_SUMMARY_FULFILLED', + + GET_BLOCKS_UNCONFIRMED_SUMMARY: 'GET_BLOCKS_UNCONFIRMED_SUMMARY', + + GET_BLOCKS_UNCONFIRMED_SUMMARY_FAILED: + 'GET_BLOCKS_UNCONFIRMED_SUMMARY_FAILED', + GET_BLOCKS_UNCONFIRMED_SUMMARY_PENDING: + 'GET_BLOCKS_UNCONFIRMED_SUMMARY_PENDING', + GET_BLOCKS_UNCONFIRMED_SUMMARY_FULFILLED: + 'GET_BLOCKS_UNCONFIRMED_SUMMARY_FULFILLED', + + get_blocks_confirmed_summary: (period = '10m') => ({ + type: nanodb_actions.GET_BLOCKS_CONFIRMED_SUMMARY, + payload: { + period + } + }), + + get_blocks_confirmed_summary_failed: (params, error) => ({ + type: nanodb_actions.GET_BLOCKS_CONFIRMED_SUMMARY_FAILED, + payload: { + params, + error + } + }), + + get_blocks_confirmed_summary_pending: (params) => ({ + type: nanodb_actions.GET_BLOCKS_CONFIRMED_SUMMARY_PENDING, + payload: { + params + } + }), + + get_blocks_confirmed_summary_fulfilled: (params, data) => ({ + type: nanodb_actions.GET_BLOCKS_CONFIRMED_SUMMARY_FULFILLED, + payload: { + params, + data + } + }), + + get_accounts_unconfirmed_summary: () => ({ + type: nanodb_actions.GET_ACCOUNTS_UNCONFIRMED_SUMMARY + }), + + get_accounts_unconfirmed_summary_failed: (params, error) => ({ + type: nanodb_actions.GET_ACCOUNTS_UNCONFIRMED_SUMMARY_FAILED, + payload: { + params, + error + } + }), + + get_accounts_unconfirmed_summary_pending: (params) => ({ + type: nanodb_actions.GET_ACCOUNTS_UNCONFIRMED_SUMMARY_PENDING, + payload: { + params + } + }), + get_accounts_unconfirmed_summary_fulfilled: (params, data) => ({ + type: nanodb_actions.GET_ACCOUNTS_UNCONFIRMED_SUMMARY_FULFILLED, + payload: { + params, + data + } + }), + + get_blocks_unconfirmed_summary: () => ({ + type: nanodb_actions.GET_BLOCKS_UNCONFIRMED_SUMMARY + }), + + get_blocks_unconfirmed_summary_failed: (params, error) => ({ + type: nanodb_actions.GET_BLOCKS_UNCONFIRMED_SUMMARY_FAILED, + payload: { + params, + error + } + }), + get_blocks_unconfirmed_summary_pending: (params) => ({ + type: nanodb_actions.GET_BLOCKS_UNCONFIRMED_SUMMARY_PENDING, + payload: { + params + } + }), + get_blocks_unconfirmed_summary_fulfilled: (params, data) => ({ + type: nanodb_actions.GET_BLOCKS_UNCONFIRMED_SUMMARY_FULFILLED, + payload: { + params, + data + } + }) +} + +export const block_confirmed_summary_request_actions = { + failed: nanodb_actions.get_blocks_confirmed_summary_failed, + fulfilled: nanodb_actions.get_blocks_confirmed_summary_fulfilled, + pending: nanodb_actions.get_blocks_confirmed_summary_pending +} + +export const accounts_unconfirmed_summary_request_actions = { + failed: nanodb_actions.get_accounts_unconfirmed_summary_failed, + fulfilled: nanodb_actions.get_accounts_unconfirmed_summary_fulfilled, + pending: nanodb_actions.get_accounts_unconfirmed_summary_pending +} + +export const blocks_unconfirmed_summary_request_actions = { + failed: nanodb_actions.get_blocks_unconfirmed_summary_failed, + fulfilled: nanodb_actions.get_blocks_unconfirmed_summary_fulfilled, + pending: nanodb_actions.get_blocks_unconfirmed_summary_pending +} diff --git a/src/core/nanodb/index.js b/src/core/nanodb/index.js new file mode 100644 index 00000000..b53c4e3f --- /dev/null +++ b/src/core/nanodb/index.js @@ -0,0 +1,9 @@ +export { + nanodb_actions, + block_confirmed_summary_request_actions, + accounts_unconfirmed_summary_request_actions, + blocks_unconfirmed_summary_request_actions +} from './actions' + +export { nanodb_reducer } from './reducer' +export { nanodb_sagas } from './sagas' diff --git a/src/core/nanodb/reducer.js b/src/core/nanodb/reducer.js new file mode 100644 index 00000000..675455b3 --- /dev/null +++ b/src/core/nanodb/reducer.js @@ -0,0 +1,22 @@ +import { Map } from 'immutable' + +import { nanodb_actions } from './actions' + +export function nanodb_reducer(state = Map(), action) { + switch (action.type) { + case nanodb_actions.GET_BLOCKS_CONFIRMED_SUMMARY_FULFILLED: + return state.set( + `block_confirmed_summary_${action.payload.params.period}`, + action.payload.data + ) + + case nanodb_actions.GET_ACCOUNTS_UNCONFIRMED_SUMMARY_FULFILLED: + return state.set('accounts_unconfirmed_summary', action.payload.data) + + case nanodb_actions.GET_BLOCKS_UNCONFIRMED_SUMMARY_FULFILLED: + return state.set('blocks_unconfirmed_summary', action.payload.data) + + default: + return state + } +} diff --git a/src/core/nanodb/sagas.js b/src/core/nanodb/sagas.js new file mode 100644 index 00000000..60fffee8 --- /dev/null +++ b/src/core/nanodb/sagas.js @@ -0,0 +1,70 @@ +import { takeEvery, fork, call, select } from 'redux-saga/effects' + +import { + get_blocks_confirmed_summary, + get_accounts_unconfirmed_summary, + get_blocks_unconfirmed_summary, + get_request_history +} from '@core/api' +import { nanodb_actions } from './actions' + +export function* load_block_confirmed_summary({ payload }) { + const request_history = yield select(get_request_history) + + if (request_history.has(`GET_BLOCKS_CONFIRMED_SUMMARY_${payload.period}`)) { + return + } + + yield call(get_blocks_confirmed_summary, payload) +} + +export function* load_accounts_unconfirmed_summary() { + const request_history = yield select(get_request_history) + if (request_history.has('GET_ACCOUNTS_UNCONFIRMED_SUMMARY')) { + return + } + yield call(get_accounts_unconfirmed_summary) +} + +export function* load_blocks_unconfirmed_summary() { + const request_history = yield select(get_request_history) + if (request_history.has('GET_BLOCKS_UNCONFIRMED_SUMMARY')) { + return + } + yield call(get_blocks_unconfirmed_summary) +} + +//= ==================================== +// WATCHERS +// ------------------------------------- + +export function* watch_get_blocks_confirmed_summary() { + yield takeEvery( + nanodb_actions.GET_BLOCKS_CONFIRMED_SUMMARY, + load_block_confirmed_summary + ) +} + +export function* watch_get_accounts_unconfirmed_summary() { + yield takeEvery( + nanodb_actions.GET_ACCOUNTS_UNCONFIRMED_SUMMARY, + load_accounts_unconfirmed_summary + ) +} + +export function* watch_get_blocks_unconfirmed_summary() { + yield takeEvery( + nanodb_actions.GET_BLOCKS_UNCONFIRMED_SUMMARY, + load_blocks_unconfirmed_summary + ) +} + +//= ==================================== +// ROOT +// ------------------------------------- + +export const nanodb_sagas = [ + fork(watch_get_blocks_confirmed_summary), + fork(watch_get_accounts_unconfirmed_summary), + fork(watch_get_blocks_unconfirmed_summary) +] diff --git a/src/core/reducers.js b/src/core/reducers.js index 6c7a6b4e..e85abb34 100644 --- a/src/core/reducers.js +++ b/src/core/reducers.js @@ -13,6 +13,8 @@ import { networkReducer } from './network' import { notificationReducer } from './notifications' import { postsReducer } from './posts' import { postlistsReducer } from './postlists' +import { nanodb_reducer } from './nanodb' +import { api_reducer } from './api' const rootReducer = (history) => combineReducers({ @@ -28,7 +30,9 @@ const rootReducer = (history) => network: networkReducer, notification: notificationReducer, posts: postsReducer, - postlists: postlistsReducer + postlists: postlistsReducer, + nanodb: nanodb_reducer, + api: api_reducer }) export default rootReducer diff --git a/src/core/sagas.js b/src/core/sagas.js index 237db53f..b121138b 100644 --- a/src/core/sagas.js +++ b/src/core/sagas.js @@ -10,6 +10,7 @@ import { githubIssuesSagas } from './github-issues' import { ledgerSagas } from './ledger' import { networkSagas } from './network' import { postlistSagas } from './postlists' +import { nanodb_sagas } from './nanodb' export default function* rootSage() { yield all([ @@ -22,6 +23,7 @@ export default function* rootSage() { ...githubIssuesSagas, ...ledgerSagas, ...networkSagas, - ...postlistSagas + ...postlistSagas, + ...nanodb_sagas ]) } diff --git a/src/styles/toggle-button-group.styl b/src/styles/toggle-button-group.styl new file mode 100644 index 00000000..0dd023ef --- /dev/null +++ b/src/styles/toggle-button-group.styl @@ -0,0 +1,14 @@ +.toggle-button-group + flex 0 0 auto + border-radius 4px + border 1px solid $borderColor + background #ffffff + + .MuiToggleButton-root + border none + margin 4px + font-size 10px + padding 3px 8px + &:not(:first-child) + &:first-child + border-radius 4px \ No newline at end of file diff --git a/src/styles/variables.styl b/src/styles/variables.styl index 0f0a0dd1..39968701 100644 --- a/src/styles/variables.styl +++ b/src/styles/variables.styl @@ -1,4 +1,5 @@ $backgroundColor = #F0F0F0 +$textColor = #444444 $borderColor = #D0D0D0 diff --git a/src/views/components/app/app.js b/src/views/components/app/app.js index 5ae1193f..1306392f 100644 --- a/src/views/components/app/app.js +++ b/src/views/components/app/app.js @@ -10,6 +10,7 @@ import '@styles/typography.styl' import '@styles/doc.styl' import '@styles/header.styl' import '@styles/markdown.styl' +import '@styles/toggle-button-group.styl' export default class App extends React.Component { async componentDidMount() { diff --git a/src/views/components/metric-card/metric-card.js b/src/views/components/metric-card/metric-card.js index 32a71039..c8e880e2 100644 --- a/src/views/components/metric-card/metric-card.js +++ b/src/views/components/metric-card/metric-card.js @@ -19,7 +19,7 @@ export default class MetricCard extends React.Component { render() { const { - metrics, + metrics = [], title, subtitle, max, @@ -27,7 +27,8 @@ export default class MetricCard extends React.Component { tooltip, field, selectedField, - selectedLabel + selectedLabel, + body } = this.props const isSelectedField = field === selectedField const rows = metrics.map((p, i) => { @@ -66,7 +67,7 @@ export default class MetricCard extends React.Component { )} -