mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-07 14:05:44 +01:00
redesign changeset list
This commit is contained in:
@@ -2,9 +2,13 @@
|
||||
import React from "react";
|
||||
import type { Changeset } from "@scm-manager/ui-types";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import {translate} from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
changeset: Changeset
|
||||
changeset: Changeset,
|
||||
|
||||
// context props
|
||||
t: (string) => string
|
||||
};
|
||||
|
||||
class ChangesetAuthor extends React.Component<Props> {
|
||||
@@ -14,39 +18,35 @@ class ChangesetAuthor extends React.Component<Props> {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { name } = changeset.author;
|
||||
const { name, mail } = changeset.author;
|
||||
if (mail) {
|
||||
return this.withExtensionPoint(this.renderWithMail(name, mail));
|
||||
}
|
||||
return this.withExtensionPoint(<>{name}</>);
|
||||
}
|
||||
|
||||
renderWithMail(name: string, mail: string) {
|
||||
const { t } = this.props;
|
||||
return (
|
||||
<>
|
||||
{name} {this.renderMail()} {this.renderAuthorMetadataExtensionPoint()}
|
||||
</>
|
||||
<a href={"mailto: " + mail} title={t("changesets.author.mailto") + " " + mail}>
|
||||
{name}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
renderAuthorMetadataExtensionPoint = () => {
|
||||
const { changeset } = this.props;
|
||||
withExtensionPoint(child: any) {
|
||||
const { t } = this.props;
|
||||
return (
|
||||
<ExtensionPoint
|
||||
name="changesets.changeset.author.metadata"
|
||||
props={{ changeset }}
|
||||
renderAll={true}
|
||||
>
|
||||
asas
|
||||
</ExtensionPoint>
|
||||
<>
|
||||
{t("changesets.author.prefix")} {child}
|
||||
<ExtensionPoint
|
||||
name="changesets.author.suffix"
|
||||
props={{ changeset: this.props.changeset }}
|
||||
renderAll={true}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
renderMail() {
|
||||
const { mail } = this.props.changeset.author;
|
||||
if (mail) {
|
||||
return (
|
||||
<a className="is-hidden-mobile" href={"mailto:" + mail}>
|
||||
<
|
||||
{mail}
|
||||
>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 React from "react";
|
||||
import type {Changeset, Repository} from "@scm-manager/ui-types";
|
||||
import { createChangesetLink } from "./changesets";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
@@ -20,13 +21,11 @@ export default class ChangesetId extends React.Component<Props> {
|
||||
};
|
||||
|
||||
renderLink = () => {
|
||||
const { changeset, repository } = this.props;
|
||||
const { repository, changeset } = this.props;
|
||||
const link = createChangesetLink(repository, changeset);
|
||||
|
||||
return (
|
||||
<Link
|
||||
to={`/repo/${repository.namespace}/${repository.name}/changeset/${
|
||||
changeset.id
|
||||
}`}
|
||||
>
|
||||
<Link to={link}>
|
||||
{this.shortId(changeset)}
|
||||
</Link>
|
||||
);
|
||||
|
||||
@@ -8,21 +8,39 @@ import ChangesetId from "./ChangesetId";
|
||||
import injectSheet from "react-jss";
|
||||
import { DateFromNow } from "../..";
|
||||
import ChangesetAuthor from "./ChangesetAuthor";
|
||||
import ChangesetTag from "./ChangesetTag";
|
||||
|
||||
import { parseDescription } from "./changesets";
|
||||
import { AvatarWrapper, AvatarImage } from "../../avatar";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import ChangesetTags from "./ChangesetTags";
|
||||
import ChangesetButtonGroup from "./ChangesetButtonGroup";
|
||||
|
||||
const styles = {
|
||||
pointer: {
|
||||
cursor: "pointer"
|
||||
changeset: {
|
||||
// & 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: {
|
||||
marginBottom: "1em"
|
||||
avatarFigure: {
|
||||
marginTop: ".25rem",
|
||||
marginRight: ".5rem",
|
||||
},
|
||||
withOverflow: {
|
||||
overflow: "auto"
|
||||
avatarImage: {
|
||||
height: "35px",
|
||||
width: "35px"
|
||||
},
|
||||
isVcentered: {
|
||||
marginTop: "auto",
|
||||
marginBottom: "auto"
|
||||
},
|
||||
metadata: {
|
||||
marginLeft: 0
|
||||
},
|
||||
tag: {
|
||||
marginTop: ".5rem"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -34,74 +52,70 @@ type Props = {
|
||||
};
|
||||
|
||||
class ChangesetRow extends React.Component<Props> {
|
||||
createLink = (changeset: Changeset) => {
|
||||
createChangesetId = (changeset: Changeset) => {
|
||||
const { repository } = this.props;
|
||||
return <ChangesetId changeset={changeset} repository={repository} />;
|
||||
};
|
||||
|
||||
getTags = () => {
|
||||
const { changeset } = this.props;
|
||||
return changeset._embedded.tags || [];
|
||||
};
|
||||
|
||||
render() {
|
||||
const { changeset, classes } = this.props;
|
||||
const changesetLink = this.createLink(changeset);
|
||||
const dateFromNow = <DateFromNow date={changeset.date} />;
|
||||
const authorLine = <ChangesetAuthor changeset={changeset} />;
|
||||
const { repository, changeset, classes } = this.props;
|
||||
const description = parseDescription(changeset.description);
|
||||
const changesetId = this.createChangesetId(changeset);
|
||||
const dateFromNow = <DateFromNow date={changeset.date} />;
|
||||
|
||||
return (
|
||||
<article className={classNames("media", classes.inner)}>
|
||||
<AvatarWrapper>
|
||||
<div>
|
||||
<figure className="media-left">
|
||||
<p className="image is-64x64">
|
||||
<AvatarImage person={changeset.author} />
|
||||
</p>
|
||||
</figure>
|
||||
<div className={classes.changeset}>
|
||||
<div className="columns">
|
||||
<div className="column is-three-fifths">
|
||||
|
||||
<h4 className="has-text-weight-bold is-ellipsis-overflow">
|
||||
<ExtensionPoint
|
||||
name="changesets.changeset.description"
|
||||
props={{ changeset, value: description.title }}
|
||||
renderAll={false}
|
||||
>
|
||||
{description.title}
|
||||
</ExtensionPoint>
|
||||
</h4>
|
||||
|
||||
<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
|
||||
i18nKey="changesets.changeset.summary"
|
||||
id={changesetId}
|
||||
time={dateFromNow}
|
||||
/>
|
||||
</p>
|
||||
<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>
|
||||
</AvatarWrapper>
|
||||
<div className={classNames("media-content", classes.withOverflow)}>
|
||||
<div className="content">
|
||||
<p className="is-ellipsis-overflow">
|
||||
<strong>
|
||||
<ExtensionPoint
|
||||
name="changesets.changeset.description"
|
||||
props={{ changeset, value: description.title }}
|
||||
renderAll={false}
|
||||
>
|
||||
{description.title}
|
||||
</ExtensionPoint>
|
||||
</strong>
|
||||
<br />
|
||||
<Interpolate
|
||||
i18nKey="changesets.changeset.summary"
|
||||
id={changesetLink}
|
||||
time={dateFromNow}
|
||||
/>
|
||||
</p>{" "}
|
||||
<div className="is-size-7">{authorLine}</div>
|
||||
<div className={classNames("column", classes.isVcentered)}>
|
||||
<ChangesetTags changeset={changeset} />
|
||||
<ChangesetButtonGroup repository={repository} changeset={changeset} />
|
||||
</div>
|
||||
</div>
|
||||
{this.renderTags()}
|
||||
</article>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderTags = () => {
|
||||
const tags = this.getTags();
|
||||
if (tags.length > 0) {
|
||||
return (
|
||||
<div className="media-right">
|
||||
{tags.map((tag: Tag) => {
|
||||
return <ChangesetTag key={tag.name} tag={tag} />;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
export default injectSheet(styles)(translate("repos")(ChangesetRow));
|
||||
|
||||
@@ -1,32 +1,17 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Tag } from "@scm-manager/ui-types";
|
||||
import injectSheet from "react-jss";
|
||||
import classNames from "classnames";
|
||||
|
||||
const styles = {
|
||||
spacing: {
|
||||
marginRight: "4px"
|
||||
}
|
||||
};
|
||||
import ChangesetTagBase from "./ChangesetTagBase";
|
||||
|
||||
type Props = {
|
||||
tag: Tag,
|
||||
|
||||
// context props
|
||||
classes: Object
|
||||
tag: Tag
|
||||
};
|
||||
|
||||
class ChangesetTag extends React.Component<Props> {
|
||||
render() {
|
||||
const { tag, classes } = this.props;
|
||||
return (
|
||||
<span className="tag is-info">
|
||||
<span className={classNames("fa", "fa-tag", classes.spacing)} />{" "}
|
||||
{tag.name}
|
||||
</span>
|
||||
);
|
||||
const { tag } = this.props;
|
||||
return <ChangesetTagBase icon={"fa-tag"} label={tag.name} />;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
import type { Changeset, Repository } from "@scm-manager/ui-types";
|
||||
|
||||
export type Description = {
|
||||
title: 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 {
|
||||
const desc = description ? description : "";
|
||||
const lineBreak = desc.indexOf("\n");
|
||||
|
||||
@@ -3,8 +3,12 @@ import * as changesets from "./changesets";
|
||||
export { changesets };
|
||||
|
||||
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 ChangesetList } from "./ChangesetList";
|
||||
export { default as ChangesetRow } from "./ChangesetRow";
|
||||
export { default as ChangesetTag } from "./ChangesetTag";
|
||||
export { default as ChangesetTags } from "./ChangesetTags";
|
||||
export { default as ChangesetTagsCollapsed } from "./ChangesetTagsCollapsed";
|
||||
export { default as ChangesetDiff } from "./ChangesetDiff";
|
||||
|
||||
Reference in New Issue
Block a user