Improve initialState loading
This commit is contained in:
		
					parent
					
						
							
								2e71bb031b
							
						
					
				
			
			
				commit
				
					
						23ebf60b95
					
				
			
		
					 11 changed files with 108 additions and 90 deletions
				
			
		|  | @ -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,17 +57,11 @@ 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', { | ||||||
|  | @ -74,11 +69,14 @@ const Mastodon = React.createClass({ | ||||||
|         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))); | ||||||
|  |             break; | ||||||
|           case 'delete': |           case 'delete': | ||||||
|               return store.dispatch(deleteFromTimelines(data.id)); |             store.dispatch(deleteFromTimelines(data.id)); | ||||||
|  |             break; | ||||||
|           case 'notification': |           case 'notification': | ||||||
|               return store.dispatch(updateNotifications(JSON.parse(data.message), getMessagesForLocale(locale), locale)); |             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) => { | ||||||
|  |   return { | ||||||
|     account: state.getIn(['accounts', state.getIn(['meta', 'me'])]) |     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,7 +67,8 @@ 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: | ||||||
|  |     return state.merge(action.state.get('accounts')); | ||||||
|   case ACCOUNT_FETCH_SUCCESS: |   case ACCOUNT_FETCH_SUCCESS: | ||||||
|   case NOTIFICATIONS_UPDATE: |   case NOTIFICATIONS_UPDATE: | ||||||
|     return normalizeAccount(state, action.account); |     return normalizeAccount(state, action.account); | ||||||
|  |  | ||||||
|  | @ -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,15 +1,15 @@ | ||||||
| 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: |  | ||||||
|       return state.set('me', action.account.id); |  | ||||||
|   default: |   default: | ||||||
|     return state; |     return state; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -3,9 +3,10 @@ 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue