Improve initialState loading
This commit is contained in:
		@@ -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));
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
} 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;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -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));
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user