Add an extension point for contributor rows in contributor table

Co-authored-by: Rene Pfeuffer<rene.pfeuffer@cloudogu.com>
This commit is contained in:
Anna Vetcininova
2024-11-07 09:19:24 +01:00
parent f05aabc943
commit df0582dfd8
6 changed files with 111 additions and 55 deletions

View File

@@ -0,0 +1,2 @@
- type: added
description: Extension point for contributor row in contributor table

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2020 - present Cloudogu GmbH
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
import React, { FC } from "react";
import { Person } from "@scm-manager/ui-types";
import { useTranslation } from "react-i18next";
import { extensionPoints, useBinder } from "@scm-manager/ui-extensions";
import ContributorAvatar from "./ContributorAvatar";
const Contributor: FC<{ person: Person }> = ({ person }) => {
const [t] = useTranslation("repos");
const binder = useBinder();
const avatarFactory = binder.getExtension<extensionPoints.AvatarFactory>("avatar.factory");
let prefix = null;
if (avatarFactory) {
const avatar = avatarFactory(person);
if (avatar) {
prefix = (
<>
<ContributorAvatar src={avatar} alt={person.name} />{" "}
</>
);
}
}
if (person.mail) {
return (
<a href={"mailto:" + person.mail} title={t("changeset.contributors.mailto") + " " + person.mail}>
{prefix}
{person.name}
</a>
);
}
return <>{person.name}</>;
};
export default Contributor;

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2020 - present Cloudogu GmbH
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
import React, { FC } from "react";
import styled from "styled-components";
const SizedTd = styled.td`
width: 10rem;
`;
const ContributorRow: FC<{ label: string }> = ({ label, children }) => {
return (
<tr key={label}>
<SizedTd>{label}:</SizedTd>
<td className="is-ellipsis-overflow m-0">{children}</td>
</tr>
);
};
export default ContributorRow;

View File

@@ -31,3 +31,5 @@ export { default as ChangesetTagsCollapsed } from "./ChangesetTagsCollapsed";
export { default as ContributorAvatar } from "./ContributorAvatar";
export { default as SignatureIcon } from "./SignatureIcon";
export { default as SingleChangeset } from "./SingleChangeset";
export { default as Contributor } from "./Contributor";
export { default as ContributorRow } from "./ContributorRow";

View File

@@ -14,7 +14,7 @@
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
import React, { ComponentProps, ComponentType, FC, ReactNode } from "react";
import React, { ComponentProps, ComponentType, ReactNode } from "react";
import {
Branch,
BranchDetails,
@@ -305,6 +305,11 @@ export type ChangesetsAuthorSuffix = RenderableExtensionPointDefinition<
{ changeset: Changeset }
>;
export type ChangesetsContributorTableRow = RenderableExtensionPointDefinition<
"changesets.contributor.table.row",
{ changeset: Changeset }
>;
export type GroupNavigation = RenderableExtensionPointDefinition<"group.navigation", { group: Group; url: string }>;
export type GroupRoute = RenderableExtensionPointDefinition<"group.route", { group: Group; url: string }>;

View File

@@ -15,46 +15,15 @@
*/
import React, { FC } from "react";
import { Changeset, Person } from "@scm-manager/ui-types";
import styled from "styled-components";
import { Changeset } from "@scm-manager/ui-types";
import { useTranslation } from "react-i18next";
import { extensionPoints, useBinder } from "@scm-manager/ui-extensions";
import { CommaSeparatedList, ContributorAvatar } from "@scm-manager/ui-components";
import { ExtensionPoint } from "@scm-manager/ui-extensions";
import { CommaSeparatedList, Contributor, ContributorRow } from "@scm-manager/ui-components";
type Props = {
changeset: Changeset;
};
const SizedTd = styled.td`
width: 10rem;
`;
const Contributor: FC<{ person: Person }> = ({ person }) => {
const [t] = useTranslation("repos");
const binder = useBinder();
const avatarFactory = binder.getExtension<extensionPoints.AvatarFactory>("avatar.factory");
let prefix = null;
if (avatarFactory) {
const avatar = avatarFactory(person);
if (avatar) {
prefix = (
<>
<ContributorAvatar src={avatar} alt={person.name} />{" "}
</>
);
}
}
if (person.mail) {
return (
<a href={"mailto:" + person.mail} title={t("changeset.contributors.mailto") + " " + person.mail}>
{prefix}
{person.name}
</a>
);
}
return <>{person.name}</>;
};
const getUnique = (items: string[]) =>
Object.keys(
items.reduce((result, item) => {
@@ -72,11 +41,11 @@ const ContributorTable: FC<Props> = ({ changeset }) => {
if (!changeset.contributors) {
return [];
}
return getUnique(changeset.contributors.map(contributor => contributor.type));
return getUnique(changeset.contributors.map((contributor) => contributor.type));
};
const getPersonsByContributorType = (type: string) => {
return changeset.contributors?.filter(contributor => contributor.type === type).map(t => t.person);
return changeset.contributors?.filter((contributor) => contributor.type === type).map((t) => t.person);
};
const getContributorsByType = () => {
@@ -84,31 +53,27 @@ const ContributorTable: FC<Props> = ({ changeset }) => {
const personsByContributorType = [];
for (const type of availableContributorTypes) {
personsByContributorType.push({ type, persons: getPersonsByContributorType(type) });
personsByContributorType.push({ type, contributors: getPersonsByContributorType(type) });
}
return personsByContributorType;
};
return (
<table>
<tr>
<SizedTd>{t("changeset.contributor.type.author")}:</SizedTd>
<td>
<Contributor person={changeset.author} />
</td>
</tr>
{getContributorsByType().map(contributor => (
<tr key={contributor.type}>
<SizedTd>{t("changeset.contributor.type." + contributor.type)}:</SizedTd>
<td className="is-ellipsis-overflow m-0">
<CommaSeparatedList>
{contributor.persons?.map(person => (
<Contributor key={person.name} person={person} />
))}
</CommaSeparatedList>
</td>
</tr>
<ContributorRow label={t("changeset.contributor.type.author")}>
<Contributor person={changeset.author} />
</ContributorRow>
{getContributorsByType().map((contribution) => (
<ContributorRow key={contribution.type} label={t("changeset.contributor.type." + contribution.type)}>
<CommaSeparatedList>
{contribution.contributors?.map((contributor) => (
<Contributor key={contributor.name} person={contributor} />
))}
</CommaSeparatedList>
</ContributorRow>
))}
<ExtensionPoint name="changesets.contributor.table.row" props={{ changeset }} renderAll={true} />
</table>
);
};