Add new extension point for repository avatars (#1614)

Co-authored-by: René Pfeuffer <rene.pfeuffer@cloudogu.com>
This commit is contained in:
Eduard Heimbuch
2021-04-15 11:14:42 +02:00
committed by GitHub
parent 29f4c754bb
commit 84c1e7ed37
7 changed files with 56 additions and 33 deletions

View File

@@ -33,6 +33,7 @@ The following extension points are provided for the frontend:
### profile.route ### profile.route
### profile.setting ### profile.setting
### repo-config.route ### repo-config.route
### repo-config.details
### repos.branch-details.information ### repos.branch-details.information
### repos.content.metadata ### repos.content.metadata
- Location: At meta data view for file - Location: At meta data view for file
@@ -46,13 +47,13 @@ The following extension points are provided for the frontend:
### repositoryRole.role-details.information ### repositoryRole.role-details.information
### repository.setting ### repository.setting
### repos.repository-avatar ### repos.repository-avatar
### repos.repository-avatar.primary
- Location: At each repository in repository overview - Location: At each repository in repository overview
- can be used to add avatar for each repository (e.g., to mark repository type) - can be used to add avatar for each repository (e.g., to mark repository type)
### repos.repository-details.information ### repos.repository-details.information
- Location: At bottom of a single repository view - Location: At bottom of a single repository view
- can be used to show detailed information about the repository (how to clone, e.g.) - can be used to show detailed information about the repository (how to clone, e.g.)
### repos.sources.view ### repos.sources.view
### roles.route ### roles.route
### user.route ### user.route

View File

@@ -0,0 +1,2 @@
- type: added
description: New extension points for custom repository avatars ([#1614](https://github.com/scm-manager/scm-manager/pull/1614))

View File

@@ -55824,7 +55824,7 @@ exports[`Storyshots RepositoryEntry Archived 1`] = `
className="CardColumn__AvatarWrapper-sc-1w6lsih-1 lhzEPm media-left" className="CardColumn__AvatarWrapper-sc-1w6lsih-1 lhzEPm media-left"
> >
<p <p
className="image is-64x64" className="RepositoryAvatar__Avatar-uh53vb-0 igMDhE image is-64x64"
> >
<img <img
alt="Logo" alt="Logo"
@@ -55975,7 +55975,7 @@ exports[`Storyshots RepositoryEntry Avatar EP 1`] = `
className="CardColumn__AvatarWrapper-sc-1w6lsih-1 lhzEPm media-left" className="CardColumn__AvatarWrapper-sc-1w6lsih-1 lhzEPm media-left"
> >
<p <p
className="image is-64x64" className="RepositoryAvatar__Avatar-uh53vb-0 igMDhE image is-64x64"
> >
<img <img
alt="Logo" alt="Logo"
@@ -56120,7 +56120,7 @@ exports[`Storyshots RepositoryEntry Before Title EP 1`] = `
className="CardColumn__AvatarWrapper-sc-1w6lsih-1 lhzEPm media-left" className="CardColumn__AvatarWrapper-sc-1w6lsih-1 lhzEPm media-left"
> >
<p <p
className="image is-64x64" className="RepositoryAvatar__Avatar-uh53vb-0 igMDhE image is-64x64"
> >
<img <img
alt="Logo" alt="Logo"
@@ -56268,7 +56268,7 @@ exports[`Storyshots RepositoryEntry Default 1`] = `
className="CardColumn__AvatarWrapper-sc-1w6lsih-1 lhzEPm media-left" className="CardColumn__AvatarWrapper-sc-1w6lsih-1 lhzEPm media-left"
> >
<p <p
className="image is-64x64" className="RepositoryAvatar__Avatar-uh53vb-0 igMDhE image is-64x64"
> >
<img <img
alt="Logo" alt="Logo"
@@ -56413,7 +56413,7 @@ exports[`Storyshots RepositoryEntry Exporting 1`] = `
className="CardColumn__AvatarWrapper-sc-1w6lsih-1 lhzEPm media-left" className="CardColumn__AvatarWrapper-sc-1w6lsih-1 lhzEPm media-left"
> >
<p <p
className="image is-64x64" className="RepositoryAvatar__Avatar-uh53vb-0 igMDhE image is-64x64"
> >
<img <img
alt="Logo" alt="Logo"
@@ -56564,7 +56564,7 @@ exports[`Storyshots RepositoryEntry MultiRepositoryTags 1`] = `
className="CardColumn__AvatarWrapper-sc-1w6lsih-1 lhzEPm media-left" className="CardColumn__AvatarWrapper-sc-1w6lsih-1 lhzEPm media-left"
> >
<p <p
className="image is-64x64" className="RepositoryAvatar__Avatar-uh53vb-0 igMDhE image is-64x64"
> >
<img <img
alt="Logo" alt="Logo"
@@ -56721,7 +56721,7 @@ exports[`Storyshots RepositoryEntry Quick Link EP 1`] = `
className="CardColumn__AvatarWrapper-sc-1w6lsih-1 lhzEPm media-left" className="CardColumn__AvatarWrapper-sc-1w6lsih-1 lhzEPm media-left"
> >
<p <p
className="image is-64x64" className="RepositoryAvatar__Avatar-uh53vb-0 igMDhE image is-64x64"
> >
<img <img
alt="Logo" alt="Logo"

View File

@@ -27,10 +27,12 @@ import { useTranslation } from "react-i18next";
import { File } from "@scm-manager/ui-types"; import { File } from "@scm-manager/ui-types";
type Props = { type Props = {
handleFile: (file: File) => void; handleFile: (file: File, event?: ChangeEvent<HTMLInputElement>) => void;
filenamePlaceholder?: string;
disabled?: boolean;
}; };
const FileUpload: FC<Props> = ({ handleFile }) => { const FileUpload: FC<Props> = ({ handleFile, filenamePlaceholder = "", disabled = false }) => {
const [t] = useTranslation("commons"); const [t] = useTranslation("commons");
const [file, setFile] = useState<File | null>(null); const [file, setFile] = useState<File | null>(null);
@@ -41,21 +43,26 @@ const FileUpload: FC<Props> = ({ handleFile }) => {
className="file-input" className="file-input"
type="file" type="file"
name="resume" name="resume"
disabled={disabled}
onChange={(event: ChangeEvent<HTMLInputElement>) => { onChange={(event: ChangeEvent<HTMLInputElement>) => {
const uploadedFile = event?.target?.files![0]; const uploadedFile = event?.target?.files![0];
// @ts-ignore the uploaded file doesn't match our types // @ts-ignore the uploaded file doesn't match our types
setFile(uploadedFile); setFile(uploadedFile);
// @ts-ignore the uploaded file doesn't match our types // @ts-ignore the uploaded file doesn't match our types
handleFile(uploadedFile); handleFile(uploadedFile, event);
}} }}
/> />
<span className="file-cta"> <span className="file-cta">
<span className="file-icon"> <span className="file-icon">
<i className="fas fa-arrow-circle-up" /> <i className="fas fa-arrow-circle-up" />
</span> </span>
<span className="file-label">{t("fileUpload.label")}</span> <span className="file-label has-text-weight-bold">{t("fileUpload.label")}</span>
</span> </span>
<span className="file-name">{file?.name || ""}</span> {file?.name ? (
<span className="file-name">{file?.name}</span>
) : (
<span className="file-name has-text-weight-light has-text-grey-light ">{filenamePlaceholder}</span>
)}
</label> </label>
</div> </div>
); );

View File

@@ -33,6 +33,7 @@ type Props = {
closeFunction: () => void; closeFunction: () => void;
body: ReactNode; body: ReactNode;
active: boolean; active: boolean;
closeButtonLabel?: string;
}; };
const FullSizedModal = styled(Modal)` const FullSizedModal = styled(Modal)`
@@ -42,9 +43,9 @@ const FullSizedModal = styled(Modal)`
} }
`; `;
const FullscreenModal: FC<Props> = ({ title, closeFunction, body, active }) => { const FullscreenModal: FC<Props> = ({ title, closeFunction, body, active, closeButtonLabel }) => {
const [t] = useTranslation("repos"); const [t] = useTranslation("repos");
const footer = <Button label={t("diff.fullscreen.close")} action={closeFunction} color="grey" />; const footer = <Button label={closeButtonLabel || t("diff.fullscreen.close")} action={closeFunction} color="grey" />;
return <FullSizedModal title={title} closeFunction={closeFunction} body={body} footer={footer} active={active} />; return <FullSizedModal title={title} closeFunction={closeFunction} body={body} footer={footer} active={active} />;
}; };

View File

@@ -21,20 +21,28 @@
* 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 { ExtensionPoint } from "@scm-manager/ui-extensions"; import { ExtensionPoint } from "@scm-manager/ui-extensions";
import { Repository } from "@scm-manager/ui-types"; import { Repository } from "@scm-manager/ui-types";
import { Image } from "@scm-manager/ui-components"; import { Image } from "@scm-manager/ui-components";
import styled from "styled-components";
const Avatar = styled.p`
border-radius: 5px;
`;
type Props = { type Props = {
repository: Repository; repository: Repository;
}; };
class RepositoryAvatar extends React.Component<Props> { const renderExtensionPoint = (repository: Repository) => {
render() {
const { repository } = this.props;
return ( return (
<p className="image is-64x64"> <ExtensionPoint
name="repos.repository-avatar.primary"
props={{
repository
}}
>
<ExtensionPoint <ExtensionPoint
name="repos.repository-avatar" name="repos.repository-avatar"
props={{ props={{
@@ -43,9 +51,12 @@ class RepositoryAvatar extends React.Component<Props> {
> >
<Image src="/images/blib.jpg" alt="Logo" /> <Image src="/images/blib.jpg" alt="Logo" />
</ExtensionPoint> </ExtensionPoint>
</p> </ExtensionPoint>
); );
} };
}
const RepositoryAvatar: FC<Props> = ({ repository }) => {
return <Avatar className="image is-64x64">{renderExtensionPoint(repository)}</Avatar>;
};
export default RepositoryAvatar; export default RepositoryAvatar;

View File

@@ -57,6 +57,7 @@ const EditRepo: FC<Props> = ({ repository }) => {
<Subtitle subtitle={t("repositoryForm.subtitle")} /> <Subtitle subtitle={t("repositoryForm.subtitle")} />
<ErrorNotification error={error} /> <ErrorNotification error={error} />
<RepositoryForm repository={repository} loading={isLoading} modifyRepository={update} /> <RepositoryForm repository={repository} loading={isLoading} modifyRepository={update} />
<ExtensionPoint name="repo-config.details" props={extensionProps} renderAll={true}/>
{repository._links.exportInfo && <ExportRepository repository={repository} />} {repository._links.exportInfo && <ExportRepository repository={repository} />}
<ExtensionPoint name="repo-config.route" props={extensionProps} renderAll={true} /> <ExtensionPoint name="repo-config.route" props={extensionProps} renderAll={true} />
<RepositoryDangerZone repository={repository} indexLinks={indexLinks} /> <RepositoryDangerZone repository={repository} indexLinks={indexLinks} />