mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-15 09:46:16 +01:00
redesign changeset list
This commit is contained in:
@@ -2,9 +2,13 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import type { Changeset } from "@scm-manager/ui-types";
|
import type { Changeset } from "@scm-manager/ui-types";
|
||||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||||
|
import {translate} from "react-i18next";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
changeset: Changeset
|
changeset: Changeset,
|
||||||
|
|
||||||
|
// context props
|
||||||
|
t: (string) => string
|
||||||
};
|
};
|
||||||
|
|
||||||
class ChangesetAuthor extends React.Component<Props> {
|
class ChangesetAuthor extends React.Component<Props> {
|
||||||
@@ -14,39 +18,35 @@ class ChangesetAuthor extends React.Component<Props> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { name } = changeset.author;
|
const { name, mail } = changeset.author;
|
||||||
return (
|
if (mail) {
|
||||||
<>
|
return this.withExtensionPoint(this.renderWithMail(name, mail));
|
||||||
{name} {this.renderMail()} {this.renderAuthorMetadataExtensionPoint()}
|
}
|
||||||
</>
|
return this.withExtensionPoint(<>{name}</>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderAuthorMetadataExtensionPoint = () => {
|
renderWithMail(name: string, mail: string) {
|
||||||
const { changeset } = this.props;
|
const { t } = this.props;
|
||||||
return (
|
return (
|
||||||
<ExtensionPoint
|
<a href={"mailto: " + mail} title={t("changesets.author.mailto") + " " + mail}>
|
||||||
name="changesets.changeset.author.metadata"
|
{name}
|
||||||
props={{ changeset }}
|
|
||||||
renderAll={true}
|
|
||||||
>
|
|
||||||
asas
|
|
||||||
</ExtensionPoint>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
renderMail() {
|
|
||||||
const { mail } = this.props.changeset.author;
|
|
||||||
if (mail) {
|
|
||||||
return (
|
|
||||||
<a className="is-hidden-mobile" href={"mailto:" + mail}>
|
|
||||||
<
|
|
||||||
{mail}
|
|
||||||
>
|
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
withExtensionPoint(child: any) {
|
||||||
|
const { t } = this.props;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{t("changesets.author.prefix")} {child}
|
||||||
|
<ExtensionPoint
|
||||||
|
name="changesets.author.suffix"
|
||||||
|
props={{ changeset: this.props.changeset }}
|
||||||
|
renderAll={true}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ChangesetAuthor;
|
export default translate("repos")(ChangesetAuthor);
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import type { Changeset, Repository } from "@scm-manager/ui-types";
|
||||||
|
import ButtonGroup from "../../buttons/ButtonGroup";
|
||||||
|
import Button from "../../buttons/Button";
|
||||||
|
import { createChangesetLink, createSourcesLink } from "./changesets";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
repository: Repository,
|
||||||
|
changeset: Changeset
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChangesetButtonGroup extends React.Component<Props> {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { repository, changeset } = this.props;
|
||||||
|
|
||||||
|
const changesetLink = createChangesetLink(repository, changeset);
|
||||||
|
const sourcesLink = createSourcesLink(repository, changeset);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ButtonGroup className="is-pulled-right">
|
||||||
|
<Button link={changesetLink}>
|
||||||
|
<span className="icon">
|
||||||
|
<i className="fas fa-code"></i>
|
||||||
|
</span>
|
||||||
|
<span className="is-hidden-mobile is-hidden-tablet-only">
|
||||||
|
Details
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
<Button link={sourcesLink}>
|
||||||
|
<span className="icon">
|
||||||
|
<i className="fas fa-history"></i>
|
||||||
|
</span>
|
||||||
|
<span className="is-hidden-mobile is-hidden-tablet-only">
|
||||||
|
Sources
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChangesetButtonGroup;
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
import {Link} from "react-router-dom";
|
import {Link} from "react-router-dom";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import type {Changeset, Repository} from "@scm-manager/ui-types";
|
import type {Changeset, Repository} from "@scm-manager/ui-types";
|
||||||
|
import { createChangesetLink } from "./changesets";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
repository: Repository,
|
repository: Repository,
|
||||||
@@ -20,13 +21,11 @@ export default class ChangesetId extends React.Component<Props> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderLink = () => {
|
renderLink = () => {
|
||||||
const { changeset, repository } = this.props;
|
const { repository, changeset } = this.props;
|
||||||
|
const link = createChangesetLink(repository, changeset);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link to={link}>
|
||||||
to={`/repo/${repository.namespace}/${repository.name}/changeset/${
|
|
||||||
changeset.id
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{this.shortId(changeset)}
|
{this.shortId(changeset)}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,21 +8,39 @@ import ChangesetId from "./ChangesetId";
|
|||||||
import injectSheet from "react-jss";
|
import injectSheet from "react-jss";
|
||||||
import { DateFromNow } from "../..";
|
import { DateFromNow } from "../..";
|
||||||
import ChangesetAuthor from "./ChangesetAuthor";
|
import ChangesetAuthor from "./ChangesetAuthor";
|
||||||
import ChangesetTag from "./ChangesetTag";
|
|
||||||
|
|
||||||
import { parseDescription } from "./changesets";
|
import { parseDescription } from "./changesets";
|
||||||
import { AvatarWrapper, AvatarImage } from "../../avatar";
|
import { AvatarWrapper, AvatarImage } from "../../avatar";
|
||||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||||
|
import ChangesetTags from "./ChangesetTags";
|
||||||
|
import ChangesetButtonGroup from "./ChangesetButtonGroup";
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
pointer: {
|
changeset: {
|
||||||
cursor: "pointer"
|
// & references parent rule
|
||||||
|
// have a look at https://cssinjs.org/jss-plugin-nested?v=v10.0.0-alpha.9
|
||||||
|
"& + &": {
|
||||||
|
borderTop: "1px solid rgba(219, 219, 219, 0.5)",
|
||||||
|
marginTop: "1rem",
|
||||||
|
paddingTop: "1rem"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
changesetGroup: {
|
avatarFigure: {
|
||||||
marginBottom: "1em"
|
marginTop: ".25rem",
|
||||||
|
marginRight: ".5rem",
|
||||||
},
|
},
|
||||||
withOverflow: {
|
avatarImage: {
|
||||||
overflow: "auto"
|
height: "35px",
|
||||||
|
width: "35px"
|
||||||
|
},
|
||||||
|
isVcentered: {
|
||||||
|
marginTop: "auto",
|
||||||
|
marginBottom: "auto"
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
marginLeft: 0
|
||||||
|
},
|
||||||
|
tag: {
|
||||||
|
marginTop: ".5rem"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -34,38 +52,23 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class ChangesetRow extends React.Component<Props> {
|
class ChangesetRow extends React.Component<Props> {
|
||||||
createLink = (changeset: Changeset) => {
|
createChangesetId = (changeset: Changeset) => {
|
||||||
const { repository } = this.props;
|
const { repository } = this.props;
|
||||||
return <ChangesetId changeset={changeset} repository={repository} />;
|
return <ChangesetId changeset={changeset} repository={repository} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
getTags = () => {
|
|
||||||
const { changeset } = this.props;
|
|
||||||
return changeset._embedded.tags || [];
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { changeset, classes } = this.props;
|
const { repository, changeset, classes } = this.props;
|
||||||
const changesetLink = this.createLink(changeset);
|
|
||||||
const dateFromNow = <DateFromNow date={changeset.date} />;
|
|
||||||
const authorLine = <ChangesetAuthor changeset={changeset} />;
|
|
||||||
const description = parseDescription(changeset.description);
|
const description = parseDescription(changeset.description);
|
||||||
|
const changesetId = this.createChangesetId(changeset);
|
||||||
|
const dateFromNow = <DateFromNow date={changeset.date} />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article className={classNames("media", classes.inner)}>
|
<div className={classes.changeset}>
|
||||||
<AvatarWrapper>
|
<div className="columns">
|
||||||
<div>
|
<div className="column is-three-fifths">
|
||||||
<figure className="media-left">
|
|
||||||
<p className="image is-64x64">
|
<h4 className="has-text-weight-bold is-ellipsis-overflow">
|
||||||
<AvatarImage person={changeset.author} />
|
|
||||||
</p>
|
|
||||||
</figure>
|
|
||||||
</div>
|
|
||||||
</AvatarWrapper>
|
|
||||||
<div className={classNames("media-content", classes.withOverflow)}>
|
|
||||||
<div className="content">
|
|
||||||
<p className="is-ellipsis-overflow">
|
|
||||||
<strong>
|
|
||||||
<ExtensionPoint
|
<ExtensionPoint
|
||||||
name="changesets.changeset.description"
|
name="changesets.changeset.description"
|
||||||
props={{ changeset, value: description.title }}
|
props={{ changeset, value: description.title }}
|
||||||
@@ -73,35 +76,46 @@ class ChangesetRow extends React.Component<Props> {
|
|||||||
>
|
>
|
||||||
{description.title}
|
{description.title}
|
||||||
</ExtensionPoint>
|
</ExtensionPoint>
|
||||||
</strong>
|
</h4>
|
||||||
<br />
|
|
||||||
|
<div className="media">
|
||||||
|
<AvatarWrapper>
|
||||||
|
<figure className={classNames(classes.avatarFigure, "media-left")}>
|
||||||
|
<div className={classNames("image", classes.avatarImage)}>
|
||||||
|
<AvatarImage person={changeset.author} />
|
||||||
|
</div>
|
||||||
|
</figure>
|
||||||
|
</AvatarWrapper>
|
||||||
|
<div className={classNames(classes.metadata, "media-right")}>
|
||||||
|
<p className="is-hidden-mobile is-hidden-tablet-only">
|
||||||
<Interpolate
|
<Interpolate
|
||||||
i18nKey="changesets.changeset.summary"
|
i18nKey="changesets.changeset.summary"
|
||||||
id={changesetLink}
|
id={changesetId}
|
||||||
time={dateFromNow}
|
time={dateFromNow}
|
||||||
/>
|
/>
|
||||||
</p>{" "}
|
</p>
|
||||||
<div className="is-size-7">{authorLine}</div>
|
<p className="is-hidden-desktop">
|
||||||
|
<Interpolate
|
||||||
|
i18nKey="changesets.changeset.short-summary"
|
||||||
|
id={changesetId}
|
||||||
|
time={dateFromNow}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<p className="is-size-7">
|
||||||
|
<ChangesetAuthor changeset={changeset} />
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{this.renderTags()}
|
|
||||||
</article>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderTags = () => {
|
</div>
|
||||||
const tags = this.getTags();
|
<div className={classNames("column", classes.isVcentered)}>
|
||||||
if (tags.length > 0) {
|
<ChangesetTags changeset={changeset} />
|
||||||
return (
|
<ChangesetButtonGroup repository={repository} changeset={changeset} />
|
||||||
<div className="media-right">
|
</div>
|
||||||
{tags.map((tag: Tag) => {
|
</div>
|
||||||
return <ChangesetTag key={tag.name} tag={tag} />;
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default injectSheet(styles)(translate("repos")(ChangesetRow));
|
export default injectSheet(styles)(translate("repos")(ChangesetRow));
|
||||||
|
|||||||
@@ -1,32 +1,17 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import type { Tag } from "@scm-manager/ui-types";
|
import type { Tag } from "@scm-manager/ui-types";
|
||||||
import injectSheet from "react-jss";
|
import ChangesetTagBase from "./ChangesetTagBase";
|
||||||
import classNames from "classnames";
|
|
||||||
|
|
||||||
const styles = {
|
|
||||||
spacing: {
|
|
||||||
marginRight: "4px"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
tag: Tag,
|
tag: Tag
|
||||||
|
|
||||||
// context props
|
|
||||||
classes: Object
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ChangesetTag extends React.Component<Props> {
|
class ChangesetTag extends React.Component<Props> {
|
||||||
render() {
|
render() {
|
||||||
const { tag, classes } = this.props;
|
const { tag } = this.props;
|
||||||
return (
|
return <ChangesetTagBase icon={"fa-tag"} label={tag.name} />;
|
||||||
<span className="tag is-info">
|
|
||||||
<span className={classNames("fa", "fa-tag", classes.spacing)} />{" "}
|
|
||||||
{tag.name}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default injectSheet(styles)(ChangesetTag);
|
export default ChangesetTag;
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import injectSheet from "react-jss";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
tag: {
|
||||||
|
marginTop: ".5rem"
|
||||||
|
},
|
||||||
|
spacing: {
|
||||||
|
marginRight: ".25rem"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
icon: string,
|
||||||
|
label: string,
|
||||||
|
|
||||||
|
// context props
|
||||||
|
classes: Object
|
||||||
|
};
|
||||||
|
|
||||||
|
class ChangesetTagBase extends React.Component<Props> {
|
||||||
|
render() {
|
||||||
|
const { icon, label, classes } = this.props;
|
||||||
|
return (
|
||||||
|
<span className={classNames(classes.tag, "tag", "is-info")}>
|
||||||
|
<span className={classNames("fa", icon, classes.spacing)} /> {label}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default injectSheet(styles)(ChangesetTagBase);
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import type { Changeset} from "@scm-manager/ui-types";
|
||||||
|
import ChangesetTag from "./ChangesetTag";
|
||||||
|
import ChangesetTagsCollapsed from "./ChangesetTagsCollapsed";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
changeset: Changeset
|
||||||
|
};
|
||||||
|
|
||||||
|
class ChangesetTags extends React.Component<Props> {
|
||||||
|
|
||||||
|
getTags = () => {
|
||||||
|
const { changeset } = this.props;
|
||||||
|
return changeset._embedded.tags || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const tags = this.getTags();
|
||||||
|
|
||||||
|
if (tags.length === 1) {
|
||||||
|
return <ChangesetTag tag={tags[0]} />;
|
||||||
|
} else if (tags.length > 1) {
|
||||||
|
return <ChangesetTagsCollapsed tags={tags} />;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChangesetTags;
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import type { Tag } from "@scm-manager/ui-types";
|
||||||
|
import ChangesetTagBase from "./ChangesetTagBase";
|
||||||
|
import { translate } from "react-i18next";
|
||||||
|
import Tooltip from "../../Tooltip";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
tags: Tag[],
|
||||||
|
|
||||||
|
// context props
|
||||||
|
t: (string) => string
|
||||||
|
};
|
||||||
|
|
||||||
|
class ChangesetTagsCollapsed extends React.Component<Props> {
|
||||||
|
render() {
|
||||||
|
const { tags, t } = this.props;
|
||||||
|
const message = tags.map((tag) => tag.name).join(", ");
|
||||||
|
return (
|
||||||
|
<Tooltip location="top" message={message}>
|
||||||
|
<ChangesetTagBase icon={"fa-tags"} label={ tags.length + " " + t("changesets.tags") } />
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate("repos")(ChangesetTagsCollapsed);
|
||||||
@@ -1,9 +1,19 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
import type { Changeset, Repository } from "@scm-manager/ui-types";
|
||||||
|
|
||||||
export type Description = {
|
export type Description = {
|
||||||
title: string,
|
title: string,
|
||||||
message: string
|
message: string
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function createChangesetLink(repository: Repository, changeset: Changeset) {
|
||||||
|
return `/repo/${repository.namespace}/${repository.name}/changeset/${changeset.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createSourcesLink(repository: Repository, changeset: Changeset) {
|
||||||
|
return `/repo/${repository.namespace}/${repository.name}/sources/${changeset.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
export function parseDescription(description?: string): Description {
|
export function parseDescription(description?: string): Description {
|
||||||
const desc = description ? description : "";
|
const desc = description ? description : "";
|
||||||
const lineBreak = desc.indexOf("\n");
|
const lineBreak = desc.indexOf("\n");
|
||||||
|
|||||||
@@ -3,8 +3,12 @@ import * as changesets from "./changesets";
|
|||||||
export { changesets };
|
export { changesets };
|
||||||
|
|
||||||
export { default as ChangesetAuthor } from "./ChangesetAuthor";
|
export { default as ChangesetAuthor } from "./ChangesetAuthor";
|
||||||
|
export { default as ChangesetButtonGroup } from "./ChangesetButtonGroup";
|
||||||
|
export { default as ChangesetDiff } from "./ChangesetDiff";
|
||||||
export { default as ChangesetId } from "./ChangesetId";
|
export { default as ChangesetId } from "./ChangesetId";
|
||||||
export { default as ChangesetList } from "./ChangesetList";
|
export { default as ChangesetList } from "./ChangesetList";
|
||||||
export { default as ChangesetRow } from "./ChangesetRow";
|
export { default as ChangesetRow } from "./ChangesetRow";
|
||||||
export { default as ChangesetTag } from "./ChangesetTag";
|
export { default as ChangesetTag } from "./ChangesetTag";
|
||||||
|
export { default as ChangesetTags } from "./ChangesetTags";
|
||||||
|
export { default as ChangesetTagsCollapsed } from "./ChangesetTagsCollapsed";
|
||||||
export { default as ChangesetDiff } from "./ChangesetDiff";
|
export { default as ChangesetDiff } from "./ChangesetDiff";
|
||||||
|
|||||||
@@ -76,11 +76,15 @@
|
|||||||
"description": "Beschreibung",
|
"description": "Beschreibung",
|
||||||
"contact": "Kontakt",
|
"contact": "Kontakt",
|
||||||
"date": "Datum",
|
"date": "Datum",
|
||||||
"summary": "Changeset {{id}} wurde committet {{time}}"
|
"summary": "Changeset {{id}} wurde committet {{time}}",
|
||||||
|
"short-summary": "Committet {{id}} {{time}}"
|
||||||
},
|
},
|
||||||
|
"tags": "Tags",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Autor",
|
"name": "Autor",
|
||||||
"mail": "Mail"
|
"mail": "Mail",
|
||||||
|
"prefix": "Verfasst von",
|
||||||
|
"mailto": "E-Mail senden an"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"branch-selector": {
|
"branch-selector": {
|
||||||
|
|||||||
@@ -76,11 +76,15 @@
|
|||||||
"description": "Description",
|
"description": "Description",
|
||||||
"contact": "Contact",
|
"contact": "Contact",
|
||||||
"date": "Date",
|
"date": "Date",
|
||||||
"summary": "Changeset {{id}} was committed {{time}}"
|
"summary": "Changeset {{id}} was committed {{time}}",
|
||||||
|
"short-summary": "Committed {{id}} {{time}}"
|
||||||
},
|
},
|
||||||
|
"tags": "Tags",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Author",
|
"name": "Author",
|
||||||
"mail": "Mail"
|
"mail": "Mail",
|
||||||
|
"prefix": "Authored by",
|
||||||
|
"mailto": "Send mail to"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"branch-selector": {
|
"branch-selector": {
|
||||||
|
|||||||
Reference in New Issue
Block a user