mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-12-24 01:09:48 +01:00
Merge branch 'develop' into feature/template_plugin
This commit is contained in:
@@ -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
@@ -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));
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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, "-");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user