From 337462aa5e68014aa15788e4513e190b2e434d7e Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 19 Sep 2016 23:25:59 +0200 Subject: [PATCH] Re-organizing components to be more modular, adding loading bars --- app/assets/javascripts/components.js | 2 +- .../components/components/frontend.jsx | 50 ----------------- .../containers/{root.jsx => mastodon.jsx} | 8 +-- .../components/features/status/index.jsx | 4 +- .../ui}/components/character_counter.jsx | 0 .../{ => features/ui}/components/column.jsx | 0 .../ui}/components/column_header.jsx | 0 .../ui}/components/columns_area.jsx | 0 .../ui}/components/compose_form.jsx | 2 +- .../{ => features/ui}/components/drawer.jsx | 0 .../ui}/components/follow_form.jsx | 2 +- .../ui}/components/media_gallery.jsx | 0 .../ui}/components/navigation_bar.jsx | 6 +- .../ui}/components/reply_indicator.jsx | 6 +- .../ui}/components/upload_button.jsx | 2 +- .../ui}/components/upload_form.jsx | 2 +- .../ui}/containers/compose_form_container.jsx | 2 +- .../ui}/containers/follow_form_container.jsx | 2 +- .../ui}/containers/navigation_container.jsx | 0 .../containers/notifications_container.jsx | 2 +- .../ui}/containers/status_list_container.jsx | 8 +-- .../ui}/containers/upload_form_container.jsx | 2 +- .../components/features/ui/index.jsx | 56 +++++++++++++++++++ .../components/reducers/compose.jsx | 36 ++++++++---- .../components/reducers/follow.jsx | 19 ++++--- .../javascripts/components/reducers/index.jsx | 16 +++--- .../components/reducers/notifications.jsx | 2 +- .../components/reducers/timelines.jsx | 38 ++++++------- .../components/store/configureStore.jsx | 11 ++-- app/views/home/index.html.haml | 2 +- package.json | 1 + 31 files changed, 155 insertions(+), 126 deletions(-) delete mode 100644 app/assets/javascripts/components/components/frontend.jsx rename app/assets/javascripts/components/containers/{root.jsx => mastodon.jsx} (93%) rename app/assets/javascripts/components/{ => features/ui}/components/character_counter.jsx (100%) rename app/assets/javascripts/components/{ => features/ui}/components/column.jsx (100%) rename app/assets/javascripts/components/{ => features/ui}/components/column_header.jsx (100%) rename app/assets/javascripts/components/{ => features/ui}/components/columns_area.jsx (100%) rename app/assets/javascripts/components/{ => features/ui}/components/compose_form.jsx (96%) rename app/assets/javascripts/components/{ => features/ui}/components/drawer.jsx (100%) rename app/assets/javascripts/components/{ => features/ui}/components/follow_form.jsx (95%) rename app/assets/javascripts/components/{ => features/ui}/components/media_gallery.jsx (100%) rename app/assets/javascripts/components/{ => features/ui}/components/navigation_bar.jsx (83%) rename app/assets/javascripts/components/{ => features/ui}/components/reply_indicator.jsx (87%) rename app/assets/javascripts/components/{ => features/ui}/components/upload_button.jsx (93%) rename app/assets/javascripts/components/{ => features/ui}/components/upload_form.jsx (95%) rename app/assets/javascripts/components/{ => features/ui}/containers/compose_form_container.jsx (97%) rename app/assets/javascripts/components/{ => features/ui}/containers/follow_form_container.jsx (89%) rename app/assets/javascripts/components/{ => features/ui}/containers/navigation_container.jsx (100%) rename app/assets/javascripts/components/{ => features/ui}/containers/notifications_container.jsx (89%) rename app/assets/javascripts/components/{ => features/ui}/containers/status_list_container.jsx (68%) rename app/assets/javascripts/components/{ => features/ui}/containers/upload_form_container.jsx (90%) create mode 100644 app/assets/javascripts/components/features/ui/index.jsx diff --git a/app/assets/javascripts/components.js b/app/assets/javascripts/components.js index d4d9b97e4..16e33e58e 100644 --- a/app/assets/javascripts/components.js +++ b/app/assets/javascripts/components.js @@ -6,4 +6,4 @@ window.ReactDOM = require('react-dom'); //= require_tree ./components -window.Root = require('./components/containers/root'); +window.Mastodon = require('./components/containers/mastodon'); diff --git a/app/assets/javascripts/components/components/frontend.jsx b/app/assets/javascripts/components/components/frontend.jsx deleted file mode 100644 index faa141af8..000000000 --- a/app/assets/javascripts/components/components/frontend.jsx +++ /dev/null @@ -1,50 +0,0 @@ -import ColumnsArea from './columns_area'; -import Column from './column'; -import Drawer from './drawer'; -import ComposeFormContainer from '../containers/compose_form_container'; -import FollowFormContainer from '../containers/follow_form_container'; -import UploadFormContainer from '../containers/upload_form_container'; -import StatusListContainer from '../containers/status_list_container'; -import NotificationsContainer from '../containers/notifications_container'; -import NavigationContainer from '../containers/navigation_container'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; - -const Frontend = React.createClass({ - - mixins: [PureRenderMixin], - - render () { - return ( -
- -
- - - -
- - -
- - - - - - - - - - - - {this.props.children} - - - - -
- ); - } - -}); - -export default Frontend; diff --git a/app/assets/javascripts/components/containers/root.jsx b/app/assets/javascripts/components/containers/mastodon.jsx similarity index 93% rename from app/assets/javascripts/components/containers/root.jsx rename to app/assets/javascripts/components/containers/mastodon.jsx index e30330beb..ba6e568ac 100644 --- a/app/assets/javascripts/components/containers/root.jsx +++ b/app/assets/javascripts/components/containers/mastodon.jsx @@ -1,6 +1,5 @@ import { Provider } from 'react-redux'; import configureStore from '../store/configureStore'; -import Frontend from '../components/frontend'; import { setTimeline, updateTimeline, deleteFromTimelines, refreshTimeline } from '../actions/timelines'; import { setAccessToken } from '../actions/meta'; import { setAccountSelf } from '../actions/accounts'; @@ -10,10 +9,11 @@ import Account fro import Settings from '../features/settings'; import Status from '../features/status'; import Subscriptions from '../features/subscriptions'; +import UI from '../features/ui'; const store = configureStore(); -const Root = React.createClass({ +const Mastodon = React.createClass({ propTypes: { token: React.PropTypes.string.isRequired, @@ -58,7 +58,7 @@ const Root = React.createClass({ return ( - + @@ -71,4 +71,4 @@ const Root = React.createClass({ }); -export default Root; +export default Mastodon; diff --git a/app/assets/javascripts/components/features/status/index.jsx b/app/assets/javascripts/components/features/status/index.jsx index 72ff6a944..7a810d474 100644 --- a/app/assets/javascripts/components/features/status/index.jsx +++ b/app/assets/javascripts/components/features/status/index.jsx @@ -31,12 +31,12 @@ const Status = React.createClass({ mixins: [PureRenderMixin], componentWillMount () { - this.props.dispatch(fetchStatus(this.props.params.statusId)); + this.props.dispatch(fetchStatus(Number(this.props.params.statusId))); }, componentWillReceiveProps (nextProps) { if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) { - this.props.dispatch(fetchStatus(nextProps.params.statusId)); + this.props.dispatch(fetchStatus(Number(nextProps.params.statusId))); } }, diff --git a/app/assets/javascripts/components/components/character_counter.jsx b/app/assets/javascripts/components/features/ui/components/character_counter.jsx similarity index 100% rename from app/assets/javascripts/components/components/character_counter.jsx rename to app/assets/javascripts/components/features/ui/components/character_counter.jsx diff --git a/app/assets/javascripts/components/components/column.jsx b/app/assets/javascripts/components/features/ui/components/column.jsx similarity index 100% rename from app/assets/javascripts/components/components/column.jsx rename to app/assets/javascripts/components/features/ui/components/column.jsx diff --git a/app/assets/javascripts/components/components/column_header.jsx b/app/assets/javascripts/components/features/ui/components/column_header.jsx similarity index 100% rename from app/assets/javascripts/components/components/column_header.jsx rename to app/assets/javascripts/components/features/ui/components/column_header.jsx diff --git a/app/assets/javascripts/components/components/columns_area.jsx b/app/assets/javascripts/components/features/ui/components/columns_area.jsx similarity index 100% rename from app/assets/javascripts/components/components/columns_area.jsx rename to app/assets/javascripts/components/features/ui/components/columns_area.jsx diff --git a/app/assets/javascripts/components/components/compose_form.jsx b/app/assets/javascripts/components/features/ui/components/compose_form.jsx similarity index 96% rename from app/assets/javascripts/components/components/compose_form.jsx rename to app/assets/javascripts/components/features/ui/components/compose_form.jsx index 40d9b7773..1aa0b447f 100644 --- a/app/assets/javascripts/components/components/compose_form.jsx +++ b/app/assets/javascripts/components/features/ui/components/compose_form.jsx @@ -1,5 +1,5 @@ import CharacterCounter from './character_counter'; -import Button from './button'; +import Button from '../../../components/button'; import PureRenderMixin from 'react-addons-pure-render-mixin'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ReplyIndicator from './reply_indicator'; diff --git a/app/assets/javascripts/components/components/drawer.jsx b/app/assets/javascripts/components/features/ui/components/drawer.jsx similarity index 100% rename from app/assets/javascripts/components/components/drawer.jsx rename to app/assets/javascripts/components/features/ui/components/drawer.jsx diff --git a/app/assets/javascripts/components/components/follow_form.jsx b/app/assets/javascripts/components/features/ui/components/follow_form.jsx similarity index 95% rename from app/assets/javascripts/components/components/follow_form.jsx rename to app/assets/javascripts/components/features/ui/components/follow_form.jsx index 0563c511f..a9d73a9a1 100644 --- a/app/assets/javascripts/components/components/follow_form.jsx +++ b/app/assets/javascripts/components/features/ui/components/follow_form.jsx @@ -1,4 +1,4 @@ -import IconButton from './icon_button'; +import IconButton from '../../../components/icon_button'; import PureRenderMixin from 'react-addons-pure-render-mixin'; const FollowForm = React.createClass({ diff --git a/app/assets/javascripts/components/components/media_gallery.jsx b/app/assets/javascripts/components/features/ui/components/media_gallery.jsx similarity index 100% rename from app/assets/javascripts/components/components/media_gallery.jsx rename to app/assets/javascripts/components/features/ui/components/media_gallery.jsx diff --git a/app/assets/javascripts/components/components/navigation_bar.jsx b/app/assets/javascripts/components/features/ui/components/navigation_bar.jsx similarity index 83% rename from app/assets/javascripts/components/components/navigation_bar.jsx rename to app/assets/javascripts/components/features/ui/components/navigation_bar.jsx index 2e7d6e6e1..b5d374a88 100644 --- a/app/assets/javascripts/components/components/navigation_bar.jsx +++ b/app/assets/javascripts/components/features/ui/components/navigation_bar.jsx @@ -1,8 +1,8 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import Avatar from './avatar'; -import IconButton from './icon_button'; -import DisplayName from './display_name'; +import Avatar from '../../../components/avatar'; +import IconButton from '../../../components/icon_button'; +import DisplayName from '../../../components/display_name'; import { Link } from 'react-router'; const NavigationBar = React.createClass({ diff --git a/app/assets/javascripts/components/components/reply_indicator.jsx b/app/assets/javascripts/components/features/ui/components/reply_indicator.jsx similarity index 87% rename from app/assets/javascripts/components/components/reply_indicator.jsx rename to app/assets/javascripts/components/features/ui/components/reply_indicator.jsx index 9598a5874..316a23b54 100644 --- a/app/assets/javascripts/components/components/reply_indicator.jsx +++ b/app/assets/javascripts/components/features/ui/components/reply_indicator.jsx @@ -1,8 +1,8 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import Avatar from './avatar'; -import IconButton from './icon_button'; -import DisplayName from './display_name'; +import Avatar from '../../../components/avatar'; +import IconButton from '../../../components/icon_button'; +import DisplayName from '../../../components/display_name'; const ReplyIndicator = React.createClass({ diff --git a/app/assets/javascripts/components/components/upload_button.jsx b/app/assets/javascripts/components/features/ui/components/upload_button.jsx similarity index 93% rename from app/assets/javascripts/components/components/upload_button.jsx rename to app/assets/javascripts/components/features/ui/components/upload_button.jsx index a77aacc10..d1b093242 100644 --- a/app/assets/javascripts/components/components/upload_button.jsx +++ b/app/assets/javascripts/components/features/ui/components/upload_button.jsx @@ -1,5 +1,5 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; -import Button from './button'; +import Button from '../../../components/button'; const UploadButton = React.createClass({ diff --git a/app/assets/javascripts/components/components/upload_form.jsx b/app/assets/javascripts/components/features/ui/components/upload_form.jsx similarity index 95% rename from app/assets/javascripts/components/components/upload_form.jsx rename to app/assets/javascripts/components/features/ui/components/upload_form.jsx index f0b6f7992..d584e9ab7 100644 --- a/app/assets/javascripts/components/components/upload_form.jsx +++ b/app/assets/javascripts/components/features/ui/components/upload_form.jsx @@ -1,7 +1,7 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; import ImmutablePropTypes from 'react-immutable-proptypes'; import UploadButton from './upload_button'; -import IconButton from './icon_button'; +import IconButton from '../../../components/icon_button'; const UploadForm = React.createClass({ diff --git a/app/assets/javascripts/components/containers/compose_form_container.jsx b/app/assets/javascripts/components/features/ui/containers/compose_form_container.jsx similarity index 97% rename from app/assets/javascripts/components/containers/compose_form_container.jsx rename to app/assets/javascripts/components/features/ui/containers/compose_form_container.jsx index d49217a90..a092a1e8e 100644 --- a/app/assets/javascripts/components/containers/compose_form_container.jsx +++ b/app/assets/javascripts/components/features/ui/containers/compose_form_container.jsx @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import ComposeForm from '../components/compose_form'; -import { changeCompose, submitCompose, cancelReplyCompose } from '../actions/compose'; +import { changeCompose, submitCompose, cancelReplyCompose } from '../../../actions/compose'; function selectStatus(state) { let statusId = state.getIn(['compose', 'in_reply_to'], null); diff --git a/app/assets/javascripts/components/containers/follow_form_container.jsx b/app/assets/javascripts/components/features/ui/containers/follow_form_container.jsx similarity index 89% rename from app/assets/javascripts/components/containers/follow_form_container.jsx rename to app/assets/javascripts/components/features/ui/containers/follow_form_container.jsx index b5f787aba..a21c1291b 100644 --- a/app/assets/javascripts/components/containers/follow_form_container.jsx +++ b/app/assets/javascripts/components/features/ui/containers/follow_form_container.jsx @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import FollowForm from '../components/follow_form'; -import { changeFollow, submitFollow } from '../actions/follow'; +import { changeFollow, submitFollow } from '../../../actions/follow'; const mapStateToProps = function (state, props) { return { diff --git a/app/assets/javascripts/components/containers/navigation_container.jsx b/app/assets/javascripts/components/features/ui/containers/navigation_container.jsx similarity index 100% rename from app/assets/javascripts/components/containers/navigation_container.jsx rename to app/assets/javascripts/components/features/ui/containers/navigation_container.jsx diff --git a/app/assets/javascripts/components/containers/notifications_container.jsx b/app/assets/javascripts/components/features/ui/containers/notifications_container.jsx similarity index 89% rename from app/assets/javascripts/components/containers/notifications_container.jsx rename to app/assets/javascripts/components/features/ui/containers/notifications_container.jsx index 68173b34e..2db1603fc 100644 --- a/app/assets/javascripts/components/containers/notifications_container.jsx +++ b/app/assets/javascripts/components/features/ui/containers/notifications_container.jsx @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { NotificationStack } from 'react-notification'; -import { dismissNotification } from '../actions/notifications'; +import { dismissNotification } from '../../../actions/notifications'; const mapStateToProps = (state, props) => { return { diff --git a/app/assets/javascripts/components/containers/status_list_container.jsx b/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx similarity index 68% rename from app/assets/javascripts/components/containers/status_list_container.jsx rename to app/assets/javascripts/components/features/ui/containers/status_list_container.jsx index a4fba95e1..4ea599fc0 100644 --- a/app/assets/javascripts/components/containers/status_list_container.jsx +++ b/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx @@ -1,8 +1,8 @@ import { connect } from 'react-redux'; -import StatusList from '../components/status_list'; -import { replyCompose } from '../actions/compose'; -import { reblog, favourite } from '../actions/interactions'; -import { selectStatus } from '../reducers/timelines'; +import StatusList from '../../../components/status_list'; +import { replyCompose } from '../../../actions/compose'; +import { reblog, favourite } from '../../../actions/interactions'; +import { selectStatus } from '../../../reducers/timelines'; const mapStateToProps = function (state, props) { return { diff --git a/app/assets/javascripts/components/containers/upload_form_container.jsx b/app/assets/javascripts/components/features/ui/containers/upload_form_container.jsx similarity index 90% rename from app/assets/javascripts/components/containers/upload_form_container.jsx rename to app/assets/javascripts/components/features/ui/containers/upload_form_container.jsx index b168e8187..6554f944f 100644 --- a/app/assets/javascripts/components/containers/upload_form_container.jsx +++ b/app/assets/javascripts/components/features/ui/containers/upload_form_container.jsx @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import UploadForm from '../components/upload_form'; -import { uploadCompose, undoUploadCompose } from '../actions/compose'; +import { uploadCompose, undoUploadCompose } from '../../../actions/compose'; const mapStateToProps = function (state, props) { return { diff --git a/app/assets/javascripts/components/features/ui/index.jsx b/app/assets/javascripts/components/features/ui/index.jsx new file mode 100644 index 000000000..fbfa361f2 --- /dev/null +++ b/app/assets/javascripts/components/features/ui/index.jsx @@ -0,0 +1,56 @@ +import ColumnsArea from './components/columns_area'; +import Column from './components/column'; +import Drawer from './components/drawer'; +import ComposeFormContainer from './containers/compose_form_container'; +import FollowFormContainer from './containers/follow_form_container'; +import UploadFormContainer from './containers/upload_form_container'; +import StatusListContainer from './containers/status_list_container'; +import NotificationsContainer from './containers/notifications_container'; +import NavigationContainer from './containers/navigation_container'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import LoadingBar from 'react-redux-loading-bar'; + +const UI = React.createClass({ + + propTypes: { + router: React.PropTypes.object + }, + + mixins: [PureRenderMixin], + + render () { + return ( +
+ +
+ + + +
+ + +
+ + + + + + + + + + + + {this.props.children} + + + + + +
+ ); + } + +}); + +export default UI; diff --git a/app/assets/javascripts/components/reducers/compose.jsx b/app/assets/javascripts/components/reducers/compose.jsx index c0d9f4ff2..688c494c0 100644 --- a/app/assets/javascripts/components/reducers/compose.jsx +++ b/app/assets/javascripts/components/reducers/compose.jsx @@ -1,4 +1,16 @@ -import * as constants from '../actions/compose'; +import { + COMPOSE_CHANGE, + COMPOSE_REPLY, + COMPOSE_REPLY_CANCEL, + COMPOSE_SUBMIT_REQUEST, + COMPOSE_SUBMIT_SUCCESS, + COMPOSE_SUBMIT_FAIL, + COMPOSE_UPLOAD_REQUEST, + COMPOSE_UPLOAD_SUCCESS, + COMPOSE_UPLOAD_FAIL, + COMPOSE_UPLOAD_UNDO, + COMPOSE_UPLOAD_PROGRESS +} from '../actions/compose'; import { TIMELINE_DELETE } from '../actions/timelines'; import Immutable from 'immutable'; @@ -13,41 +25,41 @@ const initialState = Immutable.Map({ export default function compose(state = initialState, action) { switch(action.type) { - case constants.COMPOSE_CHANGE: + case COMPOSE_CHANGE: return state.set('text', action.text); - case constants.COMPOSE_REPLY: + case COMPOSE_REPLY: return state.withMutations(map => { map.set('in_reply_to', action.status.get('id')); map.set('text', `@${action.status.getIn(['account', 'acct'])} `); }); - case constants.COMPOSE_REPLY_CANCEL: + case COMPOSE_REPLY_CANCEL: return state.withMutations(map => { map.set('in_reply_to', null); map.set('text', ''); }); - case constants.COMPOSE_SUBMIT_REQUEST: + case COMPOSE_SUBMIT_REQUEST: return state.set('is_submitting', true); - case constants.COMPOSE_SUBMIT_SUCCESS: + case COMPOSE_SUBMIT_SUCCESS: return state.withMutations(map => { map.set('text', ''); map.set('is_submitting', false); map.set('in_reply_to', null); map.update('media_attachments', list => list.clear()); }); - case constants.COMPOSE_SUBMIT_FAIL: + case COMPOSE_SUBMIT_FAIL: return state.set('is_submitting', false); - case constants.COMPOSE_UPLOAD_REQUEST: + case COMPOSE_UPLOAD_REQUEST: return state.set('is_uploading', true); - case constants.COMPOSE_UPLOAD_SUCCESS: + case COMPOSE_UPLOAD_SUCCESS: return state.withMutations(map => { map.update('media_attachments', list => list.push(Immutable.fromJS(action.media))); map.set('is_uploading', false); }); - case constants.COMPOSE_UPLOAD_FAIL: + case COMPOSE_UPLOAD_FAIL: return state.set('is_uploading', false); - case constants.COMPOSE_UPLOAD_UNDO: + case COMPOSE_UPLOAD_UNDO: return state.update('media_attachments', list => list.filterNot(item => item.get('id') === action.media_id)); - case constants.COMPOSE_UPLOAD_PROGRESS: + case COMPOSE_UPLOAD_PROGRESS: return state.set('progress', Math.round((action.loaded / action.total) * 100)); case TIMELINE_DELETE: if (action.id === state.get('in_reply_to')) { diff --git a/app/assets/javascripts/components/reducers/follow.jsx b/app/assets/javascripts/components/reducers/follow.jsx index 838f12259..e1dc293c6 100644 --- a/app/assets/javascripts/components/reducers/follow.jsx +++ b/app/assets/javascripts/components/reducers/follow.jsx @@ -1,22 +1,27 @@ -import * as constants from '../actions/follow'; -import Immutable from 'immutable'; +import { + FOLLOW_CHANGE, + FOLLOW_SUBMIT_REQUEST, + FOLLOW_SUBMIT_SUCCESS, + FOLLOW_SUBMIT_FAIL +} from '../actions/follow'; +import Immutable from 'immutable'; const initialState = Immutable.Map({ text: '', is_submitting: false }); -export default function compose(state = initialState, action) { +export default function follow(state = initialState, action) { switch(action.type) { - case constants.FOLLOW_CHANGE: + case FOLLOW_CHANGE: return state.set('text', action.text); - case constants.FOLLOW_SUBMIT_REQUEST: + case FOLLOW_SUBMIT_REQUEST: return state.set('is_submitting', true); - case constants.FOLLOW_SUBMIT_SUCCESS: + case FOLLOW_SUBMIT_SUCCESS: return state.withMutations(map => { map.set('text', '').set('is_submitting', false); }); - case constants.FOLLOW_SUBMIT_FAIL: + case FOLLOW_SUBMIT_FAIL: return state.set('is_submitting', false); default: return state; diff --git a/app/assets/javascripts/components/reducers/index.jsx b/app/assets/javascripts/components/reducers/index.jsx index 6d275654a..da9ab1a21 100644 --- a/app/assets/javascripts/components/reducers/index.jsx +++ b/app/assets/javascripts/components/reducers/index.jsx @@ -1,14 +1,16 @@ -import { combineReducers } from 'redux-immutable'; -import timelines from './timelines'; -import meta from './meta'; -import compose from './compose'; -import follow from './follow'; -import notifications from './notifications'; +import { combineReducers } from 'redux-immutable'; +import timelines from './timelines'; +import meta from './meta'; +import compose from './compose'; +import follow from './follow'; +import notifications from './notifications'; +import { loadingBarReducer } from 'react-redux-loading-bar'; export default combineReducers({ timelines, meta, compose, follow, - notifications + notifications, + loadingBar: loadingBarReducer, }); diff --git a/app/assets/javascripts/components/reducers/notifications.jsx b/app/assets/javascripts/components/reducers/notifications.jsx index bdd2de993..694d9ff4b 100644 --- a/app/assets/javascripts/components/reducers/notifications.jsx +++ b/app/assets/javascripts/components/reducers/notifications.jsx @@ -24,7 +24,7 @@ function notificationFromError(state, error) { return state.push(n); }; -export default function meta(state = initialState, action) { +export default function notifications(state = initialState, action) { switch(action.type) { case COMPOSE_SUBMIT_FAIL: case COMPOSE_UPLOAD_FAIL: diff --git a/app/assets/javascripts/components/reducers/timelines.jsx b/app/assets/javascripts/components/reducers/timelines.jsx index 04a49e16d..2bc4e6eb6 100644 --- a/app/assets/javascripts/components/reducers/timelines.jsx +++ b/app/assets/javascripts/components/reducers/timelines.jsx @@ -45,7 +45,7 @@ export function selectStatus(state, id) { return status; }; -function statusToMaps(state, status) { +function normalizeStatus(state, status) { // Separate account let account = status.get('account'); status = status.set('account', account.get('id')); @@ -55,7 +55,7 @@ function statusToMaps(state, status) { if (reblog !== null) { status = status.set('reblog', reblog.get('id')); - state = statusToMaps(state, reblog); + state = normalizeStatus(state, reblog); } // Replies @@ -80,26 +80,26 @@ function statusToMaps(state, status) { }); }; -function timelineToMaps(state, timeline, statuses) { +function normalizeTimeline(state, timeline, statuses) { statuses.forEach((status, i) => { - state = statusToMaps(state, status); + state = normalizeStatus(state, status); state = state.setIn([timeline, i], status.get('id')); }); return state; }; -function accountTimelineToMaps(state, accountId, statuses) { +function normalizeAccountTimeline(state, accountId, statuses) { statuses.forEach((status, i) => { - state = statusToMaps(state, status); + state = normalizeStatus(state, status); state = state.updateIn(['accounts_timelines', accountId], Immutable.List(), list => list.set(i, status.get('id'))); }); return state; }; -function updateTimelineWithMaps(state, timeline, status) { - state = statusToMaps(state, status); +function updateTimeline(state, timeline, status) { + state = normalizeStatus(state, status); state = state.update(timeline, list => list.unshift(status.get('id'))); state = state.updateIn(['accounts_timelines', status.getIn(['account', 'id'])], Immutable.List(), list => list.unshift(status.get('id'))); @@ -114,20 +114,20 @@ function deleteStatus(state, id) { return state.deleteIn(['statuses', id]); }; -function accountToMaps(state, account) { +function normalizeAccount(state, account) { return state.setIn(['accounts', account.get('id')], account); }; -function contextToMaps(state, status, ancestors, descendants) { - state = statusToMaps(state, status); +function normalizeContext(state, status, ancestors, descendants) { + state = normalizeStatus(state, status); let ancestorsIds = ancestors.map(ancestor => { - state = statusToMaps(state, ancestor); + state = normalizeStatus(state, ancestor); return ancestor.get('id'); }).toOrderedSet(); let descendantsIds = descendants.map(descendant => { - state = statusToMaps(state, descendant); + state = normalizeStatus(state, descendant); return descendant.get('id'); }).toOrderedSet(); @@ -140,14 +140,14 @@ function contextToMaps(state, status, ancestors, descendants) { export default function timelines(state = initialState, action) { switch(action.type) { case TIMELINE_SET: - return timelineToMaps(state, action.timeline, Immutable.fromJS(action.statuses)); + return normalizeTimeline(state, action.timeline, Immutable.fromJS(action.statuses)); case TIMELINE_UPDATE: - return updateTimelineWithMaps(state, action.timeline, Immutable.fromJS(action.status)); + return updateTimeline(state, action.timeline, Immutable.fromJS(action.status)); case TIMELINE_DELETE: return deleteStatus(state, action.id); case REBLOG_SUCCESS: case FAVOURITE_SUCCESS: - return statusToMaps(state, Immutable.fromJS(action.response)); + return normalizeStatus(state, Immutable.fromJS(action.response)); case ACCOUNT_SET_SELF: return state.withMutations(map => { map.setIn(['accounts', action.account.id], Immutable.fromJS(action.account)); @@ -157,11 +157,11 @@ export default function timelines(state = initialState, action) { case FOLLOW_SUBMIT_SUCCESS: case ACCOUNT_FOLLOW_SUCCESS: case ACCOUNT_UNFOLLOW_SUCCESS: - return accountToMaps(state, Immutable.fromJS(action.account)); + return normalizeAccount(state, Immutable.fromJS(action.account)); case STATUS_FETCH_SUCCESS: - return contextToMaps(state, Immutable.fromJS(action.status), Immutable.fromJS(action.context.ancestors), Immutable.fromJS(action.context.descendants)); + return normalizeContext(state, Immutable.fromJS(action.status), Immutable.fromJS(action.context.ancestors), Immutable.fromJS(action.context.descendants)); case ACCOUNT_TIMELINE_FETCH_SUCCESS: - return accountTimelineToMaps(state, action.id, Immutable.fromJS(action.statuses)); + return normalizeAccountTimeline(state, action.id, Immutable.fromJS(action.statuses)); default: return state; } diff --git a/app/assets/javascripts/components/store/configureStore.jsx b/app/assets/javascripts/components/store/configureStore.jsx index 1f2fcf2cf..89f498217 100644 --- a/app/assets/javascripts/components/store/configureStore.jsx +++ b/app/assets/javascripts/components/store/configureStore.jsx @@ -1,7 +1,10 @@ import { createStore, applyMiddleware, compose } from 'redux'; -import thunk from 'redux-thunk'; -import appReducer from '../reducers'; +import thunk from 'redux-thunk'; +import appReducer from '../reducers'; +import { loadingBarMiddleware } from 'react-redux-loading-bar'; export default function configureStore(initialState) { - return createStore(appReducer, initialState, compose(applyMiddleware(thunk), window.devToolsExtension ? window.devToolsExtension() : f => f)); -} + return createStore(appReducer, initialState, compose(applyMiddleware(thunk, loadingBarMiddleware({ + promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'], + })), window.devToolsExtension ? window.devToolsExtension() : f => f)); +}; diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index 34028462c..f06c26e9d 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1 +1 @@ -= react_component 'Root', default_props, class: 'app-holder', prerender: false += react_component 'Mastodon', default_props, class: 'app-holder', prerender: false diff --git a/package.json b/package.json index 5234a37f0..f8df13a2f 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "react-immutable-proptypes": "^2.1.0", "react-notification": "^6.1.1", "react-redux": "^4.4.5", + "react-redux-loading-bar": "^2.3.3", "react-router": "^2.8.0", "redux": "^3.5.2", "redux-immutable": "^3.0.8",