Skip to content
This repository has been archived by the owner on Jul 1, 2022. It is now read-only.

Commit

Permalink
feat(notifications): implement connection to new API
Browse files Browse the repository at this point in the history
  • Loading branch information
epiqueras committed Feb 23, 2018
1 parent 32c3ec1 commit 02f1e11
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 23 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"ethjs": "^0.3.3",
"ethjs-unit": "^0.1.6",
"history": "^4.7.2",
"kleros-api": "^0.0.58",
"kleros-api": "^0.0.59",
"normalize.css": "^7.0.0",
"react": "^16.2.0",
"react-addons-css-transition-group": "^15.6.2",
Expand Down
14 changes: 14 additions & 0 deletions src/actions/notification.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { createActions } from '../utils/redux'

/* Actions */

// Notifications
export const notifications = createActions('NOTIFICATIONS')

// Notification
export const notification = createActions('NOTIFICATION')

/* Action Creators */

// Notifications
export const fetchNotifications = () => ({ type: notifications.FETCH })
50 changes: 33 additions & 17 deletions src/containers/home/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { toastr } from 'react-redux-toastr'

import * as walletSelectors from '../../reducers/wallet'
import * as walletActions from '../../actions/wallet'
import * as notificationSelectors from '../../reducers/notification'
import * as notificationActions from '../../actions/notification'
import * as arbitratorSelectors from '../../reducers/arbitrator'
import * as arbitratorActions from '../../actions/arbitrator'
import { RenderIf } from '../../utils/redux'
Expand All @@ -29,11 +31,13 @@ class Home extends PureComponent {
// Redux State
accounts: walletSelectors.accountsShape.isRequired,
balance: walletSelectors.balanceShape.isRequired,
notifications: notificationSelectors.notificationsShape.isRequired,
PNKBalance: arbitratorSelectors.PNKBalanceShape.isRequired,
arbitratorData: arbitratorSelectors.arbitratorDataShape.isRequired,

// Action Dispatchers
fetchBalance: PropTypes.func.isRequired,
fetchNotifications: PropTypes.func.isRequired,
fetchPNKBalance: PropTypes.func.isRequired,
activatePNK: PropTypes.func.isRequired,
fetchArbitratorData: PropTypes.func.isRequired,
Expand All @@ -44,8 +48,14 @@ class Home extends PureComponent {
}

componentDidMount() {
const { fetchBalance, fetchPNKBalance, fetchArbitratorData } = this.props
const {
fetchBalance,
fetchNotifications,
fetchPNKBalance,
fetchArbitratorData
} = this.props
fetchBalance()
fetchNotifications()
fetchPNKBalance()
fetchArbitratorData()
}
Expand Down Expand Up @@ -80,7 +90,13 @@ class Home extends PureComponent {
}

render() {
const { accounts, balance, PNKBalance, arbitratorData } = this.props
const {
accounts,
balance,
notifications,
PNKBalance,
arbitratorData
} = this.props

return (
<div className="Home">
Expand Down Expand Up @@ -186,21 +202,19 @@ class Home extends PureComponent {
<h4>Notifications</h4>
</div>
<div className="Home-cardList">
<div className="Home-cardList-card">
<NotificationCard message="This is a notification card." />
</div>
<div className="Home-cardList-card">
<NotificationCard message="This is a notification card." />
</div>
<div className="Home-cardList-card">
<NotificationCard message="This is a notification card." />
</div>
<div className="Home-cardList-card">
<NotificationCard message="This is a notification card." />
</div>
<div className="Home-cardList-card">
<NotificationCard message="This is a notification card." />
</div>
<RenderIf
resource={notifications}
loading={<Icosahedron />}
done={
notifications.data &&
notifications.data.map(n => (
<div className="Home-cardList-card">
<NotificationCard message={n.message} />
</div>
))
}
failedLoading="There was an error fetching your notifications..."
/>
</div>
<div className="Home-separatorHeader">
<h4>Pending Actions</h4>
Expand Down Expand Up @@ -256,12 +270,14 @@ export default connect(
state => ({
accounts: state.wallet.accounts,
balance: state.wallet.balance,
notifications: state.notification.notifications,
PNKBalance: state.arbitrator.PNKBalance,
arbitratorData: state.arbitrator.arbitratorData,
activatePNKFormIsInvalid: getActivatePNKFormIsInvalid(state)
}),
{
fetchBalance: walletActions.fetchBalance,
fetchNotifications: notificationActions.fetchNotifications,
fetchPNKBalance: arbitratorActions.fetchPNKBalance,
activatePNK: arbitratorActions.activatePNK,
fetchArbitratorData: arbitratorActions.fetchArbitratorData,
Expand Down
2 changes: 2 additions & 0 deletions src/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { reducer as toastr } from 'react-redux-toastr'
import { reducer as form } from 'redux-form'

import wallet from './wallet'
import notification from './notification'
import arbitrator from './arbitrator'
import dispute from './dispute'

Expand All @@ -13,6 +14,7 @@ export default combineReducers({
toastr,
form,
wallet,
notification,
arbitrator,
dispute
})
27 changes: 27 additions & 0 deletions src/reducers/notification.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import PropTypes from 'prop-types'

import * as notificationActions from '../actions/notification'
import createReducer, { createResource } from '../utils/redux'

// Shapes
const {
shape: notificationsShape,
initialState: notificationsInitialState
} = createResource(PropTypes.arrayOf(PropTypes.string))
export { notificationsShape }

// Reducer
export default createReducer(
{
notifications: notificationsInitialState
},
{
[notificationActions.notification.RECEIVE]: (state, action) => ({
...state,
notifications: {
...state.notifications,
data: [...state.notifications.data, action.payload.notification]
}
})
}
)
8 changes: 7 additions & 1 deletion src/sagas/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { delay } from 'redux-saga'
import { spawn, call, all } from 'redux-saga/effects'

import walletSaga from './wallet'
import notificationSaga from './notification'
import arbitratorSaga from './arbitrator'
import disputeSaga from './dispute'

Expand Down Expand Up @@ -31,7 +32,12 @@ export function makeRestartable(saga) {
}
}

const rootSagas = [walletSaga, arbitratorSaga, disputeSaga].map(makeRestartable)
const rootSagas = [
walletSaga,
notificationSaga,
arbitratorSaga,
disputeSaga
].map(makeRestartable)

/**
* The root saga.
Expand Down
83 changes: 83 additions & 0 deletions src/sagas/notification.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { eventChannel } from 'redux-saga'

import {
fork,
take,
race,
takeLatest,
select,
call,
put
} from 'redux-saga/effects'

import * as notificationActions from '../actions/notification'
import * as walletSelectors from '../reducers/wallet'
import * as walletActions from '../actions/wallet'
import { kleros, ARBITRATOR_ADDRESS } from '../bootstrap/dapp-api'
import { action, errorAction } from '../utils/action'

/**
* Listens for push notifications.
*/
export function* pushNotificationsListener() {
// Start after fetching whole list of notifications
while (yield take(notificationActions.notifications.FETCH)) {
const account = yield select(walletSelectors.getAccount) // Current account

// Set up event channel with subscriber
const channel = eventChannel(emitter => {
kleros.watchForEvents(ARBITRATOR_ADDRESS, account, notification =>
emitter(notification)
)

return kleros.eventListener.clearArbitratorHandlers // Unsubscribe function
})

// Keep listening while on the same account
while (account === (yield select(walletSelectors.getAccount))) {
const [notification, accounts] = yield race([
take(channel), // New notifications
take(walletActions.accounts.RECEIVE) // Accounts refetch
])
if (accounts) continue // Possible account change

// Put new notification
yield put(
action(notificationActions.notification.RECEIVE, { notification })
)
}

// We changed accounts, so close the channel. This calls unsubscribe under the hood which clears handlers for the old account
channel.close()
}
}

/**
* Fetches the current account's notifications.
*/
export function* fetchNotifications() {
try {
const account = yield select(walletSelectors.getAccount)
const notifications = yield call(
kleros.notifications.getNotifications,
account
)

yield put(
action(notificationActions.notifications.RECEIVE, { notifications })
)
} catch (err) {
yield put(errorAction(notificationActions.notifications.FAIL_FETCH, err))
}
}

/**
* The root of the notification saga.
*/
export default function* notificationSaga() {
// Listeners
yield fork(pushNotificationsListener)

// Notifications
yield takeLatest(notificationActions.notifications.FETCH, fetchNotifications)
}
2 changes: 1 addition & 1 deletion src/sagas/wallet.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import unit from 'ethjs-unit'

import { takeLatest, call, put, select } from 'redux-saga/effects'
import { takeLatest, select, call, put } from 'redux-saga/effects'

import * as walletSelectors from '../reducers/wallet'
import * as walletActions from '../actions/wallet'
Expand Down
6 changes: 3 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6548,9 +6548,9 @@ klaw@^1.0.0:
optionalDependencies:
graceful-fs "^4.1.9"

kleros-api@^0.0.58:
version "0.0.58"
resolved "https://registry.yarnpkg.com/kleros-api/-/kleros-api-0.0.58.tgz#194a35f55309e49f29f28446dbddec65e97db4eb"
kleros-api@^0.0.59:
version "0.0.59"
resolved "https://registry.yarnpkg.com/kleros-api/-/kleros-api-0.0.59.tgz#045f4d0b88ec5346ab23305cc208e2f76f771fd0"
dependencies:
babel-plugin-transform-runtime "^6.23.0"
babel-preset-env "^1.6.0"
Expand Down

0 comments on commit 02f1e11

Please sign in to comment.