Compare commits
5 Commits
master
...
pagination
Author | SHA1 | Date |
---|---|---|
Cliff Meyers | 36298bcdfc | |
Cliff Meyers | 00f631afea | |
Cliff Meyers | 1c8b917fa9 | |
Cliff Meyers | 167372a33c | |
kzantow | 86ce354ad4 |
|
@ -6,7 +6,8 @@ import { ActivityRecord, ChangeSetRecord } from './records';
|
|||
import RunPipeline from './RunPipeline.jsx';
|
||||
import {
|
||||
actions,
|
||||
currentRuns as runsSelector,
|
||||
currentRuns as currentRunsSelector,
|
||||
runs as allRunsSelector,
|
||||
createSelector,
|
||||
connect,
|
||||
} from '../redux';
|
||||
|
@ -61,6 +62,12 @@ export class Activity extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
fetchNextRuns() {
|
||||
const pagination = this.props.allRuns[this.context.params.pipeline];
|
||||
pagination.currentPage++;
|
||||
this.props.fetchRunsIfNeeded(this.context.config);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { runs, pipeline } = this.props;
|
||||
// early out
|
||||
|
@ -68,12 +75,18 @@ export class Activity extends Component {
|
|||
return null;
|
||||
}
|
||||
|
||||
const currentRunData = runs.currentData;
|
||||
|
||||
if (!currentRunData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Only show the Run button for non multi-branch pipelines.
|
||||
// Multi-branch pipelines have the Run/play button beside them on
|
||||
// the Branches/PRs tab.
|
||||
const showRunButton = (pipeline && !Pipeline.isMultibranch(pipeline));
|
||||
|
||||
if (!runs.length) {
|
||||
if (!currentRunData.length) {
|
||||
return (<EmptyState repoName={this.context.params.pipeline} showRunButton={showRunButton} pipeline={pipeline} />);
|
||||
}
|
||||
|
||||
|
@ -93,7 +106,7 @@ export class Activity extends Component {
|
|||
<article className="activity">
|
||||
{showRunButton && <RunNonMultiBranchPipeline pipeline={pipeline} buttonText="Run" />}
|
||||
<Table className="activity-table fixed" headers={headers}>
|
||||
{ runs.map((run, index) => {
|
||||
{ currentRunData.map((run, index) => {
|
||||
const changeset = run.changeSet;
|
||||
let latestRecord = {};
|
||||
if (changeset && changeset.length > 0) {
|
||||
|
@ -109,6 +122,7 @@ export class Activity extends Component {
|
|||
return (<Runs {...props} />);
|
||||
})}
|
||||
</Table>
|
||||
<button className="btn-show-more btn-secondary" onClick={() => this.fetchNextRuns()}>Show More</button>
|
||||
</article>
|
||||
</main>);
|
||||
}
|
||||
|
@ -121,11 +135,15 @@ Activity.contextTypes = {
|
|||
};
|
||||
|
||||
Activity.propTypes = {
|
||||
runs: array,
|
||||
runs: object,
|
||||
allRuns: object,
|
||||
pipeline: object,
|
||||
fetchRunsIfNeeded: func,
|
||||
};
|
||||
|
||||
const selectors = createSelector([runsSelector], (runs) => ({ runs }));
|
||||
const selectors = createSelector(
|
||||
[currentRunsSelector, allRunsSelector],
|
||||
(runs, allRuns) => ({ runs, allRuns })
|
||||
);
|
||||
|
||||
export default connect(selectors, actions)(Activity);
|
||||
|
|
|
@ -86,7 +86,7 @@ class RunDetails extends Component {
|
|||
const baseUrl = buildRunDetailsUrl(organization, name, branch, runId);
|
||||
|
||||
/* eslint-disable arrow-body-style */
|
||||
const currentRun = this.props.runs.filter((run) => {
|
||||
const currentRun = this.props.runs.currentData.filter((run) => {
|
||||
return run.id === runId &&
|
||||
decodeURIComponent(run.pipeline) === branch;
|
||||
})[0];
|
||||
|
|
|
@ -5,6 +5,7 @@ import { State } from '../components/records';
|
|||
import UrlConfig from '../config';
|
||||
import { getNodesInformation } from '../util/logDisplayHelper';
|
||||
import { calculateStepsBaseUrl, calculateLogUrl, calculateNodeBaseUrl } from '../util/UrlUtils';
|
||||
import PaginationHolder from '../util/PaginationHolder';
|
||||
|
||||
// main actin logic
|
||||
export const ACTION_TYPES = keymirror({
|
||||
|
@ -56,8 +57,16 @@ export const actionHandlers = {
|
|||
[ACTION_TYPES.CLEAR_CURRENT_RUN_DATA](state) {
|
||||
return state.set('currentRuns', null);
|
||||
},
|
||||
[ACTION_TYPES.SET_CURRENT_RUN_DATA](state, { payload }): State {
|
||||
return state.set('currentRuns', payload);
|
||||
[ACTION_TYPES.SET_CURRENT_RUN_DATA](state, { payload, id, pageStart }): State {
|
||||
const runs = state.runs || {};
|
||||
|
||||
let runData = runs[id];
|
||||
if(!runData) {
|
||||
runData = runs[id] = new PaginationHolder();
|
||||
}
|
||||
runData.appendData(pageStart, payload);
|
||||
|
||||
return state.set('currentRuns', runData);
|
||||
},
|
||||
[ACTION_TYPES.SET_NODE](state, { payload }): State {
|
||||
return state.set('node', { ...payload });
|
||||
|
@ -67,9 +76,15 @@ export const actionHandlers = {
|
|||
nodes[payload.nodesBaseUrl] = payload;
|
||||
return state.set('nodes', nodes);
|
||||
},
|
||||
[ACTION_TYPES.SET_RUNS_DATA](state, { payload, id }): State {
|
||||
const runs = { ...state.runs } || {};
|
||||
runs[id] = payload;
|
||||
[ACTION_TYPES.SET_RUNS_DATA](state, { payload, id, pageStart }): State {
|
||||
const runs = state.runs || {};
|
||||
|
||||
let runData = runs[id];
|
||||
if(!runData) {
|
||||
runData = runs[id] = new PaginationHolder();
|
||||
}
|
||||
runData.appendData(pageStart, payload);
|
||||
|
||||
return state.set('runs', runs);
|
||||
},
|
||||
[ACTION_TYPES.CLEAR_CURRENT_BRANCHES_DATA](state) {
|
||||
|
@ -555,18 +570,23 @@ export const actions = {
|
|||
},
|
||||
|
||||
fetchRunsIfNeeded(config) {
|
||||
return (dispatch) => {
|
||||
return (dispatch, getState) => {
|
||||
const id = config.pipeline;
|
||||
const alwaysFetch = true;
|
||||
const runsData = getState().adminStore['runs'];
|
||||
const pagination = !runsData ? new PaginationHolder() : (runsData[id] || new PaginationHolder());
|
||||
const baseUrl = `${config.getAppURLBase()}/rest/organizations/jenkins` +
|
||||
`/pipelines/${config.pipeline}/runs/`;
|
||||
`/pipelines/${config.pipeline}/runs/?start=${pagination.getCurrentPageStart()}&limit=${pagination.pageSize}`;
|
||||
return dispatch(actions.fetchIfNeeded({
|
||||
url: baseUrl,
|
||||
id: config.pipeline,
|
||||
pageStart: pagination.getCurrentPageStart(),
|
||||
type: 'runs',
|
||||
}, {
|
||||
current: ACTION_TYPES.SET_CURRENT_RUN_DATA,
|
||||
general: ACTION_TYPES.SET_RUNS_DATA,
|
||||
clear: ACTION_TYPES.CLEAR_CURRENT_RUN_DATA,
|
||||
}));
|
||||
}, alwaysFetch));
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -592,14 +612,14 @@ export const actions = {
|
|||
* @param types TODO: what's this and what's in it?
|
||||
* @returns {Function}
|
||||
*/
|
||||
fetchIfNeeded(general, types) {
|
||||
fetchIfNeeded(general, types, alwaysFetch = false) {
|
||||
return (dispatch, getState) => {
|
||||
const data = getState().adminStore[general.type];
|
||||
dispatch({ type: types.clear });
|
||||
|
||||
const id = general.id;
|
||||
|
||||
if (!data || !data[id]) {
|
||||
if (alwaysFetch || !data || !data[id]) {
|
||||
return fetch(general.url, fetchOptions)
|
||||
.then(checkStatus)
|
||||
.then(parseJSON)
|
||||
|
@ -609,11 +629,13 @@ export const actions = {
|
|||
id,
|
||||
payload: json,
|
||||
type: types.current,
|
||||
pageStart: general.pageStart,
|
||||
});
|
||||
return dispatch({
|
||||
id,
|
||||
payload: json,
|
||||
type: types.general,
|
||||
pageStart: general.pageStart,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
export default class PaginationHolder {
|
||||
constructor(pageSize = 25) {
|
||||
this.currentPage = 0;
|
||||
this.pageSize = pageSize;
|
||||
this.currentData;
|
||||
this.hasMoreData = false;
|
||||
}
|
||||
|
||||
getTotalPages() {
|
||||
return this.currentData ? Math.floor(this.currentData.length / this.pageSize) : 0;
|
||||
}
|
||||
|
||||
appendData(pageStart, data) {
|
||||
// TODO figure out where it goes based on pageStart
|
||||
this.currentData = this.currentData || [];
|
||||
// NOTE: sometimes this array ref leaks across page holders
|
||||
// and might be updated twice because of redux oddities.
|
||||
// defend against duplicate additions
|
||||
if (this.currentData.length <= pageStart) {
|
||||
this.currentData = this.currentData.concat(data);
|
||||
}
|
||||
}
|
||||
|
||||
getCurrentPageStart() {
|
||||
return this.currentPage * this.pageSize;
|
||||
}
|
||||
}
|
|
@ -10,10 +10,10 @@
|
|||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
z-index: 1000;
|
||||
|
||||
|
||||
&.not-found {
|
||||
background: @blueocean-blue;
|
||||
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
background: url('./icons/kanagawa.svg') no-repeat bottom left;
|
||||
|
@ -29,7 +29,7 @@
|
|||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.message-box {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
|
@ -40,13 +40,18 @@
|
|||
padding: 2em;
|
||||
border-radius: .2em;
|
||||
box-shadow: 2px 2px 8px rgba(0, 0, 0, .1);
|
||||
|
||||
|
||||
.message {
|
||||
margin: 1em 0 2em 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-show-more {
|
||||
display: table;
|
||||
margin: 10px auto;
|
||||
}
|
||||
|
||||
.pipelines-table {
|
||||
th {
|
||||
width: 10%;
|
||||
|
|
Loading…
Reference in New Issue