From 69e8f0ec49a910bbd80b81a566c18b2ec19032e4 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Mon, 6 Jun 2016 11:45:34 +0200 Subject: [PATCH] refactor account app to drop redux (#1019) --- .../js/apps/account/{styles => }/account.css | 0 .../sonar-web/src/main/js/apps/account/app.js | 35 ++++----- .../{containers => components}/AccountApp.js | 66 ++++++++++------- .../Issues.js} | 2 +- .../apps/account/components/Notifications.js | 6 +- .../components/NotificationsContainer.js | 71 +++++++++++++++++++ .../js/apps/account/components/Security.js | 6 +- .../js/apps/account/components/UserCard.js | 6 +- .../containers/NotificationsContainer.js | 42 ----------- .../account/containers/SecurityContainer.js | 28 -------- .../src/main/js/apps/account/store/actions.js | 59 --------------- .../js/apps/account/store/configureStore.js | 36 ---------- .../main/js/apps/account/store/reducers.js | 71 ------------------- 13 files changed, 136 insertions(+), 292 deletions(-) rename server/sonar-web/src/main/js/apps/account/{styles => }/account.css (100%) rename server/sonar-web/src/main/js/apps/account/{containers => components}/AccountApp.js (63%) rename server/sonar-web/src/main/js/apps/account/{containers/IssuesContainer.js => components/Issues.js} (95%) create mode 100644 server/sonar-web/src/main/js/apps/account/components/NotificationsContainer.js delete mode 100644 server/sonar-web/src/main/js/apps/account/containers/NotificationsContainer.js delete mode 100644 server/sonar-web/src/main/js/apps/account/containers/SecurityContainer.js delete mode 100644 server/sonar-web/src/main/js/apps/account/store/actions.js delete mode 100644 server/sonar-web/src/main/js/apps/account/store/configureStore.js delete mode 100644 server/sonar-web/src/main/js/apps/account/store/reducers.js diff --git a/server/sonar-web/src/main/js/apps/account/styles/account.css b/server/sonar-web/src/main/js/apps/account/account.css similarity index 100% rename from server/sonar-web/src/main/js/apps/account/styles/account.css rename to server/sonar-web/src/main/js/apps/account/account.css diff --git a/server/sonar-web/src/main/js/apps/account/app.js b/server/sonar-web/src/main/js/apps/account/app.js index e4643b9349c..8bb8231f5db 100644 --- a/server/sonar-web/src/main/js/apps/account/app.js +++ b/server/sonar-web/src/main/js/apps/account/app.js @@ -21,16 +21,11 @@ import React from 'react'; import { render } from 'react-dom'; import { Router, Route, IndexRoute, Redirect, useRouterHistory } from 'react-router'; import { createHistory } from 'history'; -import { Provider } from 'react-redux'; - -import configureStore from './store/configureStore'; -import AccountApp from './containers/AccountApp'; +import AccountApp from './components/AccountApp'; import Home from './components/Home'; -import NotificationsContainer from './containers/NotificationsContainer'; -import SecurityContainer from './containers/SecurityContainer'; -import IssuesContainer from './containers/IssuesContainer'; - -import './styles/account.css'; +import NotificationsContainer from './components/NotificationsContainer'; +import Security from './components/Security'; +import Issues from './components/Issues'; window.sonarqube.appStarted.then(options => { const el = document.querySelector(options.el); @@ -39,20 +34,16 @@ window.sonarqube.appStarted.then(options => { basename: window.baseUrl + '/account' }); - const store = configureStore(); - render(( - - - - - - - + + + + + + - - - - + + + ), el); }); diff --git a/server/sonar-web/src/main/js/apps/account/containers/AccountApp.js b/server/sonar-web/src/main/js/apps/account/components/AccountApp.js similarity index 63% rename from server/sonar-web/src/main/js/apps/account/containers/AccountApp.js rename to server/sonar-web/src/main/js/apps/account/components/AccountApp.js index 2b3198b682b..84757dfd6fc 100644 --- a/server/sonar-web/src/main/js/apps/account/containers/AccountApp.js +++ b/server/sonar-web/src/main/js/apps/account/components/AccountApp.js @@ -18,37 +18,62 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import React, { Component, cloneElement } from 'react'; -import { connect } from 'react-redux'; - -import Nav from '../components/Nav'; -import { fetchUser } from '../store/actions'; +import Nav from './Nav'; +import { getCurrentUser } from '../../../api/users'; import { getIssueFilters } from '../../../api/issues'; +import '../account.css'; -class AccountApp extends Component { - state = {}; +export default class AccountApp extends Component { + state = { + loading: true + }; componentDidMount () { - this.props.fetchUser(); - this.fetchFavoriteIssueFilters(); + this.mounted = true; + Promise.all([ + this.loadUser(), + this.loadFavoriteIssueFilters() + ]).then(() => this.finishLoading()); + } + + componentWillUnmount () { + this.mounted = false; } - fetchFavoriteIssueFilters () { - getIssueFilters().then(issueFilters => { + loadUser () { + return getCurrentUser().then(user => { + if (this.mounted) { + this.setState({ user }); + } + }); + } + + loadFavoriteIssueFilters () { + return getIssueFilters().then(issueFilters => { const favoriteIssueFilters = issueFilters.filter(f => f.favorite); this.setState({ issueFilters: favoriteIssueFilters }); }); } + finishLoading () { + if (this.mounted) { + this.setState({ loading: false }); + } + } + render () { - const { user } = this.props; + const { user, issueFilters, loading } = this.state; - if (!user) { - return null; + if (loading) { + return ( +
+ +
+ ); } const { favorites } = window.sonarqube.user; const measureFilters = window.sonarqube.user.favoriteMeasureFilters; - const { issueFilters } = this.state; const children = cloneElement(this.props.children, { measureFilters, user, @@ -64,16 +89,3 @@ class AccountApp extends Component { ); } } - -function mapStateToProps (state) { - return { user: state.user }; -} - -function mapDispatchToProps (dispatch) { - return { fetchUser: () => dispatch(fetchUser()) }; -} - -export default connect( - mapStateToProps, - mapDispatchToProps -)(AccountApp); diff --git a/server/sonar-web/src/main/js/apps/account/containers/IssuesContainer.js b/server/sonar-web/src/main/js/apps/account/components/Issues.js similarity index 95% rename from server/sonar-web/src/main/js/apps/account/containers/IssuesContainer.js rename to server/sonar-web/src/main/js/apps/account/components/Issues.js index 67e927f3a4e..21c4b41c4c0 100644 --- a/server/sonar-web/src/main/js/apps/account/containers/IssuesContainer.js +++ b/server/sonar-web/src/main/js/apps/account/components/Issues.js @@ -21,7 +21,7 @@ import React, { Component } from 'react'; import IssuesApp from '../issues-app'; -export default class IssuesContainer extends Component { +export default class Issues extends Component { componentDidMount () { this.issuesApp = IssuesApp; this.issuesApp.start({ diff --git a/server/sonar-web/src/main/js/apps/account/components/Notifications.js b/server/sonar-web/src/main/js/apps/account/components/Notifications.js index 8eb4e437afb..04e5fc7124f 100644 --- a/server/sonar-web/src/main/js/apps/account/components/Notifications.js +++ b/server/sonar-web/src/main/js/apps/account/components/Notifications.js @@ -23,7 +23,7 @@ import GlobalNotifications from './GlobalNotifications'; import ProjectNotifications from './ProjectNotifications'; import { translate } from '../../../helpers/l10n'; -export default function Notifications ({ globalNotifications, projectNotifications, onAddProject, onRemoveProject }) { +const Notifications = ({ globalNotifications, projectNotifications, onAddProject, onRemoveProject }) => { const channels = globalNotifications[0].channels.map(c => c.id); return ( @@ -56,4 +56,6 @@ export default function Notifications ({ globalNotifications, projectNotificatio ); -} +}; + +export default Notifications; diff --git a/server/sonar-web/src/main/js/apps/account/components/NotificationsContainer.js b/server/sonar-web/src/main/js/apps/account/components/NotificationsContainer.js new file mode 100644 index 00000000000..81a770a9e66 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/account/components/NotificationsContainer.js @@ -0,0 +1,71 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import Notifications from './Notifications'; + +export default class NotificationsContainer extends React.Component { + state = { + globalNotifications: window.sonarqube.notifications.global, + projectNotifications: window.sonarqube.notifications.project + }; + + componentWillMount () { + this.handleAddProject = this.handleAddProject.bind(this); + this.handleRemoveProject = this.handleRemoveProject.bind(this); + } + + handleAddProject (project) { + const { projectNotifications } = this.state; + const found = projectNotifications + .find(notification => notification.project.internalId === project.internalId); + + if (!found) { + const newProjectNotification = { + project, + notifications: window.sonarqube.notifications.projectDispatchers.map(dispatcher => { + const channels = window.sonarqube.notifications.channels.map(channel => { + return { id: channel, checked: false }; + }); + return { dispatcher, channels }; + }) + }; + + this.setState({ + projectNotifications: [...projectNotifications, newProjectNotification] + }); + } + } + + handleRemoveProject (project) { + const projectNotifications = this.state.projectNotifications + .filter(notification => notification.project.internalId !== project.internalId); + this.setState({ projectNotifications }); + } + + render () { + return ( + + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/account/components/Security.js b/server/sonar-web/src/main/js/apps/account/components/Security.js index ec220d082c5..a82ff5e748d 100644 --- a/server/sonar-web/src/main/js/apps/account/components/Security.js +++ b/server/sonar-web/src/main/js/apps/account/components/Security.js @@ -22,7 +22,7 @@ import React from 'react'; import Password from './Password'; import Tokens from './Tokens'; -export default function Security ({ user }) { +const Security = ({ user }) => { return (
@@ -38,4 +38,6 @@ export default function Security ({ user }) {
); -} +}; + +export default Security; diff --git a/server/sonar-web/src/main/js/apps/account/components/UserCard.js b/server/sonar-web/src/main/js/apps/account/components/UserCard.js index 82f9b939b28..4c90d7fe255 100644 --- a/server/sonar-web/src/main/js/apps/account/components/UserCard.js +++ b/server/sonar-web/src/main/js/apps/account/components/UserCard.js @@ -23,7 +23,7 @@ import { IndexLink } from 'react-router'; import UserExternalIdentity from './UserExternalIdentity'; import Avatar from '../../../components/shared/avatar'; -export default function UserCard ({ user }) { +const UserCard = ({ user }) => { return (
@@ -45,4 +45,6 @@ export default function UserCard ({ user }) {
{user.email}
); -} +}; + +export default UserCard; diff --git a/server/sonar-web/src/main/js/apps/account/containers/NotificationsContainer.js b/server/sonar-web/src/main/js/apps/account/containers/NotificationsContainer.js deleted file mode 100644 index 0764dae9c46..00000000000 --- a/server/sonar-web/src/main/js/apps/account/containers/NotificationsContainer.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { connect } from 'react-redux'; - -import { addProjectNotifications, removeProjectNotifications } from '../store/actions'; -import Notifications from './../components/Notifications'; - -function mapStateToProps (state) { - return { - globalNotifications: window.sonarqube.notifications.global, - projectNotifications: state.projectNotifications - }; -} - -function mapDispatchToProps (dispatch) { - return { - onAddProject: project => dispatch(addProjectNotifications(project)), - onRemoveProject: project => dispatch(removeProjectNotifications(project)) - }; -} - -export default connect( - mapStateToProps, - mapDispatchToProps -)(Notifications); diff --git a/server/sonar-web/src/main/js/apps/account/containers/SecurityContainer.js b/server/sonar-web/src/main/js/apps/account/containers/SecurityContainer.js deleted file mode 100644 index 8a800b71fb4..00000000000 --- a/server/sonar-web/src/main/js/apps/account/containers/SecurityContainer.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { connect } from 'react-redux'; - -import Security from './../components/Security'; - -function mapStateToProps (state) { - return { user: state.user }; -} - -export default connect(mapStateToProps)(Security); diff --git a/server/sonar-web/src/main/js/apps/account/store/actions.js b/server/sonar-web/src/main/js/apps/account/store/actions.js deleted file mode 100644 index f241d4c22c5..00000000000 --- a/server/sonar-web/src/main/js/apps/account/store/actions.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { getCurrentUser } from '../../../api/users'; - -export const REQUEST_USER = 'REQUEST_USER'; -export const RECEIVE_USER = 'RECEIVE_USER'; -export const ADD_PROJECT_NOTIFICATIONS = 'ADD_PROJECT_NOTIFICATIONS'; -export const REMOVE_PROJECT_NOTIFICATIONS = 'REMOVE_PROJECT_NOTIFICATIONS'; - -export function requestUser () { - return { - type: REQUEST_USER - }; -} - -export function receiveUser (user) { - return { - type: RECEIVE_USER, - user - }; -} - -export function addProjectNotifications (project) { - return { - type: ADD_PROJECT_NOTIFICATIONS, - project - }; -} - -export function removeProjectNotifications (project) { - return { - type: REMOVE_PROJECT_NOTIFICATIONS, - project - }; -} - -export function fetchUser () { - return dispatch => { - dispatch(requestUser()); - return getCurrentUser().then(user => dispatch(receiveUser(user))); - }; -} diff --git a/server/sonar-web/src/main/js/apps/account/store/configureStore.js b/server/sonar-web/src/main/js/apps/account/store/configureStore.js deleted file mode 100644 index e412913dc76..00000000000 --- a/server/sonar-web/src/main/js/apps/account/store/configureStore.js +++ /dev/null @@ -1,36 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { createStore, applyMiddleware } from 'redux'; -import thunk from 'redux-thunk'; -import createLogger from 'redux-logger'; -import rootReducer from './reducers'; - -const logger = createLogger({ - predicate: () => process.env.NODE_ENV !== 'production' -}); - -const createStoreWithMiddleware = applyMiddleware( - thunk, - logger -)(createStore); - -export default function configureStore () { - return createStoreWithMiddleware(rootReducer); -} diff --git a/server/sonar-web/src/main/js/apps/account/store/reducers.js b/server/sonar-web/src/main/js/apps/account/store/reducers.js deleted file mode 100644 index e129a65f17f..00000000000 --- a/server/sonar-web/src/main/js/apps/account/store/reducers.js +++ /dev/null @@ -1,71 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { RECEIVE_USER, ADD_PROJECT_NOTIFICATIONS, REMOVE_PROJECT_NOTIFICATIONS } from './actions'; - -function addProjectNotifications (state, project) { - const found = state.find(notification => notification.project.internalId === project.internalId); - - if (found) { - return state; - } - - const newProjectNotification = { - project, - notifications: window.sonarqube.notifications.projectDispatchers.map(dispatcher => { - const channels = window.sonarqube.notifications.channels.map(channel => { - return { id: channel, checked: false }; - }); - return { dispatcher, channels }; - }) - }; - - return [...state, newProjectNotification]; -} - -function removeProjectNotifications (state, project) { - return state.filter(notification => notification.project.internalId !== project.internalId); -} - -export const initialState = { - user: null, - projectNotifications: window.sonarqube.notifications.project -}; - -export default function (state = initialState, action = {}) { - switch (action.type) { - case RECEIVE_USER: - return { - ...state, - user: action.user - }; - case ADD_PROJECT_NOTIFICATIONS: - return { - ...state, - projectNotifications: addProjectNotifications(state.projectNotifications, action.project) - }; - case REMOVE_PROJECT_NOTIFICATIONS: - return { - ...state, - projectNotifications: removeProjectNotifications(state.projectNotifications, action.project) - }; - default: - return state; - } -} -- 2.39.5