forked from fedi/mastodon
Improve initialState loading
This commit is contained in:
parent
2e71bb031b
commit
23ebf60b95
|
@ -1,8 +1,6 @@
|
||||||
import api, { getLinks } from '../api'
|
import api, { getLinks } from '../api'
|
||||||
import Immutable from 'immutable';
|
import Immutable from 'immutable';
|
||||||
|
|
||||||
export const ACCOUNT_SET_SELF = 'ACCOUNT_SET_SELF';
|
|
||||||
|
|
||||||
export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST';
|
export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST';
|
||||||
export const ACCOUNT_FETCH_SUCCESS = 'ACCOUNT_FETCH_SUCCESS';
|
export const ACCOUNT_FETCH_SUCCESS = 'ACCOUNT_FETCH_SUCCESS';
|
||||||
export const ACCOUNT_FETCH_FAIL = 'ACCOUNT_FETCH_FAIL';
|
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_SUCCESS = 'FOLLOW_REQUEST_REJECT_SUCCESS';
|
||||||
export const FOLLOW_REQUEST_REJECT_FAIL = 'FOLLOW_REQUEST_REJECT_FAIL';
|
export const FOLLOW_REQUEST_REJECT_FAIL = 'FOLLOW_REQUEST_REJECT_FAIL';
|
||||||
|
|
||||||
export function setAccountSelf(account) {
|
|
||||||
return {
|
|
||||||
type: ACCOUNT_SET_SELF,
|
|
||||||
account
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export function fetchAccount(id) {
|
export function fetchAccount(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
dispatch(fetchAccountRequest(id));
|
dispatch(fetchAccountRequest(id));
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
export const ACCESS_TOKEN_SET = 'ACCESS_TOKEN_SET';
|
|
||||||
|
|
||||||
export function setAccessToken(token) {
|
|
||||||
return {
|
|
||||||
type: ACCESS_TOKEN_SET,
|
|
||||||
token: token
|
|
||||||
};
|
|
||||||
};
|
|
17
app/assets/javascripts/components/actions/store.jsx
Normal file
17
app/assets/javascripts/components/actions/store.jsx
Normal file
|
@ -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
|
||||||
|
};
|
||||||
|
};
|
|
@ -7,8 +7,6 @@ import {
|
||||||
refreshTimeline
|
refreshTimeline
|
||||||
} from '../actions/timelines';
|
} from '../actions/timelines';
|
||||||
import { updateNotifications } from '../actions/notifications';
|
import { updateNotifications } from '../actions/notifications';
|
||||||
import { setAccessToken } from '../actions/meta';
|
|
||||||
import { setAccountSelf } from '../actions/accounts';
|
|
||||||
import createBrowserHistory from 'history/lib/createBrowserHistory';
|
import createBrowserHistory from 'history/lib/createBrowserHistory';
|
||||||
import {
|
import {
|
||||||
applyRouterMiddleware,
|
applyRouterMiddleware,
|
||||||
|
@ -44,9 +42,12 @@ import pt from 'react-intl/locale-data/pt';
|
||||||
import hu from 'react-intl/locale-data/hu';
|
import hu from 'react-intl/locale-data/hu';
|
||||||
import uk from 'react-intl/locale-data/uk';
|
import uk from 'react-intl/locale-data/uk';
|
||||||
import getMessagesForLocale from '../locales';
|
import getMessagesForLocale from '../locales';
|
||||||
|
import { hydrateStore } from '../actions/store';
|
||||||
|
|
||||||
const store = configureStore();
|
const store = configureStore();
|
||||||
|
|
||||||
|
store.dispatch(hydrateStore(window.INITIAL_STATE));
|
||||||
|
|
||||||
const browserHistory = useRouterHistory(createBrowserHistory)({
|
const browserHistory = useRouterHistory(createBrowserHistory)({
|
||||||
basename: '/web'
|
basename: '/web'
|
||||||
});
|
});
|
||||||
|
@ -56,29 +57,26 @@ addLocaleData([...en, ...de, ...es, ...fr, ...pt, ...hu, ...uk]);
|
||||||
const Mastodon = React.createClass({
|
const Mastodon = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
token: React.PropTypes.string.isRequired,
|
|
||||||
timelines: React.PropTypes.object,
|
|
||||||
account: React.PropTypes.string,
|
|
||||||
locale: React.PropTypes.string.isRequired
|
locale: React.PropTypes.string.isRequired
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
const { token, account, locale } = this.props;
|
const { locale } = this.props;
|
||||||
|
|
||||||
store.dispatch(setAccessToken(token));
|
|
||||||
store.dispatch(setAccountSelf(JSON.parse(account)));
|
|
||||||
|
|
||||||
if (typeof App !== 'undefined') {
|
if (typeof App !== 'undefined') {
|
||||||
this.subscription = App.cable.subscriptions.create('TimelineChannel', {
|
this.subscription = App.cable.subscriptions.create('TimelineChannel', {
|
||||||
|
|
||||||
received (data) {
|
received (data) {
|
||||||
switch(data.type) {
|
switch(data.type) {
|
||||||
case 'update':
|
case 'update':
|
||||||
return store.dispatch(updateTimeline(data.timeline, JSON.parse(data.message)));
|
store.dispatch(updateTimeline(data.timeline, JSON.parse(data.message)));
|
||||||
case 'delete':
|
break;
|
||||||
return store.dispatch(deleteFromTimelines(data.id));
|
case 'delete':
|
||||||
case 'notification':
|
store.dispatch(deleteFromTimelines(data.id));
|
||||||
return store.dispatch(updateNotifications(JSON.parse(data.message), getMessagesForLocale(locale), locale));
|
break;
|
||||||
|
case 'notification':
|
||||||
|
store.dispatch(updateNotifications(JSON.parse(data.message), getMessagesForLocale(locale), locale));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import NavigationBar from '../components/navigation_bar';
|
import NavigationBar from '../components/navigation_bar';
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => ({
|
const mapStateToProps = (state, props) => {
|
||||||
account: state.getIn(['accounts', state.getIn(['meta', 'me'])])
|
return {
|
||||||
});
|
account: state.getIn(['accounts', state.getIn(['meta', 'me'])])
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default connect(mapStateToProps)(NavigationBar);
|
export default connect(mapStateToProps)(NavigationBar);
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
ACCOUNT_SET_SELF,
|
|
||||||
ACCOUNT_FETCH_SUCCESS,
|
ACCOUNT_FETCH_SUCCESS,
|
||||||
FOLLOWERS_FETCH_SUCCESS,
|
FOLLOWERS_FETCH_SUCCESS,
|
||||||
FOLLOWERS_EXPAND_SUCCESS,
|
FOLLOWERS_EXPAND_SUCCESS,
|
||||||
|
@ -33,6 +32,7 @@ import {
|
||||||
NOTIFICATIONS_REFRESH_SUCCESS,
|
NOTIFICATIONS_REFRESH_SUCCESS,
|
||||||
NOTIFICATIONS_EXPAND_SUCCESS
|
NOTIFICATIONS_EXPAND_SUCCESS
|
||||||
} from '../actions/notifications';
|
} from '../actions/notifications';
|
||||||
|
import { STORE_HYDRATE } from '../actions/store';
|
||||||
import Immutable from 'immutable';
|
import Immutable from 'immutable';
|
||||||
|
|
||||||
const normalizeAccount = (state, account) => state.set(account.id, Immutable.fromJS(account));
|
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) {
|
export default function accounts(state = initialState, action) {
|
||||||
switch(action.type) {
|
switch(action.type) {
|
||||||
case ACCOUNT_SET_SELF:
|
case STORE_HYDRATE:
|
||||||
case ACCOUNT_FETCH_SUCCESS:
|
return state.merge(action.state.get('accounts'));
|
||||||
case NOTIFICATIONS_UPDATE:
|
case ACCOUNT_FETCH_SUCCESS:
|
||||||
return normalizeAccount(state, action.account);
|
case NOTIFICATIONS_UPDATE:
|
||||||
case FOLLOWERS_FETCH_SUCCESS:
|
return normalizeAccount(state, action.account);
|
||||||
case FOLLOWERS_EXPAND_SUCCESS:
|
case FOLLOWERS_FETCH_SUCCESS:
|
||||||
case FOLLOWING_FETCH_SUCCESS:
|
case FOLLOWERS_EXPAND_SUCCESS:
|
||||||
case FOLLOWING_EXPAND_SUCCESS:
|
case FOLLOWING_FETCH_SUCCESS:
|
||||||
case REBLOGS_FETCH_SUCCESS:
|
case FOLLOWING_EXPAND_SUCCESS:
|
||||||
case FAVOURITES_FETCH_SUCCESS:
|
case REBLOGS_FETCH_SUCCESS:
|
||||||
case COMPOSE_SUGGESTIONS_READY:
|
case FAVOURITES_FETCH_SUCCESS:
|
||||||
case SEARCH_SUGGESTIONS_READY:
|
case COMPOSE_SUGGESTIONS_READY:
|
||||||
case FOLLOW_REQUESTS_FETCH_SUCCESS:
|
case SEARCH_SUGGESTIONS_READY:
|
||||||
return normalizeAccounts(state, action.accounts);
|
case FOLLOW_REQUESTS_FETCH_SUCCESS:
|
||||||
case NOTIFICATIONS_REFRESH_SUCCESS:
|
return normalizeAccounts(state, action.accounts);
|
||||||
case NOTIFICATIONS_EXPAND_SUCCESS:
|
case NOTIFICATIONS_REFRESH_SUCCESS:
|
||||||
return normalizeAccountsFromStatuses(normalizeAccounts(state, action.accounts), action.statuses);
|
case NOTIFICATIONS_EXPAND_SUCCESS:
|
||||||
case TIMELINE_REFRESH_SUCCESS:
|
return normalizeAccountsFromStatuses(normalizeAccounts(state, action.accounts), action.statuses);
|
||||||
case TIMELINE_EXPAND_SUCCESS:
|
case TIMELINE_REFRESH_SUCCESS:
|
||||||
case ACCOUNT_TIMELINE_FETCH_SUCCESS:
|
case TIMELINE_EXPAND_SUCCESS:
|
||||||
case ACCOUNT_TIMELINE_EXPAND_SUCCESS:
|
case ACCOUNT_TIMELINE_FETCH_SUCCESS:
|
||||||
case CONTEXT_FETCH_SUCCESS:
|
case ACCOUNT_TIMELINE_EXPAND_SUCCESS:
|
||||||
return normalizeAccountsFromStatuses(state, action.statuses);
|
case CONTEXT_FETCH_SUCCESS:
|
||||||
case REBLOG_SUCCESS:
|
return normalizeAccountsFromStatuses(state, action.statuses);
|
||||||
case FAVOURITE_SUCCESS:
|
case REBLOG_SUCCESS:
|
||||||
case UNREBLOG_SUCCESS:
|
case FAVOURITE_SUCCESS:
|
||||||
case UNFAVOURITE_SUCCESS:
|
case UNREBLOG_SUCCESS:
|
||||||
return normalizeAccountFromStatus(state, action.response);
|
case UNFAVOURITE_SUCCESS:
|
||||||
case TIMELINE_UPDATE:
|
return normalizeAccountFromStatus(state, action.response);
|
||||||
case STATUS_FETCH_SUCCESS:
|
case TIMELINE_UPDATE:
|
||||||
return normalizeAccountFromStatus(state, action.status);
|
case STATUS_FETCH_SUCCESS:
|
||||||
default:
|
return normalizeAccountFromStatus(state, action.status);
|
||||||
return state;
|
default:
|
||||||
|
return state;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,7 +21,7 @@ import {
|
||||||
COMPOSE_LISTABILITY_CHANGE
|
COMPOSE_LISTABILITY_CHANGE
|
||||||
} from '../actions/compose';
|
} from '../actions/compose';
|
||||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||||
import { ACCOUNT_SET_SELF } from '../actions/accounts';
|
import { STORE_HYDRATE } from '../actions/store';
|
||||||
import Immutable from 'immutable';
|
import Immutable from 'immutable';
|
||||||
|
|
||||||
const initialState = Immutable.Map({
|
const initialState = Immutable.Map({
|
||||||
|
@ -88,6 +88,8 @@ const insertSuggestion = (state, position, token, completion) => {
|
||||||
|
|
||||||
export default function compose(state = initialState, action) {
|
export default function compose(state = initialState, action) {
|
||||||
switch(action.type) {
|
switch(action.type) {
|
||||||
|
case STORE_HYDRATE:
|
||||||
|
return state.merge(action.state.get('compose'));
|
||||||
case COMPOSE_MOUNT:
|
case COMPOSE_MOUNT:
|
||||||
return state.set('mounted', true);
|
return state.set('mounted', true);
|
||||||
case COMPOSE_UNMOUNT:
|
case COMPOSE_UNMOUNT:
|
||||||
|
@ -143,8 +145,6 @@ export default function compose(state = initialState, action) {
|
||||||
} else {
|
} else {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
case ACCOUNT_SET_SELF:
|
|
||||||
return state.set('me', action.account.id).set('private', action.account.locked);
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import { ACCESS_TOKEN_SET } from '../actions/meta';
|
import { STORE_HYDRATE } from '../actions/store';
|
||||||
import { ACCOUNT_SET_SELF } from '../actions/accounts';
|
|
||||||
import Immutable from 'immutable';
|
import Immutable from 'immutable';
|
||||||
|
|
||||||
const initialState = Immutable.Map();
|
const initialState = Immutable.Map({
|
||||||
|
access_token: null,
|
||||||
|
me: null
|
||||||
|
});
|
||||||
|
|
||||||
export default function meta(state = initialState, action) {
|
export default function meta(state = initialState, action) {
|
||||||
switch(action.type) {
|
switch(action.type) {
|
||||||
case ACCESS_TOKEN_SET:
|
case STORE_HYDRATE:
|
||||||
return state.set('access_token', action.token);
|
return state.merge(action.state.get('meta'));
|
||||||
case ACCOUNT_SET_SELF:
|
default:
|
||||||
return state.set('me', action.account.id);
|
return state;
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { createStore, applyMiddleware, compose } from 'redux';
|
import { createStore, applyMiddleware, compose } from 'redux';
|
||||||
import thunk from 'redux-thunk';
|
import thunk from 'redux-thunk';
|
||||||
import appReducer from '../reducers';
|
import appReducer from '../reducers';
|
||||||
import { loadingBarMiddleware } from 'react-redux-loading-bar';
|
import { loadingBarMiddleware } from 'react-redux-loading-bar';
|
||||||
import errorsMiddleware from '../middleware/errors';
|
import errorsMiddleware from '../middleware/errors';
|
||||||
|
import Immutable from 'immutable';
|
||||||
|
|
||||||
export default function configureStore(initialState) {
|
export default function configureStore() {
|
||||||
return createStore(appReducer, initialState, compose(applyMiddleware(thunk, loadingBarMiddleware({
|
return createStore(appReducer, compose(applyMiddleware(thunk, loadingBarMiddleware({
|
||||||
promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'],
|
promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'],
|
||||||
}), errorsMiddleware()), window.devToolsExtension ? window.devToolsExtension() : f => f));
|
}), errorsMiddleware()), window.devToolsExtension ? window.devToolsExtension() : f => f));
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
module HomeHelper
|
module HomeHelper
|
||||||
def default_props
|
def default_props
|
||||||
{
|
{
|
||||||
token: @token,
|
|
||||||
account: render(file: 'api/v1/accounts/show', locals: { account: current_user.account }, formats: :json),
|
|
||||||
locale: I18n.locale,
|
locale: I18n.locale,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,22 @@
|
||||||
- content_for :header_tags do
|
- 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'
|
= javascript_include_tag 'application'
|
||||||
|
|
||||||
= react_component 'Mastodon', default_props, class: 'app-holder', prerender: false
|
= react_component 'Mastodon', default_props, class: 'app-holder', prerender: false
|
||||||
|
|
Loading…
Reference in a new issue