diff --git a/app/soapbox/actions/instance.js b/app/soapbox/actions/instance.js index d4e084f51..d92a0604d 100644 --- a/app/soapbox/actions/instance.js +++ b/app/soapbox/actions/instance.js @@ -2,12 +2,17 @@ import api from '../api'; import { get } from 'lodash'; import { parseVersion } from 'soapbox/utils/features'; import { getAuthUserUrl } from 'soapbox/utils/auth'; -import localforage from 'localforage'; +import KVStore from 'soapbox/storage/kv_store'; -export const INSTANCE_FETCH_SUCCESS = 'INSTANCE_FETCH_SUCCESS'; +export const INSTANCE_FETCH_REQUEST = 'INSTANCE_FETCH_REQUEST'; +export const INSTANCE_FETCH_SUCCESS = 'INSTANCE_FETCH_SUCCESS'; +export const INSTANCE_FETCH_FAIL = 'INSTANCE_FETCH_FAIL'; + +export const INSTANCE_REMEMBER_REQUEST = 'INSTANCE_REMEMBER_REQUEST'; export const INSTANCE_REMEMBER_SUCCESS = 'INSTANCE_REMEMBER_SUCCESS'; -export const INSTANCE_FETCH_FAIL = 'INSTANCE_FETCH_FAIL'; +export const INSTANCE_REMEMBER_FAIL = 'INSTANCE_REMEMBER_FAIL'; +export const NODEINFO_FETCH_REQUEST = 'NODEINFO_FETCH_REQUEST'; export const NODEINFO_FETCH_SUCCESS = 'NODEINFO_FETCH_SUCCESS'; export const NODEINFO_FETCH_FAIL = 'NODEINFO_FETCH_FAIL'; @@ -29,23 +34,32 @@ const getHost = state => { export function rememberInstance(host) { return (dispatch, getState) => { - return localforage.getItem(`instance:${host}`).then(instance => { - dispatch({ type: INSTANCE_REMEMBER_SUCCESS, instance }); + dispatch({ type: INSTANCE_REMEMBER_REQUEST, host }); + return KVStore.getItemOrError(`instance:${host}`).then(instance => { + dispatch({ type: INSTANCE_REMEMBER_SUCCESS, host, instance }); return instance; + }).catch(error => { + dispatch({ type: INSTANCE_REMEMBER_FAIL, host, error, skipAlert: true }); }); }; } +// We may need to fetch nodeinfo on Pleroma < 2.1 +const needsNodeinfo = instance => { + const v = parseVersion(get(instance, 'version')); + return v.software === 'Pleroma' && !get(instance, ['pleroma', 'metadata']); +}; + export function fetchInstance() { return (dispatch, getState) => { - return api(getState).get('/api/v1/instance').then(response => { - dispatch(importInstance(response.data)); - const v = parseVersion(get(response.data, 'version')); - if (v.software === 'Pleroma' && !get(response.data, ['pleroma', 'metadata'])) { + dispatch({ type: INSTANCE_FETCH_REQUEST }); + return api(getState).get('/api/v1/instance').then(({ data: instance }) => { + dispatch({ type: INSTANCE_FETCH_SUCCESS, instance }); + if (needsNodeinfo(instance)) { dispatch(fetchNodeinfo()); // Pleroma < 2.1 backwards compatibility } }).catch(error => { - dispatch(instanceFail(error)); + dispatch({ type: INSTANCE_FETCH_FAIL, error, skipAlert: true }); }); }; } @@ -55,7 +69,7 @@ export function loadInstance() { return (dispatch, getState) => { const host = getHost(getState()); - return dispatch(rememberInstance(host)).then(instance => { + return dispatch(rememberInstance(host)).finally(instance => { return dispatch(fetchInstance()); }); }; @@ -63,40 +77,11 @@ export function loadInstance() { export function fetchNodeinfo() { return (dispatch, getState) => { - api(getState).get('/nodeinfo/2.1.json').then(response => { - dispatch(importNodeinfo(response.data)); + dispatch({ type: NODEINFO_FETCH_REQUEST }); + api(getState).get('/nodeinfo/2.1.json').then(({ data: nodeinfo }) => { + dispatch({ type: NODEINFO_FETCH_SUCCESS, nodeinfo }); }).catch(error => { - dispatch(nodeinfoFail(error)); + dispatch({ type: NODEINFO_FETCH_FAIL, error, skipAlert: true }); }); }; } - -export function importInstance(instance) { - return { - type: INSTANCE_FETCH_SUCCESS, - instance, - }; -} - -export function instanceFail(error) { - return { - type: INSTANCE_FETCH_FAIL, - error, - skipAlert: true, - }; -} - -export function importNodeinfo(nodeinfo) { - return { - type: NODEINFO_FETCH_SUCCESS, - nodeinfo, - }; -} - -export function nodeinfoFail(error) { - return { - type: NODEINFO_FETCH_FAIL, - error, - skipAlert: true, - }; -} diff --git a/app/soapbox/main.js b/app/soapbox/main.js index d517883af..91822efef 100644 --- a/app/soapbox/main.js +++ b/app/soapbox/main.js @@ -10,7 +10,6 @@ import ReactDOM from 'react-dom'; import * as OfflinePluginRuntime from '@lcdp/offline-plugin/runtime'; import * as perf from './performance'; import * as monitoring from './monitoring'; -import localforage from 'localforage'; import ready from './ready'; import { NODE_ENV } from 'soapbox/build_config'; @@ -20,15 +19,6 @@ function main() { // Sentry monitoring.start(); - // localForage - // https://localforage.github.io/localForage/#settings-api-config - localforage.config({ - name: 'soapbox', - description: 'Soapbox offline data store', - driver: localforage.INDEXEDDB, - storeName: 'keyvaluepairs', - }); - ready(() => { const mountNode = document.getElementById('soapbox'); diff --git a/app/soapbox/reducers/auth.js b/app/soapbox/reducers/auth.js index c5a7cf5bc..30f02d89a 100644 --- a/app/soapbox/reducers/auth.js +++ b/app/soapbox/reducers/auth.js @@ -13,6 +13,7 @@ import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; import { validId, isURL } from 'soapbox/utils/auth'; import { trim } from 'lodash'; import { FE_SUBDIRECTORY } from 'soapbox/build_config'; +import KVStore from 'soapbox/storage/kv_store'; const defaultState = ImmutableMap({ app: ImmutableMap(), @@ -254,6 +255,12 @@ const importMastodonPreload = (state, data) => { }); }; +const persistAuthAccount = account => { + if (account && account.url) { + KVStore.setItem(`authAccount:${account.url}`, account).catch(console.error); + } +}; + const reducer = (state, action) => { switch(action.type) { case AUTH_APP_CREATED: @@ -265,6 +272,7 @@ const reducer = (state, action) => { case AUTH_LOGGED_OUT: return deleteUser(state, action.account); case VERIFY_CREDENTIALS_SUCCESS: + persistAuthAccount(action.account); return importCredentials(state, action.token, action.account); case VERIFY_CREDENTIALS_FAIL: return [401, 403].includes(action.error.response.status) ? deleteToken(state, action.token) : state; diff --git a/app/soapbox/reducers/instance.js b/app/soapbox/reducers/instance.js index 6a3ae5e78..5866791f2 100644 --- a/app/soapbox/reducers/instance.js +++ b/app/soapbox/reducers/instance.js @@ -8,7 +8,7 @@ import { PLEROMA_PRELOAD_IMPORT } from 'soapbox/actions/preload'; import { ADMIN_CONFIG_UPDATE_REQUEST, ADMIN_CONFIG_UPDATE_SUCCESS } from 'soapbox/actions/admin'; import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; import { ConfigDB } from 'soapbox/utils/config_db'; -import localforage from 'localforage'; +import KVStore from 'soapbox/storage/kv_store'; const nodeinfoToInstance = nodeinfo => { // Match Pleroma's develop branch @@ -106,7 +106,7 @@ const persistInstance = instance => { const host = getHost(instance); if (host) { - localforage.setItem(`instance:${host}`, instance); + KVStore.setItem(`instance:${host}`, instance).catch(console.error); } }; diff --git a/app/soapbox/storage/kv_store.js b/app/soapbox/storage/kv_store.js new file mode 100644 index 000000000..3c957fdc1 --- /dev/null +++ b/app/soapbox/storage/kv_store.js @@ -0,0 +1,24 @@ +import localforage from 'localforage'; + +// localForage +// https://localforage.github.io/localForage/#settings-api-config +export const KVStore = localforage.createInstance({ + name: 'soapbox', + description: 'Soapbox offline data store', + driver: localforage.INDEXEDDB, + storeName: 'keyvaluepairs', +}); + +// localForage returns 'null' when a key isn't found. +// In the Redux action flow, we want it to fail harder. +KVStore.getItemOrError = key => { + return KVStore.getItem(key).then(value => { + if (value === null) { + throw new Error(`KVStore: null value for key ${key}`); + } else { + return value; + } + }); +}; + +export default KVStore;