mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Code] Implement the clone/index/delete progress design for admin page project lists (#27952)
This commit is contained in:
parent
e7f9090060
commit
3e0cf0518e
11 changed files with 185 additions and 37 deletions
|
@ -15,6 +15,7 @@ export const fetchReposFailed = createAction<Error>('FETCH REPOS FAILED');
|
|||
|
||||
export const deleteRepo = createAction<string>('DELETE REPOS');
|
||||
export const deleteRepoSuccess = createAction<string>('DELETE REPOS SUCCESS');
|
||||
export const deleteRepoFinished = createAction<string>('DELETE REPOS FINISHED');
|
||||
export const deleteRepoFailed = createAction<Error>('DELETE REPOS FAILED');
|
||||
|
||||
export const indexRepo = createAction<string>('INDEX REPOS');
|
||||
|
|
|
@ -13,3 +13,13 @@ export const loadStatusFailed = createAction<string>('LOAD STATUS FAILED');
|
|||
export const loadRepo = createAction<string>('LOAD REPO');
|
||||
export const loadRepoSuccess = createAction<any>('LOAD REPO SUCCESS');
|
||||
export const loadRepoFailed = createAction<any>('LOAD REPO FAILED');
|
||||
|
||||
export interface RepoProgress {
|
||||
repoUri: string;
|
||||
progress: number;
|
||||
cloneProgress?: any;
|
||||
}
|
||||
|
||||
export const updateCloneProgress = createAction<RepoProgress>('UPDATE CLONE PROGRESS');
|
||||
export const updateIndexProgress = createAction<RepoProgress>('UPDATE INDEX PROGRESS');
|
||||
export const updateDeleteProgress = createAction<RepoProgress>('UPDATE DELETE PROGRESS');
|
||||
|
|
|
@ -4,7 +4,15 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiPanel, EuiText, EuiTextColor } from '@elastic/eui';
|
||||
import {
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiIcon,
|
||||
EuiPanel,
|
||||
EuiProgress,
|
||||
EuiText,
|
||||
EuiTextColor,
|
||||
} from '@elastic/eui';
|
||||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
@ -12,37 +20,42 @@ import { Link } from 'react-router-dom';
|
|||
import styled from 'styled-components';
|
||||
import { Repository } from 'x-pack/plugins/code/model';
|
||||
import { deleteRepo, indexRepo, initRepoCommand } from '../../actions';
|
||||
import { RepoState, RepoStatus } from '../../reducers/status';
|
||||
|
||||
const Footer = styled.div``;
|
||||
|
||||
const stateColor = {
|
||||
[RepoState.CLONING]: 'secondary',
|
||||
[RepoState.DELETING]: 'accent',
|
||||
[RepoState.INDEXING]: 'primary',
|
||||
};
|
||||
|
||||
class CodeProjectItem extends React.PureComponent<{
|
||||
project: Repository;
|
||||
isDeleting?: boolean;
|
||||
isIndexing?: boolean;
|
||||
isCloning?: boolean;
|
||||
isAdmin: boolean;
|
||||
status: any;
|
||||
status: RepoStatus;
|
||||
deleteRepo: (uri: string) => void;
|
||||
indexRepo: (uri: string) => void;
|
||||
initRepoCommand: (uri: string) => void;
|
||||
}> {
|
||||
public render() {
|
||||
const { isDeleting, project, isIndexing, isCloning } = this.props;
|
||||
const { project, status } = this.props;
|
||||
const { name, org, nextUpdateTimestamp, uri } = project;
|
||||
const onClickDelete = () => this.props.deleteRepo(uri);
|
||||
const onClickIndex = () => this.props.indexRepo(uri);
|
||||
let footer = null;
|
||||
if (isDeleting) {
|
||||
footer = <Footer>DELETING...</Footer>;
|
||||
} else if (isIndexing) {
|
||||
footer = <Footer>INDEXING...</Footer>;
|
||||
} else if (isCloning) {
|
||||
footer = <Footer />;
|
||||
} else {
|
||||
if (!status || status.state === RepoState.READY) {
|
||||
footer = <Footer>LAST UPDATED: {moment(nextUpdateTimestamp).fromNow()}</Footer>;
|
||||
} else if (status.state === RepoState.DELETING) {
|
||||
footer = <Footer>DELETING...</Footer>;
|
||||
} else if (status.state === RepoState.INDEXING) {
|
||||
footer = <Footer>INDEXING...</Footer>;
|
||||
} else if (status.state === RepoState.CLONING) {
|
||||
footer = <Footer>CLONING...</Footer>;
|
||||
}
|
||||
return (
|
||||
<EuiPanel>
|
||||
<EuiPanel style={{ position: 'relative', marginBottom: '8px' }}>
|
||||
{this.renderProgress()}
|
||||
<EuiFlexGroup alignItems="center" justifyContent="flexStart">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon type="starEmpty" color="subdued" />
|
||||
|
@ -98,6 +111,26 @@ class CodeProjectItem extends React.PureComponent<{
|
|||
</EuiPanel>
|
||||
);
|
||||
}
|
||||
|
||||
private renderProgress() {
|
||||
const { status } = this.props;
|
||||
if (status && status.state !== RepoState.READY) {
|
||||
const color = stateColor[status.state] as 'primary' | 'secondary' | 'accent';
|
||||
if (status.progress! > 0) {
|
||||
return (
|
||||
<EuiProgress
|
||||
max={100}
|
||||
value={status.progress}
|
||||
size="xs"
|
||||
color={color}
|
||||
position="absolute"
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return <EuiProgress size="xs" color={color} position="absolute" />;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
|
|
|
@ -53,7 +53,7 @@ class CodeProjectTab extends React.PureComponent<
|
|||
};
|
||||
|
||||
public render() {
|
||||
const { projects, isAdmin } = this.props;
|
||||
const { projects, isAdmin, status } = this.props;
|
||||
const projectsCount = projects.length;
|
||||
const modal = this.state.showImportProjectModal && (
|
||||
<EuiModal onClose={this.closeModal}>
|
||||
|
|
|
@ -87,4 +87,5 @@ const mapStateToProps = (state: RootState) => ({
|
|||
isNotFound: state.file.isNotFound,
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
export const Main = connect(mapStateToProps)(CodeMain);
|
||||
|
|
|
@ -10,7 +10,7 @@ import { Repository } from '../../model';
|
|||
|
||||
import { RepoConfigs } from '../../model/workspace';
|
||||
import {
|
||||
deleteRepoSuccess,
|
||||
deleteRepoFinished,
|
||||
fetchRepoConfigSuccess,
|
||||
fetchRepos,
|
||||
fetchReposFailed,
|
||||
|
@ -68,6 +68,7 @@ export const repository = handleActions(
|
|||
},
|
||||
[String(deleteRepoSuccess)]: (state: RepositoryState, action: Action<any>) =>
|
||||
produce<RepositoryState>(state, (draft: RepositoryState) => {
|
||||
feature/merge-code
|
||||
draft.repositories = state.repositories.filter(repo => repo.uri !== action.payload);
|
||||
}),
|
||||
[String(importRepo)]: (state: RepositoryState) =>
|
||||
|
|
|
@ -6,10 +6,30 @@
|
|||
|
||||
import produce from 'immer';
|
||||
import { handleActions } from 'redux-actions';
|
||||
import { loadStatus, loadStatusFailed, loadStatusSuccess } from '../actions/status';
|
||||
import {
|
||||
loadStatus,
|
||||
loadStatusFailed,
|
||||
loadStatusSuccess,
|
||||
updateCloneProgress,
|
||||
updateDeleteProgress,
|
||||
updateIndexProgress,
|
||||
} from '../actions/status';
|
||||
|
||||
export enum RepoState {
|
||||
CLONING,
|
||||
DELETING,
|
||||
INDEXING,
|
||||
READY,
|
||||
}
|
||||
|
||||
export interface RepoStatus {
|
||||
state: RepoState;
|
||||
progress?: number;
|
||||
cloneProgress?: any;
|
||||
}
|
||||
|
||||
export interface StatusState {
|
||||
status: { [key: string]: any };
|
||||
status: { [key: string]: RepoStatus };
|
||||
loading: boolean;
|
||||
error?: Error;
|
||||
}
|
||||
|
@ -27,7 +47,10 @@ export const status = handleActions(
|
|||
}),
|
||||
[String(loadStatusSuccess)]: (state: StatusState, action: any) =>
|
||||
produce<StatusState>(state, draft => {
|
||||
draft.status[action.payload.repoUri] = action.payload.status;
|
||||
draft.status[action.payload.repoUri] = {
|
||||
...action.payload.status,
|
||||
state: RepoState.READY,
|
||||
};
|
||||
draft.loading = false;
|
||||
}),
|
||||
[String(loadStatusFailed)]: (state: StatusState, action: any) =>
|
||||
|
@ -35,6 +58,28 @@ export const status = handleActions(
|
|||
draft.loading = false;
|
||||
draft.error = action.payload;
|
||||
}),
|
||||
[String(updateCloneProgress)]: (state: StatusState, action: any) =>
|
||||
produce<StatusState>(state, draft => {
|
||||
draft.status[action.payload.repoUri] = {
|
||||
...action.payload,
|
||||
state: RepoState.CLONING,
|
||||
};
|
||||
}),
|
||||
[String(updateIndexProgress)]: (state: StatusState, action: any) =>
|
||||
produce<StatusState>(state, draft => {
|
||||
const progress = action.payload.progress;
|
||||
draft.status[action.payload.repoUri] = {
|
||||
...action.payload,
|
||||
state: progress < 100 ? RepoState.INDEXING : RepoState.READY,
|
||||
};
|
||||
}),
|
||||
[String(updateDeleteProgress)]: (state: StatusState, action: any) =>
|
||||
produce<StatusState>(state, draft => {
|
||||
draft.status[action.payload.repoUri] = {
|
||||
...action.payload,
|
||||
state: RepoState.DELETING,
|
||||
};
|
||||
}),
|
||||
},
|
||||
initialState
|
||||
);
|
||||
|
|
|
@ -27,7 +27,7 @@ import {
|
|||
watchInitRepoCmd,
|
||||
} from './repository';
|
||||
import { watchDocumentSearch, watchRepositorySearch, watchSearchRouteChange } from './search';
|
||||
import { watchRepoCloneSuccess } from './status';
|
||||
import { watchRepoCloneSuccess, watchRepoDeleteFinished } from './status';
|
||||
import { watchLoadStructure } from './structure';
|
||||
import { watchLoadUserConfig } from './user';
|
||||
|
||||
|
@ -57,6 +57,7 @@ export function* rootSaga() {
|
|||
yield fork(watchLoadBlame);
|
||||
yield fork(watchBlame);
|
||||
yield fork(watchRepoCloneSuccess);
|
||||
yield fork(watchRepoDeleteFinished);
|
||||
yield fork(watchLoadLanguageServers);
|
||||
yield fork(watchInstallLanguageServer);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@ import {
|
|||
indexRepoSuccess,
|
||||
initRepoCommand,
|
||||
loadUserConfig,
|
||||
updateCloneProgress,
|
||||
updateDeleteProgress,
|
||||
updateIndexProgress,
|
||||
} from '../actions';
|
||||
import { loadLanguageServers } from '../actions/language_server';
|
||||
import { history } from '../utils/url';
|
||||
|
@ -56,6 +59,12 @@ function* handleDeleteRepo(action: Action<string>) {
|
|||
try {
|
||||
yield call(requestDeleteRepo, action.payload || '');
|
||||
yield put(deleteRepoSuccess(action.payload || ''));
|
||||
yield put(
|
||||
updateDeleteProgress({
|
||||
repoUri: action.payload as string,
|
||||
progress: 0,
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
yield put(deleteRepoFailed(err));
|
||||
}
|
||||
|
@ -65,6 +74,12 @@ function* handleIndexRepo(action: Action<string>) {
|
|||
try {
|
||||
yield call(requestIndexRepo, action.payload || '');
|
||||
yield put(indexRepoSuccess(action.payload || ''));
|
||||
yield put(
|
||||
updateIndexProgress({
|
||||
repoUri: action.payload as string,
|
||||
progress: 0,
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
yield put(indexRepoFailed(err));
|
||||
}
|
||||
|
@ -82,6 +97,12 @@ function* handleImportRepo(action: Action<string>) {
|
|||
try {
|
||||
const data = yield call(requestImportRepo, action.payload || '');
|
||||
yield put(importRepoSuccess(data));
|
||||
yield put(
|
||||
updateCloneProgress({
|
||||
repoUri: action.payload as string,
|
||||
progress: 0,
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
yield put(importRepoFailed(err));
|
||||
}
|
||||
|
|
|
@ -6,10 +6,9 @@
|
|||
|
||||
import { Action } from 'redux-actions';
|
||||
import { put, select, takeEvery } from 'redux-saga/effects';
|
||||
|
||||
import { WorkerReservedProgress } from '../../model';
|
||||
import { Match, routeChange } from '../actions';
|
||||
import { loadStatusSuccess } from '../actions/status';
|
||||
import { deleteRepoFinished, Match, routeChange, updateDeleteProgress } from '../actions';
|
||||
import { loadStatusSuccess } from '../actions';
|
||||
import * as ROUTES from '../components/routes';
|
||||
import { RootState } from '../reducers';
|
||||
|
||||
|
@ -19,6 +18,9 @@ const pattern = (action: Action<any>) =>
|
|||
action.type === String(loadStatusSuccess) &&
|
||||
action.payload!.status.progress === WorkerReservedProgress.COMPLETED;
|
||||
|
||||
const deletePattern = (action: Action<any>) =>
|
||||
action.type === String(updateDeleteProgress) && action.payload.progress === 100;
|
||||
|
||||
function* handleRepoCloneSuccess() {
|
||||
const match: Match = yield select(matchSelector);
|
||||
if (match.path === ROUTES.MAIN || match.path === ROUTES.MAIN_ROOT) {
|
||||
|
@ -29,3 +31,11 @@ function* handleRepoCloneSuccess() {
|
|||
export function* watchRepoCloneSuccess() {
|
||||
yield takeEvery(pattern, handleRepoCloneSuccess);
|
||||
}
|
||||
|
||||
function* handleRepoDeleteFinished(action: any) {
|
||||
yield put(deleteRepoFinished(action.payload.repoUri));
|
||||
}
|
||||
|
||||
export function* watchRepoDeleteFinished() {
|
||||
yield takeEvery(deletePattern, handleRepoDeleteFinished);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,12 @@ import io from 'socket.io-client';
|
|||
import chrome from 'ui/chrome';
|
||||
|
||||
import { SocketKind } from '../model';
|
||||
import { loadStatusSuccess } from './actions';
|
||||
import {
|
||||
loadStatusSuccess,
|
||||
updateCloneProgress,
|
||||
updateDeleteProgress,
|
||||
updateIndexProgress,
|
||||
} from './actions';
|
||||
import { installLanguageServerSuccess } from './actions/language_server';
|
||||
|
||||
export function bindSocket(store: Store) {
|
||||
|
@ -18,26 +23,46 @@ export function bindSocket(store: Store) {
|
|||
|
||||
socket.on(SocketKind.CLONE_PROGRESS, (data: any) => {
|
||||
const { repoUri, progress, cloneProgress } = data;
|
||||
store.dispatch(
|
||||
loadStatusSuccess({
|
||||
repoUri,
|
||||
status: {
|
||||
uri: repoUri,
|
||||
if (progress === 100) {
|
||||
store.dispatch(
|
||||
loadStatusSuccess({
|
||||
repoUri,
|
||||
status: {
|
||||
uri: repoUri,
|
||||
progress,
|
||||
cloneProgress,
|
||||
},
|
||||
})
|
||||
);
|
||||
} else {
|
||||
store.dispatch(
|
||||
updateCloneProgress({
|
||||
repoUri,
|
||||
progress,
|
||||
cloneProgress,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on(SocketKind.INDEX_PROGRESS, (data: any) => {
|
||||
const { repoUri, progress } = data;
|
||||
store.dispatch(
|
||||
updateIndexProgress({
|
||||
progress,
|
||||
repoUri,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
socket.on(SocketKind.INDEX_PROGRESS, (data: any) => {
|
||||
// const { repoUri, progress } = data;
|
||||
// TODO(qianliang): distribute index progress update actions to store.
|
||||
});
|
||||
|
||||
socket.on(SocketKind.DELETE_PROGRESS, (data: any) => {
|
||||
// const { repoUri, progress } = data;
|
||||
// TODO(qianliang): distribute delete progress update actions to store.
|
||||
const { repoUri, progress } = data;
|
||||
store.dispatch(
|
||||
updateDeleteProgress({
|
||||
progress,
|
||||
repoUri,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
socket.on(SocketKind.INSTALL_PROGRESS, (data: any) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue