Compare commits
6 Commits
master
...
feature/JE
Author | SHA1 | Date |
---|---|---|
Cliff Meyers | 0eb274b243 | |
Cliff Meyers | f3a54bb3cd | |
Cliff Meyers | 9d719924a6 | |
Cliff Meyers | a934db43c7 | |
Cliff Meyers | fb621d21b8 | |
Cliff Meyers | 18b5e48c3e |
|
@ -89,6 +89,7 @@ export default class PipelineRowItem extends Component {
|
||||||
<Extensions.Renderer
|
<Extensions.Renderer
|
||||||
extensionPoint="jenkins.pipeline.list.action"
|
extensionPoint="jenkins.pipeline.list.action"
|
||||||
store={this.context.store}
|
store={this.context.store}
|
||||||
|
mobxStores={this.context.mobxStores}
|
||||||
pipeline={this.props.pipeline}
|
pipeline={this.props.pipeline}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
|
@ -104,5 +105,5 @@ PipelineRowItem.propTypes = {
|
||||||
|
|
||||||
PipelineRowItem.contextTypes = {
|
PipelineRowItem.contextTypes = {
|
||||||
location: PropTypes.object,
|
location: PropTypes.object,
|
||||||
store: PropTypes.object,
|
mobxStores: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
|
@ -60,6 +60,7 @@ export default class Pipelines extends Component {
|
||||||
<Extensions.Renderer
|
<Extensions.Renderer
|
||||||
extensionPoint="jenkins.pipeline.list.top"
|
extensionPoint="jenkins.pipeline.list.top"
|
||||||
store={this.context.store}
|
store={this.context.store}
|
||||||
|
mobxStores={this.context.mobxStores}
|
||||||
/>
|
/>
|
||||||
<Table
|
<Table
|
||||||
className="pipelines-table fixed"
|
className="pipelines-table fixed"
|
||||||
|
@ -88,4 +89,5 @@ Pipelines.contextTypes = {
|
||||||
params: PropTypes.object,
|
params: PropTypes.object,
|
||||||
pipelines: array,
|
pipelines: array,
|
||||||
store: PropTypes.object,
|
store: PropTypes.object,
|
||||||
|
mobxStores: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
{
|
{
|
||||||
"presets": ["es2015", "stage-0", "react"]
|
"presets": [
|
||||||
|
"react",
|
||||||
|
"es2015",
|
||||||
|
"stage-0"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"transform-decorators-legacy"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"babel": "^6.5.2",
|
"babel": "^6.5.2",
|
||||||
"babel-core": "^6.7.6",
|
"babel-core": "^6.7.6",
|
||||||
"babel-eslint": "^6.0.2",
|
"babel-eslint": "^6.0.2",
|
||||||
|
"babel-plugin-transform-decorators-legacy": "1.3.4",
|
||||||
"babel-preset-es2015": "^6.6.0",
|
"babel-preset-es2015": "^6.6.0",
|
||||||
"babel-preset-react": "^6.5.0",
|
"babel-preset-react": "^6.5.0",
|
||||||
"babel-preset-stage-0": "^6.5.0",
|
"babel-preset-stage-0": "^6.5.0",
|
||||||
|
@ -41,6 +42,8 @@
|
||||||
"immutable": "3.8.1",
|
"immutable": "3.8.1",
|
||||||
"isomorphic-fetch": "2.2.1",
|
"isomorphic-fetch": "2.2.1",
|
||||||
"keymirror": "0.1.1",
|
"keymirror": "0.1.1",
|
||||||
|
"mobx": "2.4.0",
|
||||||
|
"mobx-react": "3.5.1",
|
||||||
"moment": "2.13.0",
|
"moment": "2.13.0",
|
||||||
"moment-duration-format": "1.3.0",
|
"moment-duration-format": "1.3.0",
|
||||||
"react": "15.1.0",
|
"react": "15.1.0",
|
||||||
|
|
|
@ -2,14 +2,8 @@
|
||||||
* Created by cmeyers on 7/6/16.
|
* Created by cmeyers on 7/6/16.
|
||||||
*/
|
*/
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { observer } from 'mobx-react';
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { List } from 'immutable';
|
|
||||||
|
|
||||||
import { favoritesSelector } from '../redux/FavoritesStore';
|
|
||||||
import { actions } from '../redux/FavoritesActions';
|
|
||||||
|
|
||||||
import FavoritesProvider from './FavoritesProvider';
|
|
||||||
import { PipelineCard } from './PipelineCard';
|
import { PipelineCard } from './PipelineCard';
|
||||||
|
|
||||||
// the order the cards should be displayed based on their result/state (aka 'status')
|
// the order the cards should be displayed based on their result/state (aka 'status')
|
||||||
|
@ -89,18 +83,19 @@ const extractPath = (path, begin, end) => {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
|
@observer
|
||||||
export class DashboardCards extends Component {
|
export class DashboardCards extends Component {
|
||||||
|
|
||||||
_onFavoriteToggle(isFavorite, favorite) {
|
_onFavoriteToggle(isFavorite, favorite) {
|
||||||
this.props.toggleFavorite(isFavorite, favorite.item);
|
this.props.favoritesList.toggleFavorite(isFavorite, favorite.item);
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderCardStack() {
|
_renderCardStack() {
|
||||||
if (!this.props.favorites) {
|
if (!this.props.favoritesList.favorites) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortedFavorites = this.props.favorites.sort(sortComparator);
|
const sortedFavorites = this.props.favoritesList.favorites.sort(sortComparator);
|
||||||
|
|
||||||
const favoriteCards = sortedFavorites.map(favorite => {
|
const favoriteCards = sortedFavorites.map(favorite => {
|
||||||
const pipeline = favorite.item;
|
const pipeline = favorite.item;
|
||||||
|
@ -168,22 +163,15 @@ export class DashboardCards extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<FavoritesProvider store={this.props.store}>
|
<div>
|
||||||
{ this._renderCardStack() }
|
{ this._renderCardStack() }
|
||||||
</FavoritesProvider>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DashboardCards.propTypes = {
|
DashboardCards.propTypes = {
|
||||||
store: PropTypes.object,
|
favoritesList: PropTypes.object,
|
||||||
favorites: PropTypes.instanceOf(List),
|
|
||||||
toggleFavorite: PropTypes.func,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectors = createSelector(
|
export default DashboardCards;
|
||||||
[favoritesSelector],
|
|
||||||
(favorites) => ({ favorites })
|
|
||||||
);
|
|
||||||
|
|
||||||
export default connect(selectors, actions)(DashboardCards);
|
|
||||||
|
|
|
@ -2,22 +2,20 @@
|
||||||
* Created by cmeyers on 7/8/16.
|
* Created by cmeyers on 7/8/16.
|
||||||
*/
|
*/
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { autorun } from 'mobx';
|
||||||
import { createSelector } from 'reselect';
|
import { observer } from 'mobx-react';
|
||||||
import { List } from 'immutable';
|
|
||||||
|
|
||||||
import { Favorite } from '@jenkins-cd/design-language';
|
import { Favorite } from '@jenkins-cd/design-language';
|
||||||
|
|
||||||
import { favoritesSelector } from '../redux/FavoritesStore';
|
import PersonalizationStore from '../model/PersonalizationStore';
|
||||||
import { actions } from '../redux/FavoritesActions';
|
|
||||||
import { checkMatchingFavoriteUrls } from '../util/FavoriteUtils';
|
import { checkMatchingFavoriteUrls } from '../util/FavoriteUtils';
|
||||||
import FavoritesProvider from './FavoritesProvider';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A toggle button to favorite or unfavorite the provided item (pipeline or branch)
|
* A toggle button to favorite or unfavorite the provided item (pipeline or branch)
|
||||||
* Contains all logic for rendering the current favorite status of that item
|
* Contains all logic for rendering the current favorite status of that item
|
||||||
* and toggling favorited state on the server.
|
* and toggling favorited state on the server.
|
||||||
*/
|
*/
|
||||||
|
@observer
|
||||||
export class FavoritePipeline extends Component {
|
export class FavoritePipeline extends Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -29,21 +27,23 @@ export class FavoritePipeline extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this._updateState(this.props);
|
this.favoritesList = PersonalizationStore.favoritesStore.favoritesList;
|
||||||
|
this.favoritesList.initialize();
|
||||||
|
this._unsubscribe = autorun(() => this.updateFavorite());
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillUnmount() {
|
||||||
if (this.props.favorites !== nextProps.favorites) {
|
if (this._unsubscribe) {
|
||||||
this._updateState(nextProps);
|
this._unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateState(props) {
|
updateFavorite() {
|
||||||
const { pipeline } = props;
|
const { pipeline } = this.props;
|
||||||
let favorite = null;
|
let favorite = null;
|
||||||
|
|
||||||
if (props.favorites) {
|
if (this.favoritesList.favorites) {
|
||||||
favorite = props.favorites.find((fav) => {
|
favorite = this.favoritesList.favorites.find((fav) => {
|
||||||
const favUrl = fav.item._links.self.href;
|
const favUrl = fav.item._links.self.href;
|
||||||
const pipelineUrl = pipeline._links.self.href;
|
const pipelineUrl = pipeline._links.self.href;
|
||||||
|
|
||||||
|
@ -62,18 +62,18 @@ export class FavoritePipeline extends Component {
|
||||||
favorite: isFavorite,
|
favorite: isFavorite,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.props.toggleFavorite) {
|
debugger;
|
||||||
this.props.toggleFavorite(isFavorite, this.props.pipeline);
|
|
||||||
|
if (this.favoritesList.toggleFavorite) {
|
||||||
|
this.favoritesList.toggleFavorite(isFavorite, this.props.pipeline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<FavoritesProvider store={this.props.store}>
|
<Favorite checked={this.state.favorite} className={this.props.className}
|
||||||
<Favorite checked={this.state.favorite} className={this.props.className}
|
onToggle={() => this._onFavoriteToggle()}
|
||||||
onToggle={() => this._onFavoriteToggle()}
|
/>
|
||||||
/>
|
|
||||||
</FavoritesProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,18 +81,10 @@ export class FavoritePipeline extends Component {
|
||||||
FavoritePipeline.propTypes = {
|
FavoritePipeline.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
pipeline: PropTypes.object,
|
pipeline: PropTypes.object,
|
||||||
favorites: PropTypes.instanceOf(List),
|
|
||||||
toggleFavorite: PropTypes.func,
|
|
||||||
store: PropTypes.object,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
FavoritePipeline.defaultProps = {
|
FavoritePipeline.defaultProps = {
|
||||||
favorite: false,
|
favorite: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectors = createSelector(
|
export default FavoritePipeline;
|
||||||
[favoritesSelector],
|
|
||||||
(favorites) => ({ favorites })
|
|
||||||
);
|
|
||||||
|
|
||||||
export default connect(selectors, actions)(FavoritePipeline);
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
* Created by cmeyers on 7/26/16.
|
||||||
|
*/
|
||||||
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
import DashboardCards from './DashboardCards';
|
||||||
|
import PersonalizationStore from '../model/PersonalizationStore';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Top-level component bound to extension point that passes down store to child components.
|
||||||
|
*/
|
||||||
|
class FavoritesDashboard extends Component {
|
||||||
|
render() {
|
||||||
|
const favoritesList = PersonalizationStore.favoritesStore.favoritesList;
|
||||||
|
favoritesList.initialize();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<DashboardCards favoritesList={favoritesList} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FavoritesDashboard;
|
|
@ -1,64 +0,0 @@
|
||||||
/**
|
|
||||||
* Created by cmeyers on 7/20/16.
|
|
||||||
*/
|
|
||||||
import React, { Component, PropTypes } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { List } from 'immutable';
|
|
||||||
|
|
||||||
import { userSelector, favoritesSelector } from '../redux/FavoritesStore';
|
|
||||||
import { actions } from '../redux/FavoritesActions';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FavoritesProvider ensures that the current user's favorites
|
|
||||||
* are loaded for any components which may need it.
|
|
||||||
*
|
|
||||||
* Components that require this data can simply wrap themselves in
|
|
||||||
* FavoritesProvider which will ensure the store is updated correctly.
|
|
||||||
*/
|
|
||||||
export class FavoritesProvider extends Component {
|
|
||||||
|
|
||||||
componentWillMount() {
|
|
||||||
this._initialize(this.props);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillReceiveProps(props) {
|
|
||||||
this._initialize(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
_initialize(props) {
|
|
||||||
const { user, favorites } = props;
|
|
||||||
|
|
||||||
const shouldFetchUser = !user;
|
|
||||||
const shouldFetchFavorites = user && !favorites;
|
|
||||||
|
|
||||||
if (shouldFetchUser) {
|
|
||||||
this.props.fetchUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldFetchFavorites) {
|
|
||||||
this.props.fetchFavorites(user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return this.props.children ?
|
|
||||||
React.cloneElement(this.props.children, { ...this.props }) :
|
|
||||||
null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FavoritesProvider.propTypes = {
|
|
||||||
children: PropTypes.node,
|
|
||||||
user: PropTypes.object,
|
|
||||||
favorites: PropTypes.instanceOf(List),
|
|
||||||
fetchUser: PropTypes.func,
|
|
||||||
fetchFavorites: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectors = createSelector(
|
|
||||||
[userSelector, favoritesSelector],
|
|
||||||
(user, favorites) => ({ user, favorites })
|
|
||||||
);
|
|
||||||
|
|
||||||
export default connect(selectors, actions)(FavoritesProvider);
|
|
|
@ -2,9 +2,9 @@
|
||||||
# NB: "component" currently maps to modules, not "symbols" so make sure to "export default"
|
# NB: "component" currently maps to modules, not "symbols" so make sure to "export default"
|
||||||
# WARNING: If you change this you'll have to change io.jenkins.blueocean.jsextensions.JenkinsJSExtensionsTest as well :(
|
# WARNING: If you change this you'll have to change io.jenkins.blueocean.jsextensions.JenkinsJSExtensionsTest as well :(
|
||||||
extensions:
|
extensions:
|
||||||
- component: redux/FavoritesStore
|
- component: model/PersonalizationStore
|
||||||
extensionPoint: jenkins.main.stores
|
extensionPoint: jenkins.main.stores.mobx
|
||||||
- component: components/DashboardCards
|
- component: components/FavoritesDashboard
|
||||||
extensionPoint: jenkins.pipeline.list.top
|
extensionPoint: jenkins.pipeline.list.top
|
||||||
- component: components/FavoritePipeline
|
- component: components/FavoritePipeline
|
||||||
extensionPoint: jenkins.pipeline.list.action
|
extensionPoint: jenkins.pipeline.list.action
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
/**
|
||||||
|
* Created by cmeyers on 7/25/16.
|
||||||
|
*/
|
||||||
|
import Immutable from 'immutable';
|
||||||
|
import fetch from 'isomorphic-fetch';
|
||||||
|
import { action, computed, observable, useStrict } from 'mobx';
|
||||||
|
useStrict(true);
|
||||||
|
|
||||||
|
import urlConfig from '../config';
|
||||||
|
urlConfig.loadConfig();
|
||||||
|
|
||||||
|
import { User } from '../model/User';
|
||||||
|
import { checkMatchingFavoriteUrls } from '../util/FavoriteUtils';
|
||||||
|
|
||||||
|
const { List } = Immutable;
|
||||||
|
|
||||||
|
const defaultFetchOptions = {
|
||||||
|
credentials: 'same-origin',
|
||||||
|
};
|
||||||
|
|
||||||
|
function checkStatus(response) {
|
||||||
|
if (response.status >= 300 || response.status < 200) {
|
||||||
|
const error = new Error(response.statusText);
|
||||||
|
error.response = response;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseJSON(response) {
|
||||||
|
return response.json()
|
||||||
|
// FIXME: workaround for status=200 w/ empty response body that causes error in Chrome
|
||||||
|
// server should probably return HTTP 204 instead
|
||||||
|
.catch((error) => {
|
||||||
|
if (error.message === 'Unexpected end of JSON input') {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function execFetch(url, options) {
|
||||||
|
const fetchOptions = options || { ... defaultFetchOptions };
|
||||||
|
|
||||||
|
return fetch(url, fetchOptions)
|
||||||
|
.then(checkStatus)
|
||||||
|
.then(parseJSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FavoritesList {
|
||||||
|
|
||||||
|
@observable favorites = new List();
|
||||||
|
user = null;
|
||||||
|
_initializing = false;
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
const shouldFetchUser = !this.user;
|
||||||
|
|
||||||
|
console.log('intialize()?', this._initializing);
|
||||||
|
|
||||||
|
if (shouldFetchUser && !this._initializing) {
|
||||||
|
this._initializing = true;
|
||||||
|
this.fetchCurrentUser()
|
||||||
|
.then(() => {
|
||||||
|
this.fetchFavorites()
|
||||||
|
.then(() => {
|
||||||
|
this._initializing = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchCurrentUser() {
|
||||||
|
const baseUrl = urlConfig.blueoceanAppURL;
|
||||||
|
const url = `${baseUrl}/rest/organizations/jenkins/user/`;
|
||||||
|
|
||||||
|
return execFetch(url)
|
||||||
|
.then((data) => {
|
||||||
|
this.user = new User(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchFavorites() {
|
||||||
|
const baseUrl = urlConfig.blueoceanAppURL;
|
||||||
|
const username = this.user.id;
|
||||||
|
const url = `${baseUrl}/rest/users/${username}/favorites/`;
|
||||||
|
|
||||||
|
return execFetch(url)
|
||||||
|
.then(action((data) => {
|
||||||
|
this.favorites = new List(data);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleFavorite(addFavorite, pipelineOrBranch) {
|
||||||
|
const baseUrl = urlConfig.jenkinsRootURL;
|
||||||
|
const url = `${baseUrl}${pipelineOrBranch._links.self.href}/favorite`;
|
||||||
|
|
||||||
|
const fetchOptions = {
|
||||||
|
...defaultFetchOptions,
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(
|
||||||
|
{ favorite: addFavorite }
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
return execFetch(url, fetchOptions)
|
||||||
|
.then((favoritePayload) => {
|
||||||
|
this._updateToggledFavorite(addFavorite, pipelineOrBranch, favoritePayload);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
_updateToggledFavorite(addFavorite, pipelineOrBranch, favoritePayload) {
|
||||||
|
if (addFavorite) {
|
||||||
|
this.favorites = this.favorites.push(favoritePayload);
|
||||||
|
} else {
|
||||||
|
const toggledBranchHref = pipelineOrBranch._links.self.href;
|
||||||
|
// filter the list so that only favorites which didn't match the branch's href are returned
|
||||||
|
this.favorites = this.favorites.filter(fav => {
|
||||||
|
const favoritedBranch = fav.item;
|
||||||
|
return !checkMatchingFavoriteUrls(
|
||||||
|
favoritedBranch._links.self.href,
|
||||||
|
toggledBranchHref,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get count() {
|
||||||
|
return this.favorites && this.favorites.length || 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* Created by cmeyers on 7/25/16.
|
||||||
|
*/
|
||||||
|
import { FavoritesList } from './FavoritesList';
|
||||||
|
|
||||||
|
class PersonalizationStore {
|
||||||
|
|
||||||
|
favoritesList = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.favoritesList = new FavoritesList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
favoritesStore: new PersonalizationStore(),
|
||||||
|
};
|
|
@ -31,6 +31,8 @@
|
||||||
"history": "2.0.2",
|
"history": "2.0.2",
|
||||||
"immutable": "3.8.1",
|
"immutable": "3.8.1",
|
||||||
"keymirror": "0.1.1",
|
"keymirror": "0.1.1",
|
||||||
|
"mobx": "2.4.0",
|
||||||
|
"mobx-react": "3.5.1",
|
||||||
"moment": "2.13.0",
|
"moment": "2.13.0",
|
||||||
"react": "15.1.0",
|
"react": "15.1.0",
|
||||||
"react-addons-css-transition-group": "15.1.0",
|
"react-addons-css-transition-group": "15.1.0",
|
||||||
|
|
|
@ -2,7 +2,8 @@ import React, { Component, PropTypes } from 'react';
|
||||||
import { render } from 'react-dom';
|
import { render } from 'react-dom';
|
||||||
import { Router, Route, Link, useRouterHistory, IndexRedirect } from 'react-router';
|
import { Router, Route, Link, useRouterHistory, IndexRedirect } from 'react-router';
|
||||||
import { createHistory } from 'history';
|
import { createHistory } from 'history';
|
||||||
import { Provider, configureStore, combineReducers} from './redux';
|
import { Provider as ReduxProvider, configureStore, combineReducers} from './redux';
|
||||||
|
import { Provider as MobXProvider } from 'mobx-react';
|
||||||
import { DevelopmentFooter } from './DevelopmentFooter';
|
import { DevelopmentFooter } from './DevelopmentFooter';
|
||||||
|
|
||||||
import Extensions from '@jenkins-cd/js-extensions';
|
import Extensions from '@jenkins-cd/js-extensions';
|
||||||
|
@ -75,7 +76,7 @@ function makeRoutes(routes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function startApp(routes, stores) {
|
function startApp(routes, stores, mobxStores) {
|
||||||
|
|
||||||
const rootElement = document.getElementById("root");
|
const rootElement = document.getElementById("root");
|
||||||
const headElement = document.getElementsByTagName("head")[0];
|
const headElement = document.getElementsByTagName("head")[0];
|
||||||
|
@ -117,6 +118,21 @@ function startApp(routes, stores) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// build up a 'master store' of all registered component stores
|
||||||
|
let mobxMasterStore = {};
|
||||||
|
if (mobxStores.length > 0) {
|
||||||
|
/*
|
||||||
|
TODO: watch for store name collisions and warn
|
||||||
|
for (const store of mobxStores) {
|
||||||
|
for (const storeName in store) {
|
||||||
|
console.log(storeName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
mobxMasterStore = Object.assign(mobxMasterStore, ...mobxStores);
|
||||||
|
}
|
||||||
|
|
||||||
// on each change of the url we need to update the location object
|
// on each change of the url we need to update the location object
|
||||||
history.listen(newLocation => {
|
history.listen(newLocation => {
|
||||||
const { dispatch, getState } = store;
|
const { dispatch, getState } = store;
|
||||||
|
@ -137,12 +153,19 @@ function startApp(routes, stores) {
|
||||||
|
|
||||||
// Start React
|
// Start React
|
||||||
render(
|
render(
|
||||||
<Provider store={store}>
|
React.cloneElement(
|
||||||
<Router history={history}>{ makeRoutes(routes) }</Router>
|
<MobXProvider>
|
||||||
</Provider>
|
<ReduxProvider store={store}>
|
||||||
, rootElement);
|
<Router history={history}>{ makeRoutes(routes) }</Router>
|
||||||
|
</ReduxProvider>
|
||||||
|
</MobXProvider>,
|
||||||
|
mobxMasterStore
|
||||||
|
),
|
||||||
|
rootElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
Extensions.store.getExtensions(['jenkins.main.routes', 'jenkins.main.stores'], (routes = [], stores = []) => {
|
Extensions.store.getExtensions(
|
||||||
startApp(routes, stores);
|
['jenkins.main.routes', 'jenkins.main.stores', 'jenkins.main.stores.mobx'],
|
||||||
|
(routes = [], stores = [], mobxStores =[]) => {
|
||||||
|
startApp(routes, stores, mobxStores);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue