Browse Source

Merge "web: refactor nodes page to use a reducer"

tags/3.4.0
Zuul 5 months ago
parent
commit
c66af50f64
4 changed files with 140 additions and 37 deletions
  1. 63
    0
      web/src/actions/nodes.js
  2. 33
    37
      web/src/pages/Nodes.jsx
  3. 2
    0
      web/src/reducers/index.js
  4. 42
    0
      web/src/reducers/nodes.js

+ 63
- 0
web/src/actions/nodes.js View File

@@ -0,0 +1,63 @@
1
+// Copyright 2018 Red Hat, Inc
2
+//
3
+// Licensed under the Apache License, Version 2.0 (the "License"); you may
4
+// not use this file except in compliance with the License. You may obtain
5
+// a copy of the License at
6
+//
7
+//      http://www.apache.org/licenses/LICENSE-2.0
8
+//
9
+// Unless required by applicable law or agreed to in writing, software
10
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
+// License for the specific language governing permissions and limitations
13
+// under the License.
14
+
15
+import * as API from '../api'
16
+
17
+export const NODES_FETCH_REQUEST = 'NODES_FETCH_REQUEST'
18
+export const NODES_FETCH_SUCCESS = 'NODES_FETCH_SUCCESS'
19
+export const NODES_FETCH_FAIL = 'NODES_FETCH_FAIL'
20
+
21
+export const requestNodes = () => ({
22
+  type: NODES_FETCH_REQUEST
23
+})
24
+
25
+export const receiveNodes = (tenant, json) => ({
26
+  type: NODES_FETCH_SUCCESS,
27
+  nodes: json,
28
+  receivedAt: Date.now()
29
+})
30
+
31
+const failedNodes = error => ({
32
+  type: NODES_FETCH_FAIL,
33
+  error
34
+})
35
+
36
+const fetchNodes = (tenant) => dispatch => {
37
+  dispatch(requestNodes())
38
+  return API.fetchNodes(tenant.apiPrefix)
39
+    .then(response => dispatch(receiveNodes(tenant.name, response.data)))
40
+    .catch(error => dispatch(failedNodes(error)))
41
+}
42
+
43
+const shouldFetchNodes = (tenant, state) => {
44
+  const nodes = state.nodes
45
+  if (!nodes || nodes.nodes.length === 0) {
46
+    return true
47
+  }
48
+  if (nodes.isFetching) {
49
+    return false
50
+  }
51
+  if (Date.now() - nodes.receivedAt > 60000) {
52
+    // Refetch after 1 minutes
53
+    return true
54
+  }
55
+  return false
56
+}
57
+
58
+export const fetchNodesIfNeeded = (tenant, force) => (
59
+  dispatch, getState) => {
60
+    if (force || shouldFetchNodes(tenant, getState())) {
61
+      return dispatch(fetchNodes(tenant))
62
+    }
63
+}

+ 33
- 37
web/src/pages/Nodes.jsx View File

@@ -18,42 +18,29 @@ import { connect } from 'react-redux'
18 18
 import { Table } from 'patternfly-react'
19 19
 import * as moment from 'moment'
20 20
 
21
-import { fetchNodes } from '../api'
21
+import { fetchNodesIfNeeded } from '../actions/nodes'
22
+import Refreshable from '../containers/Refreshable'
22 23
 
23 24
 
24
-class NodesPage extends React.Component {
25
+class NodesPage extends Refreshable {
25 26
   static propTypes = {
26
-    tenant: PropTypes.object
27
+    tenant: PropTypes.object,
28
+    remoteData: PropTypes.object,
29
+    dispatch: PropTypes.func
27 30
   }
28 31
 
29
-  state = {
30
-    nodes: null
31
-  }
32
-
33
-  updateData () {
34
-    fetchNodes(this.props.tenant.apiPrefix).then(response => {
35
-      this.setState({nodes: response.data})
36
-    })
32
+  updateData (force) {
33
+    this.props.dispatch(fetchNodesIfNeeded(this.props.tenant, force))
37 34
   }
38 35
 
39 36
   componentDidMount () {
40 37
     document.title = 'Zuul Nodes'
41
-    if (this.props.tenant.name) {
42
-      this.updateData()
43
-    }
44
-  }
45
-
46
-  componentDidUpdate (prevProps) {
47
-    if (this.props.tenant.name !== prevProps.tenant.name) {
48
-      this.updateData()
49
-    }
38
+    super.componentDidMount()
50 39
   }
51 40
 
52 41
   render () {
53
-    const { nodes } = this.state
54
-    if (!nodes) {
55
-      return (<p>Loading...</p>)
56
-    }
42
+    const { remoteData } = this.props
43
+    const nodes = remoteData.nodes
57 44
 
58 45
     const headerFormat = value => <Table.Heading>{value}</Table.Heading>
59 46
     const cellFormat = value => <Table.Cell>{value}</Table.Cell>
@@ -92,19 +79,28 @@ class NodesPage extends React.Component {
92 79
       })
93 80
     })
94 81
     return (
95
-      <Table.PfProvider
96
-        striped
97
-        bordered
98
-        hover
99
-        columns={columns}
100
-      >
101
-        <Table.Header/>
102
-        <Table.Body
103
-          rows={nodes}
104
-          rowKey="id"
105
-        />
106
-      </Table.PfProvider>)
82
+      <React.Fragment>
83
+        <div style={{float: 'right'}}>
84
+          {this.renderSpinner()}
85
+        </div>
86
+        <Table.PfProvider
87
+          striped
88
+          bordered
89
+          hover
90
+          columns={columns}
91
+        >
92
+          <Table.Header/>
93
+          <Table.Body
94
+            rows={nodes}
95
+            rowKey="id"
96
+          />
97
+        </Table.PfProvider>
98
+      </React.Fragment>
99
+    )
107 100
   }
108 101
 }
109 102
 
110
-export default connect(state => ({tenant: state.tenant}))(NodesPage)
103
+export default connect(state => ({
104
+  tenant: state.tenant,
105
+  remoteData: state.nodes,
106
+}))(NodesPage)

+ 2
- 0
web/src/reducers/index.js View File

@@ -21,6 +21,7 @@ import info from './info'
21 21
 import job from './job'
22 22
 import jobs from './jobs'
23 23
 import labels from './labels'
24
+import nodes from './nodes'
24 25
 import project from './project'
25 26
 import projects from './projects'
26 27
 import status from './status'
@@ -33,6 +34,7 @@ const reducers = {
33 34
   job,
34 35
   jobs,
35 36
   labels,
37
+  nodes,
36 38
   project,
37 39
   projects,
38 40
   configErrors,

+ 42
- 0
web/src/reducers/nodes.js View File

@@ -0,0 +1,42 @@
1
+// Copyright 2018 Red Hat, Inc
2
+//
3
+// Licensed under the Apache License, Version 2.0 (the "License"); you may
4
+// not use this file except in compliance with the License. You may obtain
5
+// a copy of the License at
6
+//
7
+//      http://www.apache.org/licenses/LICENSE-2.0
8
+//
9
+// Unless required by applicable law or agreed to in writing, software
10
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
+// License for the specific language governing permissions and limitations
13
+// under the License.
14
+
15
+import {
16
+  NODES_FETCH_FAIL,
17
+  NODES_FETCH_REQUEST,
18
+  NODES_FETCH_SUCCESS
19
+} from '../actions/nodes'
20
+
21
+import update from 'immutability-helper'
22
+
23
+export default (state = {
24
+  receivedAt: 0,
25
+  isFetching: false,
26
+  nodes: [],
27
+}, action) => {
28
+  switch (action.type) {
29
+    case NODES_FETCH_REQUEST:
30
+      return update(state, {$merge: {isFetching: true}})
31
+    case NODES_FETCH_SUCCESS:
32
+      return update(state, {$merge: {
33
+        isFetching: false,
34
+        nodes: action.nodes,
35
+        receivedAt: action.receivedAt
36
+      }})
37
+    case NODES_FETCH_FAIL:
38
+      return update(state, {$merge: {isFetching: false}})
39
+    default:
40
+      return state
41
+  }
42
+}

Loading…
Cancel
Save