mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-10 07:25:44 +01:00
implement react-query for all content views (#1708)
Implements react-query and replaces direct apiClient usage for sources, annotate and history content views. Co-authored-by: Eduard Heimbuch <eduard.heimbuch@cloudogu.com>
This commit is contained in:
committed by
GitHub
parent
8e47238bf7
commit
d6e36e7145
@@ -21,27 +21,20 @@
|
|||||||
* 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 { AnnotatedSource, File, Link, Repository } from "@scm-manager/ui-types";
|
||||||
|
import { useQuery } from "react-query";
|
||||||
|
import { apiClient } from "./apiclient";
|
||||||
|
import { ApiResult } from "./base";
|
||||||
|
import { repoQueryKey } from "./keys";
|
||||||
|
|
||||||
import { apiClient } from "@scm-manager/ui-components";
|
export const useAnnotations = (repository: Repository, revision: string, file: File): ApiResult<AnnotatedSource> => {
|
||||||
|
const { isLoading, error, data } = useQuery<AnnotatedSource, Error>(
|
||||||
export function getHistory(url: string) {
|
repoQueryKey(repository, "annotations", revision, file.path),
|
||||||
return apiClient
|
() => apiClient.get((file._links.annotate as Link).href).then((response) => response.json())
|
||||||
.get(url)
|
);
|
||||||
.then(response => response.json())
|
|
||||||
.then(result => {
|
|
||||||
return {
|
return {
|
||||||
changesets: result._embedded.changesets,
|
isLoading,
|
||||||
pageCollection: {
|
error,
|
||||||
_embedded: result._embedded,
|
data,
|
||||||
_links: result._links,
|
|
||||||
page: result.page,
|
|
||||||
pageTotal: result.pageTotal
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
})
|
};
|
||||||
.catch(err => {
|
|
||||||
return {
|
|
||||||
error: err
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -34,7 +34,7 @@ type UseChangesetsRequest = {
|
|||||||
page?: string | number;
|
page?: string | number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const changesetQueryKey = (repository: NamespaceAndName, id: string) => {
|
export const changesetQueryKey = (repository: NamespaceAndName, id: string) => {
|
||||||
return repoQueryKey(repository, "changeset", id);
|
return repoQueryKey(repository, "changeset", id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -21,21 +21,29 @@
|
|||||||
* 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 { apiClient } from "./apiclient";
|
||||||
import { apiClient } from "@scm-manager/ui-components";
|
import { useQuery } from "react-query";
|
||||||
|
import { ApiResult } from "./base";
|
||||||
|
|
||||||
export type ContentType = {
|
export type ContentType = {
|
||||||
type : string;
|
type: string;
|
||||||
language?: string;
|
language?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export function getContentType(url: string) : Promise<ContentType> {
|
function getContentType(url: string): Promise<ContentType> {
|
||||||
return apiClient
|
return apiClient.head(url).then((response) => {
|
||||||
.head(url)
|
|
||||||
.then(response => {
|
|
||||||
return {
|
return {
|
||||||
type: response.headers.get("Content-Type") || "application/octet-stream",
|
type: response.headers.get("Content-Type") || "application/octet-stream",
|
||||||
language: response.headers.get("X-Programming-Language") || undefined
|
language: response.headers.get("X-Programming-Language") || undefined,
|
||||||
};
|
};
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useContentType = (url: string): ApiResult<ContentType> => {
|
||||||
|
const { isLoading, error, data } = useQuery<ContentType, Error>(["contentType", url], () => getContentType(url));
|
||||||
|
return {
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
};
|
||||||
62
scm-ui/ui-api/src/history.ts
Normal file
62
scm-ui/ui-api/src/history.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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 { ApiResult } from "./base";
|
||||||
|
import { Changeset, ChangesetCollection, File, Link, Repository } from "@scm-manager/ui-types";
|
||||||
|
import { useQuery, useQueryClient } from "react-query";
|
||||||
|
import { apiClient } from "./apiclient";
|
||||||
|
import { createQueryString } from "./utils";
|
||||||
|
import { changesetQueryKey } from "./changesets";
|
||||||
|
import { repoQueryKey } from "./keys";
|
||||||
|
|
||||||
|
export type UseHistoryRequest = {
|
||||||
|
page?: number | string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useHistory = (
|
||||||
|
repository: Repository,
|
||||||
|
revision: string,
|
||||||
|
file: File,
|
||||||
|
request?: UseHistoryRequest
|
||||||
|
): ApiResult<ChangesetCollection> => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const link = (file._links.history as Link).href;
|
||||||
|
|
||||||
|
const queryParams: Record<string, string> = {};
|
||||||
|
if (request?.page) {
|
||||||
|
queryParams.page = request.page.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return useQuery<ChangesetCollection, Error>(
|
||||||
|
repoQueryKey(repository, "history", revision, file.path, request?.page || 0),
|
||||||
|
() => apiClient.get(`${link}?${createQueryString(queryParams)}`).then((response) => response.json()),
|
||||||
|
{
|
||||||
|
keepPreviousData: true,
|
||||||
|
onSuccess: (changesets: ChangesetCollection) => {
|
||||||
|
changesets._embedded.changesets.forEach((changeset: Changeset) =>
|
||||||
|
queryClient.setQueryData(changesetQueryKey(repository, changeset.id), changeset)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -50,6 +50,9 @@ export * from "./import";
|
|||||||
export * from "./diff";
|
export * from "./diff";
|
||||||
export * from "./notifications";
|
export * from "./notifications";
|
||||||
export * from "./configLink";
|
export * from "./configLink";
|
||||||
|
export * from "./history";
|
||||||
|
export * from "./contentType";
|
||||||
|
export * from "./annotations";
|
||||||
|
|
||||||
export { default as ApiProvider } from "./ApiProvider";
|
export { default as ApiProvider } from "./ApiProvider";
|
||||||
export * from "./ApiProvider";
|
export * from "./ApiProvider";
|
||||||
|
|||||||
@@ -22,46 +22,39 @@
|
|||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { FC, useEffect, useState } from "react";
|
import React, { FC } from "react";
|
||||||
import { Link, Repository, File, AnnotatedSource } from "@scm-manager/ui-types";
|
import { File, Link, Repository } from "@scm-manager/ui-types";
|
||||||
import { Annotate, apiClient, ErrorNotification, Loading } from "@scm-manager/ui-components";
|
import { Annotate, ErrorNotification, Loading } from "@scm-manager/ui-components";
|
||||||
import { getContentType } from "./contentType";
|
import { useAnnotations, useContentType } from "@scm-manager/ui-api";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
file: File;
|
file: File;
|
||||||
repository: Repository;
|
repository: Repository;
|
||||||
|
revision: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AnnotateView: FC<Props> = ({ file, repository }) => {
|
const AnnotateView: FC<Props> = ({ file, repository, revision }) => {
|
||||||
const [annotation, setAnnotation] = useState<AnnotatedSource | undefined>(undefined);
|
const {
|
||||||
const [language, setLanguage] = useState<string | undefined>(undefined);
|
data: annotation,
|
||||||
const [error, setError] = useState<Error | undefined>(undefined);
|
isLoading: isAnnotationLoading,
|
||||||
const [loading, setLoading] = useState(true);
|
error: annotationLoadError,
|
||||||
|
} = useAnnotations(repository, revision, file);
|
||||||
useEffect(() => {
|
const {
|
||||||
const languagePromise = getContentType((file._links.self as Link).href).then(result =>
|
data: contentType,
|
||||||
setLanguage(result.language)
|
isLoading: isContentTypeLoading,
|
||||||
);
|
error: contentTypeLoadError,
|
||||||
|
} = useContentType((file._links.self as Link).href);
|
||||||
const apiClientPromise = apiClient
|
const error = annotationLoadError || contentTypeLoadError;
|
||||||
.get((file._links.annotate as Link).href)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(setAnnotation);
|
|
||||||
|
|
||||||
Promise.all([languagePromise, apiClientPromise])
|
|
||||||
.then(() => setLoading(false))
|
|
||||||
.catch(setError);
|
|
||||||
}, [file]);
|
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return <ErrorNotification error={error} />;
|
return <ErrorNotification error={error} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!annotation || loading) {
|
if (isAnnotationLoading || isContentTypeLoading || !annotation || !contentType) {
|
||||||
return <Loading />;
|
return <Loading />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Annotate source={{ ...annotation, language }} repository={repository} />;
|
return <Annotate source={{ ...annotation, language: contentType.language }} repository={repository} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AnnotateView;
|
export default AnnotateView;
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ type Props = {
|
|||||||
file: File;
|
file: File;
|
||||||
repository: Repository;
|
repository: Repository;
|
||||||
revision: string;
|
revision: string;
|
||||||
path: string;
|
|
||||||
breadcrumb: React.ReactNode;
|
breadcrumb: React.ReactNode;
|
||||||
error?: Error;
|
error?: Error;
|
||||||
};
|
};
|
||||||
@@ -78,7 +77,7 @@ const BorderLessDiv = styled.div`
|
|||||||
|
|
||||||
export type SourceViewSelection = "source" | "annotations" | "history";
|
export type SourceViewSelection = "source" | "annotations" | "history";
|
||||||
|
|
||||||
const Content: FC<Props> = ({ file, repository, revision, path, breadcrumb, error }) => {
|
const Content: FC<Props> = ({ file, repository, revision, breadcrumb, error }) => {
|
||||||
const [t] = useTranslation("repos");
|
const [t] = useTranslation("repos");
|
||||||
const [collapsed, setCollapsed] = useState(true);
|
const [collapsed, setCollapsed] = useState(true);
|
||||||
const [selected, setSelected] = useState<SourceViewSelection>("source");
|
const [selected, setSelected] = useState<SourceViewSelection>("source");
|
||||||
@@ -215,13 +214,13 @@ const Content: FC<Props> = ({ file, repository, revision, path, breadcrumb, erro
|
|||||||
let body;
|
let body;
|
||||||
switch (selected) {
|
switch (selected) {
|
||||||
case "source":
|
case "source":
|
||||||
body = <SourcesView revision={revision} file={file} repository={repository} path={path} />;
|
body = <SourcesView file={file} repository={repository} revision={revision} />;
|
||||||
break;
|
break;
|
||||||
case "annotations":
|
case "annotations":
|
||||||
body = <AnnotateView file={file} repository={repository} />;
|
body = <AnnotateView file={file} repository={repository} revision={revision} />;
|
||||||
break;
|
break;
|
||||||
case "history":
|
case "history":
|
||||||
body = <HistoryView file={file} repository={repository} />;
|
body = <HistoryView file={file} repository={repository} revision={revision} />;
|
||||||
}
|
}
|
||||||
const header = showHeader(body);
|
const header = showHeader(body);
|
||||||
const moreInformation = showMoreInformation();
|
const moreInformation = showMoreInformation();
|
||||||
|
|||||||
@@ -21,115 +21,39 @@
|
|||||||
* 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 { Changeset, File, PagedCollection, Repository, Link } from "@scm-manager/ui-types";
|
import { File, Repository } from "@scm-manager/ui-types";
|
||||||
import { ChangesetList, ErrorNotification, Loading, StatePaginator } from "@scm-manager/ui-components";
|
import { ChangesetList, ErrorNotification, Loading, StatePaginator } from "@scm-manager/ui-components";
|
||||||
import { getHistory } from "./history";
|
import { useHistory } from "@scm-manager/ui-api";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
file: File;
|
file: File;
|
||||||
repository: Repository;
|
repository: Repository;
|
||||||
|
revision: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
const HistoryView: FC<Props> = ({ repository, file, revision }) => {
|
||||||
loaded: boolean;
|
const [page, setPage] = useState(0);
|
||||||
changesets: Changeset[];
|
const { error, isLoading, data: history } = useHistory(repository, revision, file, { page });
|
||||||
page: number;
|
|
||||||
pageCollection?: PagedCollection;
|
|
||||||
error?: Error;
|
|
||||||
currentRevision: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
class HistoryView extends React.Component<Props, State> {
|
if (!history || isLoading) {
|
||||||
constructor(props: Props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
loaded: false,
|
|
||||||
page: 1,
|
|
||||||
changesets: [],
|
|
||||||
currentRevision: ""
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const { file } = this.props;
|
|
||||||
if (file) {
|
|
||||||
this.updateHistory((file._links.history as Link).href);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate() {
|
|
||||||
const { file } = this.props;
|
|
||||||
const { currentRevision } = this.state;
|
|
||||||
if (file?.revision !== currentRevision) {
|
|
||||||
this.updateHistory((file._links.history as Link).href);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateHistory(link: string) {
|
|
||||||
const { file } = this.props;
|
|
||||||
getHistory(link)
|
|
||||||
.then(result => {
|
|
||||||
this.setState({
|
|
||||||
...this.state,
|
|
||||||
loaded: true,
|
|
||||||
changesets: result.changesets,
|
|
||||||
pageCollection: result.pageCollection,
|
|
||||||
page: result.pageCollection.page,
|
|
||||||
currentRevision: file.revision
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(error =>
|
|
||||||
this.setState({
|
|
||||||
...this.state,
|
|
||||||
error,
|
|
||||||
loaded: true
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
updatePage(page: number) {
|
|
||||||
const { file } = this.props;
|
|
||||||
const internalPage = page - 1;
|
|
||||||
this.updateHistory((file._links.history as Link).href + "?page=" + internalPage.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
showHistory() {
|
|
||||||
const { repository } = this.props;
|
|
||||||
const { changesets, page, pageCollection } = this.state;
|
|
||||||
const currentPage = page + 1;
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="panel-block">
|
|
||||||
<ChangesetList repository={repository} changesets={changesets} />
|
|
||||||
</div>
|
|
||||||
<div className="panel-footer">
|
|
||||||
<StatePaginator
|
|
||||||
page={currentPage}
|
|
||||||
collection={pageCollection}
|
|
||||||
updatePage={(newPage: number) => this.updatePage(newPage)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { file } = this.props;
|
|
||||||
const { loaded, error } = this.state;
|
|
||||||
|
|
||||||
if (!file || !loaded) {
|
|
||||||
return <Loading />;
|
return <Loading />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return <ErrorNotification error={error} />;
|
return <ErrorNotification error={error} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const history = this.showHistory();
|
return (
|
||||||
|
<>
|
||||||
return <>{history}</>;
|
<div className="panel-block">
|
||||||
}
|
<ChangesetList repository={repository} changesets={history._embedded.changesets} />
|
||||||
}
|
</div>
|
||||||
|
<div className="panel-footer">
|
||||||
|
<StatePaginator page={page + 1} collection={history} updatePage={(newPage: number) => setPage(newPage - 1)} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default HistoryView;
|
export default HistoryView;
|
||||||
|
|||||||
@@ -176,7 +176,6 @@ const Sources: FC<Props> = ({ repository, branches, selectedBranch, baseUrl }) =
|
|||||||
file={file}
|
file={file}
|
||||||
repository={repository}
|
repository={repository}
|
||||||
revision={revision || file.revision}
|
revision={revision || file.revision}
|
||||||
path={path}
|
|
||||||
breadcrumb={renderBreadcrumb()}
|
breadcrumb={renderBreadcrumb()}
|
||||||
error={error}
|
error={error}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -21,16 +21,17 @@
|
|||||||
* 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 } from "react";
|
||||||
import SourcecodeViewer from "../components/content/SourcecodeViewer";
|
import SourcecodeViewer from "../components/content/SourcecodeViewer";
|
||||||
import ImageViewer from "../components/content/ImageViewer";
|
import ImageViewer from "../components/content/ImageViewer";
|
||||||
import DownloadViewer from "../components/content/DownloadViewer";
|
import DownloadViewer from "../components/content/DownloadViewer";
|
||||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||||
import { getContentType } from "./contentType";
|
import { File, Link, Repository } from "@scm-manager/ui-types";
|
||||||
import { File, Repository } from "@scm-manager/ui-types";
|
|
||||||
import { ErrorNotification, Loading } from "@scm-manager/ui-components";
|
import { ErrorNotification, Loading } from "@scm-manager/ui-components";
|
||||||
import SwitchableMarkdownViewer from "../components/content/SwitchableMarkdownViewer";
|
import SwitchableMarkdownViewer from "../components/content/SwitchableMarkdownViewer";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
import { useContentType } from "@scm-manager/ui-api";
|
||||||
|
|
||||||
const NoSpacingSyntaxHighlighterContainer = styled.div`
|
const NoSpacingSyntaxHighlighterContainer = styled.div`
|
||||||
& pre {
|
& pre {
|
||||||
@@ -43,66 +44,33 @@ type Props = {
|
|||||||
repository: Repository;
|
repository: Repository;
|
||||||
file: File;
|
file: File;
|
||||||
revision: string;
|
revision: string;
|
||||||
path: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
const SourcesView: FC<Props> = ({ file, repository, revision }) => {
|
||||||
contentType: string;
|
const { data: contentTypeData, error, isLoading } = useContentType((file._links.self as Link).href);
|
||||||
language: string;
|
|
||||||
loaded: boolean;
|
|
||||||
error?: Error;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SourcesView extends React.Component<Props, State> {
|
if (error) {
|
||||||
constructor(props: Props) {
|
return <ErrorNotification error={error} />;
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
contentType: "",
|
|
||||||
language: "",
|
|
||||||
loaded: false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
if (!contentTypeData || isLoading) {
|
||||||
const { file } = this.props;
|
return <Loading />;
|
||||||
getContentType(file._links.self.href)
|
|
||||||
.then((result) => {
|
|
||||||
this.setState({
|
|
||||||
...this.state,
|
|
||||||
contentType: result.type,
|
|
||||||
language: result.language,
|
|
||||||
loaded: true,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
this.setState({
|
|
||||||
...this.state,
|
|
||||||
error,
|
|
||||||
loaded: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createBasePath() {
|
let sources;
|
||||||
const { repository, revision } = this.props;
|
|
||||||
return `/repo/${repository.namespace}/${repository.name}/code/sources/${revision}/`;
|
|
||||||
}
|
|
||||||
|
|
||||||
showSources() {
|
const { type: contentType, language } = contentTypeData;
|
||||||
const { file, revision } = this.props;
|
const basePath = `/repo/${repository.namespace}/${repository.name}/code/sources/${revision}/`;
|
||||||
const { contentType, language } = this.state;
|
|
||||||
const basePath = this.createBasePath();
|
|
||||||
if (contentType.startsWith("image/")) {
|
if (contentType.startsWith("image/")) {
|
||||||
return <ImageViewer file={file} />;
|
sources = <ImageViewer file={file} />;
|
||||||
} else if (contentType.includes("markdown") || (language && language.toLowerCase() === "markdown")) {
|
} else if (contentType.includes("markdown") || (language && language.toLowerCase() === "markdown")) {
|
||||||
return <SwitchableMarkdownViewer file={file} basePath={basePath} />;
|
sources = <SwitchableMarkdownViewer file={file} basePath={basePath} />;
|
||||||
} else if (language) {
|
} else if (language) {
|
||||||
return <SourcecodeViewer file={file} language={language} />;
|
sources = <SourcecodeViewer file={file} language={language} />;
|
||||||
} else if (contentType.startsWith("text/")) {
|
} else if (contentType.startsWith("text/")) {
|
||||||
return <SourcecodeViewer file={file} language="none" />;
|
sources = <SourcecodeViewer file={file} language="none" />;
|
||||||
} else {
|
} else {
|
||||||
return (
|
sources = (
|
||||||
<ExtensionPoint
|
<ExtensionPoint
|
||||||
name="repos.sources.view"
|
name="repos.sources.view"
|
||||||
props={{
|
props={{
|
||||||
@@ -116,23 +84,8 @@ class SourcesView extends React.Component<Props, State> {
|
|||||||
</ExtensionPoint>
|
</ExtensionPoint>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { file } = this.props;
|
|
||||||
const { loaded, error } = this.state;
|
|
||||||
|
|
||||||
if (!file || !loaded) {
|
|
||||||
return <Loading />;
|
|
||||||
}
|
|
||||||
if (error) {
|
|
||||||
return <ErrorNotification error={error} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sources = this.showSources();
|
|
||||||
|
|
||||||
return <NoSpacingSyntaxHighlighterContainer className="panel-block">{sources}</NoSpacingSyntaxHighlighterContainer>;
|
return <NoSpacingSyntaxHighlighterContainer className="panel-block">{sources}</NoSpacingSyntaxHighlighterContainer>;
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export default SourcesView;
|
export default SourcesView;
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 fetchMock from "fetch-mock";
|
|
||||||
import { getContentType } from "./contentType";
|
|
||||||
|
|
||||||
describe("get content type", () => {
|
|
||||||
const CONTENT_URL = "/repositories/scmadmin/TestRepo/content/testContent";
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
fetchMock.reset();
|
|
||||||
fetchMock.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return content", done => {
|
|
||||||
const headers = {
|
|
||||||
"Content-Type": "application/text",
|
|
||||||
"X-Programming-Language": "JAVA"
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchMock.head("/api/v2" + CONTENT_URL, {
|
|
||||||
headers
|
|
||||||
});
|
|
||||||
|
|
||||||
getContentType(CONTENT_URL).then(content => {
|
|
||||||
expect(content.type).toBe("application/text");
|
|
||||||
expect(content.language).toBe("JAVA");
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 fetchMock from "fetch-mock";
|
|
||||||
import { getHistory } from "./history";
|
|
||||||
|
|
||||||
describe("get content type", () => {
|
|
||||||
const FILE_URL = "/repositories/scmadmin/TestRepo/history/file";
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
fetchMock.reset();
|
|
||||||
fetchMock.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
const history = {
|
|
||||||
page: 0,
|
|
||||||
pageTotal: 10,
|
|
||||||
_links: {
|
|
||||||
self: {
|
|
||||||
href: "/repositories/scmadmin/TestRepo/history/file?page=0&pageSize=10"
|
|
||||||
},
|
|
||||||
first: {
|
|
||||||
href: "/repositories/scmadmin/TestRepo/history/file?page=0&pageSize=10"
|
|
||||||
},
|
|
||||||
next: {
|
|
||||||
href: "/repositories/scmadmin/TestRepo/history/file?page=1&pageSize=10"
|
|
||||||
},
|
|
||||||
last: {
|
|
||||||
href: "/repositories/scmadmin/TestRepo/history/file?page=9&pageSize=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_embedded: {
|
|
||||||
changesets: [
|
|
||||||
{
|
|
||||||
id: "1234"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "2345"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
it("should return history", done => {
|
|
||||||
fetchMock.get("/api/v2" + FILE_URL, history);
|
|
||||||
|
|
||||||
getHistory(FILE_URL).then(content => {
|
|
||||||
expect(content.changesets).toEqual(history._embedded.changesets);
|
|
||||||
expect(content.pageCollection.page).toEqual(history.page);
|
|
||||||
expect(content.pageCollection.pageTotal).toEqual(history.pageTotal);
|
|
||||||
expect(content.pageCollection._links).toEqual(history._links);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user