Merge pull request #1080 from scm-manager/feature/favourite_repository

Feature/favourite repository
This commit is contained in:
eheimbuch
2020-03-26 12:54:13 +01:00
committed by GitHub
19 changed files with 719 additions and 10 deletions

View File

@@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Libc based restart strategy for posix operating systems ([#1079](https://github.com/scm-manager/scm-manager/pull/1079))
- Simple restart strategy with System.exit ([#1079](https://github.com/scm-manager/scm-manager/pull/1079))
- Notification if restart is not supported on the underlying platform ([#1079](https://github.com/scm-manager/scm-manager/pull/1079))
- Extension point before title in repository cards ([#1080](https://github.com/scm-manager/scm-manager/pull/1080))
- Extension point after title on repository detail page ([#1080](https://github.com/scm-manager/scm-manager/pull/1080))
### Changed
- Update resteasy to version 4.5.2.Final

View File

@@ -0,0 +1,45 @@
/*
* 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.
*/
package sonia.scm.web.api;
import de.otto.edison.hal.HalRepresentation;
import sonia.scm.repository.Repository;
/**
* Maps a repository to a hal representation.
* This is especially useful if a plugin would deliver a repository to the frontend.
*
* @since 2.0.0
*/
public interface RepositoryToHalMapper {
/**
* Returns the hal representation of the repository.
*
* @param repository repository to map
* @return hal representation
*/
HalRepresentation map(Repository repository);
}

View File

@@ -27,7 +27,7 @@ import styled from "styled-components";
import { Link } from "react-router-dom";
type Props = {
title: string;
title: ReactNode;
description?: string;
avatar: ReactNode;
contentRight?: ReactNode;
@@ -100,7 +100,7 @@ export default class CardColumn extends React.Component<Props> {
<div className="is-flex">
<ContentLeft className="content">
<p className="shorten-text is-marginless">
<strong>{title}</strong>
{title}
</p>
<p className="shorten-text">{description}</p>
</ContentLeft>

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,67 @@
/*
* 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.
*/
export default {
contact: "heart-of-gold@hitchhiher.com",
creationDate: "2020-03-23T08:26:01.164Z",
description: "The starship Heart of Gold was the first spacecraft to make use of the Infinite Improbability Drive",
healthCheckFailures: [],
lastModified: "2020-03-23T08:26:01.876Z",
namespace: "hitchhiher",
name: "heartOfGold",
type: "git",
_links: {
self: { href: "http://localhost:8081/scm/api/v2/repositories/scmadmin/Git" },
delete: { href: "http://localhost:8081/scm/api/v2/repositories/scmadmin/Git" },
update: { href: "http://localhost:8081/scm/api/v2/repositories/scmadmin/Git" },
permissions: { href: "http://localhost:8081/scm/api/v2/repositories/scmadmin/Git/permissions/" },
protocol: [
{ href: "ssh://scmadmin@localhost:4567/repo/scmadmin/Git", name: "ssh" },
{ href: "http://localhost:8081/scm/repo/scmadmin/Git", name: "http" }
],
tags: { href: "http://localhost:8081/scm/api/v2/repositories/scmadmin/Git/tags/" },
branches: { href: "http://localhost:8081/scm/api/v2/repositories/scmadmin/Git/branches/" },
incomingChangesets: {
href: "http://localhost:8081/scm/api/v2/repositories/scmadmin/Git/incoming/{source}/{target}/changesets",
templated: true
},
incomingDiff: {
href: "http://localhost:8081/scm/api/v2/repositories/scmadmin/Git/incoming/{source}/{target}/diff",
templated: true
},
incomingDiffParsed: {
href: "http://localhost:8081/scm/api/v2/repositories/scmadmin/Git/incoming/{source}/{target}/diff/parsed",
templated: true
},
changesets: { href: "http://localhost:8081/scm/api/v2/repositories/scmadmin/Git/changesets/" },
sources: { href: "http://localhost:8081/scm/api/v2/repositories/scmadmin/Git/sources/" },
authorMappingConfig: {
href: "http://localhost:8081/scm/api/v2/authormapping/configuration/scmadmin/Git"
},
unfavorize: { href: "http://localhost:8081/scm/api/v2/unfavorize/scmadmin/Git" },
favorites: [
{ href: "http://localhost:8081/scm/api/v2/unfavorize/scmadmin/Git", name: "unfavorize" },
{ href: "http://localhost:8081/scm/api/v2/favorize/scmadmin/Git", name: "favorize" }
]
}
};

View File

@@ -34188,6 +34188,472 @@ exports[`Storyshots MarkdownView Xml Code Block 1`] = `
</div>
`;
exports[`Storyshots RepositoryEntry Avatar EP 1`] = `
<div
className="RepositoryEntrystories__Spacing-toppdg-0 khfzcK box box-link-shadow"
>
<a
className="overlay-column"
href="/repo/hitchhiher/heartOfGold"
onClick={[Function]}
/>
<article
className="CardColumn__NoEventWrapper-sc-1w6lsih-0 kZKqpc media"
>
<figure
className="CardColumn__AvatarWrapper-sc-1w6lsih-1 bZyfne media-left"
>
<p
className="image is-64x64"
>
<img
alt="Logo"
src="test-file-stub"
/>
</p>
</figure>
<div
className="CardColumn__FlexFullHeight-sc-1w6lsih-2 cAdfGj media-content text-box is-flex"
>
<div
className="is-flex"
>
<div
className="CardColumn__ContentLeft-sc-1w6lsih-4 dumWkw content"
>
<p
className="shorten-text is-marginless"
>
<strong>
heartOfGold
</strong>
</p>
<p
className="shorten-text"
>
The starship Heart of Gold was the first spacecraft to make use of the Infinite Improbability Drive
</p>
</div>
<div
className="CardColumn__ContentRight-sc-1w6lsih-5 kyEPRa"
/>
</div>
<div
className="CardColumn__FooterWrapper-sc-1w6lsih-3 jlTqlS level is-flex"
>
<div
className="level-left is-hidden-mobile"
>
<a
className="RepositoryEntryLink__PointerEventsLink-sc-1hpqj0w-0 gtboNN level-item"
href="/repo/hitchhiher/heartOfGold/branches"
onClick={[Function]}
>
<i
className="fas fa-code-branch has-text-inherit fa-lg"
/>
</a>
<a
className="RepositoryEntryLink__PointerEventsLink-sc-1hpqj0w-0 gtboNN level-item"
href="/repo/hitchhiher/heartOfGold/code/changesets/"
onClick={[Function]}
>
<i
className="fas fa-exchange-alt has-text-inherit fa-lg"
/>
</a>
<a
className="RepositoryEntryLink__PointerEventsLink-sc-1hpqj0w-0 gtboNN level-item"
href="/repo/hitchhiher/heartOfGold/code/sources/"
onClick={[Function]}
>
<i
className="fas fa-code has-text-inherit fa-lg"
/>
</a>
<a
className="RepositoryEntryLink__PointerEventsLink-sc-1hpqj0w-0 gtboNN level-item"
href="/repo/hitchhiher/heartOfGold/settings/general"
onClick={[Function]}
>
<i
className="fas fa-cog has-text-inherit fa-lg"
/>
</a>
</div>
<div
className="level-right is-mobile is-marginless"
>
<small
className="level-item"
>
<time
className="DateFromNow__DateElement-sc-16hnz72-0 ehlCzi"
title="2020-03-23 09:26:01"
>
3 days ago
</time>
</small>
</div>
</div>
</div>
</article>
</div>
`;
exports[`Storyshots RepositoryEntry Before Title EP 1`] = `
<div
className="RepositoryEntrystories__Spacing-toppdg-0 khfzcK box box-link-shadow"
>
<a
className="overlay-column"
href="/repo/hitchhiher/heartOfGold"
onClick={[Function]}
/>
<article
className="CardColumn__NoEventWrapper-sc-1w6lsih-0 kZKqpc media"
>
<figure
className="CardColumn__AvatarWrapper-sc-1w6lsih-1 bZyfne media-left"
>
<p
className="image is-64x64"
>
<img
alt="Logo"
src="/images/blib.jpg"
/>
</p>
</figure>
<div
className="CardColumn__FlexFullHeight-sc-1w6lsih-2 cAdfGj media-content text-box is-flex"
>
<div
className="is-flex"
>
<div
className="CardColumn__ContentLeft-sc-1w6lsih-4 dumWkw content"
>
<p
className="shorten-text is-marginless"
>
<i
className="fas fa-star"
/>
<strong>
heartOfGold
</strong>
</p>
<p
className="shorten-text"
>
The starship Heart of Gold was the first spacecraft to make use of the Infinite Improbability Drive
</p>
</div>
<div
className="CardColumn__ContentRight-sc-1w6lsih-5 kyEPRa"
/>
</div>
<div
className="CardColumn__FooterWrapper-sc-1w6lsih-3 jlTqlS level is-flex"
>
<div
className="level-left is-hidden-mobile"
>
<a
className="RepositoryEntryLink__PointerEventsLink-sc-1hpqj0w-0 gtboNN level-item"
href="/repo/hitchhiher/heartOfGold/branches"
onClick={[Function]}
>
<i
className="fas fa-code-branch has-text-inherit fa-lg"
/>
</a>
<a
className="RepositoryEntryLink__PointerEventsLink-sc-1hpqj0w-0 gtboNN level-item"
href="/repo/hitchhiher/heartOfGold/code/changesets/"
onClick={[Function]}
>
<i
className="fas fa-exchange-alt has-text-inherit fa-lg"
/>
</a>
<a
className="RepositoryEntryLink__PointerEventsLink-sc-1hpqj0w-0 gtboNN level-item"
href="/repo/hitchhiher/heartOfGold/code/sources/"
onClick={[Function]}
>
<i
className="fas fa-code has-text-inherit fa-lg"
/>
</a>
<a
className="RepositoryEntryLink__PointerEventsLink-sc-1hpqj0w-0 gtboNN level-item"
href="/repo/hitchhiher/heartOfGold/settings/general"
onClick={[Function]}
>
<i
className="fas fa-cog has-text-inherit fa-lg"
/>
</a>
</div>
<div
className="level-right is-mobile is-marginless"
>
<small
className="level-item"
>
<time
className="DateFromNow__DateElement-sc-16hnz72-0 ehlCzi"
title="2020-03-23 09:26:01"
>
3 days ago
</time>
</small>
</div>
</div>
</div>
</article>
</div>
`;
exports[`Storyshots RepositoryEntry Default 1`] = `
<div
className="RepositoryEntrystories__Spacing-toppdg-0 khfzcK box box-link-shadow"
>
<a
className="overlay-column"
href="/repo/hitchhiher/heartOfGold"
onClick={[Function]}
/>
<article
className="CardColumn__NoEventWrapper-sc-1w6lsih-0 kZKqpc media"
>
<figure
className="CardColumn__AvatarWrapper-sc-1w6lsih-1 bZyfne media-left"
>
<p
className="image is-64x64"
>
<img
alt="Logo"
src="/images/blib.jpg"
/>
</p>
</figure>
<div
className="CardColumn__FlexFullHeight-sc-1w6lsih-2 cAdfGj media-content text-box is-flex"
>
<div
className="is-flex"
>
<div
className="CardColumn__ContentLeft-sc-1w6lsih-4 dumWkw content"
>
<p
className="shorten-text is-marginless"
>
<strong>
heartOfGold
</strong>
</p>
<p
className="shorten-text"
>
The starship Heart of Gold was the first spacecraft to make use of the Infinite Improbability Drive
</p>
</div>
<div
className="CardColumn__ContentRight-sc-1w6lsih-5 kyEPRa"
/>
</div>
<div
className="CardColumn__FooterWrapper-sc-1w6lsih-3 jlTqlS level is-flex"
>
<div
className="level-left is-hidden-mobile"
>
<a
className="RepositoryEntryLink__PointerEventsLink-sc-1hpqj0w-0 gtboNN level-item"
href="/repo/hitchhiher/heartOfGold/branches"
onClick={[Function]}
>
<i
className="fas fa-code-branch has-text-inherit fa-lg"
/>
</a>
<a
className="RepositoryEntryLink__PointerEventsLink-sc-1hpqj0w-0 gtboNN level-item"
href="/repo/hitchhiher/heartOfGold/code/changesets/"
onClick={[Function]}
>
<i
className="fas fa-exchange-alt has-text-inherit fa-lg"
/>
</a>
<a
className="RepositoryEntryLink__PointerEventsLink-sc-1hpqj0w-0 gtboNN level-item"
href="/repo/hitchhiher/heartOfGold/code/sources/"
onClick={[Function]}
>
<i
className="fas fa-code has-text-inherit fa-lg"
/>
</a>
<a
className="RepositoryEntryLink__PointerEventsLink-sc-1hpqj0w-0 gtboNN level-item"
href="/repo/hitchhiher/heartOfGold/settings/general"
onClick={[Function]}
>
<i
className="fas fa-cog has-text-inherit fa-lg"
/>
</a>
</div>
<div
className="level-right is-mobile is-marginless"
>
<small
className="level-item"
>
<time
className="DateFromNow__DateElement-sc-16hnz72-0 ehlCzi"
title="2020-03-23 09:26:01"
>
3 days ago
</time>
</small>
</div>
</div>
</div>
</article>
</div>
`;
exports[`Storyshots RepositoryEntry Quick Link EP 1`] = `
<div
className="RepositoryEntrystories__Spacing-toppdg-0 khfzcK box box-link-shadow"
>
<a
className="overlay-column"
href="/repo/hitchhiher/heartOfGold"
onClick={[Function]}
/>
<article
className="CardColumn__NoEventWrapper-sc-1w6lsih-0 kZKqpc media"
>
<figure
className="CardColumn__AvatarWrapper-sc-1w6lsih-1 bZyfne media-left"
>
<p
className="image is-64x64"
>
<img
alt="Logo"
src="/images/blib.jpg"
/>
</p>
</figure>
<div
className="CardColumn__FlexFullHeight-sc-1w6lsih-2 cAdfGj media-content text-box is-flex"
>
<div
className="is-flex"
>
<div
className="CardColumn__ContentLeft-sc-1w6lsih-4 dumWkw content"
>
<p
className="shorten-text is-marginless"
>
<strong>
heartOfGold
</strong>
</p>
<p
className="shorten-text"
>
The starship Heart of Gold was the first spacecraft to make use of the Infinite Improbability Drive
</p>
</div>
<div
className="CardColumn__ContentRight-sc-1w6lsih-5 kyEPRa"
/>
</div>
<div
className="CardColumn__FooterWrapper-sc-1w6lsih-3 jlTqlS level is-flex"
>
<div
className="level-left is-hidden-mobile"
>
<a
className="RepositoryEntryLink__PointerEventsLink-sc-1hpqj0w-0 gtboNN level-item"
href="/repo/hitchhiher/heartOfGold/branches"
onClick={[Function]}
>
<i
className="fas fa-code-branch has-text-inherit fa-lg"
/>
</a>
<a
className="RepositoryEntryLink__PointerEventsLink-sc-1hpqj0w-0 gtboNN level-item"
href="/repo/hitchhiher/heartOfGold/code/changesets/"
onClick={[Function]}
>
<i
className="fas fa-exchange-alt has-text-inherit fa-lg"
/>
</a>
<a
className="RepositoryEntryLink__PointerEventsLink-sc-1hpqj0w-0 gtboNN level-item"
href="/repo/hitchhiher/heartOfGold/code/sources/"
onClick={[Function]}
>
<i
className="fas fa-code has-text-inherit fa-lg"
/>
</a>
<a
className="level-item"
>
<i
className="fas fa-fas fa-code-branch fa-rotate-180 fa-fw has-text-inherit fa-lg"
/>
</a>
<a
className="RepositoryEntryLink__PointerEventsLink-sc-1hpqj0w-0 gtboNN level-item"
href="/repo/hitchhiher/heartOfGold/settings/general"
onClick={[Function]}
>
<i
className="fas fa-cog has-text-inherit fa-lg"
/>
</a>
</div>
<div
className="level-right is-mobile is-marginless"
>
<small
className="level-item"
>
<time
className="DateFromNow__DateElement-sc-16hnz72-0 ehlCzi"
title="2020-03-23 09:26:01"
>
3 days ago
</time>
</small>
</div>
</div>
</div>
</article>
</div>
`;
exports[`Storyshots SyntaxHighlighter Go 1`] = `
<div
className="SyntaxHighlighterstories__Spacing-sc-1dcldp5-0 eofOgh"

View File

@@ -33,6 +33,7 @@ import ErrorBoundary from "../ErrorBoundary";
type Props = {
title?: string;
afterTitle?: ReactNode;
subtitle?: string;
loading?: boolean;
error?: Error;
@@ -50,6 +51,15 @@ const PageActionContainer = styled.div`
}
`;
const MarginLeft = styled.div`
margin-left: 0.5rem;
`;
const FlexContainer = styled.div`
display: flex;
flex-direction: row;
`;
export default class Page extends React.Component<Props> {
componentDidUpdate() {
const { title } = this.props;
@@ -80,7 +90,7 @@ export default class Page extends React.Component<Props> {
}
renderPageHeader() {
const { error, title, subtitle, children } = this.props;
const { error, title, afterTitle, subtitle, children } = this.props;
let pageActions = null;
let pageActionsExists = false;
@@ -104,7 +114,9 @@ export default class Page extends React.Component<Props> {
<>
<div className="columns">
<div className="column">
<Title title={title} />
<FlexContainer>
<Title title={title} /> {afterTitle && <MarginLeft>{afterTitle}</MarginLeft>}
</FlexContainer>
<Subtitle subtitle={subtitle} />
</div>
{pageActions}

View File

@@ -0,0 +1,93 @@
/*
* 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 {storiesOf} from "@storybook/react";
import React, {FC, ReactNode} from "react";
import styled from "styled-components";
import repository from "../__resources__/repository";
// @ts-ignore ignore unknown png
import Git from "../__resources__/git-logo.png";
import RepositoryEntry from "./RepositoryEntry";
import {Binder, BinderContext} from "@scm-manager/ui-extensions";
import {Repository} from "@scm-manager/ui-types";
import Image from "../Image";
import classNames from "classnames";
import Icon from "../Icon";
const Spacing = styled.div`
margin: 2rem;
`;
const Container: FC = ({children}) => (
<Spacing className="box box-link-shadow">{children}</Spacing>
);
const bindAvatar = (binder: Binder, avatar: string) => {
binder.bind("repos.repository-avatar", () => {
return <Image src={avatar} alt="Logo"/>;
});
};
const bindBeforeTitle = (binder: Binder, extension: ReactNode) => {
binder.bind("repository.card.beforeTitle", () => {
return extension;
});
};
const bindQuickLink = (binder: Binder, extension: ReactNode) => {
binder.bind("repository.card.quickLink", () => {
return extension;
});
};
const withBinder = (binder: Binder, repository: Repository) => {
return (
<BinderContext.Provider value={binder}>
<RepositoryEntry repository={repository}/>
</BinderContext.Provider>
);
};
const QuickLink = <a className="level-item"><Icon className="fa-lg" name="fas fa-code-branch fa-rotate-180 fa-fw"
color="inherit"/></a>;
storiesOf("RepositoryEntry", module)
.addDecorator(storyFn => <Container>{storyFn()}</Container>)
.add("Default", () => {
return <RepositoryEntry repository={repository}/>;
})
.add("Avatar EP", () => {
const binder = new Binder("avatar");
bindAvatar(binder, Git);
return withBinder(binder, repository);
})
.add("Before Title EP", () => {
const binder = new Binder("title");
bindBeforeTitle(binder, <i className="fas fa-star"/>);
return withBinder(binder, repository);
})
.add("Quick Link EP", () => {
const binder = new Binder("title");
bindQuickLink(binder, QuickLink);
return withBinder(binder, repository);
});

View File

@@ -85,15 +85,25 @@ class RepositoryEntry extends React.Component<Props> {
);
};
createTitle = () => {
const { repository } = this.props;
return (
<>
<ExtensionPoint name="repository.card.beforeTitle" props={{ repository }} /> <strong>{repository.name}</strong>
</>
);
};
render() {
const { repository } = this.props;
const repositoryLink = this.createLink(repository);
const footerLeft = this.createFooterLeft(repository, repositoryLink);
const footerRight = this.createFooterRight(repository);
const title = this.createTitle();
return (
<CardColumn
avatar={<RepositoryAvatar repository={repository} />}
title={repository.name}
title={title}
description={repository.description}
link={repositoryLink}
footerLeft={footerLeft}

View File

@@ -46,6 +46,9 @@ export { default as DiffFile } from "./DiffFile";
export { default as DiffButton } from "./DiffButton";
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 {
File,

View File

@@ -433,6 +433,7 @@ $danger-25: scale-color($danger, $lightness: 75%);
@import "~@fortawesome/fontawesome-free/scss/fontawesome";
$fa-font-path: "~@fortawesome/fontawesome-free/webfonts";
@import "~@fortawesome/fontawesome-free/scss/solid";
@import "~@fortawesome/fontawesome-free/scss/regular";
@import "~@fortawesome/fontawesome-free/scss/brands";
@import "~react-diff-view/style/index";

View File

@@ -181,7 +181,7 @@ class PluginEntry extends React.Component<Props, State> {
<CardColumn
action={this.isInstallable() ? () => this.toggleModal("showInstallModal") : null}
avatar={avatar}
title={plugin.displayName ? plugin.displayName : plugin.name}
title={plugin.displayName ? <strong>{plugin.displayName}</strong> : <strong>{plugin.name}</strong>}
description={plugin.description}
contentRight={plugin.pending || plugin.markedForUninstall ? this.createPendingSpinner() : actionbar}
footerRight={footerRight}

View File

@@ -25,6 +25,7 @@ import React from "react";
import { WithTranslation, withTranslation } from "react-i18next";
import { Repository } from "@scm-manager/ui-types";
import { DateFromNow, MailLink } from "@scm-manager/ui-components";
import { ExtensionPoint } from "@scm-manager/ui-extensions";
type Props = WithTranslation & {
repository: Repository;

View File

@@ -22,9 +22,8 @@
* SOFTWARE.
*/
import React from "react";
import { CardColumnGroup } from "@scm-manager/ui-components";
import { CardColumnGroup, RepositoryEntry } from "@scm-manager/ui-components";
import { RepositoryGroup } from "@scm-manager/ui-types";
import RepositoryEntry from "./RepositoryEntry";
type Props = {
group: RepositoryGroup;

View File

@@ -172,7 +172,10 @@ class RepositoryRoot extends React.Component<Props, State> {
setMenuCollapsed: (collapsed: boolean) => this.setState({ menuCollapsed: collapsed })
}}
>
<Page title={repository.namespace + "/" + repository.name}>
<Page
title={repository.namespace + "/" + repository.name}
afterTitle={<ExtensionPoint name={"repository.afterTitle"} props={{ repository }} />}
>
<CustomQueryFlexWrappedColumns>
<PrimaryContentColumn collapsed={menuCollapsed}>
<Switch>

View File

@@ -27,6 +27,7 @@ package sonia.scm.api.v2.resources;
import com.google.inject.AbstractModule;
import com.google.inject.servlet.ServletScopes;
import org.mapstruct.factory.Mappers;
import sonia.scm.web.api.RepositoryToHalMapper;
public class MapperModule extends AbstractModule {
@Override
@@ -70,6 +71,8 @@ public class MapperModule extends AbstractModule {
bind(ScmViolationExceptionToErrorDtoMapper.class).to(Mappers.getMapper(ScmViolationExceptionToErrorDtoMapper.class).getClass());
bind(ExceptionWithContextToErrorDtoMapper.class).to(Mappers.getMapper(ExceptionWithContextToErrorDtoMapper.class).getClass());
bind(RepositoryToHalMapper.class).to(Mappers.getMapper(RepositoryToRepositoryDtoMapper.class).getClass());
// no mapstruct required
bind(MeDtoFactory.class);
bind(UIPluginDtoMapper.class);

View File

@@ -38,6 +38,7 @@ import sonia.scm.repository.api.Command;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
import sonia.scm.repository.api.ScmProtocol;
import sonia.scm.web.api.RepositoryToHalMapper;
import java.util.List;
@@ -49,7 +50,7 @@ import static java.util.stream.Collectors.toList;
// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection.
@SuppressWarnings("squid:S3306")
@Mapper
public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper<Repository, RepositoryDto> {
public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper<Repository, RepositoryDto> implements RepositoryToHalMapper {
@Inject
private ResourceLinks resourceLinks;
@@ -58,6 +59,9 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper<Reposit
abstract HealthCheckFailureDto toDto(HealthCheckFailure failure);
@Override
public abstract RepositoryDto map(Repository modelObject);
@ObjectFactory
RepositoryDto createDto(Repository repository) {
Links.Builder linksBuilder = linkingTo().self(resourceLinks.repository().self(repository.getNamespace(), repository.getName()));