diff --git a/app/soapbox/actions/auth.js b/app/soapbox/actions/auth.js index 68b60205c..128df1027 100644 --- a/app/soapbox/actions/auth.js +++ b/app/soapbox/actions/auth.js @@ -19,6 +19,7 @@ import { obtainOAuthToken, revokeOAuthToken } from 'soapbox/actions/oauth'; import sourceCode from 'soapbox/utils/code'; import { getFeatures } from 'soapbox/utils/features'; import { isStandalone } from 'soapbox/utils/state'; +import KVStore from 'soapbox/storage/kv_store'; export const SWITCH_ACCOUNT = 'SWITCH_ACCOUNT'; @@ -31,6 +32,10 @@ export const VERIFY_CREDENTIALS_REQUEST = 'VERIFY_CREDENTIALS_REQUEST'; export const VERIFY_CREDENTIALS_SUCCESS = 'VERIFY_CREDENTIALS_SUCCESS'; export const VERIFY_CREDENTIALS_FAIL = 'VERIFY_CREDENTIALS_FAIL'; +export const AUTH_ACCOUNT_REMEMBER_REQUEST = 'AUTH_ACCOUNT_REMEMBER_REQUEST'; +export const AUTH_ACCOUNT_REMEMBER_SUCCESS = 'AUTH_ACCOUNT_REMEMBER_SUCCESS'; +export const AUTH_ACCOUNT_REMEMBER_FAIL = 'AUTH_ACCOUNT_REMEMBER_FAIL'; + export const messages = defineMessages({ loggedOut: { id: 'auth.logged_out', defaultMessage: 'Logged out.' }, invalidCredentials: { id: 'auth.invalid_credentials', defaultMessage: 'Wrong username or password' }, @@ -157,6 +162,28 @@ export function verifyCredentials(token, accountUrl) { }; } +export function rememberAuthAccount(accountUrl) { + return (dispatch, getState) => { + dispatch({ type: AUTH_ACCOUNT_REMEMBER_REQUEST, accountUrl }); + return KVStore.getItemOrError(`authAccount:${accountUrl}`).then(account => { + dispatch(importFetchedAccount(account)); + dispatch({ type: AUTH_ACCOUNT_REMEMBER_SUCCESS, account, accountUrl }); + if (account.id === getState().get('me')) dispatch(fetchMeSuccess(account)); + return account; + }).catch(error => { + dispatch({ type: AUTH_ACCOUNT_REMEMBER_FAIL, error, accountUrl, skipAlert: true }); + }); + }; +} + +export function loadCredentials(token, accountUrl) { + return (dispatch, getState) => { + return dispatch(rememberAuthAccount(accountUrl)).finally(() => { + return dispatch(verifyCredentials(token, accountUrl)); + }); + }; +} + export function logIn(intl, username, password) { return (dispatch, getState) => { return dispatch(createAppAndToken()).then(() => { diff --git a/app/soapbox/actions/instance.js b/app/soapbox/actions/instance.js index d92a0604d..ec77742b2 100644 --- a/app/soapbox/actions/instance.js +++ b/app/soapbox/actions/instance.js @@ -69,7 +69,7 @@ export function loadInstance() { return (dispatch, getState) => { const host = getHost(getState()); - return dispatch(rememberInstance(host)).finally(instance => { + return dispatch(rememberInstance(host)).finally(() => { return dispatch(fetchInstance()); }); }; diff --git a/app/soapbox/actions/me.js b/app/soapbox/actions/me.js index 91388b65c..2dcf36ab9 100644 --- a/app/soapbox/actions/me.js +++ b/app/soapbox/actions/me.js @@ -1,6 +1,6 @@ import api from '../api'; import { importFetchedAccount } from './importer'; -import { verifyCredentials } from './auth'; +import { loadCredentials } from './auth'; import { getAuthUserId, getAuthUserUrl } from 'soapbox/utils/auth'; export const ME_FETCH_REQUEST = 'ME_FETCH_REQUEST'; @@ -38,7 +38,7 @@ export function fetchMe() { } dispatch(fetchMeRequest()); - return dispatch(verifyCredentials(token, accountUrl)).catch(error => { + return dispatch(loadCredentials(token, accountUrl)).catch(error => { dispatch(fetchMeFail(error)); }); }; @@ -66,7 +66,6 @@ export function fetchMeRequest() { export function fetchMeSuccess(me) { return (dispatch, getState) => { - dispatch(importFetchedAccount(me)); dispatch({ type: ME_FETCH_SUCCESS, me, diff --git a/app/soapbox/middleware/errors.js b/app/soapbox/middleware/errors.js index 1e36cc55f..edec65a31 100644 --- a/app/soapbox/middleware/errors.js +++ b/app/soapbox/middleware/errors.js @@ -1,15 +1,16 @@ import { showAlertForError } from '../actions/alerts'; -const defaultFailSuffix = 'FAIL'; +const isFailType = type => type.endsWith('_FAIL'); +const isRememberFailType = type => type.endsWith('_REMEMBER_FAIL'); + +const shouldShowError = ({ type, skipAlert }) => { + return !skipAlert && isFailType(type) && !isRememberFailType(type); +}; export default function errorsMiddleware() { return ({ dispatch }) => next => action => { - if (action.type && !action.skipAlert) { - const isFail = new RegExp(`${defaultFailSuffix}$`, 'g'); - - if (action.type.match(isFail)) { - dispatch(showAlertForError(action.error)); - } + if (shouldShowError(action)) { + dispatch(showAlertForError(action.error)); } return next(action); diff --git a/app/soapbox/reducers/accounts_meta.js b/app/soapbox/reducers/accounts_meta.js index d26ce0911..731568313 100644 --- a/app/soapbox/reducers/accounts_meta.js +++ b/app/soapbox/reducers/accounts_meta.js @@ -4,7 +4,7 @@ */ import { ME_FETCH_SUCCESS, ME_PATCH_SUCCESS } from 'soapbox/actions/me'; -import { VERIFY_CREDENTIALS_SUCCESS } from 'soapbox/actions/auth'; +import { VERIFY_CREDENTIALS_SUCCESS, AUTH_ACCOUNT_REMEMBER_SUCCESS } from 'soapbox/actions/auth'; import { Map as ImmutableMap, fromJS } from 'immutable'; const initialState = ImmutableMap(); @@ -24,6 +24,7 @@ export default function accounts_meta(state = initialState, action) { case ME_PATCH_SUCCESS: return importAccount(state, fromJS(action.me)); case VERIFY_CREDENTIALS_SUCCESS: + case AUTH_ACCOUNT_REMEMBER_SUCCESS: return importAccount(state, fromJS(action.account)); default: return state; diff --git a/app/soapbox/reducers/me.js b/app/soapbox/reducers/me.js index d42ace12d..ac6a23402 100644 --- a/app/soapbox/reducers/me.js +++ b/app/soapbox/reducers/me.js @@ -4,7 +4,11 @@ import { ME_FETCH_SKIP, ME_PATCH_SUCCESS, } from '../actions/me'; -import { AUTH_LOGGED_OUT, VERIFY_CREDENTIALS_SUCCESS } from '../actions/auth'; +import { + AUTH_LOGGED_OUT, + AUTH_ACCOUNT_REMEMBER_SUCCESS, + VERIFY_CREDENTIALS_SUCCESS, +} from '../actions/auth'; const initialState = null; @@ -14,11 +18,13 @@ export default function me(state = initialState, action) { case ME_PATCH_SUCCESS: return action.me.id; case VERIFY_CREDENTIALS_SUCCESS: + case AUTH_ACCOUNT_REMEMBER_SUCCESS: return state || action.account.id; - case ME_FETCH_FAIL: case ME_FETCH_SKIP: case AUTH_LOGGED_OUT: return false; + case ME_FETCH_FAIL: + return [401, 403].includes(action.error.response.status) ? false : state; default: return state; }