added avatar support

This commit is contained in:
Sebastian Sdorra
2020-06-09 14:11:15 +02:00
parent d61a60e1cd
commit 9174b89e8f
3 changed files with 164 additions and 82 deletions

View File

@@ -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;

View File

@@ -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);
});