mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-10-26 08:06:09 +01:00
Merge branch 'develop' into feature/full_anonymous_mode
This commit is contained in:
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Added
|
||||
- Introduced merge detection for receive hooks ([#1278](https://github.com/scm-manager/scm-manager/pull/1278))
|
||||
- Anonymous mode for the web ui ([#1284](https://github.com/scm-manager/scm-manager/pull/1284))
|
||||
- Add link to source file in diff sections ([#1267](https://github.com/scm-manager/scm-manager/pull/1267))
|
||||
- Check versions of plugin dependencies on plugin installation ([#1283](https://github.com/scm-manager/scm-manager/pull/1283))
|
||||
|
||||
### Fixed
|
||||
@@ -27,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Avoid stacktrace logging when protocol url is accessed outside of request scope ([#1276](https://github.com/scm-manager/scm-manager/pull/1276))
|
||||
|
||||
## [2.3.0] - 2020-07-23
|
||||
|
||||
### Added
|
||||
- Add branch link provider to access branch links in plugins ([#1243](https://github.com/scm-manager/scm-manager/pull/1243))
|
||||
- Add key value input field component ([#1246](https://github.com/scm-manager/scm-manager/pull/1246))
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 221 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 218 KiB |
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { ReactNode, useEffect, useState } from "react";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import Diff from "./Diff";
|
||||
// @ts-ignore
|
||||
@@ -29,11 +29,15 @@ import parser from "gitdiff-parser";
|
||||
import simpleDiff from "../__resources__/Diff.simple";
|
||||
import hunksDiff from "../__resources__/Diff.hunks";
|
||||
import binaryDiff from "../__resources__/Diff.binary";
|
||||
import { DiffEventContext, File } from "./DiffTypes";
|
||||
import { DiffEventContext, File, FileControlFactory } from "./DiffTypes";
|
||||
import Toast from "../toast/Toast";
|
||||
import { getPath } from "./diffs";
|
||||
import DiffButton from "./DiffButton";
|
||||
import styled from "styled-components";
|
||||
import { MemoryRouter } from "react-router-dom";
|
||||
import { one, two } from "../__resources__/changesets";
|
||||
import { Changeset } from "@scm-manager/ui-types";
|
||||
import JumpToFileButton from "./JumpToFileButton";
|
||||
|
||||
const diffFiles = parser.parse(simpleDiff);
|
||||
|
||||
@@ -41,11 +45,46 @@ const Container = styled.div`
|
||||
padding: 2rem 6rem;
|
||||
`;
|
||||
|
||||
const RoutingDecorator = (story: () => ReactNode) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>;
|
||||
|
||||
const fileControlFactory: (changeset: Changeset) => FileControlFactory = changeset => file => {
|
||||
const baseUrl = "/repo/hitchhiker/heartOfGold/code/changeset";
|
||||
const sourceLink = {
|
||||
url: `${baseUrl}/${changeset.id}/${file.newPath}/`,
|
||||
label: "Jump to source"
|
||||
};
|
||||
const targetLink = changeset._embedded?.parents?.length === 1 && {
|
||||
url: `${baseUrl}/${changeset._embedded.parents[0].id}/${file.oldPath}`,
|
||||
label: "Jump to target"
|
||||
};
|
||||
|
||||
const links = [];
|
||||
switch (file.type) {
|
||||
case "add":
|
||||
links.push(sourceLink);
|
||||
break;
|
||||
case "delete":
|
||||
if (targetLink) {
|
||||
links.push(targetLink);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (targetLink) {
|
||||
links.push(sourceLink, targetLink);
|
||||
} else {
|
||||
links.push(sourceLink);
|
||||
}
|
||||
}
|
||||
|
||||
return links.map(({ url, label }) => <JumpToFileButton tooltip={label} link={url} />);
|
||||
};
|
||||
|
||||
storiesOf("Diff", module)
|
||||
.addDecorator(RoutingDecorator)
|
||||
.addDecorator(storyFn => <Container>{storyFn()}</Container>)
|
||||
.add("Default", () => <Diff diff={diffFiles} />)
|
||||
.add("Side-By-Side", () => <Diff diff={diffFiles} sideBySide={true} />)
|
||||
.add("Collapsed", () => <Diff diff={diffFiles} defaultCollapse={true} />)
|
||||
.add("Collapsed", () => <Diff diff={diffFiles} defaultCollapse={true} fileControlFactory={fileControlFactory(two)} />)
|
||||
.add("File Controls", () => (
|
||||
<Diff
|
||||
diff={diffFiles}
|
||||
@@ -121,4 +160,5 @@ storiesOf("Diff", module)
|
||||
return file;
|
||||
});
|
||||
return <Diff diff={filesWithLanguage} />;
|
||||
});
|
||||
})
|
||||
.add("WithLinkToFile", () => <Diff diff={diffFiles} />);
|
||||
|
||||
@@ -23,13 +23,14 @@
|
||||
*/
|
||||
import React from "react";
|
||||
import DiffFile from "./DiffFile";
|
||||
import { DiffObjectProps, File } from "./DiffTypes";
|
||||
import { DiffObjectProps, File, FileControlFactory } from "./DiffTypes";
|
||||
import Notification from "../Notification";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
|
||||
type Props = WithTranslation &
|
||||
DiffObjectProps & {
|
||||
diff: File[];
|
||||
fileControlFactory?: FileControlFactory;
|
||||
};
|
||||
|
||||
class Diff extends React.Component<Props> {
|
||||
|
||||
@@ -309,7 +309,11 @@ class DiffFile extends React.Component<Props, State> {
|
||||
if (file._links?.lines) {
|
||||
items.push(this.createHunkHeader(expandableHunk));
|
||||
} else if (i > 0) {
|
||||
items.push(<Decoration><HunkDivider /></Decoration>);
|
||||
items.push(
|
||||
<Decoration>
|
||||
<HunkDivider />
|
||||
</Decoration>
|
||||
);
|
||||
}
|
||||
|
||||
items.push(
|
||||
@@ -411,29 +415,31 @@ class DiffFile extends React.Component<Props, State> {
|
||||
}
|
||||
const collapseIcon = this.hasContent(file) ? <Icon name={icon} color="inherit" /> : null;
|
||||
const fileControls = fileControlFactory ? fileControlFactory(file, this.setCollapse) : null;
|
||||
const sideBySideToggle =
|
||||
file.hunks && file.hunks.length > 0 ? (
|
||||
<ButtonWrapper className={classNames("level-right", "is-flex")}>
|
||||
<ButtonGroup>
|
||||
<MenuContext.Consumer>
|
||||
{({ setCollapsed }) => (
|
||||
<DiffButton
|
||||
icon={sideBySide ? "align-left" : "columns"}
|
||||
tooltip={t(sideBySide ? "diff.combined" : "diff.sideBySide")}
|
||||
onClick={() =>
|
||||
this.toggleSideBySide(() => {
|
||||
if (this.state.sideBySide) {
|
||||
setCollapsed(true);
|
||||
}
|
||||
})
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</MenuContext.Consumer>
|
||||
{fileControls}
|
||||
</ButtonGroup>
|
||||
</ButtonWrapper>
|
||||
) : null;
|
||||
const sideBySideToggle = file.hunks && file.hunks.length && (
|
||||
<MenuContext.Consumer>
|
||||
{({ setCollapsed }) => (
|
||||
<DiffButton
|
||||
icon={sideBySide ? "align-left" : "columns"}
|
||||
tooltip={t(sideBySide ? "diff.combined" : "diff.sideBySide")}
|
||||
onClick={() =>
|
||||
this.toggleSideBySide(() => {
|
||||
if (this.state.sideBySide) {
|
||||
setCollapsed(true);
|
||||
}
|
||||
})
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</MenuContext.Consumer>
|
||||
);
|
||||
const headerButtons = (
|
||||
<ButtonWrapper className={classNames("level-right", "is-flex")}>
|
||||
<ButtonGroup>
|
||||
{sideBySideToggle}
|
||||
{fileControls}
|
||||
</ButtonGroup>
|
||||
</ButtonWrapper>
|
||||
);
|
||||
|
||||
let errorModal;
|
||||
if (expansionError) {
|
||||
@@ -463,7 +469,7 @@ class DiffFile extends React.Component<Props, State> {
|
||||
</TitleWrapper>
|
||||
{this.renderChangeTag(file)}
|
||||
</FullWidthTitleHeader>
|
||||
{sideBySideToggle}
|
||||
{headerButtons}
|
||||
</FlexWrapLevel>
|
||||
</div>
|
||||
{body}
|
||||
|
||||
54
scm-ui/ui-components/src/repos/JumpToFileButton.tsx
Normal file
54
scm-ui/ui-components/src/repos/JumpToFileButton.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React, { FC } from "react";
|
||||
import styled from "styled-components";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Tooltip from "../Tooltip";
|
||||
import Icon from "../Icon";
|
||||
|
||||
const Button = styled(Link)`
|
||||
width: 50px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: #33b2e8;
|
||||
}
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
link: string;
|
||||
tooltip: string;
|
||||
};
|
||||
|
||||
const JumpToFileButton: FC<Props> = ({ link, tooltip }) => {
|
||||
return (
|
||||
<Tooltip message={tooltip} location="top">
|
||||
<Button aria-label={tooltip} className="button" to={link}>
|
||||
<Icon name="file-code" color="inherit" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export default JumpToFileButton;
|
||||
@@ -22,14 +22,16 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React from "react";
|
||||
import { Changeset, Link, Collection } from "@scm-manager/ui-types";
|
||||
import { Changeset, Collection, Link } from "@scm-manager/ui-types";
|
||||
import LoadingDiff from "../LoadingDiff";
|
||||
import Notification from "../../Notification";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { FileControlFactory } from "../DiffTypes";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
changeset: Changeset;
|
||||
defaultCollapse?: boolean;
|
||||
fileControlFactory?: FileControlFactory;
|
||||
};
|
||||
|
||||
export const isDiffSupported = (changeset: Collection) => {
|
||||
@@ -47,12 +49,19 @@ export const createUrl = (changeset: Collection) => {
|
||||
|
||||
class ChangesetDiff extends React.Component<Props> {
|
||||
render() {
|
||||
const { changeset, defaultCollapse, t } = this.props;
|
||||
const { changeset, fileControlFactory, defaultCollapse, t } = this.props;
|
||||
if (!isDiffSupported(changeset)) {
|
||||
return <Notification type="danger">{t("changeset.diffNotSupported")}</Notification>;
|
||||
} else {
|
||||
const url = createUrl(changeset);
|
||||
return <LoadingDiff url={url} defaultCollapse={defaultCollapse} sideBySide={false} />;
|
||||
return (
|
||||
<LoadingDiff
|
||||
url={url}
|
||||
defaultCollapse={defaultCollapse}
|
||||
sideBySide={false}
|
||||
fileControlFactory={fileControlFactory}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ class ChangesetRow extends React.Component<Props> {
|
||||
<AvatarWrapper>
|
||||
<AvatarFigure className="media-left">
|
||||
<FixedSizedAvatar className="image">
|
||||
<AvatarImage person={changeset.author}/>
|
||||
<AvatarImage person={changeset.author} />
|
||||
</FixedSizedAvatar>
|
||||
</AvatarFigure>
|
||||
</AvatarWrapper>
|
||||
@@ -119,24 +119,24 @@ class ChangesetRow extends React.Component<Props> {
|
||||
</ExtensionPoint>
|
||||
</h4>
|
||||
<p className="is-hidden-touch">
|
||||
<Trans i18nKey="repos:changeset.summary" components={[changesetId, dateFromNow]}/>
|
||||
<Trans i18nKey="repos:changeset.summary" components={[changesetId, dateFromNow]} />
|
||||
</p>
|
||||
<p className="is-hidden-desktop">
|
||||
<Trans i18nKey="repos:changeset.shortSummary" components={[changesetId, dateFromNow]}/>
|
||||
<Trans i18nKey="repos:changeset.shortSummary" components={[changesetId, dateFromNow]} />
|
||||
</p>
|
||||
<AuthorWrapper className="is-size-7 is-ellipsis-overflow">
|
||||
<ChangesetAuthor changeset={changeset}/>
|
||||
<ChangesetAuthor changeset={changeset} />
|
||||
</AuthorWrapper>
|
||||
</Metadata>
|
||||
</div>
|
||||
</div>
|
||||
<VCenteredColumn className="column">
|
||||
<ChangesetTags changeset={changeset}/>
|
||||
<ChangesetTags changeset={changeset} />
|
||||
</VCenteredColumn>
|
||||
</div>
|
||||
</div>
|
||||
<VCenteredChildColumn className={classNames("column", "is-flex")}>
|
||||
<ChangesetButtonGroup repository={repository} changeset={changeset}/>
|
||||
<ChangesetButtonGroup repository={repository} changeset={changeset} />
|
||||
<ExtensionPoint
|
||||
name="changeset.right"
|
||||
props={{
|
||||
|
||||
@@ -45,11 +45,13 @@ export * from "./changesets";
|
||||
export { default as Diff } from "./Diff";
|
||||
export { default as DiffFile } from "./DiffFile";
|
||||
export { default as DiffButton } from "./DiffButton";
|
||||
export { FileControlFactory } from "./DiffTypes";
|
||||
export { default as LoadingDiff } from "./LoadingDiff";
|
||||
export { DefaultCollapsed, DefaultCollapsedFunction } from "./defaultCollapsed";
|
||||
export { default as RepositoryAvatar } from "./RepositoryAvatar";
|
||||
export { default as RepositoryEntry } from "./RepositoryEntry";
|
||||
export { default as RepositoryEntryLink } from "./RepositoryEntryLink";
|
||||
export { default as JumpToFileButton } from "./JumpToFileButton";
|
||||
|
||||
export {
|
||||
File,
|
||||
|
||||
@@ -223,6 +223,8 @@
|
||||
}
|
||||
},
|
||||
"diff": {
|
||||
"jumpToSource": "Zur Quelldatei springen",
|
||||
"jumpToTarget": "Zur vorherigen Version der Datei springen",
|
||||
"sideBySide": "Zur zweispaltigen Ansicht wechseln",
|
||||
"combined": "Zur kombinierten Ansicht wechseln",
|
||||
"noDiffFound": "Kein Diff zwischen den ausgewählten Branches gefunden.",
|
||||
|
||||
@@ -230,6 +230,8 @@
|
||||
"rename": "renamed",
|
||||
"copy": "copied"
|
||||
},
|
||||
"jumpToSource": "Jump to source file",
|
||||
"jumpToTarget": "Jump to the previous version of the file",
|
||||
"sideBySide": "Switch to side-by-side view",
|
||||
"combined": "Switch to combined view",
|
||||
"noDiffFound": "No Diff between the selected branches found.",
|
||||
|
||||
@@ -43,10 +43,12 @@ import {
|
||||
} from "@scm-manager/ui-components";
|
||||
import ContributorTable from "./ContributorTable";
|
||||
import { Link as ReactLink } from "react-router-dom";
|
||||
import { FileControlFactory } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
changeset: Changeset;
|
||||
repository: Repository;
|
||||
fileControlFactory?: FileControlFactory;
|
||||
};
|
||||
|
||||
type State = {
|
||||
@@ -130,6 +132,7 @@ const Contributors: FC<{ changeset: Changeset }> = ({ changeset }) => {
|
||||
</ContributorDetails>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ContributorLine onClick={e => setOpen(!open)}>
|
||||
@@ -157,7 +160,7 @@ class ChangesetDetails extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { changeset, repository, t } = this.props;
|
||||
const { changeset, repository, fileControlFactory, t } = this.props;
|
||||
const { collapsed } = this.state;
|
||||
|
||||
const description = changesets.parseDescription(changeset.description);
|
||||
@@ -238,7 +241,7 @@ class ChangesetDetails extends React.Component<Props, State> {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<ChangesetDiff changeset={changeset} defaultCollapse={collapsed} />
|
||||
<ChangesetDiff changeset={changeset} fileControlFactory={fileControlFactory} defaultCollapse={collapsed} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -35,11 +35,13 @@ import {
|
||||
isFetchChangesetPending
|
||||
} from "../modules/changesets";
|
||||
import ChangesetDetails from "../components/changesets/ChangesetDetails";
|
||||
import { FileControlFactory } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
id: string;
|
||||
changeset: Changeset;
|
||||
repository: Repository;
|
||||
fileControlFactoryFactory?: (changeset: Changeset) => FileControlFactory;
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
fetchChangesetIfNeeded: (repository: Repository, id: string) => void;
|
||||
@@ -60,7 +62,7 @@ class ChangesetView extends React.Component<Props> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { changeset, loading, error, t, repository } = this.props;
|
||||
const { changeset, loading, error, t, repository, fileControlFactoryFactory } = this.props;
|
||||
|
||||
if (error) {
|
||||
return <ErrorPage title={t("changesets.errorTitle")} subtitle={t("changesets.errorSubtitle")} error={error} />;
|
||||
@@ -68,7 +70,13 @@ class ChangesetView extends React.Component<Props> {
|
||||
|
||||
if (!changeset || loading) return <Loading />;
|
||||
|
||||
return <ChangesetDetails changeset={changeset} repository={repository} />;
|
||||
return (
|
||||
<ChangesetDetails
|
||||
changeset={changeset}
|
||||
repository={repository}
|
||||
fileControlFactory={fileControlFactoryFactory && fileControlFactoryFactory(changeset)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,21 +23,21 @@
|
||||
*/
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Redirect, Route, Switch, RouteComponentProps } from "react-router-dom";
|
||||
import { Redirect, Route, RouteComponentProps, Switch } from "react-router-dom";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { binder, ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import { Repository } from "@scm-manager/ui-types";
|
||||
import { Changeset, Repository } from "@scm-manager/ui-types";
|
||||
import {
|
||||
CustomQueryFlexWrappedColumns,
|
||||
ErrorPage,
|
||||
Loading,
|
||||
NavLink,
|
||||
Page,
|
||||
CustomQueryFlexWrappedColumns,
|
||||
PrimaryContentColumn,
|
||||
SecondaryNavigationColumn,
|
||||
SecondaryNavigation,
|
||||
SubNavigation,
|
||||
StateMenuContextProvider
|
||||
SecondaryNavigationColumn,
|
||||
StateMenuContextProvider,
|
||||
SubNavigation
|
||||
} from "@scm-manager/ui-components";
|
||||
import { fetchRepoByName, getFetchRepoFailure, getRepository, isFetchRepoPending } from "../modules/repos";
|
||||
import RepositoryDetails from "../components/RepositoryDetails";
|
||||
@@ -53,6 +53,7 @@ import { getLinks, getRepositoriesLink } from "../../modules/indexResource";
|
||||
import CodeOverview from "../codeSection/containers/CodeOverview";
|
||||
import ChangesetView from "./ChangesetView";
|
||||
import SourceExtensions from "../sources/containers/SourceExtensions";
|
||||
import { FileControlFactory, JumpToFileButton } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = RouteComponentProps &
|
||||
WithTranslation & {
|
||||
@@ -117,7 +118,7 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
|
||||
evaluateDestinationForCodeLink = () => {
|
||||
const { repository } = this.props;
|
||||
let url = `${this.matchedUrl()}/code`;
|
||||
const url = `${this.matchedUrl()}/code`;
|
||||
if (repository?._links?.sources) {
|
||||
return `${url}/sources/`;
|
||||
}
|
||||
@@ -153,6 +154,38 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
redirectedUrl = url + "/info";
|
||||
}
|
||||
|
||||
const fileControlFactoryFactory: (changeset: Changeset) => FileControlFactory = changeset => file => {
|
||||
const baseUrl = `${url}/code/sources`;
|
||||
const sourceLink = {
|
||||
url: `${baseUrl}/${changeset.id}/${file.newPath}/`,
|
||||
label: t("diff.jumpToSource")
|
||||
};
|
||||
const targetLink = changeset._embedded?.parents?.length === 1 && {
|
||||
url: `${baseUrl}/${changeset._embedded.parents[0].id}/${file.oldPath}`,
|
||||
label: t("diff.jumpToTarget")
|
||||
};
|
||||
|
||||
const links = [];
|
||||
switch (file.type) {
|
||||
case "add":
|
||||
links.push(sourceLink);
|
||||
break;
|
||||
case "delete":
|
||||
if (targetLink) {
|
||||
links.push(targetLink);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (targetLink) {
|
||||
links.push(targetLink, sourceLink); // Target link first because its the previous file
|
||||
} else {
|
||||
links.push(sourceLink);
|
||||
}
|
||||
}
|
||||
|
||||
return links.map(({ url, label }) => <JumpToFileButton tooltip={label} link={url} />);
|
||||
};
|
||||
|
||||
return (
|
||||
<StateMenuContextProvider>
|
||||
<Page
|
||||
@@ -182,7 +215,9 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
<Route
|
||||
exact
|
||||
path={`${url}/code/changeset/:id`}
|
||||
render={() => <ChangesetView repository={repository} />}
|
||||
render={() => (
|
||||
<ChangesetView repository={repository} fileControlFactoryFactory={fileControlFactoryFactory} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={`${url}/code/sourceext/:extension`}
|
||||
|
||||
Reference in New Issue
Block a user