commit: 23ebf60b95984764992c4b356048786ed0ab2953
parent: 2e71bb031b2dff90a2b0f9854bdcd804c069268a
Author: Eugen Rochko <eugen@zeonfederated.com>
Date: Mon, 9 Jan 2017 12:37:15 +0100
Improve initialState loading
Diffstat:
11 files changed, 108 insertions(+), 90 deletions(-)
diff --git a/app/assets/javascripts/components/actions/accounts.jsx b/app/assets/javascripts/components/actions/accounts.jsx
@@ -1,8 +1,6 @@
import api, { getLinks } from '../api'
import Immutable from 'immutable';
-export const ACCOUNT_SET_SELF = 'ACCOUNT_SET_SELF';
-
export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST';
export const ACCOUNT_FETCH_SUCCESS = 'ACCOUNT_FETCH_SUCCESS';
export const ACCOUNT_FETCH_FAIL = 'ACCOUNT_FETCH_FAIL';
@@ -67,13 +65,6 @@ export const FOLLOW_REQUEST_REJECT_REQUEST = 'FOLLOW_REQUEST_REJECT_REQUEST';
export const FOLLOW_REQUEST_REJECT_SUCCESS = 'FOLLOW_REQUEST_REJECT_SUCCESS';
export const FOLLOW_REQUEST_REJECT_FAIL = 'FOLLOW_REQUEST_REJECT_FAIL';
-export function setAccountSelf(account) {
- return {
- type: ACCOUNT_SET_SELF,
- account
- };
-};
-
export function fetchAccount(id) {
return (dispatch, getState) => {
dispatch(fetchAccountRequest(id));
diff --git a/app/assets/javascripts/components/actions/meta.jsx b/app/assets/javascripts/components/actions/meta.jsx
@@ -1,8 +0,0 @@
-export const ACCESS_TOKEN_SET = 'ACCESS_TOKEN_SET';
-
-export function setAccessToken(token) {
- return {
- type: ACCESS_TOKEN_SET,
- token: token
- };
-};
diff --git a/app/assets/javascripts/components/actions/store.jsx b/app/assets/javascripts/components/actions/store.jsx
@@ -0,0 +1,17 @@
+import Immutable from 'immutable';
+
+export const STORE_HYDRATE = 'STORE_HYDRATE';
+
+const convertState = rawState =>
+ Immutable.fromJS(rawState, (k, v) =>
+ Immutable.Iterable.isIndexed(v) ? v.toList() : v.toMap().mapKeys(x =>
+ Number.isNaN(x * 1) ? x : x * 1));
+
+export function hydrateStore(rawState) {
+ const state = convertState(rawState);
+
+ return {
+ type: STORE_HYDRATE,
+ state
+ };
+};
diff --git a/app/assets/javascripts/components/containers/mastodon.jsx b/app/assets/javascripts/components/containers/mastodon.jsx
@@ -7,8 +7,6 @@ import {
refreshTimeline
} from '../actions/timelines';
import { updateNotifications } from '../actions/notifications';
-import { setAccessToken } from '../actions/meta';
-import { setAccountSelf } from '../actions/accounts';
import createBrowserHistory from 'history/lib/createBrowserHistory';
import {
applyRouterMiddleware,
@@ -44,9 +42,12 @@ import pt from 'react-intl/locale-data/pt';
import hu from 'react-intl/locale-data/hu';
import uk from 'react-intl/locale-data/uk';
import getMessagesForLocale from '../locales';
+import { hydrateStore } from '../actions/store';
const store = configureStore();
+store.dispatch(hydrateStore(window.INITIAL_STATE));
+
const browserHistory = useRouterHistory(createBrowserHistory)({
basename: '/web'
});
@@ -56,29 +57,26 @@ addLocaleData([...en, ...de, ...es, ...fr, ...pt, ...hu, ...uk]);
const Mastodon = React.createClass({
propTypes: {
- token: React.PropTypes.string.isRequired,
- timelines: React.PropTypes.object,
- account: React.PropTypes.string,
locale: React.PropTypes.string.isRequired
},
componentWillMount() {
- const { token, account, locale } = this.props;
-
- store.dispatch(setAccessToken(token));
- store.dispatch(setAccountSelf(JSON.parse(account)));
+ const { locale } = this.props;
if (typeof App !== 'undefined') {
this.subscription = App.cable.subscriptions.create('TimelineChannel', {
received (data) {
switch(data.type) {
- case 'update':
- return store.dispatch(updateTimeline(data.timeline, JSON.parse(data.message)));
- case 'delete':
- return store.dispatch(deleteFromTimelines(data.id));
- case 'notification':
- return store.dispatch(updateNotifications(JSON.parse(data.message), getMessagesForLocale(locale), locale));
+ case 'update':
+ store.dispatch(updateTimeline(data.timeline, JSON.parse(data.message)));
+ break;
+ case 'delete':
+ store.dispatch(deleteFromTimelines(data.id));
+ break;
+ case 'notification':
+ store.dispatch(updateNotifications(JSON.parse(data.message), getMessagesForLocale(locale), locale));
+ break;
}
}
diff --git a/app/assets/javascripts/components/features/compose/containers/navigation_container.jsx b/app/assets/javascripts/components/features/compose/containers/navigation_container.jsx
@@ -1,8 +1,10 @@
import { connect } from 'react-redux';
import NavigationBar from '../components/navigation_bar';
-const mapStateToProps = (state, props) => ({
- account: state.getIn(['accounts', state.getIn(['meta', 'me'])])
-});
+const mapStateToProps = (state, props) => {
+ return {
+ account: state.getIn(['accounts', state.getIn(['meta', 'me'])])
+ };
+};
export default connect(mapStateToProps)(NavigationBar);
diff --git a/app/assets/javascripts/components/reducers/accounts.jsx b/app/assets/javascripts/components/reducers/accounts.jsx
@@ -1,5 +1,4 @@
import {
- ACCOUNT_SET_SELF,
ACCOUNT_FETCH_SUCCESS,
FOLLOWERS_FETCH_SUCCESS,
FOLLOWERS_EXPAND_SUCCESS,
@@ -33,6 +32,7 @@ import {
NOTIFICATIONS_REFRESH_SUCCESS,
NOTIFICATIONS_EXPAND_SUCCESS
} from '../actions/notifications';
+import { STORE_HYDRATE } from '../actions/store';
import Immutable from 'immutable';
const normalizeAccount = (state, account) => state.set(account.id, Immutable.fromJS(account));
@@ -67,38 +67,39 @@ const initialState = Immutable.Map();
export default function accounts(state = initialState, action) {
switch(action.type) {
- case ACCOUNT_SET_SELF:
- case ACCOUNT_FETCH_SUCCESS:
- case NOTIFICATIONS_UPDATE:
- return normalizeAccount(state, action.account);
- case FOLLOWERS_FETCH_SUCCESS:
- case FOLLOWERS_EXPAND_SUCCESS:
- case FOLLOWING_FETCH_SUCCESS:
- case FOLLOWING_EXPAND_SUCCESS:
- case REBLOGS_FETCH_SUCCESS:
- case FAVOURITES_FETCH_SUCCESS:
- case COMPOSE_SUGGESTIONS_READY:
- case SEARCH_SUGGESTIONS_READY:
- case FOLLOW_REQUESTS_FETCH_SUCCESS:
- return normalizeAccounts(state, action.accounts);
- case NOTIFICATIONS_REFRESH_SUCCESS:
- case NOTIFICATIONS_EXPAND_SUCCESS:
- return normalizeAccountsFromStatuses(normalizeAccounts(state, action.accounts), action.statuses);
- case TIMELINE_REFRESH_SUCCESS:
- case TIMELINE_EXPAND_SUCCESS:
- case ACCOUNT_TIMELINE_FETCH_SUCCESS:
- case ACCOUNT_TIMELINE_EXPAND_SUCCESS:
- case CONTEXT_FETCH_SUCCESS:
- return normalizeAccountsFromStatuses(state, action.statuses);
- case REBLOG_SUCCESS:
- case FAVOURITE_SUCCESS:
- case UNREBLOG_SUCCESS:
- case UNFAVOURITE_SUCCESS:
- return normalizeAccountFromStatus(state, action.response);
- case TIMELINE_UPDATE:
- case STATUS_FETCH_SUCCESS:
- return normalizeAccountFromStatus(state, action.status);
- default:
- return state;
+ case STORE_HYDRATE:
+ return state.merge(action.state.get('accounts'));
+ case ACCOUNT_FETCH_SUCCESS:
+ case NOTIFICATIONS_UPDATE:
+ return normalizeAccount(state, action.account);
+ case FOLLOWERS_FETCH_SUCCESS:
+ case FOLLOWERS_EXPAND_SUCCESS:
+ case FOLLOWING_FETCH_SUCCESS:
+ case FOLLOWING_EXPAND_SUCCESS:
+ case REBLOGS_FETCH_SUCCESS:
+ case FAVOURITES_FETCH_SUCCESS:
+ case COMPOSE_SUGGESTIONS_READY:
+ case SEARCH_SUGGESTIONS_READY:
+ case FOLLOW_REQUESTS_FETCH_SUCCESS:
+ return normalizeAccounts(state, action.accounts);
+ case NOTIFICATIONS_REFRESH_SUCCESS:
+ case NOTIFICATIONS_EXPAND_SUCCESS:
+ return normalizeAccountsFromStatuses(normalizeAccounts(state, action.accounts), action.statuses);
+ case TIMELINE_REFRESH_SUCCESS:
+ case TIMELINE_EXPAND_SUCCESS:
+ case ACCOUNT_TIMELINE_FETCH_SUCCESS:
+ case ACCOUNT_TIMELINE_EXPAND_SUCCESS:
+ case CONTEXT_FETCH_SUCCESS:
+ return normalizeAccountsFromStatuses(state, action.statuses);
+ case REBLOG_SUCCESS:
+ case FAVOURITE_SUCCESS:
+ case UNREBLOG_SUCCESS:
+ case UNFAVOURITE_SUCCESS:
+ return normalizeAccountFromStatus(state, action.response);
+ case TIMELINE_UPDATE:
+ case STATUS_FETCH_SUCCESS:
+ return normalizeAccountFromStatus(state, action.status);
+ default:
+ return state;
}
};
diff --git a/app/assets/javascripts/components/reducers/compose.jsx b/app/assets/javascripts/components/reducers/compose.jsx
@@ -21,7 +21,7 @@ import {
COMPOSE_LISTABILITY_CHANGE
} from '../actions/compose';
import { TIMELINE_DELETE } from '../actions/timelines';
-import { ACCOUNT_SET_SELF } from '../actions/accounts';
+import { STORE_HYDRATE } from '../actions/store';
import Immutable from 'immutable';
const initialState = Immutable.Map({
@@ -88,6 +88,8 @@ const insertSuggestion = (state, position, token, completion) => {
export default function compose(state = initialState, action) {
switch(action.type) {
+ case STORE_HYDRATE:
+ return state.merge(action.state.get('compose'));
case COMPOSE_MOUNT:
return state.set('mounted', true);
case COMPOSE_UNMOUNT:
@@ -97,7 +99,7 @@ export default function compose(state = initialState, action) {
case COMPOSE_VISIBILITY_CHANGE:
return state.set('private', action.checked);
case COMPOSE_LISTABILITY_CHANGE:
- return state.set('unlisted', action.checked);
+ return state.set('unlisted', action.checked);
case COMPOSE_CHANGE:
return state.set('text', action.text);
case COMPOSE_REPLY:
@@ -143,8 +145,6 @@ export default function compose(state = initialState, action) {
} else {
return state;
}
- case ACCOUNT_SET_SELF:
- return state.set('me', action.account.id).set('private', action.account.locked);
default:
return state;
}
diff --git a/app/assets/javascripts/components/reducers/meta.jsx b/app/assets/javascripts/components/reducers/meta.jsx
@@ -1,16 +1,16 @@
-import { ACCESS_TOKEN_SET } from '../actions/meta';
-import { ACCOUNT_SET_SELF } from '../actions/accounts';
+import { STORE_HYDRATE } from '../actions/store';
import Immutable from 'immutable';
-const initialState = Immutable.Map();
+const initialState = Immutable.Map({
+ access_token: null,
+ me: null
+});
export default function meta(state = initialState, action) {
switch(action.type) {
- case ACCESS_TOKEN_SET:
- return state.set('access_token', action.token);
- case ACCOUNT_SET_SELF:
- return state.set('me', action.account.id);
- default:
- return state;
+ case STORE_HYDRATE:
+ return state.merge(action.state.get('meta'));
+ default:
+ return state;
}
};
diff --git a/app/assets/javascripts/components/store/configureStore.jsx b/app/assets/javascripts/components/store/configureStore.jsx
@@ -1,11 +1,12 @@
import { createStore, applyMiddleware, compose } from 'redux';
-import thunk from 'redux-thunk';
-import appReducer from '../reducers';
-import { loadingBarMiddleware } from 'react-redux-loading-bar';
-import errorsMiddleware from '../middleware/errors';
+import thunk from 'redux-thunk';
+import appReducer from '../reducers';
+import { loadingBarMiddleware } from 'react-redux-loading-bar';
+import errorsMiddleware from '../middleware/errors';
+import Immutable from 'immutable';
-export default function configureStore(initialState) {
- return createStore(appReducer, initialState, compose(applyMiddleware(thunk, loadingBarMiddleware({
+export default function configureStore() {
+ return createStore(appReducer, compose(applyMiddleware(thunk, loadingBarMiddleware({
promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'],
}), errorsMiddleware()), window.devToolsExtension ? window.devToolsExtension() : f => f));
};
diff --git a/app/helpers/home_helper.rb b/app/helpers/home_helper.rb
@@ -3,8 +3,6 @@
module HomeHelper
def default_props
{
- token: @token,
- account: render(file: 'api/v1/accounts/show', locals: { account: current_user.account }, formats: :json),
locale: I18n.locale,
}
end
diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml
@@ -1,4 +1,22 @@
- content_for :header_tags do
+ :javascript
+ window.INITIAL_STATE = {
+ "meta": {
+ "access_token": "#{@token}",
+ "locale": "#{I18n.locale}",
+ "me": #{current_account.id}
+ },
+
+ "compose": {
+ "me": #{current_account.id},
+ "private": #{current_account.locked?}
+ },
+
+ "accounts": {
+ #{current_account.id}: #{render(file: 'api/v1/accounts/show', locals: { account: current_user.account }, formats: :json)}
+ }
+ };
+
= javascript_include_tag 'application'
= react_component 'Mastodon', default_props, class: 'app-holder', prerender: false