mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-02 11:35:57 +01:00
Implemented LinkPaginator, refactored code for changeset list
This commit is contained in:
129
scm-ui-components/packages/ui-components/src/LinkPaginator.js
Normal file
129
scm-ui-components/packages/ui-components/src/LinkPaginator.js
Normal file
@@ -0,0 +1,129 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import {translate} from "react-i18next";
|
||||
import type {PagedCollection} from "@scm-manager/ui-types";
|
||||
import {Button} from "./buttons";
|
||||
import {withRouter} from "react-router-dom";
|
||||
|
||||
type Props = {
|
||||
collection: PagedCollection,
|
||||
t: string => string,
|
||||
match: any
|
||||
};
|
||||
|
||||
class LinkPaginator extends React.Component<Props> {
|
||||
renderFirstButton() {
|
||||
return (
|
||||
<Button
|
||||
className={"pagination-link"}
|
||||
label={"1"}
|
||||
disabled={false}
|
||||
link={"1"}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderPreviousButton(label?: string) {
|
||||
const { match } = this.props;
|
||||
const page = parseInt(match.params.page) || 1;
|
||||
const previousPage = page - 1;
|
||||
|
||||
return (
|
||||
<Button
|
||||
className={"pagination-previous"}
|
||||
label={label ? label : previousPage.toString()}
|
||||
disabled={previousPage < 1}
|
||||
link={`${previousPage}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderNextButton(label?: string) {
|
||||
const { match, collection } = this.props;
|
||||
let page = parseInt(match.params.page) || 1;
|
||||
|
||||
const nextPage = page + 1;
|
||||
return (
|
||||
<Button
|
||||
className={"pagination-next"}
|
||||
label={label ? label : nextPage.toString()}
|
||||
disabled={nextPage >= collection.pageTotal + 1}
|
||||
link={`${nextPage}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderLastButton() {
|
||||
const { collection } = this.props;
|
||||
return (
|
||||
<Button
|
||||
className={"pagination-link"}
|
||||
label={`${collection.pageTotal}`}
|
||||
disabled={false}
|
||||
link={`${collection.pageTotal}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
separator() {
|
||||
return <span className="pagination-ellipsis">…</span>;
|
||||
}
|
||||
|
||||
currentPage(page: number) {
|
||||
return (
|
||||
<Button
|
||||
className="pagination-link is-current"
|
||||
label={page}
|
||||
disabled={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
pageLinks() {
|
||||
const { collection } = this.props;
|
||||
|
||||
const links = [];
|
||||
const page = collection.page + 1;
|
||||
const pageTotal = collection.pageTotal;
|
||||
if (page > 1) {
|
||||
links.push(this.renderFirstButton());
|
||||
}
|
||||
if (page > 3) {
|
||||
links.push(this.separator());
|
||||
}
|
||||
if (page > 2) {
|
||||
links.push(this.renderPreviousButton());
|
||||
}
|
||||
|
||||
links.push(this.currentPage(page));
|
||||
|
||||
if (page + 1 < pageTotal) {
|
||||
links.push(this.renderNextButton());
|
||||
}
|
||||
if (page + 2 < pageTotal)
|
||||
//if there exists pages between next and last
|
||||
links.push(this.separator());
|
||||
if (page < pageTotal) {
|
||||
links.push(this.renderLastButton());
|
||||
}
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
return (
|
||||
<nav className="pagination is-centered" aria-label="pagination">
|
||||
{this.renderPreviousButton(t("paginator.previous"))}
|
||||
<ul className="pagination-list">
|
||||
{this.pageLinks().map((link, index) => {
|
||||
return <li key={index}>{link}</li>;
|
||||
})}
|
||||
</ul>
|
||||
{this.renderNextButton(t("paginator.next"))}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(translate("commons")(LinkPaginator));
|
||||
@@ -1,7 +1,7 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import { Link } from "react-router-dom";
|
||||
import {Link} from "react-router-dom";
|
||||
|
||||
export type ButtonProps = {
|
||||
label: string,
|
||||
@@ -59,7 +59,7 @@ class Button extends React.Component<Props> {
|
||||
render() {
|
||||
const { link } = this.props;
|
||||
if (link) {
|
||||
return <Link to={link}>{this.renderButton()}</Link>;
|
||||
return <Link to={link}>{this.renderButton()}</Link>; // TODO: className does only apply to button, not the Link
|
||||
} else {
|
||||
return this.renderButton();
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ export { default as Logo } from "./Logo.js";
|
||||
export { default as MailLink } from "./MailLink.js";
|
||||
export { default as Notification } from "./Notification.js";
|
||||
export { default as Paginator } from "./Paginator.js";
|
||||
export { default as LinkPaginator } from "./LinkPaginator.js";
|
||||
export { default as ProtectedRoute } from "./ProtectedRoute.js";
|
||||
|
||||
export { apiClient, NOT_FOUND_ERROR, UNAUTHORIZED_ERROR } from "./apiclient.js";
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import type {Branch, Repository} from "@scm-manager/ui-types";
|
||||
import {connect} from "react-redux";
|
||||
import {fetchBranches, getBranch, getBranchNames, isFetchBranchesPending} from "../modules/branches";
|
||||
import DropDown from "../components/DropDown";
|
||||
import type {History} from "history";
|
||||
import {withRouter} from "react-router-dom";
|
||||
import ChangesetContainer from "./ChangesetContainer";
|
||||
import {Loading} from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
branches: Branch[],
|
||||
branchNames: string[],
|
||||
fetchBranches: Repository => void,
|
||||
history: History,
|
||||
match: any,
|
||||
selectedBranch: Branch,
|
||||
label: string, //TODO: Should this be here?
|
||||
loading: boolean
|
||||
};
|
||||
type State = {};
|
||||
|
||||
class BranchChangesets extends React.Component<Props, State> {
|
||||
componentDidMount() {
|
||||
this.props.fetchBranches(this.props.repository);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
repository,
|
||||
branchNames,
|
||||
match,
|
||||
selectedBranch,
|
||||
label,
|
||||
loading
|
||||
} = this.props;
|
||||
const selectedBranchName = match.params.branch;
|
||||
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
// TODO: Handle errors
|
||||
|
||||
if (!branchNames || branchNames.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={"box"}>
|
||||
<label className="label">{label}</label>
|
||||
<DropDown
|
||||
options={branchNames}
|
||||
preselectedOption={selectedBranchName}
|
||||
optionSelected={branch => this.branchSelected(branch)}
|
||||
/>
|
||||
</div>
|
||||
<ChangesetContainer repository={repository} branch={selectedBranch} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
//TODO: Maybe extract this and let it be passed from parent component
|
||||
branchSelected = (branchName: string) => {
|
||||
const { namespace, name } = this.props.repository;
|
||||
if (branchName === "") {
|
||||
this.props.history.push(`/repo/${namespace}/${name}/changesets`);
|
||||
} else {
|
||||
this.props.history.push(
|
||||
`/repo/${namespace}/${name}/branches/${branchName}/changesets`
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
fetchBranches: (repo: Repository) => {
|
||||
dispatch(fetchBranches(repo));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const mapStateToProps = (state: any, ownProps: Props) => {
|
||||
const loading = isFetchBranchesPending(state, ownProps.repository);
|
||||
const branchNames = getBranchNames(state, ownProps.repository);
|
||||
const selectedBranch = getBranch(
|
||||
state,
|
||||
ownProps.repository,
|
||||
ownProps.match.params.branch //TODO: Maybe let parent component pass selected branch
|
||||
);
|
||||
return {
|
||||
loading,
|
||||
branchNames,
|
||||
selectedBranch
|
||||
};
|
||||
};
|
||||
export default withRouter(
|
||||
connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(BranchChangesets)
|
||||
);
|
||||
@@ -1,89 +1,128 @@
|
||||
// @flow
|
||||
|
||||
import React from "react";
|
||||
import type { Repository, Branch } from "@scm-manager/ui-types";
|
||||
import { connect } from "react-redux";
|
||||
import * as React from "react";
|
||||
import type {Branch, Repository} from "@scm-manager/ui-types";
|
||||
import {connect} from "react-redux";
|
||||
import {
|
||||
fetchBranches,
|
||||
getBranches,
|
||||
getBranch,
|
||||
getBranchNames,
|
||||
getFetchBranchesFailure,
|
||||
isFetchBranchesPending
|
||||
} from "../modules/branches";
|
||||
|
||||
import { Loading } from "@scm-manager/ui-components";
|
||||
import DropDown from "../components/DropDown";
|
||||
import type {History} from "history";
|
||||
import {withRouter} from "react-router-dom";
|
||||
import {ErrorPage, Loading} from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
fetchBranches: Repository => void,
|
||||
callback: (?Branch) => void,
|
||||
branches: Branch[],
|
||||
selectedBranchName: string,
|
||||
branchNames: string[],
|
||||
fetchBranches: Repository => void,
|
||||
history: History,
|
||||
match: any,
|
||||
selectedBranch?: Branch,
|
||||
label: string, //TODO: Should this be here?
|
||||
loading: boolean,
|
||||
label: string
|
||||
branchSelected: string => void,
|
||||
error: Error,
|
||||
children: React.Node
|
||||
};
|
||||
type State = {};
|
||||
|
||||
type State = {
|
||||
selectedBranchName: string
|
||||
};
|
||||
|
||||
class BranchChooser extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
selectedBranchName: props.selectedBranchName
|
||||
};
|
||||
}
|
||||
|
||||
class WithBranchChooser extends React.Component<Props, State> {
|
||||
componentDidMount() {
|
||||
const { repository, fetchBranches } = this.props;
|
||||
fetchBranches(repository);
|
||||
this.props.fetchBranches(this.props.repository);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { branches, loading, label } = this.props;
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if (branches && branches.length > 0) {
|
||||
console.log("Children");
|
||||
console.log(this.props.children);
|
||||
console.log(React.Children.count(this.props.children));
|
||||
const { selectedBranch, loading, error } = this.props;
|
||||
|
||||
// TODO: i18n
|
||||
if (error) {
|
||||
return (
|
||||
<div className={"box"}>
|
||||
<label className="label">{label}</label>
|
||||
<DropDown
|
||||
options={branches.map(b => b.name)}
|
||||
preselectedOption={this.state.selectedBranchName}
|
||||
optionSelected={branch => this.branchChanged(branch)}
|
||||
/>
|
||||
</div>
|
||||
<ErrorPage
|
||||
title={"Failed loading branches"}
|
||||
subtitle={"Somethin went wrong"}
|
||||
error={error}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
const childrenWithBranch = React.Children.map(
|
||||
this.props.children,
|
||||
child => {
|
||||
return React.cloneElement(child, {
|
||||
branch: selectedBranch
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{this.renderBranchChooser()}
|
||||
{childrenWithBranch}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
branchChanged = (branchName: string) => {
|
||||
const { callback } = this.props;
|
||||
this.setState({ ...this.state, selectedBranchName: branchName });
|
||||
const branch = this.props.branches.find(b => b.name === branchName);
|
||||
callback(branch);
|
||||
};
|
||||
renderBranchChooser() {
|
||||
const { branchNames, label, branchSelected, match } = this.props;
|
||||
const selectedBranchName = match.params.branch;
|
||||
|
||||
if (!branchNames || branchNames.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={"box"}>
|
||||
<label className="label">{label}</label>
|
||||
<DropDown
|
||||
options={branchNames}
|
||||
preselectedOption={selectedBranchName}
|
||||
optionSelected={branch => branchSelected(branch)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: State, ownProps: Props) => {
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
branches: getBranches(state, ownProps.repository),
|
||||
loading: isFetchBranchesPending(state, ownProps.repository)
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch: any) => {
|
||||
return {
|
||||
fetchBranches: (repository: Repository) => {
|
||||
dispatch(fetchBranches(repository));
|
||||
fetchBranches: (repo: Repository) => {
|
||||
dispatch(fetchBranches(repo));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(BranchChooser);
|
||||
const mapStateToProps = (state: any, ownProps: Props) => {
|
||||
const { repository, match } = ownProps;
|
||||
const loading = isFetchBranchesPending(state, repository);
|
||||
const error = getFetchBranchesFailure(state, repository);
|
||||
const branchNames = getBranchNames(state, repository);
|
||||
const selectedBranch = getBranch(
|
||||
state,
|
||||
repository,
|
||||
match.params.branch //TODO: Maybe let parent component pass selected branch
|
||||
);
|
||||
return {
|
||||
loading,
|
||||
branchNames,
|
||||
selectedBranch,
|
||||
error
|
||||
};
|
||||
};
|
||||
|
||||
export default withRouter(
|
||||
connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(WithBranchChooser)
|
||||
);
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import React from "react";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import type { Branch, Changeset, Repository } from "@scm-manager/ui-types";
|
||||
import {
|
||||
fetchChangesetsByBranch,
|
||||
getChangesets,
|
||||
isFetchChangesetsPending
|
||||
} from "../modules/changesets";
|
||||
import { connect } from "react-redux";
|
||||
import ChangesetList from "../components/changesets/ChangesetList";
|
||||
import { Loading } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
fetchChangesetsByBranch: (Repository, Branch) => void,
|
||||
repository: Repository, //TODO: Do we really need/want this here?
|
||||
branch: Branch,
|
||||
changesets: Changeset[],
|
||||
loading: boolean
|
||||
};
|
||||
type State = {};
|
||||
|
||||
class ChangesetContainer extends React.Component<Props, State> {
|
||||
componentDidMount() {
|
||||
const { fetchChangesetsByBranch, repository, branch } = this.props;
|
||||
fetchChangesetsByBranch(repository, branch); //TODO: fetch by page
|
||||
}
|
||||
|
||||
render() {
|
||||
const { repository, changesets, loading } = this.props;
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if (!changesets || changesets.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return <ChangesetList repository={repository} changesets={changesets} />;
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
fetchChangesetsByBranch: (repo: Repository, branch: Branch) => {
|
||||
dispatch(fetchChangesetsByBranch(repo, branch));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const mapStateToProps = (state: any, ownProps: Props) => {
|
||||
const { repository, branch } = ownProps;
|
||||
const changesets = getChangesets(state, repository, branch);
|
||||
const loading = isFetchChangesetsPending(state, repository, branch);
|
||||
return { changesets, loading };
|
||||
};
|
||||
export default withRouter(
|
||||
connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ChangesetContainer)
|
||||
);
|
||||
@@ -1,249 +1,121 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { translate } from "react-i18next";
|
||||
import {
|
||||
ErrorNotification,
|
||||
Loading,
|
||||
Paginator
|
||||
} from "@scm-manager/ui-components";
|
||||
|
||||
import React from "react";
|
||||
import {withRouter} from "react-router-dom";
|
||||
import type {Branch, Changeset, PagedCollection, Repository} from "@scm-manager/ui-types";
|
||||
import {
|
||||
fetchChangesets,
|
||||
fetchChangesetsByBranch,
|
||||
fetchChangesetsByBranchAndPage,
|
||||
fetchChangesetsByLink,
|
||||
fetchChangesetsByPage,
|
||||
getChangesetsFromState,
|
||||
getChangesets,
|
||||
getFetchChangesetsFailure,
|
||||
isFetchChangesetsPending,
|
||||
selectListAsCollection
|
||||
} from "../modules/changesets";
|
||||
import type { History } from "history";
|
||||
import type {
|
||||
Changeset,
|
||||
PagedCollection,
|
||||
Repository,
|
||||
Branch
|
||||
} from "@scm-manager/ui-types";
|
||||
import {connect} from "react-redux";
|
||||
import ChangesetList from "../components/changesets/ChangesetList";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import { fetchBranches, getBranch, getBranchNames } from "../modules/branches";
|
||||
import BranchChooser from "./BranchChooser";
|
||||
import {ErrorPage, LinkPaginator, Loading} from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
branchName: string,
|
||||
branchNames: string[],
|
||||
history: History,
|
||||
fetchChangesetsByNamespaceNameAndBranch: (
|
||||
namespace: string,
|
||||
name: string,
|
||||
branch: string
|
||||
) => void,
|
||||
list: PagedCollection,
|
||||
fetchChangesetsByLink: (Repository, string, Branch) => void,
|
||||
fetchChangesetsByPage: (Repository, number) => void,
|
||||
fetchChangesetsByBranch: (Repository, Branch) => void,
|
||||
fetchChangesetsByBranchAndPage: (Repository, Branch, number) => void,
|
||||
fetchBranches: Repository => void,
|
||||
page: number,
|
||||
t: string => string,
|
||||
match: any,
|
||||
repository: Repository, //TODO: Do we really need/want this here?
|
||||
branch: Branch,
|
||||
changesets: Changeset[],
|
||||
loading: boolean,
|
||||
error: boolean,
|
||||
branch: Branch
|
||||
match: any,
|
||||
list: PagedCollection,
|
||||
error: Error
|
||||
};
|
||||
|
||||
type State = {
|
||||
branch: string
|
||||
};
|
||||
|
||||
class Changesets extends React.PureComponent<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
branch: ""
|
||||
};
|
||||
}
|
||||
|
||||
onPageChange = (link: string) => {
|
||||
const { repository, branch } = this.props;
|
||||
this.props.fetchChangesetsByLink(repository, link, branch);
|
||||
};
|
||||
type State = {};
|
||||
|
||||
class ChangesetContainer extends React.Component<Props, State> {
|
||||
componentDidMount() {
|
||||
if (!this.props.loading) {
|
||||
this.updateContent();
|
||||
}
|
||||
}
|
||||
|
||||
updateContent() {
|
||||
console.log("Component did mount");
|
||||
const {
|
||||
fetchChangesetsByBranch,
|
||||
fetchChangesetsByBranchAndPage,
|
||||
repository,
|
||||
branch,
|
||||
page,
|
||||
fetchChangesetsByPage,
|
||||
fetchChangesetsByBranchAndPage
|
||||
match
|
||||
} = this.props;
|
||||
if (branch) {
|
||||
fetchChangesetsByBranchAndPage(repository, branch, page);
|
||||
const { page } = match.params;
|
||||
if (!page) {
|
||||
fetchChangesetsByBranch(repository, branch);
|
||||
} else {
|
||||
fetchChangesetsByPage(repository, page);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
const { page, list, repository, match } = this.props;
|
||||
const { namespace, name } = repository;
|
||||
const branch = decodeURIComponent(match.params.branch);
|
||||
if (!this.props.loading) {
|
||||
if (prevProps.branch !== this.props.branch) {
|
||||
this.setState({ branch });
|
||||
this.updateContent();
|
||||
}
|
||||
|
||||
if (list && (list.page || list.page === 0)) {
|
||||
// backend starts paging at 0
|
||||
const statePage: number = list.page + 1;
|
||||
if (page !== statePage) {
|
||||
if (branch) {
|
||||
this.props.history.push(
|
||||
`/repo/${namespace}/${name}/${branch}/changesets/${statePage}`
|
||||
);
|
||||
} else {
|
||||
this.props.history.push(
|
||||
`/repo/${namespace}/${name}/changesets/${statePage}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
fetchChangesetsByBranchAndPage(repository, branch, page);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { changesets, loading, error } = this.props;
|
||||
|
||||
// TODO: i18n
|
||||
if (error) {
|
||||
return <ErrorNotification error={error} />;
|
||||
return (
|
||||
<ErrorPage
|
||||
title={"Failed loading branches"}
|
||||
subtitle={"Somethin went wrong"}
|
||||
error={error}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (loading || !changesets) {
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
if (!changesets || changesets.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
{this.renderList()}
|
||||
{this.renderPaginator()}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
renderList = () => {
|
||||
const branch = decodeURIComponent(this.props.match.params.branch);
|
||||
const { repository, changesets, t } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<BranchChooser
|
||||
repository={repository}
|
||||
selectedBranchName={branch}
|
||||
label={t("changesets.branchselector-label")}
|
||||
callback={branch => this.branchChanged(branch)}
|
||||
/>
|
||||
<ChangesetList repository={repository} changesets={changesets} />
|
||||
</>
|
||||
);
|
||||
const { repository, changesets } = this.props;
|
||||
return <ChangesetList repository={repository} changesets={changesets} />;
|
||||
};
|
||||
|
||||
renderPaginator() {
|
||||
renderPaginator = () => {
|
||||
const { list } = this.props;
|
||||
console.log(list);
|
||||
if (list) {
|
||||
return <Paginator collection={list} onPageChange={this.onPageChange} />;
|
||||
return <LinkPaginator collection={list} />;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
branchChanged = (branch: Branch): void => {
|
||||
const { history, repository } = this.props;
|
||||
if (branch === undefined) {
|
||||
history.push(
|
||||
`/repo/${repository.namespace}/${repository.name}/changesets`
|
||||
);
|
||||
} else {
|
||||
const branchName = encodeURIComponent(branch.name);
|
||||
this.setState({ branch: branchName });
|
||||
history.push(
|
||||
`/repo/${repository.namespace}/${
|
||||
repository.name
|
||||
}/${branchName}/changesets`
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const getPageFromProps = props => {
|
||||
let page = props.match.params.page;
|
||||
if (page) {
|
||||
page = parseInt(page, 10);
|
||||
} else {
|
||||
page = 1;
|
||||
}
|
||||
return page;
|
||||
};
|
||||
|
||||
const mapStateToProps = (state, ownProps: Props) => {
|
||||
const { repository } = ownProps;
|
||||
const branchName = ownProps.match.params.branch;
|
||||
const branch = getBranch(state, repository, branchName);
|
||||
const loading = isFetchChangesetsPending(state, repository, branch);
|
||||
const changesets = getChangesetsFromState(state, repository);
|
||||
const branchNames = getBranchNames(state, repository);
|
||||
const error = getFetchChangesetsFailure(state, repository, branch);
|
||||
const list = selectListAsCollection(state, repository);
|
||||
const page = getPageFromProps(ownProps);
|
||||
|
||||
return {
|
||||
loading,
|
||||
changesets,
|
||||
branchNames,
|
||||
error,
|
||||
list,
|
||||
page,
|
||||
branch
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
fetchBranches: (repository: Repository) => {
|
||||
dispatch(fetchBranches(repository));
|
||||
},
|
||||
fetchChangesets: (repository: Repository) => {
|
||||
dispatch(fetchChangesets(repository));
|
||||
},
|
||||
fetchChangesetsByPage: (repository, page: number) => {
|
||||
dispatch(fetchChangesetsByPage(repository, page));
|
||||
fetchChangesetsByBranch: (repo: Repository, branch: Branch) => {
|
||||
dispatch(fetchChangesetsByBranch(repo, branch));
|
||||
},
|
||||
fetchChangesetsByBranchAndPage: (
|
||||
repository,
|
||||
repo: Repository,
|
||||
branch: Branch,
|
||||
page: number
|
||||
) => {
|
||||
dispatch(fetchChangesetsByBranchAndPage(repository, branch, page));
|
||||
},
|
||||
fetchChangesetsByLink: (
|
||||
repository: Repository,
|
||||
link: string,
|
||||
branch?: Branch
|
||||
) => {
|
||||
dispatch(fetchChangesetsByLink(repository, link, branch));
|
||||
dispatch(fetchChangesetsByBranchAndPage(repo, branch, page));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const mapStateToProps = (state: any, ownProps: Props) => {
|
||||
const { repository, branch } = ownProps;
|
||||
const changesets = getChangesets(state, repository, branch);
|
||||
const loading = isFetchChangesetsPending(state, repository, branch);
|
||||
const error = getFetchChangesetsFailure(state, repository, branch);
|
||||
const list = selectListAsCollection(state, repository, branch);
|
||||
return { changesets, list, loading, error };
|
||||
};
|
||||
export default withRouter(
|
||||
connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(translate("repos")(Changesets))
|
||||
)(ChangesetContainer)
|
||||
);
|
||||
|
||||
@@ -12,7 +12,8 @@ import Edit from "../containers/Edit";
|
||||
|
||||
import type {History} from "history";
|
||||
import EditNavLink from "../components/EditNavLink";
|
||||
import BranchChangesets from "./BranchChangesets";
|
||||
import BranchChooser from "./BranchChooser";
|
||||
import Changesets from "./Changesets";
|
||||
|
||||
type Props = {
|
||||
namespace: string,
|
||||
@@ -63,6 +64,15 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
return route.location.pathname.match(regex);
|
||||
};
|
||||
|
||||
branchSelected = (branchName: string) => {
|
||||
const url = this.matchedUrl();
|
||||
if (branchName === "") {
|
||||
this.props.history.push(`${url}/changesets/`);
|
||||
} else {
|
||||
this.props.history.push(`${url}/branches/${branchName}/changesets/`);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { loading, error, repository, t } = this.props;
|
||||
|
||||
@@ -97,31 +107,28 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
/>
|
||||
|
||||
<Route
|
||||
exact
|
||||
path={`${url}/changesets`}
|
||||
render={() => (
|
||||
<BranchChangesets repository={repository} label={"Branches"} />
|
||||
path={`${url}/changesets/:page?`}
|
||||
component={() => (
|
||||
<BranchChooser
|
||||
repository={repository}
|
||||
label={"Branches"}
|
||||
branchSelected={this.branchSelected}
|
||||
>
|
||||
<Changesets repository={repository} />
|
||||
</BranchChooser>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Route
|
||||
exact
|
||||
path={`${url}/changesets/:page`}
|
||||
render={() => (
|
||||
<BranchChangesets repository={repository} label={"Branches"} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${url}/branches/:branch/changesets`}
|
||||
render={() => (
|
||||
<BranchChangesets repository={repository} label={"Branches"} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${url}/branches/:branch/changesets/:page`}
|
||||
render={() => (
|
||||
<BranchChangesets repository={repository} label={"Branches"} />
|
||||
path={`${url}/branches/:branch/changesets/:page?`}
|
||||
component={() => (
|
||||
<BranchChooser
|
||||
repository={repository}
|
||||
label={"Branches"}
|
||||
branchSelected={this.branchSelected}
|
||||
>
|
||||
<Changesets repository={repository} />
|
||||
</BranchChooser>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@@ -131,7 +138,7 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
<NavLink to={url} label={t("repository-root.information")} />
|
||||
<NavLink
|
||||
activeOnlyWhenExact={false}
|
||||
to={`${url}/changesets`}
|
||||
to={`${url}/changesets/`}
|
||||
label={t("repository-root.history")}
|
||||
activeWhenMatch={this.matches}
|
||||
/>
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
// @flow
|
||||
import {FAILURE_SUFFIX, PENDING_SUFFIX, SUCCESS_SUFFIX} from "../../modules/types";
|
||||
import {apiClient} from "@scm-manager/ui-components";
|
||||
import type {Action, Branch, Repository} from "@scm-manager/ui-types";
|
||||
import {isPending} from "../../modules/pending";
|
||||
import {
|
||||
FAILURE_SUFFIX,
|
||||
PENDING_SUFFIX,
|
||||
SUCCESS_SUFFIX
|
||||
} from "../../modules/types";
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import type { Action, Branch, Repository } from "@scm-manager/ui-types";
|
||||
import { isPending } from "../../modules/pending";
|
||||
import { getFailure } from "../../modules/failure";
|
||||
|
||||
export const FETCH_BRANCHES = "scm/repos/FETCH_BRANCHES";
|
||||
export const FETCH_BRANCHES_PENDING = `${FETCH_BRANCHES}_${PENDING_SUFFIX}`;
|
||||
@@ -128,7 +133,7 @@ export function getBranch(
|
||||
state: Object,
|
||||
repository: Repository,
|
||||
name: string
|
||||
): Branch {
|
||||
): ?Branch {
|
||||
const key = createKey(repository);
|
||||
if (state.branches[key]) {
|
||||
if (state.branches[key].byNames[name]) {
|
||||
@@ -145,6 +150,10 @@ export function isFetchBranchesPending(
|
||||
return isPending(state, FETCH_BRANCHES, createKey(repository));
|
||||
}
|
||||
|
||||
export function getFetchBranchesFailure(state: Object, repository: Repository) {
|
||||
return getFailure(state, FETCH_BRANCHES, createKey(repository));
|
||||
}
|
||||
|
||||
function createKey(repository: Repository): string {
|
||||
const { namespace, name } = repository;
|
||||
return `${namespace}/${name}`;
|
||||
|
||||
@@ -10,6 +10,7 @@ import reducer, {
|
||||
getBranch,
|
||||
getBranches,
|
||||
getBranchNames,
|
||||
getFetchBranchesFailure,
|
||||
isFetchBranchesPending
|
||||
} from "./branches";
|
||||
|
||||
@@ -129,6 +130,8 @@ describe("branches", () => {
|
||||
});
|
||||
|
||||
describe("branch selectors", () => {
|
||||
const error = new Error("Something went wrong");
|
||||
|
||||
const state = {
|
||||
branches: {
|
||||
[key]: {
|
||||
@@ -173,5 +176,18 @@ describe("branches", () => {
|
||||
const branch = getBranch(state, repository, "branch42");
|
||||
expect(branch).toBeFalsy();
|
||||
});
|
||||
it("should return error if fetching branches failed", () => {
|
||||
const state = {
|
||||
failure: {
|
||||
[FETCH_BRANCHES + "/foo/bar"]: error
|
||||
}
|
||||
};
|
||||
|
||||
expect(getFetchBranchesFailure(state, repository)).toEqual(error);
|
||||
});
|
||||
|
||||
it("should return false if fetching branches did not fail", () => {
|
||||
expect(getFetchBranchesFailure({}, repository)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,21 +1,11 @@
|
||||
// @flow
|
||||
|
||||
import {
|
||||
FAILURE_SUFFIX,
|
||||
PENDING_SUFFIX,
|
||||
SUCCESS_SUFFIX
|
||||
} from "../../modules/types";
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import { isPending } from "../../modules/pending";
|
||||
import { getFailure } from "../../modules/failure";
|
||||
import { combineReducers } from "redux";
|
||||
import type {
|
||||
Action,
|
||||
Changeset,
|
||||
PagedCollection,
|
||||
Repository,
|
||||
Branch
|
||||
} from "@scm-manager/ui-types";
|
||||
import {FAILURE_SUFFIX, PENDING_SUFFIX, SUCCESS_SUFFIX} from "../../modules/types";
|
||||
import {apiClient} from "@scm-manager/ui-components";
|
||||
import {isPending} from "../../modules/pending";
|
||||
import {getFailure} from "../../modules/failure";
|
||||
import {combineReducers} from "redux";
|
||||
import type {Action, Branch, Changeset, PagedCollection, Repository} from "@scm-manager/ui-types";
|
||||
|
||||
export const FETCH_CHANGESETS = "scm/repos/FETCH_CHANGESETS";
|
||||
export const FETCH_CHANGESETS_PENDING = `${FETCH_CHANGESETS}_${PENDING_SUFFIX}`;
|
||||
@@ -82,7 +72,6 @@ export function fetchChangesetsByPage(repository: Repository, page: number) {
|
||||
return fetchChangesetsWithOptions(repository, undefined, `?page=${page - 1}`);
|
||||
}
|
||||
|
||||
// TODO: Rewrite code to fetch changesets by branches, adjust tests and let BranchChooser fetch branches
|
||||
export function fetchChangesetsByBranchAndPage(
|
||||
repository: Repository,
|
||||
branch: Branch,
|
||||
@@ -167,11 +156,11 @@ function byKeyReducer(
|
||||
if (state[key]) {
|
||||
oldChangesets[key] = state[key];
|
||||
}
|
||||
const byIds = extractChangesetsByIds(changesets, oldChangesets[key].byId);
|
||||
const byIds = extractChangesetsByIds(changesets);
|
||||
return {
|
||||
...state,
|
||||
[key]: {
|
||||
byId: { ...byIds },
|
||||
byId: byIds,
|
||||
list: {
|
||||
entries: changesetIds,
|
||||
entry: {
|
||||
@@ -191,17 +180,13 @@ export default combineReducers({
|
||||
byKey: byKeyReducer
|
||||
});
|
||||
|
||||
function extractChangesetsByIds(changesets: any, oldChangesetsByIds: any) {
|
||||
function extractChangesetsByIds(changesets: any) {
|
||||
const changesetsByIds = {};
|
||||
|
||||
for (let changeset of changesets) {
|
||||
changesetsByIds[changeset.id] = changeset;
|
||||
}
|
||||
|
||||
for (let id in oldChangesetsByIds) {
|
||||
changesetsByIds[id] = oldChangesetsByIds[id];
|
||||
}
|
||||
|
||||
return changesetsByIds;
|
||||
}
|
||||
|
||||
@@ -234,16 +219,20 @@ export function getFetchChangesetsFailure(
|
||||
return getFailure(state, FETCH_CHANGESETS, createItemId(repository, branch));
|
||||
}
|
||||
|
||||
const selectList = (state: Object, repository: Repository) => {
|
||||
const itemId = createItemId(repository);
|
||||
const selectList = (state: Object, repository: Repository, branch?: Branch) => {
|
||||
const itemId = createItemId(repository, branch);
|
||||
if (state.changesets.byKey[itemId] && state.changesets.byKey[itemId].list) {
|
||||
return state.changesets.byKey[itemId].list;
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
const selectListEntry = (state: Object, repository: Repository): Object => {
|
||||
const list = selectList(state, repository);
|
||||
const selectListEntry = (
|
||||
state: Object,
|
||||
repository: Repository,
|
||||
branch?: Branch
|
||||
): Object => {
|
||||
const list = selectList(state, repository, branch);
|
||||
if (list.entry) {
|
||||
return list.entry;
|
||||
}
|
||||
@@ -252,9 +241,10 @@ const selectListEntry = (state: Object, repository: Repository): Object => {
|
||||
|
||||
export const selectListAsCollection = (
|
||||
state: Object,
|
||||
repository: Repository
|
||||
repository: Repository,
|
||||
branch?: Branch
|
||||
): PagedCollection => {
|
||||
return selectListEntry(state, repository);
|
||||
return selectListEntry(state, repository, branch);
|
||||
};
|
||||
|
||||
export function getChangesetsFromState(state: Object, repository: Repository) {
|
||||
|
||||
@@ -3,21 +3,21 @@
|
||||
import configureMockStore from "redux-mock-store";
|
||||
import thunk from "redux-thunk";
|
||||
import fetchMock from "fetch-mock";
|
||||
import {
|
||||
import reducer, {
|
||||
FETCH_CHANGESETS,
|
||||
FETCH_CHANGESETS_FAILURE,
|
||||
FETCH_CHANGESETS_PENDING,
|
||||
FETCH_CHANGESETS_SUCCESS,
|
||||
fetchChangesets,
|
||||
fetchChangesetsByBranchAndPage,
|
||||
fetchChangesetsByBranch,
|
||||
fetchChangesetsByBranchAndPage,
|
||||
fetchChangesetsByPage,
|
||||
fetchChangesetsSuccess,
|
||||
getChangesets,
|
||||
getFetchChangesetsFailure,
|
||||
isFetchChangesetsPending
|
||||
} from "./changesets";
|
||||
import reducer from "./changesets";
|
||||
|
||||
const branch = {
|
||||
name: "specific",
|
||||
revision: "123",
|
||||
@@ -40,7 +40,10 @@ const repository = {
|
||||
changesets: {
|
||||
href: "http://scm/api/rest/v2/repositories/foo/bar/changesets"
|
||||
},
|
||||
branches: [branch]
|
||||
branches: {
|
||||
href:
|
||||
"http://scm/api/rest/v2/repositories/foo/bar/branches/specific/branches"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -238,38 +241,6 @@ describe("changesets", () => {
|
||||
entries: ["changeset1", "changeset2", "changeset3"]
|
||||
});
|
||||
});
|
||||
|
||||
it("should not delete existing changesets from state", () => {
|
||||
const responseBody = {
|
||||
_embedded: {
|
||||
changesets: [
|
||||
{ id: "changeset1", author: { mail: "z@phod.com", name: "zaphod" } }
|
||||
],
|
||||
_embedded: {
|
||||
tags: [],
|
||||
branches: [],
|
||||
parents: []
|
||||
}
|
||||
}
|
||||
};
|
||||
const newState = reducer(
|
||||
{
|
||||
byKey: {
|
||||
"foo/bar": {
|
||||
byId: {
|
||||
["changeset2"]: {
|
||||
id: "changeset2",
|
||||
author: { mail: "mail@author.com", name: "author" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
fetchChangesetsSuccess(responseBody, repository)
|
||||
);
|
||||
expect(newState.byKey["foo/bar"].byId["changeset2"]).toBeDefined();
|
||||
expect(newState.byKey["foo/bar"].byId["changeset1"]).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("changeset selectors", () => {
|
||||
|
||||
Reference in New Issue
Block a user