diff --git a/app/soapbox/actions/store.js b/app/soapbox/actions/store.js deleted file mode 100644 index d6921d7f1..000000000 --- a/app/soapbox/actions/store.js +++ /dev/null @@ -1,7 +0,0 @@ -export const INIT_STORE = 'INIT_STORE'; - -export function initStore() { - return { - type: INIT_STORE, - }; -} diff --git a/app/soapbox/containers/soapbox.js b/app/soapbox/containers/soapbox.js index 041fced17..8a3a52f3e 100644 --- a/app/soapbox/containers/soapbox.js +++ b/app/soapbox/containers/soapbox.js @@ -14,7 +14,6 @@ import { ScrollContext } from 'react-router-scroll-4'; import UI from '../features/ui'; // import Introduction from '../features/introduction'; import { fetchCustomEmojis } from '../actions/custom_emojis'; -import { initStore } from '../actions/store'; import { preload } from '../actions/preload'; import { IntlProvider } from 'react-intl'; import ErrorBoundary from '../components/error_boundary'; @@ -31,7 +30,6 @@ const validLocale = locale => Object.keys(messages).includes(locale); export const store = configureStore(); -store.dispatch(initStore()); store.dispatch(preload()); store.dispatch(fetchMe()); store.dispatch(fetchInstance()); diff --git a/app/soapbox/reducers/__tests__/auth-test.js b/app/soapbox/reducers/__tests__/auth-test.js index 6ea1111dc..86ce1cbc0 100644 --- a/app/soapbox/reducers/__tests__/auth-test.js +++ b/app/soapbox/reducers/__tests__/auth-test.js @@ -1,9 +1,9 @@ import reducer from '../auth'; import { Map as ImmutableMap, fromJS } from 'immutable'; -import { INIT_STORE } from 'soapbox/actions/store'; import { AUTH_APP_CREATED, AUTH_LOGGED_IN, + AUTH_LOGGED_OUT, VERIFY_CREDENTIALS_SUCCESS, VERIFY_CREDENTIALS_FAIL, SWITCH_ACCOUNT, @@ -19,22 +19,6 @@ describe('auth reducer', () => { })); }); - describe('INIT_STORE', () => { - it('sets `me` to the next available user if blank', () => { - const state = fromJS({ - me: null, - users: { - '1234': { id: '1234', access_token: 'ABCDEFG' }, - '5678': { id: '5678', access_token: 'HIJKLMN' }, - }, - }); - - const action = { type: INIT_STORE }; - const result = reducer(state, action); - expect(result.get('me')).toEqual('1234'); - }); - }); - describe('AUTH_APP_CREATED', () => { it('should copy in the app', () => { const token = { token_type: 'Bearer', access_token: 'ABCDEFG' }; @@ -78,6 +62,40 @@ describe('auth reducer', () => { }); }); + describe('AUTH_LOGGED_OUT', () => { + it('deletes the user', () => { + const action = { type: AUTH_LOGGED_OUT, accountId: '1234' }; + + const state = fromJS({ + users: { + '1234': { id: '1234', access_token: 'ABCDEFG' }, + '5678': { id: '5678', access_token: 'HIJKLMN' }, + }, + }); + + const expected = fromJS({ + '5678': { id: '5678', access_token: 'HIJKLMN' }, + }); + + const result = reducer(state, action); + expect(result.get('users')).toEqual(expected); + }); + + it('sets `me` to the next available user', () => { + const state = fromJS({ + me: '1234', + users: { + '1234': { id: '1234', access_token: 'ABCDEFG' }, + '5678': { id: '5678', access_token: 'HIJKLMN' }, + }, + }); + + const action = { type: AUTH_LOGGED_OUT, accountId: '1234' }; + const result = reducer(state, action); + expect(result.get('me')).toEqual('5678'); + }); + }); + describe('VERIFY_CREDENTIALS_SUCCESS', () => { it('should import the user', () => { const action = { diff --git a/app/soapbox/reducers/auth.js b/app/soapbox/reducers/auth.js index b9b41c518..51b1be464 100644 --- a/app/soapbox/reducers/auth.js +++ b/app/soapbox/reducers/auth.js @@ -1,4 +1,3 @@ -import { INIT_STORE } from '../actions/store'; import { AUTH_APP_CREATED, AUTH_LOGGED_IN, @@ -17,24 +16,8 @@ const defaultState = ImmutableMap({ me: null, }); +const sessionUser = sessionStorage.getItem('soapbox:auth:me'); const localState = fromJS(JSON.parse(localStorage.getItem('soapbox:auth'))); -const initialState = defaultState.merge(localState); - -const importToken = (state, token) => { - return state.setIn(['tokens', token.access_token], fromJS(token)); -}; - -const importCredentials = (state, token, account) => { - return state.withMutations(state => { - state.setIn(['users', account.id], ImmutableMap({ - id: account.id, - access_token: token, - })); - state.setIn(['tokens', token, 'account'], account.id); - state.update('me', null, me => me || account.id); - upgradeLegacyId(state, account); - }); -}; // If `me` doesn't match an existing user, attempt to shift it. const maybeShiftMe = state => { @@ -42,27 +25,13 @@ const maybeShiftMe = state => { const me = state.get('me'); if (!users.get(me)) { - return state.set('me', users.first(ImmutableMap()).get('id')); + return state.set('me', users.first(ImmutableMap()).get('id', null)); } else { return state; } }; -const deleteToken = (state, token) => { - return state.withMutations(state => { - state.update('tokens', ImmutableMap(), tokens => tokens.delete(token)); - state.update('users', ImmutableMap(), users => users.filterNot(user => user.get('access_token') === token)); - maybeShiftMe(state); - }); -}; - -const deleteUser = (state, accountId) => { - return state.withMutations(state => { - state.update('users', ImmutableMap(), users => users.delete(accountId)); - state.update('tokens', ImmutableMap(), tokens => tokens.filterNot(token => token.get('account') === accountId)); - maybeShiftMe(state); - }); -}; +const setSessionUser = state => state.update('me', null, me => sessionUser || me); // Upgrade the initial state const migrateLegacy = state => { @@ -85,6 +54,26 @@ const migrateLegacy = state => { }); }; +const persistState = state => { + localStorage.setItem('soapbox:auth', JSON.stringify(state.toJS())); + sessionStorage.setItem('soapbox:auth:me', state.get('me')); +}; + +const initialize = state => { + return state.withMutations(state => { + maybeShiftMe(state); + setSessionUser(state); + migrateLegacy(state); + persistState(state); + }); +}; + +const initialState = initialize(defaultState.merge(localState)); + +const importToken = (state, token) => { + return state.setIn(['tokens', token.access_token], fromJS(token)); +}; + // Upgrade the `_legacy` placeholder ID with a real account const upgradeLegacyId = (state, account) => { if (localState) return state; @@ -96,28 +85,36 @@ const upgradeLegacyId = (state, account) => { // By this point it's probably safe, but we'll leave it just in case. }; -const setSessionUser = state => { - const sessionUser = sessionStorage.getItem('soapbox:auth:me'); - if (sessionUser) { - return state.set('me', sessionUser); - } else { - sessionStorage.setItem('soapbox:auth:me', state.get('me')); - return state; - } +const importCredentials = (state, token, account) => { + return state.withMutations(state => { + state.setIn(['users', account.id], ImmutableMap({ + id: account.id, + access_token: token, + })); + state.setIn(['tokens', token, 'account'], account.id); + state.update('me', null, me => me || account.id); + upgradeLegacyId(state, account); + }); }; -const initialize = state => { +const deleteToken = (state, token) => { return state.withMutations(state => { - setSessionUser(state); + state.update('tokens', ImmutableMap(), tokens => tokens.delete(token)); + state.update('users', ImmutableMap(), users => users.filterNot(user => user.get('access_token') === token)); + maybeShiftMe(state); + }); +}; + +const deleteUser = (state, accountId) => { + return state.withMutations(state => { + state.update('users', ImmutableMap(), users => users.delete(accountId)); + state.update('tokens', ImmutableMap(), tokens => tokens.filterNot(token => token.get('account') === accountId)); maybeShiftMe(state); - migrateLegacy(state); }); }; const reducer = (state, action) => { switch(action.type) { - case INIT_STORE: - return initialize(state); case AUTH_APP_CREATED: return state.set('app', fromJS(action.app)); case AUTH_APP_AUTHORIZED: @@ -164,8 +161,7 @@ export default function auth(oldState = initialState, action) { // Persist the state in localStorage if (!state.equals(oldState)) { - localStorage.setItem('soapbox:auth', JSON.stringify(state.toJS())); - sessionStorage.setItem('soapbox:auth:me', state.get('me')); + persistState(state); // Reload the page under some conditions maybeReload(oldState, state, action);