mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-08 22:45:45 +01:00
Merged in feature/create-branch (pull request #233)
Feature create branch
This commit is contained in:
@@ -30,9 +30,9 @@
|
||||
"@scm-manager/ui-types": "2.0.0-SNAPSHOT",
|
||||
"classnames": "^2.2.6",
|
||||
"moment": "^2.22.2",
|
||||
"react": "^16.5.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-diff-view": "^1.8.1",
|
||||
"react-dom": "^16.5.2",
|
||||
"react-i18next": "^7.11.0",
|
||||
"react-jss": "^8.6.1",
|
||||
"react-router-dom": "^4.3.1",
|
||||
@@ -63,4 +63,4 @@
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ type Props = {
|
||||
value?: string,
|
||||
onChange: (value: string, name?: string) => void,
|
||||
loading?: boolean,
|
||||
helpText?: string
|
||||
helpText?: string,
|
||||
disabled?: boolean
|
||||
};
|
||||
|
||||
class Select extends React.Component<Props> {
|
||||
@@ -34,7 +35,7 @@ class Select extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { options, value, label, helpText, loading } = this.props;
|
||||
const { options, value, label, helpText, loading, disabled } = this.props;
|
||||
const loadingClass = loading ? "is-loading" : "";
|
||||
|
||||
|
||||
@@ -51,6 +52,7 @@ class Select extends React.Component<Props> {
|
||||
}}
|
||||
value={value}
|
||||
onChange={this.handleInput}
|
||||
disabled={disabled}
|
||||
>
|
||||
{options.map(opt => {
|
||||
return (
|
||||
|
||||
@@ -6623,14 +6623,14 @@ react-dom@^16.4.2:
|
||||
prop-types "^15.6.2"
|
||||
scheduler "^0.10.0"
|
||||
|
||||
react-dom@^16.5.2:
|
||||
version "16.5.2"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.5.2.tgz#b69ee47aa20bab5327b2b9d7c1fe2a30f2cfa9d7"
|
||||
react-dom@^16.8.6:
|
||||
version "16.8.6"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f"
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
schedule "^0.5.0"
|
||||
scheduler "^0.13.6"
|
||||
|
||||
react-i18next@^7.11.0:
|
||||
version "7.13.0"
|
||||
@@ -6755,14 +6755,14 @@ react@^16.4.2:
|
||||
prop-types "^15.6.2"
|
||||
scheduler "^0.10.0"
|
||||
|
||||
react@^16.5.2:
|
||||
version "16.5.2"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-16.5.2.tgz#19f6b444ed139baa45609eee6dc3d318b3895d42"
|
||||
react@^16.8.6:
|
||||
version "16.8.6"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe"
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
schedule "^0.5.0"
|
||||
scheduler "^0.13.6"
|
||||
|
||||
read-only-stream@^2.0.0:
|
||||
version "2.0.0"
|
||||
@@ -7229,6 +7229,13 @@ scheduler@^0.10.0:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
scheduler@^0.13.6:
|
||||
version "0.13.6"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.6.tgz#466a4ec332467b31a91b9bf74e5347072e4cd889"
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
select@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
|
||||
|
||||
@@ -7,3 +7,8 @@ export type Branch = {
|
||||
defaultBranch?: boolean,
|
||||
_links: Links
|
||||
}
|
||||
|
||||
export type BranchRequest = {
|
||||
name: string,
|
||||
parent: string
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export type { Group, Member } from "./Group";
|
||||
export type { Repository, RepositoryCollection, RepositoryGroup } from "./Repositories";
|
||||
export type { RepositoryType, RepositoryTypeCollection } from "./RepositoryTypes";
|
||||
|
||||
export type { Branch } from "./Branches";
|
||||
export type { Branch, BranchRequest } from "./Branches";
|
||||
|
||||
export type { Changeset } from "./Changesets";
|
||||
|
||||
|
||||
@@ -17,12 +17,14 @@
|
||||
"i18next-browser-languagedetector": "^2.2.2",
|
||||
"i18next-fetch-backend": "^0.1.0",
|
||||
"jss-nested": "^6.0.1",
|
||||
"memoize-one": "^5.0.4",
|
||||
"moment": "^2.22.2",
|
||||
"node-sass": "^4.9.3",
|
||||
"postcss-easy-import": "^3.0.0",
|
||||
"react": "^16.4.2",
|
||||
"query-string": "5",
|
||||
"react": "^16.8.6",
|
||||
"react-diff-view": "^1.8.1",
|
||||
"react-dom": "^16.4.2",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-i18next": "^7.9.0",
|
||||
"react-jss": "^8.6.0",
|
||||
"react-redux": "^5.0.7",
|
||||
|
||||
@@ -11,7 +11,10 @@
|
||||
"validation": {
|
||||
"namespace-invalid": "The repository namespace is invalid",
|
||||
"name-invalid": "The repository name is invalid",
|
||||
"contact-invalid": "Contact must be a valid mail address"
|
||||
"contact-invalid": "Contact must be a valid mail address",
|
||||
"branch": {
|
||||
"nameInvalid": "The branch name is invalid"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"namespaceHelpText": "The namespace of the repository. This name will be part of the repository url.",
|
||||
|
||||
@@ -1,16 +1,125 @@
|
||||
//@flow
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import type { Repository, Branch, BranchRequest } from "@scm-manager/ui-types";
|
||||
import {
|
||||
Select,
|
||||
InputField,
|
||||
SubmitButton,
|
||||
validation as validator
|
||||
} from "@scm-manager/ui-components";
|
||||
import { orderBranches } from "../util/orderBranches";
|
||||
|
||||
type Props = {};
|
||||
type Props = {
|
||||
submitForm: BranchRequest => void,
|
||||
repository: Repository,
|
||||
branches: Branch[],
|
||||
loading?: boolean,
|
||||
transmittedName?: string,
|
||||
disabled?: boolean,
|
||||
t: string => string
|
||||
};
|
||||
|
||||
type State = {
|
||||
source?: string,
|
||||
name?: string,
|
||||
nameValidationError: boolean
|
||||
};
|
||||
|
||||
class BranchForm extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
nameValidationError: false,
|
||||
name: props.transmittedName
|
||||
};
|
||||
}
|
||||
|
||||
isFalsy(value) {
|
||||
return !value;
|
||||
}
|
||||
|
||||
isValid = () => {
|
||||
const { source, name } = this.state;
|
||||
return !(
|
||||
this.state.nameValidationError ||
|
||||
this.isFalsy(source) ||
|
||||
this.isFalsy(name)
|
||||
);
|
||||
};
|
||||
|
||||
submit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
if (this.isValid()) {
|
||||
this.props.submitForm({
|
||||
name: this.state.name,
|
||||
parent: this.state.source
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
class CreateBranch extends React.Component<Props> {
|
||||
render() {
|
||||
const { t, branches, loading, transmittedName, disabled } = this.props;
|
||||
const { name } = this.state;
|
||||
orderBranches(branches);
|
||||
const options = branches.map(branch => ({
|
||||
label: branch.name,
|
||||
value: branch.name
|
||||
}));
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>Form placeholder</p>
|
||||
<form onSubmit={this.submit}>
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<Select
|
||||
name="source"
|
||||
label={t("branches.create.source")}
|
||||
options={options}
|
||||
onChange={this.handleSourceChange}
|
||||
loading={loading}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<InputField
|
||||
name="name"
|
||||
label={t("branches.create.name")}
|
||||
onChange={this.handleNameChange}
|
||||
value={name ? name : ""}
|
||||
validationError={this.state.nameValidationError}
|
||||
errorMessage={t("validation.branch.nameInvalid")}
|
||||
disabled={!!transmittedName || disabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<SubmitButton
|
||||
disabled={disabled || !this.isValid()}
|
||||
loading={loading}
|
||||
label={t("branches.create.submit")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
handleSourceChange = (source: string) => {
|
||||
this.setState({
|
||||
...this.state,
|
||||
source
|
||||
});
|
||||
};
|
||||
|
||||
handleNameChange = (name: string) => {
|
||||
this.setState({
|
||||
nameValidationError: !validator.isNameValid(name),
|
||||
...this.state,
|
||||
name
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default translate("repos")(BranchForm);
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
import { ErrorNotification, Loading } from "@scm-manager/ui-components";
|
||||
import type { History } from "history";
|
||||
import { NotFoundError } from "@scm-manager/ui-components";
|
||||
import queryString from "query-string";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
@@ -49,25 +50,25 @@ class BranchRoot extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
repository,
|
||||
branch,
|
||||
loading,
|
||||
error,
|
||||
match,
|
||||
location
|
||||
} = this.props;
|
||||
const { repository, branch, loading, error, match, location } = this.props;
|
||||
|
||||
const url = this.matchedUrl();
|
||||
|
||||
if (error) {
|
||||
if(error instanceof NotFoundError && location.search.indexOf("?create=true") > -1) {
|
||||
return <Redirect to={`/repo/${repository.namespace}/${repository.name}/branches/create?name=${match.params.branch}`} />;
|
||||
if (
|
||||
error instanceof NotFoundError &&
|
||||
queryString.parse(location.search).create === "true"
|
||||
) {
|
||||
return (
|
||||
<Redirect
|
||||
to={`/repo/${repository.namespace}/${
|
||||
repository.name
|
||||
}/branches/create?name=${match.params.branch}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ErrorNotification error={error} />
|
||||
);
|
||||
return <ErrorNotification error={error} />;
|
||||
}
|
||||
|
||||
if (loading || !branch) {
|
||||
|
||||
@@ -4,7 +4,8 @@ import {
|
||||
fetchBranches,
|
||||
getBranches,
|
||||
getFetchBranchesFailure,
|
||||
isFetchBranchesPending
|
||||
isFetchBranchesPending,
|
||||
isPermittedToCreateBranches
|
||||
} from "../modules/branches";
|
||||
import { orderBranches } from "../util/orderBranches";
|
||||
import { connect } from "react-redux";
|
||||
@@ -75,8 +76,7 @@ class BranchesOverview extends React.Component<Props> {
|
||||
|
||||
renderCreateButton() {
|
||||
const { showCreateButton, t } = this.props;
|
||||
if (showCreateButton || true) {
|
||||
// TODO
|
||||
if (showCreateButton) {
|
||||
return (
|
||||
<CreateButton
|
||||
label={t("branches.overview.createButton")}
|
||||
@@ -93,12 +93,14 @@ const mapStateToProps = (state, ownProps) => {
|
||||
const loading = isFetchBranchesPending(state, repository);
|
||||
const error = getFetchBranchesFailure(state, repository);
|
||||
const branches = getBranches(state, repository);
|
||||
const showCreateButton = isPermittedToCreateBranches(state, repository);
|
||||
|
||||
return {
|
||||
repository,
|
||||
loading,
|
||||
error,
|
||||
branches
|
||||
branches,
|
||||
showCreateButton
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,23 +1,150 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { Subtitle } from "@scm-manager/ui-components";
|
||||
import {translate} from "react-i18next";
|
||||
import {
|
||||
ErrorNotification,
|
||||
Loading,
|
||||
Subtitle
|
||||
} from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
import BranchForm from "../components/BranchForm";
|
||||
import type { Repository, Branch, BranchRequest } from "@scm-manager/ui-types";
|
||||
import {
|
||||
fetchBranches,
|
||||
getBranches,
|
||||
getBranchCreateLink,
|
||||
createBranch,
|
||||
createBranchReset,
|
||||
isCreateBranchPending,
|
||||
getCreateBranchFailure,
|
||||
isFetchBranchesPending,
|
||||
getFetchBranchesFailure
|
||||
} from "../modules/branches";
|
||||
import type { History } from "history";
|
||||
import { connect } from "react-redux";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import queryString from "query-string";
|
||||
|
||||
type Props = {
|
||||
t: string => string
|
||||
loading?: boolean,
|
||||
error?: Error,
|
||||
repository: Repository,
|
||||
branches: Branch[],
|
||||
createBranchesLink: string,
|
||||
isPermittedToCreateBranches: boolean,
|
||||
|
||||
// dispatcher functions
|
||||
fetchBranches: Repository => void,
|
||||
createBranch: (
|
||||
createLink: string,
|
||||
repository: Repository,
|
||||
branch: BranchRequest,
|
||||
callback?: (Branch) => void
|
||||
) => void,
|
||||
resetForm: Repository => void,
|
||||
|
||||
// context objects
|
||||
t: string => string,
|
||||
history: History,
|
||||
location: any
|
||||
};
|
||||
|
||||
class CreateBranch extends React.Component<Props> {
|
||||
componentDidMount() {
|
||||
const { fetchBranches, repository } = this.props;
|
||||
fetchBranches(repository);
|
||||
this.props.resetForm(repository);
|
||||
}
|
||||
|
||||
branchCreated = (branch: Branch) => {
|
||||
const { history, repository } = this.props;
|
||||
history.push(
|
||||
`/repo/${repository.namespace}/${
|
||||
repository.name
|
||||
}/branch/${encodeURIComponent(branch.name)}/info`
|
||||
);
|
||||
};
|
||||
|
||||
createBranch = (branch: BranchRequest) => {
|
||||
this.props.createBranch(
|
||||
this.props.createBranchesLink,
|
||||
this.props.repository,
|
||||
branch,
|
||||
newBranch => this.branchCreated(newBranch)
|
||||
);
|
||||
};
|
||||
|
||||
transmittedName = (url: string) => {
|
||||
const params = queryString.parse(url);
|
||||
return params.name;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
const { t, loading, error, repository, branches, createBranchesLink, location } = this.props;
|
||||
|
||||
if (error) {
|
||||
return <ErrorNotification error={error} />;
|
||||
}
|
||||
|
||||
if (loading || !branches) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Subtitle subtitle={t("branches.create.title")} />
|
||||
<p>Create placeholder</p>
|
||||
<BranchForm
|
||||
submitForm={branchRequest => this.createBranch(branchRequest)}
|
||||
loading={loading}
|
||||
repository={repository}
|
||||
branches={branches}
|
||||
transmittedName={this.transmittedName(location.search)}
|
||||
disabled={!createBranchesLink}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("repos")(CreateBranch);
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
fetchBranches: (repository: Repository) => {
|
||||
dispatch(fetchBranches(repository));
|
||||
},
|
||||
createBranch: (
|
||||
createLink: string,
|
||||
repository: Repository,
|
||||
branchRequest: BranchRequest,
|
||||
callback?: (newBranch: Branch) => void
|
||||
) => {
|
||||
dispatch(createBranch(createLink, repository, branchRequest, callback));
|
||||
},
|
||||
resetForm: (repository: Repository) => {
|
||||
dispatch(createBranchReset(repository));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { repository } = ownProps;
|
||||
const loading =
|
||||
isFetchBranchesPending(state, repository) ||
|
||||
isCreateBranchPending(state, repository);
|
||||
const error =
|
||||
getFetchBranchesFailure(state, repository) || getCreateBranchFailure(state);
|
||||
const branches = getBranches(state, repository);
|
||||
const createBranchesLink = getBranchCreateLink(state, repository);
|
||||
return {
|
||||
repository,
|
||||
loading,
|
||||
error,
|
||||
branches,
|
||||
createBranchesLink
|
||||
};
|
||||
};
|
||||
|
||||
export default withRouter(
|
||||
connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(translate("repos")(CreateBranch))
|
||||
);
|
||||
|
||||
@@ -2,13 +2,21 @@
|
||||
import {
|
||||
FAILURE_SUFFIX,
|
||||
PENDING_SUFFIX,
|
||||
SUCCESS_SUFFIX
|
||||
SUCCESS_SUFFIX,
|
||||
RESET_SUFFIX
|
||||
} from "../../../modules/types";
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import type { Action, Branch, Repository } from "@scm-manager/ui-types";
|
||||
import type {
|
||||
Action,
|
||||
Branch,
|
||||
BranchRequest,
|
||||
Repository
|
||||
} from "@scm-manager/ui-types";
|
||||
import { isPending } from "../../../modules/pending";
|
||||
import { getFailure } from "../../../modules/failure";
|
||||
|
||||
import memoizeOne from 'memoize-one';
|
||||
|
||||
export const FETCH_BRANCHES = "scm/repos/FETCH_BRANCHES";
|
||||
export const FETCH_BRANCHES_PENDING = `${FETCH_BRANCHES}_${PENDING_SUFFIX}`;
|
||||
export const FETCH_BRANCHES_SUCCESS = `${FETCH_BRANCHES}_${SUCCESS_SUFFIX}`;
|
||||
@@ -19,6 +27,15 @@ export const FETCH_BRANCH_PENDING = `${FETCH_BRANCH}_${PENDING_SUFFIX}`;
|
||||
export const FETCH_BRANCH_SUCCESS = `${FETCH_BRANCH}_${SUCCESS_SUFFIX}`;
|
||||
export const FETCH_BRANCH_FAILURE = `${FETCH_BRANCH}_${FAILURE_SUFFIX}`;
|
||||
|
||||
export const CREATE_BRANCH = "scm/repos/CREATE_BRANCH";
|
||||
export const CREATE_BRANCH_PENDING = `${CREATE_BRANCH}_${PENDING_SUFFIX}`;
|
||||
export const CREATE_BRANCH_SUCCESS = `${CREATE_BRANCH}_${SUCCESS_SUFFIX}`;
|
||||
export const CREATE_BRANCH_FAILURE = `${CREATE_BRANCH}_${FAILURE_SUFFIX}`;
|
||||
export const CREATE_BRANCH_RESET = `${CREATE_BRANCH}_${RESET_SUFFIX}`;
|
||||
|
||||
const CONTENT_TYPE_BRANCH_REQUEST =
|
||||
"application/vnd.scmm-branchRequest+json;v=2";
|
||||
|
||||
// Fetching branches
|
||||
|
||||
export function fetchBranches(repository: Repository) {
|
||||
@@ -44,10 +61,7 @@ export function fetchBranches(repository: Repository) {
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchBranch(
|
||||
repository: Repository,
|
||||
name: string
|
||||
) {
|
||||
export function fetchBranch(repository: Repository, name: string) {
|
||||
let link = repository._links.branches.href;
|
||||
if (!link.endsWith("/")) {
|
||||
link += "/";
|
||||
@@ -69,26 +83,85 @@ export function fetchBranch(
|
||||
};
|
||||
}
|
||||
|
||||
// create branch
|
||||
|
||||
export function createBranch(
|
||||
link: string,
|
||||
repository: Repository,
|
||||
branchRequest: BranchRequest,
|
||||
callback?: (branch: Branch) => void
|
||||
) {
|
||||
return function(dispatch: any) {
|
||||
dispatch(createBranchPending(repository));
|
||||
return apiClient
|
||||
.post(link, branchRequest, CONTENT_TYPE_BRANCH_REQUEST)
|
||||
.then(response => response.headers.get("Location"))
|
||||
.then(location => apiClient.get(location))
|
||||
.then(response => response.json())
|
||||
.then(branch => {
|
||||
dispatch(createBranchSuccess(repository));
|
||||
if (callback) {
|
||||
callback(branch);
|
||||
}
|
||||
})
|
||||
.catch(error => dispatch(createBranchFailure(repository, error)));
|
||||
};
|
||||
}
|
||||
|
||||
// Selectors
|
||||
|
||||
export function getBranches(state: Object, repository: Repository) {
|
||||
const key = createKey(repository);
|
||||
if (state.branches[key]) {
|
||||
return state.branches[key];
|
||||
}
|
||||
return null;
|
||||
function collectBranches(repoState) {
|
||||
return repoState.list._embedded.branches.map(
|
||||
name => repoState.byName[name]
|
||||
);
|
||||
}
|
||||
|
||||
const memoizedBranchCollector = memoizeOne(collectBranches);
|
||||
|
||||
export function getBranches(state: Object, repository: Repository) {
|
||||
const repoState = getRepoState(state, repository);
|
||||
if (repoState && repoState.list) {
|
||||
return memoizedBranchCollector(repoState);
|
||||
}
|
||||
}
|
||||
|
||||
export function getBranchCreateLink(state: Object, repository: Repository) {
|
||||
const repoState = getRepoState(state, repository);
|
||||
if (repoState && repoState.list && repoState.list._links && repoState.list._links.create) {
|
||||
return repoState.list._links.create.href;
|
||||
}
|
||||
}
|
||||
|
||||
function getRepoState(state: Object, repository: Repository) {
|
||||
const key = createKey(repository);
|
||||
const repoState = state.branches[key];
|
||||
if (repoState && repoState.byName) {
|
||||
return repoState;
|
||||
}
|
||||
}
|
||||
|
||||
export const isPermittedToCreateBranches = (
|
||||
state: Object,
|
||||
repository: Repository
|
||||
): boolean => {
|
||||
const repoState = getRepoState(state, repository);
|
||||
return !!(
|
||||
repoState &&
|
||||
repoState.list &&
|
||||
repoState.list._links &&
|
||||
repoState.list._links.create
|
||||
);
|
||||
};
|
||||
|
||||
export function getBranch(
|
||||
state: Object,
|
||||
repository: Repository,
|
||||
name: string
|
||||
): ?Branch {
|
||||
const key = createKey(repository);
|
||||
if (state.branches[key]) {
|
||||
return state.branches[key].find((b: Branch) => b.name === name);
|
||||
const repoState = getRepoState(state, repository);
|
||||
if (repoState) {
|
||||
return repoState.byName[name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Action creators
|
||||
@@ -103,14 +176,6 @@ export function getFetchBranchesFailure(state: Object, repository: Repository) {
|
||||
return getFailure(state, FETCH_BRANCHES, createKey(repository));
|
||||
}
|
||||
|
||||
export function isFetchBranchPending(state: Object, repository: Repository, name: string) {
|
||||
return isPending(state, FETCH_BRANCH, createKey(repository) + "/" + name);
|
||||
}
|
||||
|
||||
export function getFetchBranchFailure(state: Object, repository: Repository, name: string) {
|
||||
return getFailure(state, FETCH_BRANCH, createKey(repository) + "/" + name);
|
||||
}
|
||||
|
||||
export function fetchBranchesPending(repository: Repository) {
|
||||
return {
|
||||
type: FETCH_BRANCHES_PENDING,
|
||||
@@ -135,6 +200,65 @@ export function fetchBranchesFailure(repository: Repository, error: Error) {
|
||||
};
|
||||
}
|
||||
|
||||
export function isCreateBranchPending(state: Object, repository: Repository) {
|
||||
return isPending(state, CREATE_BRANCH, createKey(repository));
|
||||
}
|
||||
|
||||
export function getCreateBranchFailure(state: Object) {
|
||||
return getFailure(state, CREATE_BRANCH);
|
||||
}
|
||||
|
||||
export function createBranchPending(repository: Repository): Action {
|
||||
return {
|
||||
type: CREATE_BRANCH_PENDING,
|
||||
payload: { repository },
|
||||
itemId: createKey(repository)
|
||||
};
|
||||
}
|
||||
|
||||
export function createBranchSuccess(repository: Repository): Action {
|
||||
return {
|
||||
type: CREATE_BRANCH_SUCCESS,
|
||||
payload: { repository },
|
||||
itemId: createKey(repository)
|
||||
};
|
||||
}
|
||||
|
||||
export function createBranchFailure(
|
||||
repository: Repository,
|
||||
error: Error
|
||||
): Action {
|
||||
return {
|
||||
type: CREATE_BRANCH_FAILURE,
|
||||
payload: { repository, error },
|
||||
itemId: createKey(repository)
|
||||
};
|
||||
}
|
||||
|
||||
export function createBranchReset(repository: Repository): Action {
|
||||
return {
|
||||
type: CREATE_BRANCH_RESET,
|
||||
payload: { repository },
|
||||
itemId: createKey(repository)
|
||||
};
|
||||
}
|
||||
|
||||
export function isFetchBranchPending(
|
||||
state: Object,
|
||||
repository: Repository,
|
||||
name: string
|
||||
) {
|
||||
return isPending(state, FETCH_BRANCH, createKey(repository) + "/" + name);
|
||||
}
|
||||
|
||||
export function getFetchBranchFailure(
|
||||
state: Object,
|
||||
repository: Repository,
|
||||
name: string
|
||||
) {
|
||||
return getFailure(state, FETCH_BRANCH, createKey(repository) + "/" + name);
|
||||
}
|
||||
|
||||
export function fetchBranchPending(
|
||||
repository: Repository,
|
||||
name: string
|
||||
@@ -171,58 +295,61 @@ export function fetchBranchFailure(
|
||||
|
||||
// Reducers
|
||||
|
||||
function extractBranchesFromPayload(payload: any) {
|
||||
if (payload._embedded && payload._embedded.branches) {
|
||||
return payload._embedded.branches;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
const reduceByBranchesSuccess = (state, payload) => {
|
||||
const repository = payload.repository;
|
||||
const response = payload.data;
|
||||
|
||||
function reduceBranchSuccess(state, repositoryName, newBranch) {
|
||||
const newBranches = [];
|
||||
// we do not use filter, because we try to keep the current order
|
||||
let found = false;
|
||||
for (const branch of state[repositoryName] || []) {
|
||||
if (branch.name === newBranch.name) {
|
||||
newBranches.push(newBranch);
|
||||
found = true;
|
||||
} else {
|
||||
newBranches.push(branch);
|
||||
const key = createKey(repository);
|
||||
const repoState = state[key] || {};
|
||||
const byName = repoState.byName || {};
|
||||
repoState.byName = byName;
|
||||
|
||||
const branches = response._embedded.branches;
|
||||
const names = branches.map(b => b.name);
|
||||
response._embedded.branches = names;
|
||||
for (let branch of branches) {
|
||||
byName[branch.name] = branch;
|
||||
}
|
||||
return {
|
||||
[key]: {
|
||||
list: response,
|
||||
byName
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
newBranches.push(newBranch);
|
||||
}
|
||||
return newBranches;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
type State = { [string]: Branch[] };
|
||||
const reduceByBranchSuccess = (state, payload) => {
|
||||
const repository = payload.repository;
|
||||
const branch = payload.branch;
|
||||
|
||||
const key = createKey(repository);
|
||||
|
||||
const repoState = state[key] || {};
|
||||
|
||||
const byName = repoState.byName || {};
|
||||
byName[branch.name] = branch;
|
||||
|
||||
repoState.byName = byName;
|
||||
|
||||
return {
|
||||
...state,
|
||||
[key]: repoState
|
||||
};
|
||||
};
|
||||
|
||||
export default function reducer(
|
||||
state: State = {},
|
||||
state: {} = {},
|
||||
action: Action = { type: "UNKNOWN" }
|
||||
): State {
|
||||
): Object {
|
||||
if (!action.payload) {
|
||||
return state;
|
||||
}
|
||||
const payload = action.payload;
|
||||
switch (action.type) {
|
||||
case FETCH_BRANCHES_SUCCESS:
|
||||
const key = createKey(payload.repository);
|
||||
return {
|
||||
...state,
|
||||
[key]: extractBranchesFromPayload(payload.data)
|
||||
};
|
||||
return reduceByBranchesSuccess(state, payload);
|
||||
case FETCH_BRANCH_SUCCESS:
|
||||
if (!action.payload.repository || !action.payload.branch) {
|
||||
return state;
|
||||
}
|
||||
const newBranch = action.payload.branch;
|
||||
const repositoryName = createKey(action.payload.repository);
|
||||
return {
|
||||
...state,
|
||||
[repositoryName]: reduceBranchSuccess(state, repositoryName, newBranch)
|
||||
};
|
||||
return reduceByBranchSuccess(state, payload);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -9,13 +9,21 @@ import reducer, {
|
||||
FETCH_BRANCH_PENDING,
|
||||
FETCH_BRANCH_SUCCESS,
|
||||
FETCH_BRANCH_FAILURE,
|
||||
CREATE_BRANCH,
|
||||
CREATE_BRANCH_FAILURE,
|
||||
CREATE_BRANCH_PENDING,
|
||||
CREATE_BRANCH_SUCCESS,
|
||||
fetchBranches,
|
||||
fetchBranch,
|
||||
fetchBranchSuccess,
|
||||
getBranch,
|
||||
getBranches,
|
||||
getFetchBranchesFailure,
|
||||
isFetchBranchesPending
|
||||
isFetchBranchesPending,
|
||||
createBranch,
|
||||
isCreateBranchPending,
|
||||
getCreateBranchFailure,
|
||||
isPermittedToCreateBranches
|
||||
} from "./branches";
|
||||
|
||||
const namespace = "foo";
|
||||
@@ -34,6 +42,8 @@ const repository = {
|
||||
const branch1 = { name: "branch1", revision: "revision1" };
|
||||
const branch2 = { name: "branch2", revision: "revision2" };
|
||||
const branch3 = { name: "branch3", revision: "revision3" };
|
||||
const branchRequest = { name: "newBranch", source: "master" };
|
||||
const newBranch = { name: "newBranch", revision: "rev3" };
|
||||
|
||||
describe("branches", () => {
|
||||
describe("fetch branches", () => {
|
||||
@@ -119,12 +129,84 @@ describe("branches", () => {
|
||||
expect(actions[1].payload).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it("should create a branch successfully", () => {
|
||||
//branchrequest answer
|
||||
fetchMock.postOnce(URL, {
|
||||
status: 201,
|
||||
headers: {
|
||||
location: URL + "/newBranch"
|
||||
}
|
||||
});
|
||||
|
||||
//branch answer
|
||||
fetchMock.getOnce(URL + "/newBranch", newBranch);
|
||||
|
||||
const store = mockStore({});
|
||||
return store
|
||||
.dispatch(createBranch(URL, repository, branchRequest))
|
||||
.then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(CREATE_BRANCH_PENDING);
|
||||
expect(actions[1].type).toEqual(CREATE_BRANCH_SUCCESS);
|
||||
});
|
||||
});
|
||||
|
||||
it("should call the callback with the branch from the location header", () => {
|
||||
//branchrequest answer
|
||||
fetchMock.postOnce(URL, {
|
||||
status: 201,
|
||||
headers: {
|
||||
location: URL + "/newBranch"
|
||||
}
|
||||
});
|
||||
|
||||
//branch answer
|
||||
fetchMock.getOnce(URL + "/newBranch", newBranch);
|
||||
|
||||
const store = mockStore({});
|
||||
|
||||
let receivedBranch = null;
|
||||
|
||||
const callback = branch => {
|
||||
receivedBranch = branch;
|
||||
};
|
||||
|
||||
return store
|
||||
.dispatch(createBranch(URL, repository, branchRequest, callback))
|
||||
.then(() => {
|
||||
expect(receivedBranch).toEqual(newBranch);
|
||||
});
|
||||
});
|
||||
|
||||
it("should fail creating a branch on HTTP 500", () => {
|
||||
fetchMock.postOnce(URL, {
|
||||
status: 500
|
||||
});
|
||||
|
||||
const store = mockStore({});
|
||||
return store
|
||||
.dispatch(createBranch(URL, repository, branchRequest))
|
||||
.then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(CREATE_BRANCH_PENDING);
|
||||
expect(actions[1].type).toEqual(CREATE_BRANCH_FAILURE);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("branches reducer", () => {
|
||||
const branches = {
|
||||
_embedded: {
|
||||
branches: [branch1, branch2]
|
||||
},
|
||||
_links: {
|
||||
self: {
|
||||
href: "/self"
|
||||
},
|
||||
create: {
|
||||
href: "/create"
|
||||
}
|
||||
}
|
||||
};
|
||||
const action = {
|
||||
@@ -135,81 +217,94 @@ describe("branches", () => {
|
||||
}
|
||||
};
|
||||
|
||||
it("should update state according to successful fetch", () => {
|
||||
it("should store the branches", () => {
|
||||
const newState = reducer({}, action);
|
||||
expect(newState).toBeDefined();
|
||||
expect(newState[key]).toBeDefined();
|
||||
expect(newState[key]).toContain(branch1);
|
||||
expect(newState[key]).toContain(branch2);
|
||||
const repoState = newState["foo/bar"];
|
||||
|
||||
expect(repoState.list._links.create.href).toEqual("/create");
|
||||
expect(repoState.list._embedded.branches).toEqual(["branch1", "branch2"]);
|
||||
|
||||
expect(repoState.byName.branch1).toEqual(branch1);
|
||||
expect(repoState.byName.branch2).toEqual(branch2);
|
||||
});
|
||||
|
||||
it("should not delete existing branches from state", () => {
|
||||
const oldState = {
|
||||
"hitchhiker/heartOfGold": [branch3]
|
||||
it("should store a single branch", () => {
|
||||
const newState = reducer({}, fetchBranchSuccess(repository, branch1));
|
||||
const repoState = newState["foo/bar"];
|
||||
|
||||
expect(repoState.list).toBeUndefined();
|
||||
expect(repoState.byName.branch1).toEqual(branch1);
|
||||
});
|
||||
|
||||
it("should add a single branch", () => {
|
||||
const state = {
|
||||
"foo/bar": {
|
||||
list: {
|
||||
_links: {},
|
||||
_embedded: {
|
||||
branches: ["branch1"]
|
||||
}
|
||||
},
|
||||
byName: {
|
||||
branch1: branch1
|
||||
}
|
||||
}
|
||||
};
|
||||
const newState = reducer(oldState, action);
|
||||
expect(newState[key]).toContain(branch1);
|
||||
expect(newState[key]).toContain(branch2);
|
||||
expect(newState["hitchhiker/heartOfGold"]).toContain(branch3);
|
||||
const newState = reducer(state, fetchBranchSuccess(repository, branch2));
|
||||
const repoState = newState["foo/bar"];
|
||||
const byName = repoState.byName;
|
||||
|
||||
expect(repoState.list._embedded.branches).toEqual(["branch1"]);
|
||||
expect(byName.branch1).toEqual(branch1);
|
||||
expect(byName.branch2).toEqual(branch2);
|
||||
});
|
||||
|
||||
it("should update state according to FETCH_BRANCH_SUCCESS action", () => {
|
||||
const newState = reducer({}, fetchBranchSuccess(repository, branch3));
|
||||
expect(newState["foo/bar"]).toEqual([branch3]);
|
||||
});
|
||||
|
||||
it("should not delete existing branch from state", () => {
|
||||
const oldState = {
|
||||
"foo/bar": [branch1]
|
||||
it("should not overwrite non related repositories", () => {
|
||||
const state = {
|
||||
"scm/core": {
|
||||
byName: {
|
||||
branch1: branch1
|
||||
}
|
||||
}
|
||||
};
|
||||
const newState = reducer(
|
||||
oldState,
|
||||
fetchBranchSuccess(repository, branch2)
|
||||
);
|
||||
expect(newState["foo/bar"]).toEqual([branch1, branch2]);
|
||||
const newState = reducer(state, fetchBranchSuccess(repository, branch1));
|
||||
const byName = newState["scm/core"].byName;
|
||||
|
||||
expect(byName.branch1).toEqual(branch1);
|
||||
});
|
||||
|
||||
it("should update required branch from state", () => {
|
||||
const oldState = {
|
||||
"foo/bar": [branch1]
|
||||
it("should overwrite existing branch", () => {
|
||||
const state = {
|
||||
"foo/bar": {
|
||||
byName: {
|
||||
branch1: {
|
||||
name: "branch1",
|
||||
revision: "xyz"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const newBranch1 = { name: "branch1", revision: "revision2" };
|
||||
const newState = reducer(
|
||||
oldState,
|
||||
fetchBranchSuccess(repository, newBranch1)
|
||||
);
|
||||
expect(newState["foo/bar"]).toEqual([newBranch1]);
|
||||
const newState = reducer(state, fetchBranchSuccess(repository, branch1));
|
||||
const byName = newState["foo/bar"].byName;
|
||||
|
||||
expect(byName.branch1.revision).toEqual("revision1");
|
||||
});
|
||||
|
||||
it("should update required branch from state and keeps old repo", () => {
|
||||
const oldState = {
|
||||
"ns/one": [branch1]
|
||||
it("should not overwrite existing branches", () => {
|
||||
const state = {
|
||||
"foo/bar": {
|
||||
byName: {
|
||||
branch1,
|
||||
branch2,
|
||||
branch3
|
||||
}
|
||||
}
|
||||
};
|
||||
const newState = reducer(
|
||||
oldState,
|
||||
fetchBranchSuccess(repository, branch3)
|
||||
);
|
||||
expect(newState["ns/one"]).toEqual([branch1]);
|
||||
expect(newState["foo/bar"]).toEqual([branch3]);
|
||||
});
|
||||
|
||||
it("should return the oldState, if action has no payload", () => {
|
||||
const state = {};
|
||||
const newState = reducer(state, { type: FETCH_BRANCH_SUCCESS });
|
||||
expect(newState).toBe(state);
|
||||
});
|
||||
|
||||
it("should return the oldState, if payload has no branch", () => {
|
||||
const action = {
|
||||
type: FETCH_BRANCH_SUCCESS,
|
||||
payload: {
|
||||
repository
|
||||
},
|
||||
itemId: "foo/bar/"
|
||||
};
|
||||
const state = {};
|
||||
const newState = reducer(state, action);
|
||||
expect(newState).toBe(state);
|
||||
expect(newState["foo/bar"].byName.branch1).toEqual(branch1);
|
||||
expect(newState["foo/bar"].byName.branch2).toEqual(branch2);
|
||||
expect(newState["foo/bar"].byName.branch3).toEqual(branch3);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -218,7 +313,18 @@ describe("branches", () => {
|
||||
|
||||
const state = {
|
||||
branches: {
|
||||
[key]: [branch1, branch2]
|
||||
"foo/bar": {
|
||||
list: {
|
||||
_links: {},
|
||||
_embedded: {
|
||||
branches: ["branch1", "branch2"]
|
||||
}
|
||||
},
|
||||
byName: {
|
||||
branch1: branch1,
|
||||
branch2: branch2
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -245,9 +351,34 @@ describe("branches", () => {
|
||||
expect(one).toBe(two);
|
||||
});
|
||||
|
||||
it("should return null, if no branches for the repository available", () => {
|
||||
it("should not return cached reference, if branches have changed", () => {
|
||||
const one = getBranches(state, repository);
|
||||
const newState = {
|
||||
branches: {
|
||||
"foo/bar": {
|
||||
list: {
|
||||
_links: {},
|
||||
_embedded: {
|
||||
branches: ["branch2", "branch3"]
|
||||
}
|
||||
},
|
||||
byName: {
|
||||
branch2,
|
||||
branch3
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const two = getBranches(newState, repository);
|
||||
expect(one).not.toBe(two);
|
||||
expect(two).not.toContain(branch1);
|
||||
expect(two).toContain(branch2);
|
||||
expect(two).toContain(branch3);
|
||||
});
|
||||
|
||||
it("should return undefined, if no branches for the repository available", () => {
|
||||
const branches = getBranches({ branches: {} }, repository);
|
||||
expect(branches).toBeNull();
|
||||
expect(branches).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should return single branch by name", () => {
|
||||
@@ -266,6 +397,32 @@ describe("branches", () => {
|
||||
expect(branch).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should return true if the branches list contains the create link", () => {
|
||||
const stateWithLink = {
|
||||
branches: {
|
||||
"foo/bar": {
|
||||
...state.branches["foo/bar"],
|
||||
list: {
|
||||
...state.branches["foo/bar"].list,
|
||||
_links: {
|
||||
create: {
|
||||
href: "http://create-it"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const permitted = isPermittedToCreateBranches(stateWithLink, repository);
|
||||
expect(permitted).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false if the create link is missing", () => {
|
||||
const permitted = isPermittedToCreateBranches(state, repository);
|
||||
expect(permitted).toBe(false);
|
||||
});
|
||||
|
||||
it("should return error if fetching branches failed", () => {
|
||||
const state = {
|
||||
failure: {
|
||||
@@ -279,5 +436,36 @@ describe("branches", () => {
|
||||
it("should return false if fetching branches did not fail", () => {
|
||||
expect(getFetchBranchesFailure({}, repository)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should return true if create branch is pending", () => {
|
||||
const state = {
|
||||
pending: {
|
||||
[CREATE_BRANCH + "/foo/bar"]: true
|
||||
}
|
||||
};
|
||||
expect(isCreateBranchPending(state, repository)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false if create branch is not pending", () => {
|
||||
const state = {
|
||||
pending: {
|
||||
[CREATE_BRANCH + "/foo/bar"]: false
|
||||
}
|
||||
};
|
||||
expect(isCreateBranchPending(state, repository)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return error when create branch did fail", () => {
|
||||
const state = {
|
||||
failure: {
|
||||
[CREATE_BRANCH]: error
|
||||
}
|
||||
};
|
||||
expect(getCreateBranchFailure(state)).toEqual(error);
|
||||
});
|
||||
|
||||
it("should return undefined when create branch did not fail", () => {
|
||||
expect(getCreateBranchFailure({})).toBe(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -701,7 +701,6 @@
|
||||
"@scm-manager/ui-bundler@^0.0.27":
|
||||
version "0.0.27"
|
||||
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.27.tgz#3ed2c7826780b9a1a9ea90464332640cfb5d54b5"
|
||||
integrity sha512-cBU1xq6gDy1Vw9AGOzsR763+JmBeraTaC/KQfxT3I6XyZJ2brIfG1m5QYcAcHWvDxq3mYMogpI5rfShw14L4/w==
|
||||
dependencies:
|
||||
"@babel/core" "^7.0.0"
|
||||
"@babel/plugin-proposal-class-properties" "^7.0.0"
|
||||
@@ -5670,6 +5669,10 @@ memoize-one@^4.0.0:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.0.3.tgz#cdfdd942853f1a1b4c71c5336b8c49da0bf0273c"
|
||||
|
||||
memoize-one@^5.0.4:
|
||||
version "5.0.4"
|
||||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.4.tgz#005928aced5c43d890a4dfab18ca908b0ec92cbc"
|
||||
|
||||
memoizee@0.4.X:
|
||||
version "0.4.14"
|
||||
resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.14.tgz#07a00f204699f9a95c2d9e77218271c7cd610d57"
|
||||
@@ -6832,6 +6835,14 @@ qs@~6.5.1, qs@~6.5.2:
|
||||
version "6.5.2"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||
|
||||
query-string@5:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb"
|
||||
dependencies:
|
||||
decode-uri-component "^0.2.0"
|
||||
object-assign "^4.1.0"
|
||||
strict-uri-encode "^1.0.0"
|
||||
|
||||
querystring-es3@~0.2.0:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
|
||||
@@ -6921,6 +6932,15 @@ react-dom@^16.4.2:
|
||||
prop-types "^15.6.2"
|
||||
schedule "^0.5.0"
|
||||
|
||||
react-dom@^16.8.6:
|
||||
version "16.8.6"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f"
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
scheduler "^0.13.6"
|
||||
|
||||
react-i18next@^7.9.0:
|
||||
version "7.13.0"
|
||||
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-7.13.0.tgz#a6f64fd749215ec70400f90da6cbde2a9c5b1588"
|
||||
@@ -7051,6 +7071,15 @@ react@^16.4.2:
|
||||
prop-types "^15.6.2"
|
||||
schedule "^0.5.0"
|
||||
|
||||
react@^16.8.6:
|
||||
version "16.8.6"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe"
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
scheduler "^0.13.6"
|
||||
|
||||
read-cache@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774"
|
||||
@@ -7563,6 +7592,13 @@ schedule@^0.5.0:
|
||||
dependencies:
|
||||
object-assign "^4.1.1"
|
||||
|
||||
scheduler@^0.13.6:
|
||||
version "0.13.6"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.6.tgz#466a4ec332467b31a91b9bf74e5347072e4cd889"
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
scss-tokenizer@^0.2.3:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1"
|
||||
@@ -7991,6 +8027,10 @@ stream-throttle@^0.1.3:
|
||||
commander "^2.2.0"
|
||||
limiter "^1.0.5"
|
||||
|
||||
strict-uri-encode@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
||||
|
||||
string-length@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"
|
||||
|
||||
Reference in New Issue
Block a user