diff --git a/scm-ui/ui-webapp/src/repos/sources/components/FileTree.tsx b/scm-ui/ui-webapp/src/repos/sources/components/FileTree.tsx index 150e4d1eb6..419171b301 100644 --- a/scm-ui/ui-webapp/src/repos/sources/components/FileTree.tsx +++ b/scm-ui/ui-webapp/src/repos/sources/components/FileTree.tsx @@ -7,7 +7,7 @@ import styled from "styled-components"; import { binder } from "@scm-manager/ui-extensions"; import { Repository, File } from "@scm-manager/ui-types"; import { ErrorNotification, Loading, Notification } from "@scm-manager/ui-components"; -import { getFetchSourcesFailure, isFetchSourcesPending, getSources } from "../modules/sources"; +import { getFetchSourcesFailure, isFetchSourcesPending, getSources, fetchSources } from "../modules/sources"; import FileTreeLeaf from "./FileTreeLeaf"; type Props = WithTranslation & { @@ -19,6 +19,8 @@ type Props = WithTranslation & { path: string; baseUrl: string; + updateSources: () => void; + // context props match: any; }; @@ -40,6 +42,13 @@ export function findParent(path: string) { } class FileTree extends React.Component { + componentDidUpdate(prevProps: Readonly, prevState: Readonly<{}>, snapshot?: any): void { + const { tree, updateSources } = this.props; + if (tree?._embedded?.children && tree._embedded.children.find(c => c.partialResult)) { + setTimeout(updateSources, 3000); + } + } + render() { const { error, loading, tree } = this.props; @@ -123,6 +132,14 @@ class FileTree extends React.Component { } } +const mapDispatchToProps = (dispatch: any, ownProps: Props) => { + const { repository, revision, path } = ownProps; + + const updateSources = () => dispatch(fetchSources(repository, revision, path, false)); + + return { updateSources }; +}; + const mapStateToProps = (state: any, ownProps: Props) => { const { repository, revision, path } = ownProps; @@ -141,5 +158,5 @@ const mapStateToProps = (state: any, ownProps: Props) => { export default compose( withRouter, - connect(mapStateToProps) + connect(mapStateToProps, mapDispatchToProps) )(withTranslation("repos")(FileTree)); diff --git a/scm-ui/ui-webapp/src/repos/sources/modules/sources.ts b/scm-ui/ui-webapp/src/repos/sources/modules/sources.ts index 7d2b8c94a7..e6ab2836b4 100644 --- a/scm-ui/ui-webapp/src/repos/sources/modules/sources.ts +++ b/scm-ui/ui-webapp/src/repos/sources/modules/sources.ts @@ -9,28 +9,29 @@ export const FETCH_SOURCES_PENDING = `${FETCH_SOURCES}_${types.PENDING_SUFFIX}`; export const FETCH_SOURCES_SUCCESS = `${FETCH_SOURCES}_${types.SUCCESS_SUFFIX}`; export const FETCH_SOURCES_FAILURE = `${FETCH_SOURCES}_${types.FAILURE_SUFFIX}`; -export function fetchSources(repository: Repository, revision: string, path: string) { - return fetchSourcesWithoutOptionalLoadingState(repository, revision, path, true); -} - -export function fetchSourcesWithoutOptionalLoadingState( +export function fetchSources( repository: Repository, revision: string, path: string, - dispatchLoading: boolean + initialLoad = true ) { - return function(dispatch: any) { - if (dispatchLoading) { + return function(dispatch: any, getState: () => any) { + const state = getState(); + if (isFetchSourcesPending(state, repository, revision, path) + || isUpdateSourcePending(state, repository, revision, path)) { + return; + } + + if (initialLoad) { dispatch(fetchSourcesPending(repository, revision, path)); + } else { + dispatch(updateSourcesPending(repository, revision, path)) } return apiClient .get(createUrl(repository, revision, path)) .then(response => response.json()) .then((sources: File) => { dispatch(fetchSourcesSuccess(repository, revision, path, sources)); - if (sources._embedded.children && sources._embedded.children.find(c => c.partialResult)) { - setTimeout(() => dispatch(fetchSourcesWithoutOptionalLoadingState(repository, revision, path, false)), 3000); - } }) .catch(err => { dispatch(fetchSourcesFailure(repository, revision, path, err)); @@ -56,10 +57,17 @@ export function fetchSourcesPending(repository: Repository, revision: string, pa }; } +export function updateSourcesPending(repository: Repository, revision: string, path: string): Action { + return { + type: "UPDATE_PENDING", + itemId: createItemId(repository, revision, path) + }; +} + export function fetchSourcesSuccess(repository: Repository, revision: string, path: string, sources: File) { return { type: FETCH_SOURCES_SUCCESS, - payload: sources, + payload: { updatePending: false, sources }, itemId: createItemId(repository, revision, path) }; } @@ -91,6 +99,11 @@ export default function reducer( ...state, [action.itemId]: action.payload }; + } else if (action.itemId && action.type === "UPDATE_PENDING") { + return { + ...state, + [action.itemId]: { updatePending: true }} + ; } return state; } @@ -113,13 +126,17 @@ export function getSources( path: string ): File | null | undefined { if (state.sources) { - return state.sources[createItemId(repository, revision, path)]; + return state.sources[createItemId(repository, revision, path)]?.sources; } return null; } export function isFetchSourcesPending(state: any, repository: Repository, revision: string, path: string): boolean { - return isPending(state, FETCH_SOURCES, createItemId(repository, revision, path)); + return state && isPending(state, FETCH_SOURCES, createItemId(repository, revision, path)); +} + +function isUpdateSourcePending(state: any, repository: Repository, revision: string, path: string): boolean { + return state?.sources[createItemId(repository, revision, path)]?.updatePending; } export function getFetchSourcesFailure(