mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-07 22:15:45 +01:00
Fixed missing update if content of diff changes (#1714)
* Fixed missing update if content of diff changes * Add property to disable automatic refetch in diffs Co-authored-by: René Pfeuffer <rene.pfeuffer@cloudogu.com>
This commit is contained in:
2
gradle/changelog/diff_disable_refetch_on_focus.yaml
Normal file
2
gradle/changelog/diff_disable_refetch_on_focus.yaml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
- type: added
|
||||||
|
description: Option to diable automatic refresh for diff view on window focus change ([#1714](https://github.com/scm-manager/scm-manager/pull/1714))
|
||||||
2
gradle/changelog/diff_update_content.yaml
Normal file
2
gradle/changelog/diff_update_content.yaml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
- type: fixed
|
||||||
|
description: Missing update if content of diff changes ([#1714](https://github.com/scm-manager/scm-manager/pull/1714))
|
||||||
@@ -30,9 +30,14 @@ import { Diff, Link } from "@scm-manager/ui-types";
|
|||||||
|
|
||||||
type UseDiffOptions = {
|
type UseDiffOptions = {
|
||||||
limit?: number;
|
limit?: number;
|
||||||
|
refetchOnWindowFocus?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useDiff = (link: string, options: UseDiffOptions = {}) => {
|
const defaultOptions: UseDiffOptions = {
|
||||||
|
refetchOnWindowFocus: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useDiff = (link: string, options: UseDiffOptions = defaultOptions) => {
|
||||||
let initialLink = link;
|
let initialLink = link;
|
||||||
if (options.limit) {
|
if (options.limit) {
|
||||||
const separator = initialLink.includes("?") ? "&" : "?";
|
const separator = initialLink.includes("?") ? "&" : "?";
|
||||||
@@ -41,7 +46,7 @@ export const useDiff = (link: string, options: UseDiffOptions = {}) => {
|
|||||||
const { isLoading, error, data, isFetchingNextPage, fetchNextPage } = useInfiniteQuery<Diff, Error, Diff>(
|
const { isLoading, error, data, isFetchingNextPage, fetchNextPage } = useInfiniteQuery<Diff, Error, Diff>(
|
||||||
["link", link],
|
["link", link],
|
||||||
({ pageParam }) => {
|
({ pageParam }) => {
|
||||||
return apiClient.get(pageParam || initialLink).then(response => {
|
return apiClient.get(pageParam || initialLink).then((response) => {
|
||||||
const contentType = response.headers.get("Content-Type");
|
const contentType = response.headers.get("Content-Type");
|
||||||
if (contentType && contentType.toLowerCase() === "application/vnd.scmm-diffparsed+json;v=2") {
|
if (contentType && contentType.toLowerCase() === "application/vnd.scmm-diffparsed+json;v=2") {
|
||||||
return response.json();
|
return response.json();
|
||||||
@@ -49,18 +54,19 @@ export const useDiff = (link: string, options: UseDiffOptions = {}) => {
|
|||||||
return response
|
return response
|
||||||
.text()
|
.text()
|
||||||
.then(parser.parse)
|
.then(parser.parse)
|
||||||
.then(parsedGit => {
|
.then((parsedGit) => {
|
||||||
return {
|
return {
|
||||||
files: parsedGit,
|
files: parsedGit,
|
||||||
partial: false,
|
partial: false,
|
||||||
_links: {}
|
_links: {},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
getNextPageParam: lastPage => (lastPage._links.next as Link)?.href
|
getNextPageParam: (lastPage) => (lastPage._links.next as Link)?.href,
|
||||||
|
refetchOnWindowFocus: options.refetchOnWindowFocus,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -71,7 +77,7 @@ export const useDiff = (link: string, options: UseDiffOptions = {}) => {
|
|||||||
fetchNextPage: () => {
|
fetchNextPage: () => {
|
||||||
fetchNextPage();
|
fetchNextPage();
|
||||||
},
|
},
|
||||||
data: merge(data?.pages)
|
data: merge(data?.pages),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -79,9 +85,9 @@ const merge = (diffs?: Diff[]): Diff | undefined => {
|
|||||||
if (!diffs || diffs.length === 0) {
|
if (!diffs || diffs.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const joinedFiles = diffs.flatMap(diff => diff.files);
|
const joinedFiles = diffs.flatMap((diff) => diff.files);
|
||||||
return {
|
return {
|
||||||
...diffs[diffs.length - 1],
|
...diffs[diffs.length - 1],
|
||||||
files: joinedFiles
|
files: joinedFiles,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -36,9 +36,10 @@ import { getPath } from "./diffs";
|
|||||||
import DiffButton from "./DiffButton";
|
import DiffButton from "./DiffButton";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { MemoryRouter } from "react-router-dom";
|
import { MemoryRouter } from "react-router-dom";
|
||||||
import { one, two } from "../__resources__/changesets";
|
import { two } from "../__resources__/changesets";
|
||||||
import { Changeset, FileDiff } from "@scm-manager/ui-types";
|
import { Changeset, FileDiff } from "@scm-manager/ui-types";
|
||||||
import JumpToFileButton from "./JumpToFileButton";
|
import JumpToFileButton from "./JumpToFileButton";
|
||||||
|
import Button from "../buttons/Button";
|
||||||
|
|
||||||
const diffFiles = parser.parse(simpleDiff);
|
const diffFiles = parser.parse(simpleDiff);
|
||||||
|
|
||||||
@@ -48,15 +49,15 @@ const Container = styled.div`
|
|||||||
|
|
||||||
const RoutingDecorator = (story: () => ReactNode) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>;
|
const RoutingDecorator = (story: () => ReactNode) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>;
|
||||||
|
|
||||||
const fileControlFactory: (changeset: Changeset) => FileControlFactory = changeset => file => {
|
const fileControlFactory: (changeset: Changeset) => FileControlFactory = (changeset) => (file) => {
|
||||||
const baseUrl = "/repo/hitchhiker/heartOfGold/code/changeset";
|
const baseUrl = "/repo/hitchhiker/heartOfGold/code/changeset";
|
||||||
const sourceLink = {
|
const sourceLink = {
|
||||||
url: `${baseUrl}/${changeset.id}/${file.newPath}/`,
|
url: `${baseUrl}/${changeset.id}/${file.newPath}/`,
|
||||||
label: "Jump to source"
|
label: "Jump to source",
|
||||||
};
|
};
|
||||||
const targetLink = changeset._embedded?.parents?.length === 1 && {
|
const targetLink = changeset._embedded?.parents?.length === 1 && {
|
||||||
url: `${baseUrl}/${changeset._embedded.parents[0].id}/${file.oldPath}`,
|
url: `${baseUrl}/${changeset._embedded.parents[0].id}/${file.oldPath}`,
|
||||||
label: "Jump to target"
|
label: "Jump to target",
|
||||||
};
|
};
|
||||||
|
|
||||||
const links = [];
|
const links = [];
|
||||||
@@ -82,7 +83,7 @@ const fileControlFactory: (changeset: Changeset) => FileControlFactory = changes
|
|||||||
|
|
||||||
storiesOf("Diff", module)
|
storiesOf("Diff", module)
|
||||||
.addDecorator(RoutingDecorator)
|
.addDecorator(RoutingDecorator)
|
||||||
.addDecorator(storyFn => <Container>{storyFn()}</Container>)
|
.addDecorator((storyFn) => <Container>{storyFn()}</Container>)
|
||||||
.add("Default", () => <Diff diff={diffFiles} />)
|
.add("Default", () => <Diff diff={diffFiles} />)
|
||||||
.add("Side-By-Side", () => <Diff diff={diffFiles} sideBySide={true} />)
|
.add("Side-By-Side", () => <Diff diff={diffFiles} sideBySide={true} />)
|
||||||
.add("Collapsed", () => <Diff diff={diffFiles} defaultCollapse={true} fileControlFactory={fileControlFactory(two)} />)
|
.add("Collapsed", () => <Diff diff={diffFiles} defaultCollapse={true} fileControlFactory={fileControlFactory(two)} />)
|
||||||
@@ -101,15 +102,15 @@ storiesOf("Diff", module)
|
|||||||
.add("File Annotation", () => (
|
.add("File Annotation", () => (
|
||||||
<Diff
|
<Diff
|
||||||
diff={diffFiles}
|
diff={diffFiles}
|
||||||
fileAnnotationFactory={file => [<p key={file.newPath}>Custom File annotation for {file.newPath}</p>]}
|
fileAnnotationFactory={(file) => [<p key={file.newPath}>Custom File annotation for {file.newPath}</p>]}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
.add("Line Annotation", () => (
|
.add("Line Annotation", () => (
|
||||||
<Diff
|
<Diff
|
||||||
diff={diffFiles}
|
diff={diffFiles}
|
||||||
annotationFactory={ctx => {
|
annotationFactory={(ctx) => {
|
||||||
return {
|
return {
|
||||||
N2: <p key="N2">Line Annotation</p>
|
N2: <p key="N2">Line Annotation</p>,
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -166,4 +167,20 @@ storiesOf("Diff", module)
|
|||||||
});
|
});
|
||||||
return <Diff diff={filesWithLanguage} />;
|
return <Diff diff={filesWithLanguage} />;
|
||||||
})
|
})
|
||||||
.add("WithLinkToFile", () => <Diff diff={diffFiles} />);
|
.add("WithLinkToFile", () => <Diff diff={diffFiles} />)
|
||||||
|
.add("Changing Content", () => {
|
||||||
|
const ChangingContentDiff = () => {
|
||||||
|
const [markdown, setMarkdown] = useState(false);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Button className="mb-5" action={() => setMarkdown((m) => !m)}>
|
||||||
|
Change content
|
||||||
|
</Button>
|
||||||
|
{/* @ts-ignore */}
|
||||||
|
<Diff diff={markdown ? markdownDiff.files : diffFiles} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return <ChangingContentDiff />;
|
||||||
|
});
|
||||||
|
|||||||
@@ -21,78 +21,57 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
import React from "react";
|
import React, { FC, useState } from "react";
|
||||||
import DiffFile from "./DiffFile";
|
import DiffFile from "./DiffFile";
|
||||||
import { DiffObjectProps, FileControlFactory } from "./DiffTypes";
|
import { DiffObjectProps, FileControlFactory } from "./DiffTypes";
|
||||||
import { FileDiff } from "@scm-manager/ui-types";
|
import { FileDiff } from "@scm-manager/ui-types";
|
||||||
import { escapeWhitespace } from "./diffs";
|
import { escapeWhitespace } from "./diffs";
|
||||||
import Notification from "../Notification";
|
import Notification from "../Notification";
|
||||||
import { WithTranslation, withTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { RouteComponentProps, withRouter } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
|
import useScrollToElement from "../useScrollToElement";
|
||||||
|
|
||||||
type Props = RouteComponentProps &
|
type Props = DiffObjectProps & {
|
||||||
WithTranslation &
|
|
||||||
DiffObjectProps & {
|
|
||||||
diff: FileDiff[];
|
diff: FileDiff[];
|
||||||
fileControlFactory?: FileControlFactory;
|
fileControlFactory?: FileControlFactory;
|
||||||
};
|
|
||||||
|
|
||||||
type State = {
|
|
||||||
contentRef?: HTMLElement | null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function getAnchorSelector(uriHashContent: string) {
|
const createKey = (file: FileDiff) => {
|
||||||
|
return `${file.oldPath}@${file.oldRevision}/${file.newPath}@${file.newRevision}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAnchorSelector = (uriHashContent: string) => {
|
||||||
return "#" + escapeWhitespace(decodeURIComponent(uriHashContent));
|
return "#" + escapeWhitespace(decodeURIComponent(uriHashContent));
|
||||||
}
|
};
|
||||||
|
|
||||||
class Diff extends React.Component<Props, State> {
|
const Diff: FC<Props> = ({ diff, ...fileProps }) => {
|
||||||
static defaultProps: Partial<Props> = {
|
const [t] = useTranslation("repos");
|
||||||
sideBySide: false
|
const [contentRef, setContentRef] = useState<HTMLElement | null>();
|
||||||
};
|
const { hash } = useLocation();
|
||||||
|
useScrollToElement(
|
||||||
constructor(props: Readonly<Props>) {
|
contentRef,
|
||||||
super(props);
|
() => {
|
||||||
this.state = {
|
const match = hash.match(/^#diff-(.*)$/);
|
||||||
contentRef: undefined
|
if (match) {
|
||||||
};
|
return getAnchorSelector(match[1]);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
componentDidUpdate() {
|
hash
|
||||||
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 (
|
return (
|
||||||
<div ref={el => this.setState({ contentRef: el })}>
|
<div ref={setContentRef}>
|
||||||
{diff.length === 0 ? (
|
{diff.length === 0 ? (
|
||||||
<Notification type="info">{t("diff.noDiffFound")}</Notification>
|
<Notification type="info">{t("diff.noDiffFound")}</Notification>
|
||||||
) : (
|
) : (
|
||||||
diff.map((file, index) => <DiffFile key={index} file={file} {...fileProps} {...this.props} />)
|
diff.map((file) => <DiffFile key={createKey(file)} file={file} {...fileProps} />)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export default withRouter(withTranslation("repos")(Diff));
|
Diff.defaultProps = {
|
||||||
|
sideBySide: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Diff;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import styled from "styled-components";
|
|||||||
type Props = DiffObjectProps & {
|
type Props = DiffObjectProps & {
|
||||||
url: string;
|
url: string;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
|
refetchOnWindowFocus?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type NotificationProps = {
|
type NotificationProps = {
|
||||||
@@ -59,8 +60,8 @@ const PartialNotification: FC<NotificationProps> = ({ fetchNextPage, isFetchingN
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const LoadingDiff: FC<Props> = ({ url, limit, ...props }) => {
|
const LoadingDiff: FC<Props> = ({ url, limit, refetchOnWindowFocus, ...props }) => {
|
||||||
const { error, isLoading, data, fetchNextPage, isFetchingNextPage } = useDiff(url, { limit });
|
const { error, isLoading, data, fetchNextPage, isFetchingNextPage } = useDiff(url, { limit, refetchOnWindowFocus });
|
||||||
const [t] = useTranslation("repos");
|
const [t] = useTranslation("repos");
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -86,7 +87,8 @@ const LoadingDiff: FC<Props> = ({ url, limit, ...props }) => {
|
|||||||
|
|
||||||
LoadingDiff.defaultProps = {
|
LoadingDiff.defaultProps = {
|
||||||
limit: 25,
|
limit: 25,
|
||||||
sideBySide: false
|
sideBySide: false,
|
||||||
|
refetchOnWindowFocus: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LoadingDiff;
|
export default LoadingDiff;
|
||||||
|
|||||||
Reference in New Issue
Block a user