diff --git a/pkg/webui/console/views/application-api-key-add/index.js b/pkg/webui/console/views/application-api-key-add/index.js
index 7ddfa3da6d..244bc56310 100644
--- a/pkg/webui/console/views/application-api-key-add/index.js
+++ b/pkg/webui/console/views/application-api-key-add/index.js
@@ -18,13 +18,13 @@ import bind from 'autobind-decorator'
import { connect } from 'react-redux'
import { replace } from 'connected-react-router'
-import Spinner from '../../../components/spinner'
import Breadcrumb from '../../../components/breadcrumbs/breadcrumb'
import { withBreadcrumb } from '../../../components/breadcrumbs/context'
import sharedMessages from '../../../lib/shared-messages'
import Message from '../../../lib/components/message'
import IntlHelmet from '../../../lib/components/intl-helmet'
import { ApiKeyCreateForm } from '../../components/api-key-form'
+import withRequest from '../../../lib/components/with-request'
import { getApplicationsRightsList } from '../../store/actions/applications'
import {
@@ -43,7 +43,15 @@ import api from '../../api'
error: selectApplicationRightsError(state),
rights: selectApplicationRights(state),
universalRights: selectApplicationUniversalRights(state),
+}),
+dispatch => ({
+ getApplicationsRightsList: appId => dispatch(getApplicationsRightsList(appId)),
+ navigateToList: appId => dispatch(replace(`/console/applications/${appId}/api-keys`)),
}))
+@withRequest(
+ ({ appId, getApplicationsRightsList }) => getApplicationsRightsList(appId),
+ ({ fetching, rights }) => fetching || !Boolean(rights.length)
+)
@withBreadcrumb('apps.single.api-keys.add', function (props) {
const appId = props.appId
return (
@@ -63,28 +71,14 @@ export default class ApplicationApiKeyAdd extends React.Component {
this.createApplicationKey = key => api.application.apiKeys.create(props.appId, key)
}
- componentDidMount () {
- const { dispatch, appId } = this.props
-
- dispatch(getApplicationsRightsList(appId))
- }
-
handleApprove () {
- const { dispatch, appId } = this.props
+ const { navigateToList, appId } = this.props
- dispatch(replace(`/console/applications/${appId}/api-keys`))
+ navigateToList(appId)
}
render () {
- const { rights, fetching, error, universalRights } = this.props
-
- if (error) {
- throw error
- }
-
- if (fetching || !rights.length) {
- return
- }
+ const { rights, universalRights } = this.props
return (
diff --git a/pkg/webui/console/views/application-api-key-edit/index.js b/pkg/webui/console/views/application-api-key-edit/index.js
index 127417d5fa..eb0ce455aa 100644
--- a/pkg/webui/console/views/application-api-key-edit/index.js
+++ b/pkg/webui/console/views/application-api-key-edit/index.js
@@ -21,10 +21,10 @@ import { replace } from 'connected-react-router'
import { withBreadcrumb } from '../../../components/breadcrumbs/context'
import Breadcrumb from '../../../components/breadcrumbs/breadcrumb'
import sharedMessages from '../../../lib/shared-messages'
-import Spinner from '../../../components/spinner'
import Message from '../../../lib/components/message'
import IntlHelmet from '../../../lib/components/intl-helmet'
import { ApiKeyEditForm } from '../../components/api-key-form'
+import withRequest from '../../../lib/components/with-request'
import {
getApplicationApiKey,
@@ -62,12 +62,16 @@ import api from '../../api'
}
},
dispatch => ({
- async loadPageData (appId, apiKeyId) {
- await dispatch(getApplicationsRightsList(appId))
+ loadData (appId, apiKeyId) {
+ dispatch(getApplicationsRightsList(appId))
dispatch(getApplicationApiKey(appId, apiKeyId))
},
deleteSuccess: appId => dispatch(replace(`/console/applications/${appId}/api-keys`)),
}))
+@withRequest(
+ ({ loadData, appId, keyId }) => loadData(appId, keyId),
+ ({ fetching, apiKey }) => fetching || !Boolean(apiKey)
+)
@withBreadcrumb('apps.single.api-keys.edit', function (props) {
const { appId, keyId } = props
@@ -93,12 +97,6 @@ export default class ApplicationApiKeyEdit extends React.Component {
)
}
- componentDidMount () {
- const { loadPageData, appId, keyId } = this.props
-
- loadPageData(appId, keyId)
- }
-
onDeleteSuccess () {
const { appId, deleteSuccess } = this.props
@@ -106,15 +104,7 @@ export default class ApplicationApiKeyEdit extends React.Component {
}
render () {
- const { apiKey, rights, fetching, error, universalRights } = this.props
-
- if (error) {
- throw error
- }
-
- if (fetching || !apiKey) {
- return
- }
+ const { apiKey, rights, universalRights } = this.props
return (
diff --git a/pkg/webui/console/views/application-collaborator-add/index.js b/pkg/webui/console/views/application-collaborator-add/index.js
index d7567e8b37..fd5edde00d 100644
--- a/pkg/webui/console/views/application-collaborator-add/index.js
+++ b/pkg/webui/console/views/application-collaborator-add/index.js
@@ -18,13 +18,13 @@ import bind from 'autobind-decorator'
import { connect } from 'react-redux'
import { push } from 'connected-react-router'
-import Spinner from '../../../components/spinner'
import Breadcrumb from '../../../components/breadcrumbs/breadcrumb'
import { withBreadcrumb } from '../../../components/breadcrumbs/context'
import sharedMessages from '../../../lib/shared-messages'
import CollaboratorForm from '../../components/collaborator-form'
import Message from '../../../lib/components/message'
import IntlHelmet from '../../../lib/components/intl-helmet'
+import withRequest from '../../../lib/components/with-request'
import { getApplicationsRightsList } from '../../store/actions/applications'
import {
@@ -54,6 +54,10 @@ import api from '../../api'
redirectToList: () => dispatchProps.redirectToList(stateProps.appId),
getApplicationsRightsList: () => dispatchProps.getApplicationsRightsList(stateProps.appId),
}))
+@withRequest(
+ ({ getApplicationsRightsList }) => getApplicationsRightsList(),
+ ({ fetching, rights }) => fetching || !Boolean(rights.length)
+)
@withBreadcrumb('apps.single.collaborators.add', function (props) {
const appId = props.appId
return (
@@ -71,39 +75,19 @@ export default class ApplicationCollaboratorAdd extends React.Component {
error: '',
}
- componentDidMount () {
- const { getApplicationsRightsList } = this.props
-
- getApplicationsRightsList()
- }
-
- componentDidUpdate (prevProps) {
- const { error } = this.props
-
- if (Boolean(error) && prevProps.error !== error) {
- throw error
- }
- }
-
async handleSubmit (collaborator) {
const { appId } = this.props
await api.application.collaborators.add(appId, collaborator)
-
}
render () {
const {
rights,
- fetching,
universalRights,
redirectToList,
} = this.props
- if (fetching && !rights.length) {
- return
- }
-
return (
diff --git a/pkg/webui/console/views/application-collaborator-edit/index.js b/pkg/webui/console/views/application-collaborator-edit/index.js
index 2e9cb9b67e..35dcc77512 100644
--- a/pkg/webui/console/views/application-collaborator-edit/index.js
+++ b/pkg/webui/console/views/application-collaborator-edit/index.js
@@ -22,10 +22,10 @@ import { withBreadcrumb } from '../../../components/breadcrumbs/context'
import Breadcrumb from '../../../components/breadcrumbs/breadcrumb'
import sharedMessages from '../../../lib/shared-messages'
import CollaboratorForm from '../../components/collaborator-form'
-import Spinner from '../../../components/spinner'
import Message from '../../../lib/components/message'
import IntlHelmet from '../../../lib/components/intl-helmet'
import toast from '../../../components/toast'
+import withRequest from '../../../lib/components/with-request'
import {
getApplicationCollaborator,
@@ -84,6 +84,10 @@ import api from '../../api'
redirectToList: () => dispatchProps.redirectToList(stateProps.appId),
})
)
+@withRequest(
+ ({ loadData }) => loadData(),
+ ({ fetching, collaborator }) => fetching || !Boolean(collaborator)
+)
@withBreadcrumb('apps.single.collaborators.edit', function (props) {
const { appId, collaboratorId, collaboratorType } = props
@@ -102,20 +106,6 @@ export default class ApplicationCollaboratorEdit extends React.Component {
error: '',
}
- componentDidMount () {
- const { loadData } = this.props
-
- loadData()
- }
-
- componentDidUpdate (prevProps) {
- const { error } = this.props
-
- if (Boolean(error) && prevProps.error !== error) {
- throw error
- }
- }
-
async handleSubmit (updatedCollaborator) {
const { appId } = this.props
@@ -158,15 +148,10 @@ export default class ApplicationCollaboratorEdit extends React.Component {
const {
collaborator,
rights,
- fetching,
universalRights,
redirectToList,
} = this.props
- if (fetching || !collaborator) {
- return
- }
-
return (
diff --git a/pkg/webui/console/views/application-integration-edit/index.js b/pkg/webui/console/views/application-integration-edit/index.js
index ad78b58381..15c56a1f2a 100644
--- a/pkg/webui/console/views/application-integration-edit/index.js
+++ b/pkg/webui/console/views/application-integration-edit/index.js
@@ -25,9 +25,9 @@ import IntlHelmet from '../../../lib/components/intl-helmet'
import Message from '../../../lib/components/message'
import WebhookForm from '../../components/webhook-form'
import toast from '../../../components/toast'
-import Spinner from '../../../components/spinner'
import diff from '../../../lib/diff'
import sharedMessages from '../../../lib/shared-messages'
+import withRequest from '../../../lib/components/with-request'
import {
selectSelectedWebhook,
@@ -70,6 +70,10 @@ const webhookEntitySelector = [
navigateToList: () => dispatch(replace(`/console/applications/${appId}/integrations`)),
}
})
+@withRequest(
+ ({ getWebhook }) => getWebhook(),
+ ({ fetching, webhook }) => fetching || !Boolean(webhook)
+)
@withBreadcrumb('apps.single.integrations.edit', function (props) {
const { appId, match: { params: { webhookId }}} = props
return (
@@ -82,11 +86,6 @@ const webhookEntitySelector = [
})
@bind
export default class ApplicationIntegrationEdit extends Component {
- componentDidMount () {
- const { getWebhook } = this.props
-
- getWebhook()
- }
async handleSubmit (webhook) {
const { appId, match: { params: { webhookId }}, webhook: originalWebhook } = this.props
@@ -120,15 +119,7 @@ export default class ApplicationIntegrationEdit extends Component {
}
render () {
- const { webhook, fetching, error } = this.props
-
- if (fetching || !webhook) {
- return
- }
-
- if (error) {
- throw error
- }
+ const { webhook } = this.props
return (
diff --git a/pkg/webui/console/views/gateway-api-key-add/index.js b/pkg/webui/console/views/gateway-api-key-add/index.js
index dd15503221..113e287c90 100644
--- a/pkg/webui/console/views/gateway-api-key-add/index.js
+++ b/pkg/webui/console/views/gateway-api-key-add/index.js
@@ -18,13 +18,13 @@ import bind from 'autobind-decorator'
import { connect } from 'react-redux'
import { replace } from 'connected-react-router'
-import Spinner from '../../../components/spinner'
import Breadcrumb from '../../../components/breadcrumbs/breadcrumb'
import { withBreadcrumb } from '../../../components/breadcrumbs/context'
import sharedMessages from '../../../lib/shared-messages'
import Message from '../../../lib/components/message'
import IntlHelmet from '../../../lib/components/intl-helmet'
import { ApiKeyCreateForm } from '../../components/api-key-form'
+import withRequest from '../../../lib/components/with-request'
import { getGatewaysRightsList } from '../../store/actions/gateways'
import {
@@ -43,7 +43,15 @@ import api from '../../api'
error: selectGatewayRightsError(state),
rights: selectGatewayRights(state),
universalRights: selectGatewayUniversalRights(state),
+}),
+dispatch => ({
+ getGatewaysRightsList: gtwId => dispatch(getGatewaysRightsList(gtwId)),
+ navigateToList: gtwId => dispatch(replace(`/console/gateways/${gtwId}/api-keys`)),
}))
+@withRequest(
+ ({ gtwId, getGatewaysRightsList }) => getGatewaysRightsList(gtwId),
+ ({ fetching, rights }) => fetching || !Boolean(rights.length)
+)
@withBreadcrumb('gtws.single.api-keys.add', function (props) {
const gtwId = props.gtwId
@@ -64,28 +72,14 @@ export default class GatewayApiKeyAdd extends React.Component {
this.createGatewayKey = key => api.gateway.apiKeys.create(props.gtwId, key)
}
- componentDidMount () {
- const { dispatch, gtwId } = this.props
-
- dispatch(getGatewaysRightsList(gtwId))
- }
-
handleApprove () {
- const { dispatch, gtwId } = this.props
+ const { navigateToList, gtwId } = this.props
- dispatch(replace(`/console/gateways/${gtwId}/api-keys`))
+ navigateToList(gtwId)
}
render () {
- const { rights, fetching, error, universalRights } = this.props
-
- if (error) {
- throw error
- }
-
- if (fetching || !rights.length) {
- return
- }
+ const { rights, universalRights } = this.props
return (
diff --git a/pkg/webui/console/views/gateway-api-key-edit/index.js b/pkg/webui/console/views/gateway-api-key-edit/index.js
index 8d75816d63..77cf78b3b8 100644
--- a/pkg/webui/console/views/gateway-api-key-edit/index.js
+++ b/pkg/webui/console/views/gateway-api-key-edit/index.js
@@ -21,10 +21,10 @@ import { replace } from 'connected-react-router'
import { withBreadcrumb } from '../../../components/breadcrumbs/context'
import Breadcrumb from '../../../components/breadcrumbs/breadcrumb'
import sharedMessages from '../../../lib/shared-messages'
-import Spinner from '../../../components/spinner'
import Message from '../../../lib/components/message'
import IntlHelmet from '../../../lib/components/intl-helmet'
import { ApiKeyEditForm } from '../../components/api-key-form'
+import withRequest from '../../../lib/components/with-request'
import {
getGatewayApiKey,
@@ -61,12 +61,16 @@ import api from '../../api'
}
},
dispatch => ({
- async loadPageData (gtwId, apiKeyId) {
- await dispatch(getGatewaysRightsList(gtwId))
+ loadData (gtwId, apiKeyId) {
+ dispatch(getGatewaysRightsList(gtwId))
dispatch(getGatewayApiKey(gtwId, apiKeyId))
},
deleteSuccess: gtwId => dispatch(replace(`/console/gateways/${gtwId}/api-keys`)),
}))
+@withRequest(
+ ({ gtwId, keyId, loadData }) => loadData(gtwId, keyId),
+ ({ fetching, apiKey }) => fetching || !Boolean(apiKey)
+)
@withBreadcrumb('gtws.single.api-keys.edit', function (props) {
const { gtwId, keyId } = props
@@ -92,12 +96,6 @@ export default class GatewayApiKeyEdit extends React.Component {
)
}
- componentDidMount () {
- const { loadPageData, gtwId, keyId } = this.props
-
- loadPageData(gtwId, keyId)
- }
-
onDeleteSuccess () {
const { gtwId, deleteSuccess } = this.props
@@ -105,15 +103,7 @@ export default class GatewayApiKeyEdit extends React.Component {
}
render () {
- const { apiKey, rights, fetching, error, universalRights } = this.props
-
- if (error) {
- throw error
- }
-
- if (fetching || !apiKey) {
- return
- }
+ const { apiKey, rights, universalRights } = this.props
return (
diff --git a/pkg/webui/console/views/gateway-collaborator-add/index.js b/pkg/webui/console/views/gateway-collaborator-add/index.js
index 3ee902e661..08aa923d98 100644
--- a/pkg/webui/console/views/gateway-collaborator-add/index.js
+++ b/pkg/webui/console/views/gateway-collaborator-add/index.js
@@ -18,13 +18,13 @@ import bind from 'autobind-decorator'
import { connect } from 'react-redux'
import { push } from 'connected-react-router'
-import Spinner from '../../../components/spinner'
import Breadcrumb from '../../../components/breadcrumbs/breadcrumb'
import { withBreadcrumb } from '../../../components/breadcrumbs/context'
import sharedMessages from '../../../lib/shared-messages'
import Message from '../../../lib/components/message'
import IntlHelmet from '../../../lib/components/intl-helmet'
import CollaboratorForm from '../../components/collaborator-form'
+import withRequest from '../../../lib/components/with-request'
import {
selectSelectedGatewayId,
@@ -56,6 +56,10 @@ import api from '../../api'
getGatewaysRightsList: () => dispatchProps.getGatewaysRightsList(stateProps.gtwId),
redirectToList: () => dispatchProps.redirectToList(stateProps.gtwId),
}))
+@withRequest(
+ ({ getGatewaysRightsList }) => getGatewaysRightsList(),
+ ({ fetching, rights }) => fetching || !Boolean(rights.length)
+)
@withBreadcrumb('gtws.single.collaborators.add', function (props) {
const gtwId = props.gtwId
return (
@@ -73,20 +77,6 @@ export default class GatewayCollaboratorAdd extends React.Component {
error: '',
}
- componentDidMount () {
- const { getGatewaysRightsList } = this.props
-
- getGatewaysRightsList()
- }
-
- componentDidUpdate (prevProps) {
- const { error } = this.props
-
- if (Boolean(error) && prevProps.error !== error) {
- throw error
- }
- }
-
handleSubmit (collaborator) {
const { gtwId } = this.props
@@ -96,15 +86,10 @@ export default class GatewayCollaboratorAdd extends React.Component {
render () {
const {
rights,
- fetching,
redirectToList,
universalRights,
} = this.props
- if (fetching && !rights.length) {
- return
- }
-
return (
diff --git a/pkg/webui/console/views/gateway-collaborator-edit/index.js b/pkg/webui/console/views/gateway-collaborator-edit/index.js
index 932976972b..bc015c3d2f 100644
--- a/pkg/webui/console/views/gateway-collaborator-edit/index.js
+++ b/pkg/webui/console/views/gateway-collaborator-edit/index.js
@@ -22,10 +22,10 @@ import { withBreadcrumb } from '../../../components/breadcrumbs/context'
import Breadcrumb from '../../../components/breadcrumbs/breadcrumb'
import sharedMessages from '../../../lib/shared-messages'
import CollaboratorForm from '../../components/collaborator-form'
-import Spinner from '../../../components/spinner'
import Message from '../../../lib/components/message'
import IntlHelmet from '../../../lib/components/intl-helmet'
import toast from '../../../components/toast'
+import withRequest from '../../../lib/components/with-request'
import {
getGatewayCollaborator,
@@ -85,6 +85,10 @@ import api from '../../api'
),
redirectToList: () => dispatchProps.redirectToList(stateProps.gtwId),
}))
+@withRequest(
+ ({ loadData }) => loadData(),
+ ({ fetching, collaborator }) => fetching || !Boolean(collaborator)
+)
@withBreadcrumb('gtws.single.collaborators.edit', function (props) {
const { gtwId, collaboratorId } = props
@@ -103,20 +107,6 @@ export default class GatewayCollaboratorEdit extends React.Component {
error: '',
}
- componentDidMount () {
- const { loadData } = this.props
-
- loadData()
- }
-
- componentDidUpdate (prevProps) {
- const { error } = this.props
-
- if (Boolean(error) && prevProps.error !== error) {
- throw error
- }
- }
-
handleSubmit (updatedCollaborator) {
const { gtwId } = this.props
@@ -140,15 +130,10 @@ export default class GatewayCollaboratorEdit extends React.Component {
const {
collaborator,
rights,
- fetching,
redirectToList,
universalRights,
} = this.props
- if (fetching || !collaborator) {
- return
- }
-
return (
diff --git a/pkg/webui/lib/components/with-request.js b/pkg/webui/lib/components/with-request.js
new file mode 100644
index 0000000000..ab5435ed92
--- /dev/null
+++ b/pkg/webui/lib/components/with-request.js
@@ -0,0 +1,60 @@
+// Copyright © 2019 The Things Network Foundation, The Things Industries B.V.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import React from 'react'
+
+import Spinner from '../../components/spinner'
+
+/**
+ * `withRequest` is a HOC that handles:
+ * * Requesting data on initial mount using the `loadData` prop.
+ * * Showing the loading spinner while the request in is progress using the `isFetchingTest` predicate.
+ * * Throwing an error received as the `error` prop.
+ * @param {Function} mapPropsToRequest - Selects the `request` given the wrapped component props.
+ * @param {Function} mapPropsToFetching - Selects the `fetching` value given the wrapped component props.
+ * If evaluates to `true`, then the loading spinner is rendered, otherwise renders the wrapped component.
+ * @param {Function} mapPropsToError - Selects the `error` value given the wrapped component props.
+ * @returns {Function} - An instance of the `withRequest` HOC.
+ */
+const withRequest = (
+ mapPropsToRequest,
+ mapPropsToFetching = ({ fetching } = {}) => fetching,
+ mapPropsToError = ({ error } = {}) => error
+) => Component =>
+ class WithRequest extends React.Component {
+
+ componentDidMount () {
+ mapPropsToRequest(this.props)
+ }
+
+ componentDidUpdate (prevProps) {
+ const error = mapPropsToError(this.props)
+ const prevError = mapPropsToError(prevProps)
+
+ // Check for errors only after component mounts and makes the request.
+ if (Boolean(error) && prevError !== error) {
+ throw error
+ }
+ }
+
+ render () {
+ if (mapPropsToFetching(this.props)) {
+ return
+ }
+
+ return
+ }
+ }
+
+export default withRequest