Merge branch 'develop' into feature/template_plugin

This commit is contained in:
Eduard Heimbuch
2020-09-08 15:44:47 +02:00
51 changed files with 38889 additions and 38437 deletions

View File

@@ -72,10 +72,10 @@ class ErrorBoundary extends React.Component<Props, State> {
}
redirectToLogin = (error: Error) => {
const { loginLink } = this.props;
const { loginLink, location } = this.props;
if (error instanceof MissingLinkError) {
if (loginLink) {
window.location.assign(withContextPath("/login"));
window.location.assign(withContextPath("/login?from=" + location.pathname));
}
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -24,32 +24,74 @@
import React from "react";
import DiffFile from "./DiffFile";
import { DiffObjectProps, File, FileControlFactory } from "./DiffTypes";
import { escapeWhitespace } from "./diffs";
import Notification from "../Notification";
import { WithTranslation, withTranslation } from "react-i18next";
import { RouteComponentProps, withRouter } from "react-router-dom";
type Props = WithTranslation &
type Props = RouteComponentProps &
WithTranslation &
DiffObjectProps & {
diff: File[];
fileControlFactory?: FileControlFactory;
};
class Diff extends React.Component<Props> {
type State = {
contentRef?: HTMLElement | null;
};
function getAnchorSelector(uriHashContent: string) {
return "#" + escapeWhitespace(decodeURIComponent(uriHashContent));
}
class Diff extends React.Component<Props, State> {
static defaultProps: Partial<Props> = {
sideBySide: false
};
constructor(props: Readonly<Props>) {
super(props);
this.state = {
contentRef: undefined
};
}
componentDidUpdate() {
const { contentRef } = this.state;
// we have to use componentDidUpdate, because we have to wait until all
// children are rendered and componentDidMount is called before the
// changeset content was rendered.
const hash = this.props.location.hash;
const match = hash && hash.match(/^#diff-(.*)$/);
if (contentRef && match) {
const selector = getAnchorSelector(match[1]);
const element = contentRef.querySelector(selector);
if (element && element.scrollIntoView) {
element.scrollIntoView();
}
}
}
shouldComponentUpdate(nextProps: Readonly<Props>, nextState: Readonly<State>): boolean {
// We have check if the contentRef changed and update afterwards so the page can scroll to the anchor links.
// Otherwise it can happen that componentDidUpdate is never executed depending on how fast the markdown got rendered
return this.state.contentRef !== nextState.contentRef || this.props !== nextProps;
}
render() {
const { diff, t, ...fileProps } = this.props;
return (
<>
<div ref={el => this.setState({ contentRef: el })}>
{diff.length === 0 ? (
<Notification type="info">{t("diff.noDiffFound")}</Notification>
) : (
diff.map((file, index) => <DiffFile key={index} file={file} {...fileProps} {...this.props} />)
)}
</>
</div>
);
}
}
export default withTranslation("repos")(Diff);
export default withRouter(withTranslation("repos")(Diff));

View File

@@ -39,6 +39,7 @@ import HunkExpandLink from "./HunkExpandLink";
import { Modal } from "../modals";
import ErrorNotification from "../ErrorNotification";
import HunkExpandDivider from "./HunkExpandDivider";
import { escapeWhitespace } from "./diffs";
const EMPTY_ANNOTATION_FACTORY = {};
@@ -106,7 +107,7 @@ class DiffFile extends React.Component<Props, State> {
};
}
componentDidUpdate(prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any): void {
componentDidUpdate(prevProps: Readonly<Props>) {
if (this.props.defaultCollapse !== prevProps.defaultCollapse) {
this.setState({
collapsed: this.defaultCollapse()
@@ -350,6 +351,16 @@ class DiffFile extends React.Component<Props, State> {
}
};
getAnchorId(file: File) {
let path: string;
if (file.type === "delete") {
path = file.oldPath;
} else {
path = file.newPath;
}
return escapeWhitespace(path);
}
renderFileTitle = (file: File) => {
if (file.oldPath !== file.newPath && (file.type === "copy" || file.type === "rename")) {
return (
@@ -454,7 +465,7 @@ class DiffFile extends React.Component<Props, State> {
}
return (
<DiffFilePanel className={classNames("panel", "is-size-6")} collapsed={(file && file.isBinary) || collapsed}>
<DiffFilePanel className={classNames("panel", "is-size-6")} collapsed={(file && file.isBinary) || collapsed} id={this.getAnchorId(file)}>
{errorModal}
<div className="panel-heading">
<FlexWrapLevel className="level">

View File

@@ -23,7 +23,7 @@
*/
import { File, FileChangeType, Hunk } from "./DiffTypes";
import { getPath, createHunkIdentifier, createHunkIdentifierFromContext } from "./diffs";
import { getPath, createHunkIdentifier, createHunkIdentifierFromContext, escapeWhitespace } from "./diffs";
describe("tests for diff util functions", () => {
const file = (type: FileChangeType, oldPath: string, newPath: string): File => {
@@ -88,4 +88,15 @@ describe("tests for diff util functions", () => {
expect(identifier).toBe("delete_/etc/passwd_@@ -1,42 +1,39 @@");
});
});
describe("escapeWhitespace tests", () => {
it("should escape whitespaces", () => {
const escaped = escapeWhitespace("spaceship hog");
expect(escaped).toBe("spaceship-hog");
});
it("should escape multiple whitespaces", () => {
const escaped = escapeWhitespace("spaceship heart of gold");
expect(escaped).toBe("spaceship-heart-of-gold");
});
});
});

View File

@@ -39,3 +39,7 @@ export function createHunkIdentifier(file: File, hunk: Hunk) {
export function createHunkIdentifierFromContext(ctx: BaseContext) {
return createHunkIdentifier(ctx.file, ctx.hunk);
}
export function escapeWhitespace(path: string) {
return path.toLowerCase().replace(/\W/g, "-");
}