From 20c00e7222e30474d97e74958244672983b2715f Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Tue, 7 Jan 2020 10:30:46 +0100 Subject: [PATCH 01/29] create new codeSection // add actionBar to codeSection // change routes for sources and changesets --- scm-ui/ui-components/src/BranchSelector.tsx | 2 +- scm-ui/ui-plugins/bin/ui-plugins.js | 0 scm-ui/ui-plugins/package.json | 2 +- scm-ui/ui-webapp/public/locales/de/repos.json | 8 +- scm-ui/ui-webapp/public/locales/en/repos.json | 8 +- scm-ui/ui-webapp/public/locales/es/repos.json | 8 +- .../components/CodeViewSwitcher.tsx | 38 +++++ .../containers/CodeSectionOverview.tsx | 155 ++++++++++++++++++ .../repos/components/RepositoryNavLink.tsx | 1 + .../src/repos/containers/Changesets.tsx | 11 +- .../src/repos/containers/ChangesetsRoot.tsx | 16 +- .../src/repos/containers/RepositoryRoot.tsx | 90 +++------- .../src/repos/sources/containers/Sources.tsx | 38 +---- 13 files changed, 251 insertions(+), 126 deletions(-) mode change 100644 => 100755 scm-ui/ui-plugins/bin/ui-plugins.js create mode 100644 scm-ui/ui-webapp/src/repos/codeSection/components/CodeViewSwitcher.tsx create mode 100644 scm-ui/ui-webapp/src/repos/codeSection/containers/CodeSectionOverview.tsx diff --git a/scm-ui/ui-components/src/BranchSelector.tsx b/scm-ui/ui-components/src/BranchSelector.tsx index 970417fc3c..95cb95189e 100644 --- a/scm-ui/ui-components/src/BranchSelector.tsx +++ b/scm-ui/ui-components/src/BranchSelector.tsx @@ -6,7 +6,7 @@ import DropDown from "./forms/DropDown"; type Props = { branches: Branch[]; - selected: (branch?: Branch) => void; + selected: (branch: Branch) => void; selectedBranch?: string; label: string; disabled?: boolean; diff --git a/scm-ui/ui-plugins/bin/ui-plugins.js b/scm-ui/ui-plugins/bin/ui-plugins.js old mode 100644 new mode 100755 diff --git a/scm-ui/ui-plugins/package.json b/scm-ui/ui-plugins/package.json index d48bdbb3fd..1fd6de4014 100644 --- a/scm-ui/ui-plugins/package.json +++ b/scm-ui/ui-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@scm-manager/ui-plugins", - "version": "2.0.0-11", + "version": "2.0.0-SNAPSHOT", "license": "BSD-3-Clause", "bin": { "ui-plugins": "./bin/ui-plugins.js" diff --git a/scm-ui/ui-webapp/public/locales/de/repos.json b/scm-ui/ui-webapp/public/locales/de/repos.json index 91e37dee25..b9c26b692e 100644 --- a/scm-ui/ui-webapp/public/locales/de/repos.json +++ b/scm-ui/ui-webapp/public/locales/de/repos.json @@ -30,8 +30,7 @@ "navigationLabel": "Repository Navigation", "informationNavLink": "Informationen", "branchesNavLink": "Branches", - "historyNavLink": "Commits", - "sourcesNavLink": "Sources", + "sourcesNavLink": "Code", "settingsNavLink": "Einstellungen", "generalNavLink": "Generell", "permissionsNavLink": "Berechtigungen" @@ -69,6 +68,11 @@ "sources": "Sources", "defaultTag": "Default" }, + "code": { + "sources": "Sources", + "commits": "Commits", + "branchSelector": "Branches" + }, "changesets": { "errorTitle": "Fehler", "errorSubtitle": "Changesets konnten nicht abgerufen werden", diff --git a/scm-ui/ui-webapp/public/locales/en/repos.json b/scm-ui/ui-webapp/public/locales/en/repos.json index 078436a599..c493e9605e 100644 --- a/scm-ui/ui-webapp/public/locales/en/repos.json +++ b/scm-ui/ui-webapp/public/locales/en/repos.json @@ -30,8 +30,7 @@ "navigationLabel": "Repository Navigation", "informationNavLink": "Information", "branchesNavLink": "Branches", - "historyNavLink": "Commits", - "sourcesNavLink": "Sources", + "sourcesNavLink": "Code", "settingsNavLink": "Settings", "generalNavLink": "General", "permissionsNavLink": "Permissions" @@ -69,6 +68,11 @@ "sources": "Sources", "defaultTag": "Default" }, + "code": { + "sources": "Sources", + "commits": "Commits", + "branchSelector": "Branches" + }, "changesets": { "errorTitle": "Error", "errorSubtitle": "Could not fetch changesets", diff --git a/scm-ui/ui-webapp/public/locales/es/repos.json b/scm-ui/ui-webapp/public/locales/es/repos.json index 48fb76be0f..71169ae0d2 100644 --- a/scm-ui/ui-webapp/public/locales/es/repos.json +++ b/scm-ui/ui-webapp/public/locales/es/repos.json @@ -30,8 +30,7 @@ "navigationLabel": "Menú de repositorio", "informationNavLink": "Información", "branchesNavLink": "Ramas", - "historyNavLink": "Commits", - "sourcesNavLink": "Fuentes", + "sourcesNavLink": "Código", "settingsNavLink": "Ajustes", "generalNavLink": "General", "permissionsNavLink": "Permisos" @@ -69,6 +68,11 @@ "sources": "Fuentes", "defaultTag": "Por defecto" }, + "code": { + "sources": "Sources", + "commits": "Commits", + "branchSelector": "Branches" + }, "changesets": { "errorTitle": "Error", "errorSubtitle": "No se han podido recuperar los changesets", diff --git a/scm-ui/ui-webapp/src/repos/codeSection/components/CodeViewSwitcher.tsx b/scm-ui/ui-webapp/src/repos/codeSection/components/CodeViewSwitcher.tsx new file mode 100644 index 0000000000..d7fa275dd7 --- /dev/null +++ b/scm-ui/ui-webapp/src/repos/codeSection/components/CodeViewSwitcher.tsx @@ -0,0 +1,38 @@ +import React, { FC } from "react"; +import styled from "styled-components"; +import Button from "@scm-manager/ui-components/src/buttons/Button"; +import ButtonAddons from "@scm-manager/ui-components/src/buttons/ButtonAddons"; +import { useTranslation } from "react-i18next"; + +const SmallButton = styled(Button).attrs(() => ({}))` + border-radius: 4px; + font-size: 0.75rem; + font-weight: 500; +`; + +type Props = { + url: string; +}; + +const CodeViewSwitcher: FC = ({ url }) => { + const [t] = useTranslation("repos"); + + return ( + + + + + ); +}; + +export default CodeViewSwitcher; diff --git a/scm-ui/ui-webapp/src/repos/codeSection/containers/CodeSectionOverview.tsx b/scm-ui/ui-webapp/src/repos/codeSection/containers/CodeSectionOverview.tsx new file mode 100644 index 0000000000..3d8b69cfb5 --- /dev/null +++ b/scm-ui/ui-webapp/src/repos/codeSection/containers/CodeSectionOverview.tsx @@ -0,0 +1,155 @@ +import React from "react"; +import styled from "styled-components"; +import { Route, withRouter, RouteComponentProps } from "react-router-dom"; +import ChangesetView from "../../containers/ChangesetView"; +import Sources from "../../sources/containers/Sources"; +import SourceExtensions from "../../sources/containers/SourceExtensions"; +import ChangesetsRoot from "../../containers/ChangesetsRoot"; +import { Repository, Branch } from "@scm-manager/ui-types"; +import { BranchSelector, Level } from "@scm-manager/ui-components"; +import CodeViewSwitcher from "../components/CodeViewSwitcher"; +import { compose } from "redux"; +import { WithTranslation, withTranslation } from "react-i18next"; +import { connect } from "react-redux"; +import { + fetchBranches, + getBranches, + getFetchBranchesFailure, + isFetchBranchesPending +} from "../../branches/modules/branches"; + +type Props = RouteComponentProps & + WithTranslation & { + repository: Repository; + baseUrl: string; + + // State props + branches: Branch[]; + loading: boolean; + error: Error; + selectedView: string; + selectedBranch: string; + + // Dispatch props + fetchBranches: (p: Repository) => void; + }; + +const CodeSectionActionBar = styled.div.attrs(() => ({}))` + background-color: whitesmoke; + border: 1px solid #dbdbdb; + border-radius: 4px; + color: #363636; + font-size: 1.25em; + font-weight: 300; + line-height: 1.25; + padding: 0.5em 0.75em; + margin-bottom: 1em; +`; + +class CodeSectionOverview extends React.Component { + componentDidMount() { + const { repository } = this.props; + this.props.fetchBranches(repository); + } + + findSelectedBranch = () => { + const { selectedBranch, branches } = this.props; + return branches?.find((branch: Branch) => branch.name === selectedBranch); + }; + + branchSelected = (branch?: Branch) => { + let url; + if (branch) { + url = `${this.props.baseUrl}/${this.props.selectedView}/${encodeURIComponent(branch.name)}`; + } else { + url = `${this.props.baseUrl}/${this.props.selectedView}/`; + } + this.props.history.push(url); + }; + + render() { + const { repository, baseUrl, branches, t } = this.props; + const url = baseUrl; + + return ( +
+ + 0 && ( + { + this.branchSelected(b); + }} + /> + ) + } + right={} + /> + + } /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> +
+ ); + } +} + +const mapDispatchToProps = dispatch => { + return { + fetchBranches: (repo: Repository) => { + dispatch(fetchBranches(repo)); + } + }; +}; + +const mapStateToProps = (state: any, ownProps: Props) => { + const { repository, location } = ownProps; + const loading = isFetchBranchesPending(state, repository); + const error = getFetchBranchesFailure(state, repository); + const branches = getBranches(state, repository); + const selectedView = decodeURIComponent(location.pathname.split("/")[5]); + const selectedBranch = decodeURIComponent(location.pathname.split("/")[6]); + + return { + loading, + error, + branches, + selectedView, + selectedBranch + }; +}; + +export default compose( + withRouter, + withTranslation("repos"), + connect(mapStateToProps, mapDispatchToProps) +)(CodeSectionOverview); diff --git a/scm-ui/ui-webapp/src/repos/components/RepositoryNavLink.tsx b/scm-ui/ui-webapp/src/repos/components/RepositoryNavLink.tsx index 4f501853a9..34e4c47ab4 100644 --- a/scm-ui/ui-webapp/src/repos/components/RepositoryNavLink.tsx +++ b/scm-ui/ui-webapp/src/repos/components/RepositoryNavLink.tsx @@ -9,6 +9,7 @@ type Props = { linkName: string; activeWhenMatch?: (route: any) => boolean; activeOnlyWhenExact: boolean; + icon?: string; }; /** diff --git a/scm-ui/ui-webapp/src/repos/containers/Changesets.tsx b/scm-ui/ui-webapp/src/repos/containers/Changesets.tsx index 05cebb7a01..3b7b85d1b5 100644 --- a/scm-ui/ui-webapp/src/repos/containers/Changesets.tsx +++ b/scm-ui/ui-webapp/src/repos/containers/Changesets.tsx @@ -93,7 +93,7 @@ class Changesets extends React.Component { }; } -const mapDispatchToProps = dispatch => { +const mapDispatchToProps = (dispatch: any) => { return { fetchChangesets: (repo: Repository, branch: Branch, page: number) => { dispatch(fetchChangesets(repo, branch, page)); @@ -118,11 +118,4 @@ const mapStateToProps = (state: any, ownProps: Props) => { }; }; -export default compose( - withTranslation("repos"), - withRouter, - connect( - mapStateToProps, - mapDispatchToProps - ) -)(Changesets); +export default compose(withTranslation("repos"), withRouter, connect(mapStateToProps, mapDispatchToProps))(Changesets); diff --git a/scm-ui/ui-webapp/src/repos/containers/ChangesetsRoot.tsx b/scm-ui/ui-webapp/src/repos/containers/ChangesetsRoot.tsx index 50658ab26b..4eaeaf26eb 100644 --- a/scm-ui/ui-webapp/src/repos/containers/ChangesetsRoot.tsx +++ b/scm-ui/ui-webapp/src/repos/containers/ChangesetsRoot.tsx @@ -15,10 +15,8 @@ import { type Props = WithTranslation & { repository: Repository; - baseUrl: string; selected: string; - baseUrlWithBranch: string; - baseUrlWithoutBranch: string; + baseUrl: string; // State props branches: Branch[]; @@ -66,9 +64,9 @@ class ChangesetsRoot extends React.Component { branchSelected = (branch?: Branch) => { let url; if (branch) { - url = `${this.props.baseUrlWithBranch}/${encodeURIComponent(branch.name)}/changesets/`; + url = `${this.props.baseUrl}/${encodeURIComponent(branch.name)}`; } else { - url = `${this.props.baseUrlWithoutBranch}/`; + url = `${this.props.baseUrl}/`; } this.props.history.push(url); }; @@ -99,7 +97,6 @@ class ChangesetsRoot extends React.Component { return (
- {this.renderBranchSelector()} changesets} />
); @@ -125,7 +122,7 @@ class ChangesetsRoot extends React.Component { }; } -const mapDispatchToProps = dispatch => { +const mapDispatchToProps = (dispatch: any) => { return { fetchBranches: (repo: Repository) => { dispatch(fetchBranches(repo)); @@ -151,8 +148,5 @@ const mapStateToProps = (state: any, ownProps: Props) => { export default compose( withRouter, withTranslation("repos"), - connect( - mapStateToProps, - mapDispatchToProps - ) + connect(mapStateToProps, mapDispatchToProps) )(ChangesetsRoot); diff --git a/scm-ui/ui-webapp/src/repos/containers/RepositoryRoot.tsx b/scm-ui/ui-webapp/src/repos/containers/RepositoryRoot.tsx index d65f2c9892..a0a8ad8b95 100644 --- a/scm-ui/ui-webapp/src/repos/containers/RepositoryRoot.tsx +++ b/scm-ui/ui-webapp/src/repos/containers/RepositoryRoot.tsx @@ -14,13 +14,10 @@ import CreateBranch from "../branches/containers/CreateBranch"; import Permissions from "../permissions/containers/Permissions"; import EditRepoNavLink from "../components/EditRepoNavLink"; import BranchRoot from "../branches/containers/BranchRoot"; -import ChangesetsRoot from "./ChangesetsRoot"; -import ChangesetView from "./ChangesetView"; import PermissionsNavLink from "../components/PermissionsNavLink"; -import Sources from "../sources/containers/Sources"; import RepositoryNavLink from "../components/RepositoryNavLink"; import { getLinks, getRepositoriesLink } from "../../modules/indexResource"; -import SourceExtensions from "../sources/containers/SourceExtensions"; +import CodeSectionOverview from "../codeSection/containers/CodeSectionOverview"; type Props = WithTranslation & { namespace: string; @@ -62,16 +59,30 @@ class RepositoryRoot extends React.Component { return route.location.pathname.match(regex); }; - matchesChangesets = (route: any) => { + matchesCode = (route: any) => { const url = this.matchedUrl(); - const regex = new RegExp(`${url}(/branch)?/?[^/]*/changesets?.*`); + const regex = new RegExp(`${url}(/code)/.*`); return route.location.pathname.match(regex); }; - matchesSources = (route: any) => { - const url = this.matchedUrl(); - const regex = new RegExp(`${url}(/sources|/sourceext)/.*`); - return route.location.pathname.match(regex); + getCodeLinkname = () => { + const { repository } = this.props; + if (repository?._links?.sources) { + return "sources"; + } + if (repository?._links?.changesets) { + return "changesets"; + } + return ""; + }; + + evaluateDestinationForCodeLink = () => { + const { repository } = this.props; + let url = `${this.matchedUrl()}/code`; + if (repository?._links?.sources) { + return `${url}/sources/`; + } + return `${url}/changesets`; }; render() { @@ -117,44 +128,9 @@ class RepositoryRoot extends React.Component { )} /> - } /> } - /> - } - /> - } - /> - } - /> - ( - - )} - /> - ( - - )} + path={`${url}/code`} + render={() => } /> { /> - @@ -246,7 +213,4 @@ const mapDispatchToProps = dispatch => { }; }; -export default connect( - mapStateToProps, - mapDispatchToProps -)(withTranslation("repos")(RepositoryRoot)); +export default connect(mapStateToProps, mapDispatchToProps)(withTranslation("repos")(RepositoryRoot)); diff --git a/scm-ui/ui-webapp/src/repos/sources/containers/Sources.tsx b/scm-ui/ui-webapp/src/repos/sources/containers/Sources.tsx index bdf654b08a..8b75cbe903 100644 --- a/scm-ui/ui-webapp/src/repos/sources/containers/Sources.tsx +++ b/scm-ui/ui-webapp/src/repos/sources/containers/Sources.tsx @@ -13,7 +13,7 @@ import { } from "../../branches/modules/branches"; import { compose } from "redux"; import Content from "./Content"; -import {fetchSources, getSources, isDirectory} from "../modules/sources"; +import { fetchSources, getSources, isDirectory } from "../modules/sources"; type Props = WithTranslation & { repository: Repository; @@ -63,8 +63,6 @@ class Sources extends React.Component { if (prevProps.revision !== revision || prevProps.path !== path) { fetchSources(repository, this.decodeRevision(revision), path); } - - this.redirectToDefaultBranch(); } decodeRevision = (revision: string) => { @@ -122,7 +120,6 @@ class Sources extends React.Component { if (currentFileIsDirectory) { return (
- {this.renderBranchSelector()} {this.renderBreadcrumb()}
@@ -132,26 +129,6 @@ class Sources extends React.Component { } } - renderBranchSelector = () => { - const { branches, revision, t } = this.props; - - if (branches) { - return ( -
- { - this.branchSelected(b); - }} - /> -
- ); - } - return null; - }; - renderBreadcrumb = () => { const { revision, path, baseUrl, branches, sources, repository } = this.props; const { selectedBranch } = this.state; @@ -163,9 +140,7 @@ class Sources extends React.Component { path={path} baseUrl={baseUrl} branch={selectedBranch} - defaultBranch={ - branches && branches.filter(b => b.defaultBranch === true)[0] - } + defaultBranch={branches && branches.filter(b => b.defaultBranch === true)[0]} sources={sources} /> ); @@ -207,11 +182,4 @@ const mapDispatchToProps = dispatch => { }; }; -export default compose( - withTranslation("repos"), - withRouter, - connect( - mapStateToProps, - mapDispatchToProps - ) -)(Sources); +export default compose(withTranslation("repos"), withRouter, connect(mapStateToProps, mapDispatchToProps))(Sources); From c73e85e3d663f209cd9d6a9ac5ecc698ba76cb23 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 8 Jan 2020 10:31:43 +0100 Subject: [PATCH 02/29] fix routes for sources and changesets // fix typing errors --- .../src/main/js/RepositoryConfig.tsx | 29 +++-- scm-ui/ui-components/src/BranchSelector.tsx | 93 ++++--------- .../src/repos/changesets/changesets.ts | 2 +- .../repos/branches/containers/BranchRoot.tsx | 4 +- .../branches/containers/CreateBranch.tsx | 16 +-- .../components/CodeViewSwitcher.tsx | 14 +- ...deSectionOverview.tsx => CodeOverview.tsx} | 63 ++++----- .../src/repos/containers/Changesets.tsx | 1 - .../src/repos/containers/ChangesetsRoot.tsx | 123 ++---------------- .../src/repos/containers/RepositoryRoot.tsx | 24 +++- .../sources/containers/SourceExtensions.tsx | 4 +- .../src/repos/sources/containers/Sources.tsx | 10 +- .../src/repos/sources/modules/sources.ts | 4 +- 13 files changed, 130 insertions(+), 257 deletions(-) rename scm-ui/ui-webapp/src/repos/codeSection/containers/{CodeSectionOverview.tsx => CodeOverview.tsx} (66%) diff --git a/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.tsx b/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.tsx index a23fc5040a..7d30bb58b4 100644 --- a/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.tsx +++ b/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.tsx @@ -1,7 +1,15 @@ import React, { FormEvent } from "react"; import { WithTranslation, withTranslation } from "react-i18next"; import { Branch, Repository, Link } from "@scm-manager/ui-types"; -import { apiClient, BranchSelector, ErrorPage, Loading, Subtitle, Level, SubmitButton } from "@scm-manager/ui-components"; +import { + apiClient, + BranchSelector, + ErrorPage, + Loading, + Subtitle, + Level, + SubmitButton +} from "@scm-manager/ui-components"; type Props = WithTranslation & { repository: Repository; @@ -13,7 +21,7 @@ type State = { submitPending: boolean; error?: Error; branches: Branch[]; - selectedBranchName?: string; + selectedBranchName: string; defaultBranchChanged: boolean; disabled: boolean; }; @@ -29,6 +37,7 @@ class RepositoryConfig extends React.Component { loadingDefaultBranch: true, submitPending: false, branches: [], + selectedBranchName: "", defaultBranchChanged: false, disabled: true }; @@ -87,16 +96,16 @@ class RepositoryConfig extends React.Component { if (!branch) { this.setState({ ...this.state, - selectedBranchName: undefined, + selectedBranchName: "", + defaultBranchChanged: false + }); + } else { + this.setState({ + ...this.state, + selectedBranchName: branch.name, defaultBranchChanged: false }); - return; } - this.setState({ - ...this.state, - selectedBranchName: branch.name, - defaultBranchChanged: false - }); }; submit = (event: FormEvent) => { @@ -164,7 +173,7 @@ class RepositoryConfig extends React.Component { diff --git a/scm-ui/ui-components/src/BranchSelector.tsx b/scm-ui/ui-components/src/BranchSelector.tsx index 95cb95189e..2e4972e592 100644 --- a/scm-ui/ui-components/src/BranchSelector.tsx +++ b/scm-ui/ui-components/src/BranchSelector.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { FC } from "react"; import classNames from "classnames"; import styled from "styled-components"; import { Branch } from "@scm-manager/ui-types"; @@ -6,16 +6,12 @@ import DropDown from "./forms/DropDown"; type Props = { branches: Branch[]; - selected: (branch: Branch) => void; + onSelectBranch: (branch: Branch | undefined) => void; selectedBranch?: string; label: string; disabled?: boolean; }; -type State = { - selectedBranch?: Branch; -}; - const ZeroflexFieldLabel = styled.div` flex-basis: inherit; flex-grow: 0; @@ -29,66 +25,31 @@ const NoBottomMarginField = styled.div` margin-bottom: 0 !important; `; -export default class BranchSelector extends React.Component { - constructor(props: Props) { - super(props); - this.state = {}; - } - - componentDidMount() { - const { branches } = this.props; - if (branches) { - const selectedBranch = branches.find(branch => branch.name === this.props.selectedBranch); - this.setState({ - selectedBranch - }); - } - } - - render() { - const { branches, label, disabled } = this.props; - - if (branches) { - return ( -
- - - -
- - - b.name)} - optionSelected={this.branchSelected} - disabled={!!disabled} - preselectedOption={this.state.selectedBranch ? this.state.selectedBranch.name : ""} - /> - - -
+const BranchSelector: FC = ({ branches, onSelectBranch, selectedBranch, label, disabled }) => { + if (branches) { + return ( +
+ + + +
+ + + b.name)} + optionSelected={branch => onSelectBranch(branches.filter(b => b.name === branch)[0])} + disabled={!!disabled} + preselectedOption={selectedBranch && selectedBranch} + /> + +
- ); - } else { - return null; - } +
+ ); + } else { + return null; } +}; - branchSelected = (branchName: string) => { - const { branches, selected } = this.props; - - if (!branchName) { - this.setState({ - selectedBranch: undefined - }); - selected(undefined); - return; - } - const branch = branches.find(b => b.name === branchName); - - selected(branch); - this.setState({ - selectedBranch: branch - }); - }; -} +export default BranchSelector; diff --git a/scm-ui/ui-components/src/repos/changesets/changesets.ts b/scm-ui/ui-components/src/repos/changesets/changesets.ts index 1ffc2b5db6..c095e30e27 100644 --- a/scm-ui/ui-components/src/repos/changesets/changesets.ts +++ b/scm-ui/ui-components/src/repos/changesets/changesets.ts @@ -6,7 +6,7 @@ export type Description = { }; export function createChangesetLink(repository: Repository, changeset: Changeset) { - return `/repo/${repository.namespace}/${repository.name}/changeset/${changeset.id}`; + return `/repo/${repository.namespace}/${repository.name}/code/changeset/${changeset.id}`; } export function createSourcesLink(repository: Repository, changeset: Changeset) { diff --git a/scm-ui/ui-webapp/src/repos/branches/containers/BranchRoot.tsx b/scm-ui/ui-webapp/src/repos/branches/containers/BranchRoot.tsx index efc3ae1a50..34ada822b6 100644 --- a/scm-ui/ui-webapp/src/repos/branches/containers/BranchRoot.tsx +++ b/scm-ui/ui-webapp/src/repos/branches/containers/BranchRoot.tsx @@ -73,7 +73,7 @@ class BranchRoot extends React.Component { } } -const mapStateToProps = (state, ownProps) => { +const mapStateToProps = (state: any, ownProps: Props) => { const { repository } = ownProps; const branchName = decodeURIComponent(ownProps.match.params.branch); const branch = getBranch(state, repository, branchName); @@ -88,7 +88,7 @@ const mapStateToProps = (state, ownProps) => { }; }; -const mapDispatchToProps = dispatch => { +const mapDispatchToProps = (dispatch: any) => { return { fetchBranch: (repository: Repository, branchName: string) => { dispatch(fetchBranch(repository, branchName)); diff --git a/scm-ui/ui-webapp/src/repos/branches/containers/CreateBranch.tsx b/scm-ui/ui-webapp/src/repos/branches/containers/CreateBranch.tsx index 36fe6746e3..a5948c1d36 100644 --- a/scm-ui/ui-webapp/src/repos/branches/containers/CreateBranch.tsx +++ b/scm-ui/ui-webapp/src/repos/branches/containers/CreateBranch.tsx @@ -18,6 +18,7 @@ import { isFetchBranchesPending, getFetchBranchesFailure } from "../modules/branches"; +import { compose } from "redux"; type Props = WithTranslation & { loading?: boolean; @@ -92,7 +93,7 @@ class CreateBranch extends React.Component { } } -const mapDispatchToProps = dispatch => { +const mapDispatchToProps = (dispatch: any) => { return { fetchBranches: (repository: Repository) => { dispatch(fetchBranches(repository)); @@ -111,7 +112,7 @@ const mapDispatchToProps = dispatch => { }; }; -const mapStateToProps = (state, ownProps) => { +const mapStateToProps = (state: any, ownProps: Props) => { const { repository } = ownProps; const loading = isFetchBranchesPending(state, repository) || isCreateBranchPending(state, repository); const error = getFetchBranchesFailure(state, repository) || getCreateBranchFailure(state, repository); @@ -126,9 +127,8 @@ const mapStateToProps = (state, ownProps) => { }; }; -export default withRouter( - connect( - mapStateToProps, - mapDispatchToProps - )(withTranslation("repos")(CreateBranch)) -); +export default compose( + withTranslation("repos"), + connect(mapStateToProps, mapDispatchToProps), + withRouter +)(CreateBranch); diff --git a/scm-ui/ui-webapp/src/repos/codeSection/components/CodeViewSwitcher.tsx b/scm-ui/ui-webapp/src/repos/codeSection/components/CodeViewSwitcher.tsx index d7fa275dd7..69e94e3024 100644 --- a/scm-ui/ui-webapp/src/repos/codeSection/components/CodeViewSwitcher.tsx +++ b/scm-ui/ui-webapp/src/repos/codeSection/components/CodeViewSwitcher.tsx @@ -17,19 +17,25 @@ type Props = { const CodeViewSwitcher: FC = ({ url }) => { const [t] = useTranslation("repos"); + const createDestinationUrl = (destination: string) => { + let splittedUrl = url.split("/"); + splittedUrl[5] = destination; + return splittedUrl.join("/") + }; + return ( ); diff --git a/scm-ui/ui-webapp/src/repos/codeSection/containers/CodeSectionOverview.tsx b/scm-ui/ui-webapp/src/repos/codeSection/containers/CodeOverview.tsx similarity index 66% rename from scm-ui/ui-webapp/src/repos/codeSection/containers/CodeSectionOverview.tsx rename to scm-ui/ui-webapp/src/repos/codeSection/containers/CodeOverview.tsx index 3d8b69cfb5..9dbd517175 100644 --- a/scm-ui/ui-webapp/src/repos/codeSection/containers/CodeSectionOverview.tsx +++ b/scm-ui/ui-webapp/src/repos/codeSection/containers/CodeOverview.tsx @@ -1,11 +1,9 @@ import React from "react"; import styled from "styled-components"; -import { Route, withRouter, RouteComponentProps } from "react-router-dom"; -import ChangesetView from "../../containers/ChangesetView"; +import { Route, RouteComponentProps, withRouter } from "react-router-dom"; import Sources from "../../sources/containers/Sources"; -import SourceExtensions from "../../sources/containers/SourceExtensions"; import ChangesetsRoot from "../../containers/ChangesetsRoot"; -import { Repository, Branch } from "@scm-manager/ui-types"; +import { Branch, Repository } from "@scm-manager/ui-types"; import { BranchSelector, Level } from "@scm-manager/ui-components"; import CodeViewSwitcher from "../components/CodeViewSwitcher"; import { compose } from "redux"; @@ -34,7 +32,7 @@ type Props = RouteComponentProps & fetchBranches: (p: Repository) => void; }; -const CodeSectionActionBar = styled.div.attrs(() => ({}))` +const CodeActionBar = styled.div.attrs(() => ({}))` background-color: whitesmoke; border: 1px solid #dbdbdb; border-radius: 4px; @@ -46,10 +44,17 @@ const CodeSectionActionBar = styled.div.attrs(() => ({}))` margin-bottom: 1em; `; -class CodeSectionOverview extends React.Component { +class CodeOverview extends React.Component { componentDidMount() { - const { repository } = this.props; - this.props.fetchBranches(repository); + const { repository, branches } = this.props; + new Promise(() => { + this.props.fetchBranches(repository); + }).then(() => { + if (branches?.length > 0) { + const defaultBranch = branches.filter((branch: Branch) => branch.defaultBranch === true)[0]; + this.branchSelected(defaultBranch); + } + }); } findSelectedBranch = () => { @@ -58,13 +63,11 @@ class CodeSectionOverview extends React.Component { }; branchSelected = (branch?: Branch) => { - let url; + let splittedUrl = this.props.location.pathname.split("/"); if (branch) { - url = `${this.props.baseUrl}/${this.props.selectedView}/${encodeURIComponent(branch.name)}`; - } else { - url = `${this.props.baseUrl}/${this.props.selectedView}/`; + splittedUrl[6] = encodeURIComponent(branch.name); } - this.props.history.push(url); + this.props.history.push(splittedUrl.join("/")); }; render() { @@ -73,24 +76,19 @@ class CodeSectionOverview extends React.Component { return (
- + 0 && ( - { - this.branchSelected(b); - }} - /> - ) + } right={} /> - - } /> + { path={`${url}/sources/:revision/:path*`} render={() => } /> - } - /> - } - /> { } } -const mapDispatchToProps = dispatch => { +const mapDispatchToProps = (dispatch: any) => { return { fetchBranches: (repo: Repository) => { dispatch(fetchBranches(repo)); @@ -152,4 +141,4 @@ export default compose( withRouter, withTranslation("repos"), connect(mapStateToProps, mapDispatchToProps) -)(CodeSectionOverview); +)(CodeOverview); diff --git a/scm-ui/ui-webapp/src/repos/containers/Changesets.tsx b/scm-ui/ui-webapp/src/repos/containers/Changesets.tsx index 3b7b85d1b5..9bce6006e5 100644 --- a/scm-ui/ui-webapp/src/repos/containers/Changesets.tsx +++ b/scm-ui/ui-webapp/src/repos/containers/Changesets.tsx @@ -41,7 +41,6 @@ type Props = WithTranslation & { class Changesets extends React.Component { componentDidMount() { const { fetchChangesets, repository, branch, page } = this.props; - fetchChangesets(repository, branch, page); } diff --git a/scm-ui/ui-webapp/src/repos/containers/ChangesetsRoot.tsx b/scm-ui/ui-webapp/src/repos/containers/ChangesetsRoot.tsx index 4eaeaf26eb..13129af208 100644 --- a/scm-ui/ui-webapp/src/repos/containers/ChangesetsRoot.tsx +++ b/scm-ui/ui-webapp/src/repos/containers/ChangesetsRoot.tsx @@ -1,59 +1,20 @@ import React from "react"; -import { compose } from "redux"; -import { connect } from "react-redux"; import { Route, withRouter } from "react-router-dom"; import { WithTranslation, withTranslation } from "react-i18next"; -import { Branch, Repository } from "@scm-manager/ui-types"; -import { BranchSelector, ErrorNotification, Loading } from "@scm-manager/ui-components"; +import { Repository } from "@scm-manager/ui-types"; import Changesets from "./Changesets"; -import { - fetchBranches, - getBranches, - getFetchBranchesFailure, - isFetchBranchesPending -} from "../branches/modules/branches"; type Props = WithTranslation & { repository: Repository; - selected: string; + selectedBranch: string; baseUrl: string; - // State props - branches: Branch[]; - loading: boolean; - error: Error; - - // Dispatch props - fetchBranches: (p: Repository) => void; - // Context props history: any; // TODO flow type match: any; }; class ChangesetsRoot extends React.Component { - componentDidMount() { - this.props.fetchBranches(this.props.repository); - this.redirectToDefaultBranch(); - } - - redirectToDefaultBranch = () => { - if (this.shouldRedirectToDefaultBranch()) { - const defaultBranches = this.props.branches.filter(b => b.defaultBranch === true); - if (defaultBranches.length > 0) { - this.branchSelected(defaultBranches[0]); - } - } - }; - - shouldRedirectToDefaultBranch = () => { - return ( - this.props.branches && - this.props.branches.length > 0 && - this.props.branches.filter(b => b.name === this.props.selected).length === 0 - ); - }; - stripEndingSlash = (url: string) => { if (url.endsWith("/")) { return url.substring(0, url.length - 1); @@ -61,92 +22,24 @@ class ChangesetsRoot extends React.Component { return url; }; - branchSelected = (branch?: Branch) => { - let url; - if (branch) { - url = `${this.props.baseUrl}/${encodeURIComponent(branch.name)}`; - } else { - url = `${this.props.baseUrl}/`; - } - this.props.history.push(url); - }; - - findSelectedBranch = () => { - const { selected, branches } = this.props; - return branches.find((branch: Branch) => branch.name === selected); - }; - render() { - const { repository, error, loading, match, branches } = this.props; - - if (error) { - return ; - } - - if (loading) { - return ; - } + const { repository, match, selectedBranch } = this.props; if (!repository) { return null; } const url = this.stripEndingSlash(match.url); - const branch = branches ? this.findSelectedBranch() : null; - const changesets = ; return (
- changesets} /> + } + />
); } - - renderBranchSelector = () => { - const { repository, branches, selected, t } = this.props; - if (repository._links.branches) { - return ( -
- { - this.branchSelected(b); - }} - /> -
- ); - } - return null; - }; } -const mapDispatchToProps = (dispatch: any) => { - return { - fetchBranches: (repo: Repository) => { - dispatch(fetchBranches(repo)); - } - }; -}; - -const mapStateToProps = (state: any, ownProps: Props) => { - const { repository, match } = ownProps; - const loading = isFetchBranchesPending(state, repository); - const error = getFetchBranchesFailure(state, repository); - const branches = getBranches(state, repository); - const selected = decodeURIComponent(match.params.branch); - - return { - loading, - error, - branches, - selected - }; -}; - -export default compose( - withRouter, - withTranslation("repos"), - connect(mapStateToProps, mapDispatchToProps) -)(ChangesetsRoot); +export default withRouter(withTranslation("repos")(ChangesetsRoot)); diff --git a/scm-ui/ui-webapp/src/repos/containers/RepositoryRoot.tsx b/scm-ui/ui-webapp/src/repos/containers/RepositoryRoot.tsx index a0a8ad8b95..56b705881c 100644 --- a/scm-ui/ui-webapp/src/repos/containers/RepositoryRoot.tsx +++ b/scm-ui/ui-webapp/src/repos/containers/RepositoryRoot.tsx @@ -17,7 +17,9 @@ import BranchRoot from "../branches/containers/BranchRoot"; import PermissionsNavLink from "../components/PermissionsNavLink"; import RepositoryNavLink from "../components/RepositoryNavLink"; import { getLinks, getRepositoriesLink } from "../../modules/indexResource"; -import CodeSectionOverview from "../codeSection/containers/CodeSectionOverview"; +import CodeOverview from "../codeSection/containers/CodeOverview"; +import ChangesetView from "./ChangesetView"; +import SourceExtensions from "../sources/containers/SourceExtensions"; type Props = WithTranslation & { namespace: string; @@ -128,9 +130,23 @@ class RepositoryRoot extends React.Component { )} /> + } + /> + } + /> + } + /> } + render={() => } /> { } } -const mapStateToProps = (state, ownProps) => { +const mapStateToProps = (state: any, ownProps: Props) => { const { namespace, name } = ownProps.match.params; const repository = getRepository(state, namespace, name); const loading = isFetchRepoPending(state, namespace, name); @@ -205,7 +221,7 @@ const mapStateToProps = (state, ownProps) => { }; }; -const mapDispatchToProps = dispatch => { +const mapDispatchToProps = (dispatch: any) => { return { fetchRepoByName: (link: string, namespace: string, name: string) => { dispatch(fetchRepoByName(link, namespace, name)); diff --git a/scm-ui/ui-webapp/src/repos/sources/containers/SourceExtensions.tsx b/scm-ui/ui-webapp/src/repos/sources/containers/SourceExtensions.tsx index 2ea57a65ba..613bf51828 100644 --- a/scm-ui/ui-webapp/src/repos/sources/containers/SourceExtensions.tsx +++ b/scm-ui/ui-webapp/src/repos/sources/containers/SourceExtensions.tsx @@ -24,7 +24,7 @@ type Props = WithTranslation & sources?: File | null; // dispatch props - fetchSources: (repository: Repository, revision: string, path: string) => void; + fetchSources: (repository: Repository, revision: string | undefined, path: string | undefined) => void; }; const extensionPointName = "repos.sources.extensions"; @@ -33,7 +33,7 @@ class SourceExtensions extends React.Component { componentDidMount() { const { fetchSources, repository, revision, path } = this.props; // TODO get typing right - fetchSources(repository, revision || "", path || ""); + fetchSources(repository, revision, path); } render() { diff --git a/scm-ui/ui-webapp/src/repos/sources/containers/Sources.tsx b/scm-ui/ui-webapp/src/repos/sources/containers/Sources.tsx index 8b75cbe903..83f68ea2f6 100644 --- a/scm-ui/ui-webapp/src/repos/sources/containers/Sources.tsx +++ b/scm-ui/ui-webapp/src/repos/sources/containers/Sources.tsx @@ -1,9 +1,9 @@ import React from "react"; -import { connect } from "react-redux"; +import { connect, Dispatch, DispatchProp, MapDispatchToProps } from "react-redux"; import { withRouter } from "react-router-dom"; import { WithTranslation, withTranslation } from "react-i18next"; import { Branch, Repository } from "@scm-manager/ui-types"; -import { BranchSelector, Breadcrumb, ErrorNotification, Loading } from "@scm-manager/ui-components"; +import { Breadcrumb, ErrorNotification, Loading } from "@scm-manager/ui-components"; import FileTree from "../components/FileTree"; import { fetchBranches, @@ -58,7 +58,7 @@ class Sources extends React.Component { this.redirectToDefaultBranch(); } - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps: Props) { const { fetchSources, repository, revision, path } = this.props; if (prevProps.revision !== revision || prevProps.path !== path) { fetchSources(repository, this.decodeRevision(revision), path); @@ -147,7 +147,7 @@ class Sources extends React.Component { }; } -const mapStateToProps = (state, ownProps) => { +const mapStateToProps = (state: any, ownProps: Props) => { const { repository, match } = ownProps; const { revision, path } = match.params; const decodedRevision = revision ? decodeURIComponent(revision) : undefined; @@ -171,7 +171,7 @@ const mapStateToProps = (state, ownProps) => { }; }; -const mapDispatchToProps = dispatch => { +const mapDispatchToProps = (dispatch: any) => { return { fetchBranches: (repository: Repository) => { dispatch(fetchBranches(repository)); 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 7387c3d57f..ae51a1c55e 100644 --- a/scm-ui/ui-webapp/src/repos/sources/modules/sources.ts +++ b/scm-ui/ui-webapp/src/repos/sources/modules/sources.ts @@ -84,7 +84,7 @@ export function fetchSourcesFailure(repository: Repository, revision: string, pa }; } -function createItemId(repository: Repository, revision: string, path: string) { +function createItemId(repository: Repository, revision: string | undefined, path: string) { const revPart = revision ? revision : "_"; const pathPart = path ? path : ""; return `${repository.namespace}/${repository.name}/${decodeURIComponent(revPart)}/${pathPart}`; @@ -121,7 +121,7 @@ export function isDirectory(state: any, repository: Repository, revision: string export function getSources( state: any, repository: Repository, - revision: string, + revision: string | undefined, path: string ): File | null | undefined { if (state.sources) { From 987436e3356fdfdb8bd01d7ab02a26dea5b38f28 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 8 Jan 2020 11:01:00 +0100 Subject: [PATCH 03/29] add loading spinner and error handling for codeOverview --- .../codeSection/containers/CodeOverview.tsx | 26 +++++++++++++------ .../src/repos/sources/containers/Sources.tsx | 9 +------ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/scm-ui/ui-webapp/src/repos/codeSection/containers/CodeOverview.tsx b/scm-ui/ui-webapp/src/repos/codeSection/containers/CodeOverview.tsx index 9dbd517175..3f7fa5c03f 100644 --- a/scm-ui/ui-webapp/src/repos/codeSection/containers/CodeOverview.tsx +++ b/scm-ui/ui-webapp/src/repos/codeSection/containers/CodeOverview.tsx @@ -4,7 +4,7 @@ import { Route, RouteComponentProps, withRouter } from "react-router-dom"; import Sources from "../../sources/containers/Sources"; import ChangesetsRoot from "../../containers/ChangesetsRoot"; import { Branch, Repository } from "@scm-manager/ui-types"; -import { BranchSelector, Level } from "@scm-manager/ui-components"; +import { BranchSelector, ErrorPage, Level, Loading } from "@scm-manager/ui-components"; import CodeViewSwitcher from "../components/CodeViewSwitcher"; import { compose } from "redux"; import { WithTranslation, withTranslation } from "react-i18next"; @@ -23,8 +23,8 @@ type Props = RouteComponentProps & // State props branches: Branch[]; - loading: boolean; error: Error; + loading: boolean; selectedView: string; selectedBranch: string; @@ -50,7 +50,7 @@ class CodeOverview extends React.Component { new Promise(() => { this.props.fetchBranches(repository); }).then(() => { - if (branches?.length > 0) { + if (this.props.branches?.length > 0) { const defaultBranch = branches.filter((branch: Branch) => branch.defaultBranch === true)[0]; this.branchSelected(defaultBranch); } @@ -71,9 +71,19 @@ class CodeOverview extends React.Component { }; render() { - const { repository, baseUrl, branches, t } = this.props; + const { repository, baseUrl, branches, error, loading, t } = this.props; const url = baseUrl; + if (!branches || loading) { + return ; + } + + if (error) { + return ( + + ); + } + return (
@@ -92,11 +102,11 @@ class CodeOverview extends React.Component { } + render={() => } /> } + render={() => } /> { const mapStateToProps = (state: any, ownProps: Props) => { const { repository, location } = ownProps; - const loading = isFetchBranchesPending(state, repository); const error = getFetchBranchesFailure(state, repository); + const loading = isFetchBranchesPending(state, repository); const branches = getBranches(state, repository); const selectedView = decodeURIComponent(location.pathname.split("/")[5]); const selectedBranch = decodeURIComponent(location.pathname.split("/")[6]); return { - loading, error, + loading, branches, selectedView, selectedBranch diff --git a/scm-ui/ui-webapp/src/repos/sources/containers/Sources.tsx b/scm-ui/ui-webapp/src/repos/sources/containers/Sources.tsx index 83f68ea2f6..492e48c7a8 100644 --- a/scm-ui/ui-webapp/src/repos/sources/containers/Sources.tsx +++ b/scm-ui/ui-webapp/src/repos/sources/containers/Sources.tsx @@ -27,7 +27,6 @@ type Props = WithTranslation & { sources: File; // dispatch props - fetchBranches: (p: Repository) => void; fetchSources: (p1: Repository, p2: string, p3: string) => void; // Context props @@ -50,9 +49,8 @@ class Sources extends React.Component { } componentDidMount() { - const { fetchBranches, repository, revision, path, fetchSources } = this.props; + const { repository, revision, path, fetchSources } = this.props; - fetchBranches(repository); fetchSources(repository, this.decodeRevision(revision), path); this.redirectToDefaultBranch(); @@ -153,7 +151,6 @@ const mapStateToProps = (state: any, ownProps: Props) => { const decodedRevision = revision ? decodeURIComponent(revision) : undefined; const loading = isFetchBranchesPending(state, repository); const error = getFetchBranchesFailure(state, repository); - const branches = getBranches(state, repository); const currentFileIsDirectory = decodedRevision ? isDirectory(state, repository, decodedRevision, path) : isDirectory(state, repository, revision, path); @@ -165,7 +162,6 @@ const mapStateToProps = (state: any, ownProps: Props) => { path, loading, error, - branches, currentFileIsDirectory, sources }; @@ -173,9 +169,6 @@ const mapStateToProps = (state: any, ownProps: Props) => { const mapDispatchToProps = (dispatch: any) => { return { - fetchBranches: (repository: Repository) => { - dispatch(fetchBranches(repository)); - }, fetchSources: (repository: Repository, revision: string, path: string) => { dispatch(fetchSources(repository, revision, path)); } From 15a9a5b09b2a277c54820dc97ef3b2c180ff9ab8 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 8 Jan 2020 12:38:21 +0100 Subject: [PATCH 04/29] fix links and routes --- scm-ui/ui-components/src/CardColumn.tsx | 2 +- scm-ui/ui-components/src/forms/DropDown.tsx | 7 ++++++- scm-ui/ui-components/src/repos/changesets/changesets.ts | 2 +- .../src/repos/codeSection/components/CodeViewSwitcher.tsx | 4 ++-- .../src/repos/codeSection/containers/CodeOverview.tsx | 2 +- .../src/repos/components/list/RepositoryEntry.tsx | 4 ++-- 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/scm-ui/ui-components/src/CardColumn.tsx b/scm-ui/ui-components/src/CardColumn.tsx index fd84b9b11e..f7e7570d1d 100644 --- a/scm-ui/ui-components/src/CardColumn.tsx +++ b/scm-ui/ui-components/src/CardColumn.tsx @@ -5,7 +5,7 @@ import { Link } from "react-router-dom"; type Props = { title: string; - description: string; + description?: string; avatar: ReactNode; contentRight?: ReactNode; footerLeft: ReactNode; diff --git a/scm-ui/ui-components/src/forms/DropDown.tsx b/scm-ui/ui-components/src/forms/DropDown.tsx index b65c48b49d..97b31d4413 100644 --- a/scm-ui/ui-components/src/forms/DropDown.tsx +++ b/scm-ui/ui-components/src/forms/DropDown.tsx @@ -13,10 +13,15 @@ type Props = { class DropDown extends React.Component { render() { const { options, optionValues, preselectedOption, className, disabled } = this.props; + + if (preselectedOption && !options.includes(preselectedOption)) { + options.unshift(preselectedOption) + } + return (