mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-11 16:05:44 +01:00
added avatar support
This commit is contained in:
@@ -23,24 +23,59 @@
|
||||
*/
|
||||
import React, { FC } from "react";
|
||||
import { Changeset } from "@scm-manager/ui-types";
|
||||
import { useTranslation, WithTranslation, withTranslation } from "react-i18next";
|
||||
import { binder } from "@scm-manager/ui-extensions";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useBinder } from "@scm-manager/ui-extensions";
|
||||
import { EXTENSION_POINT, Person } from "../../avatar/Avatar";
|
||||
import Image from "../../Image";
|
||||
import styled from "styled-components";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
type Props = {
|
||||
changeset: Changeset;
|
||||
};
|
||||
|
||||
type PersonType = {
|
||||
name: string;
|
||||
mail?: string;
|
||||
};
|
||||
|
||||
type PersonProps = {
|
||||
person: PersonType;
|
||||
person: Person;
|
||||
displayTextOnly?: boolean;
|
||||
};
|
||||
|
||||
const Person: FC<PersonProps> = ({ person }) => {
|
||||
const useAvatar = (person: Person): string | undefined => {
|
||||
const binder = useBinder();
|
||||
const factory: (person: Person) => string | undefined = binder.getExtension(EXTENSION_POINT);
|
||||
if (factory) {
|
||||
return factory(person);
|
||||
}
|
||||
};
|
||||
|
||||
const AvatarImage = styled(Image)`
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
margin-right: 0.25em;
|
||||
`;
|
||||
|
||||
type PersonAvatarProps = {
|
||||
person: Person;
|
||||
avatar: string;
|
||||
};
|
||||
|
||||
const PersonAvatar: FC<PersonAvatarProps> = ({ person, avatar }) => {
|
||||
const [t] = useTranslation("repos");
|
||||
const img = <AvatarImage src={avatar} alt={person.name} title={person.name} />;
|
||||
if (person.mail) {
|
||||
return (
|
||||
<a href={"mailto:" + person.mail} title={t("changeset.author.mailto") + " " + person.mail}>
|
||||
{img}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
return img;
|
||||
};
|
||||
|
||||
const SinglePerson: FC<PersonProps> = ({ person, displayTextOnly }) => {
|
||||
const [t] = useTranslation("repos");
|
||||
const avatar = useAvatar(person);
|
||||
if (!displayTextOnly && avatar) {
|
||||
return <PersonAvatar person={person} avatar={avatar} />;
|
||||
}
|
||||
if (person.mail) {
|
||||
return (
|
||||
<a href={"mailto:" + person.mail} title={t("changeset.author.mailto") + " " + person.mail}>
|
||||
@@ -52,86 +87,107 @@ const Person: FC<PersonProps> = ({ person }) => {
|
||||
};
|
||||
|
||||
type PersonsProps = {
|
||||
persons: PersonType[];
|
||||
persons: Person[];
|
||||
label: string;
|
||||
displayTextOnly?: boolean;
|
||||
};
|
||||
|
||||
const Persons: FC<PersonsProps> = ({ persons, label }) => {
|
||||
const Persons: FC<PersonsProps> = ({ persons, label, displayTextOnly }) => {
|
||||
const binder = useBinder();
|
||||
|
||||
const [t] = useTranslation("repos");
|
||||
if (persons.length === 1) {
|
||||
return (
|
||||
<>
|
||||
{t(label)} <Person person={persons[0]} />
|
||||
{t(label)} <SinglePerson person={persons[0]} displayTextOnly={displayTextOnly} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const avatarFactory = binder.getExtension(EXTENSION_POINT);
|
||||
if (avatarFactory) {
|
||||
return (
|
||||
<>
|
||||
{t(label)}{" "}
|
||||
{persons.map(p => (
|
||||
<PersonAvatar person={p} avatar={avatarFactory(p)} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{t(label)}{" "}
|
||||
<a title={label + ":\n" + persons.map(person => "- " + person.name).join("\n")}>
|
||||
{t("changesets.authors.more", { count: persons.length })}
|
||||
</a>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const ChangesetAuthor: FC<Props> = ({ changeset }) => {
|
||||
const [t] = useTranslation("repos");
|
||||
const binder = useBinder();
|
||||
|
||||
const getCoAuthors = () => {
|
||||
return filterTrailersByType("Co-authored-by");
|
||||
};
|
||||
|
||||
const getCommiters = () => {
|
||||
return filterTrailersByType("Committed-by");
|
||||
};
|
||||
|
||||
const filterTrailersByType = (trailerType: string) => {
|
||||
return changeset.trailers.filter(p => p.trailerType === trailerType).map(trailer => trailer.person);
|
||||
};
|
||||
|
||||
const authorLine = [];
|
||||
|
||||
const commiters = getCommiters();
|
||||
if (commiters.length > 0) {
|
||||
authorLine.push(<Persons persons={commiters} label={"changesets.authors.committedBy"} />);
|
||||
}
|
||||
|
||||
const coAuthors = getCoAuthors();
|
||||
if (coAuthors.length > 0) {
|
||||
authorLine.push(<Persons persons={coAuthors} label={"changesets.authors.coAuthoredBy"} />);
|
||||
}
|
||||
|
||||
// extensions
|
||||
const extensions = binder.getExtensions("changesets.author.suffix", { changeset });
|
||||
if (extensions) {
|
||||
coAuthors.push(...extensions);
|
||||
}
|
||||
|
||||
if (changeset.author) {
|
||||
authorLine.unshift(
|
||||
<Persons
|
||||
persons={[changeset.author]}
|
||||
label={"changesets.authors.authoredBy"}
|
||||
displayTextOnly={authorLine.length === 0}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{t(label)}{" "}
|
||||
<a title={label + ":\n" + persons.map(person => "- " + person.name).join("\n")}>
|
||||
{t("changesets.authors.more", { count: persons.length })}
|
||||
</a>
|
||||
</>
|
||||
<span style={{ verticalAlign: "middle" }}>
|
||||
{authorLine.map((p, i) => {
|
||||
if (i === 0) {
|
||||
return <>{p}</>;
|
||||
} else if (i + 1 === authorLine.length) {
|
||||
return (
|
||||
<>
|
||||
{" "}
|
||||
{t("changesets.authors.and")} {p}{" "}
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return <>, {p}</>;
|
||||
}
|
||||
})}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
class ChangesetAuthor extends React.Component<Props> {
|
||||
render() {
|
||||
const { changeset, t } = this.props;
|
||||
|
||||
const authorLine = [];
|
||||
|
||||
if (changeset.author) {
|
||||
authorLine.push(<Persons persons={[changeset.author]} label={"changesets.authors.authoredBy"} />);
|
||||
}
|
||||
|
||||
const commiters = this.getCommiters();
|
||||
if (commiters.length > 0) {
|
||||
authorLine.push(<Persons persons={commiters} label={"changesets.authors.committedBy"} />);
|
||||
}
|
||||
|
||||
const coAuthors = this.getCoAuthors();
|
||||
if (coAuthors.length > 0) {
|
||||
authorLine.push(<Persons persons={coAuthors} label={"changesets.authors.coAuthoredBy"} />);
|
||||
}
|
||||
|
||||
// extensions
|
||||
const extensions = binder.getExtensions("changesets.author.suffix", this.props);
|
||||
if (extensions) {
|
||||
coAuthors.push(...extensions);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{authorLine.map((p, i) => {
|
||||
if (i === 0) {
|
||||
return <>{p}</>;
|
||||
} else if (i + 1 === authorLine.length) {
|
||||
return (
|
||||
<>
|
||||
{" "}
|
||||
{t("changesets.authors.and")} {p}{" "}
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return <>, {p}</>;
|
||||
}
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
getCoAuthors() {
|
||||
return this.filterTrailersByType("Co-authored-by");
|
||||
}
|
||||
|
||||
getCommiters() {
|
||||
return this.filterTrailersByType("Committed-by");
|
||||
}
|
||||
|
||||
filterTrailersByType(t: string) {
|
||||
return this.props.changeset.trailers.filter(p => p.trailerType === t).map(trailer => trailer.person);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation("repos")(ChangesetAuthor);
|
||||
export default ChangesetAuthor;
|
||||
|
||||
@@ -29,11 +29,30 @@ import { MemoryRouter } from "react-router-dom";
|
||||
import repository from "../../__resources__/repository";
|
||||
import ChangesetRow from "./ChangesetRow";
|
||||
import {one, two, three, four} from "../../__resources__/changesets";
|
||||
import {Binder, BinderContext} from "@scm-manager/ui-extensions";
|
||||
// @ts-ignore
|
||||
import hitchhiker from "../../__resources__/hitchhiker.png";
|
||||
import {Person} from "../../avatar/Avatar";
|
||||
import {Changeset} from "@scm-manager/ui-types/src";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
margin: 2rem;
|
||||
`;
|
||||
|
||||
const robohash = (person: Person) => {
|
||||
return `https://robohash.org/${person.mail}`;
|
||||
}
|
||||
|
||||
const withAvatarFactory = (factory: (person: Person) => string, changeset: Changeset) => {
|
||||
const binder = new Binder("changeset stories");
|
||||
binder.bind("avatar.factory", factory);
|
||||
return (
|
||||
<BinderContext.Provider value={binder}>
|
||||
<ChangesetRow repository={repository} changeset={changeset} />
|
||||
</BinderContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
storiesOf("Changesets", module)
|
||||
.addDecorator(story => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
|
||||
.addDecorator(storyFn => <Wrapper className="box box-link-shadow">{storyFn()}</Wrapper>)
|
||||
@@ -48,4 +67,10 @@ storiesOf("Changesets", module)
|
||||
))
|
||||
.add("With multiple Co-Authors", () => (
|
||||
<ChangesetRow repository={repository} changeset={four} />
|
||||
));
|
||||
))
|
||||
.add("With avatar", () => {
|
||||
return withAvatarFactory(person => hitchhiker, three);
|
||||
})
|
||||
.add("Co-Authors with avatar", () => {
|
||||
return withAvatarFactory(robohash, four);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user