localforage: create custom KVStore instance, refactor Instance actions

This commit is contained in:
Alex Gleason 2021-10-20 15:17:02 -05:00
parent 3c5384f318
commit c8cec8fdac
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
5 changed files with 63 additions and 56 deletions

View file

@ -2,12 +2,17 @@ import api from '../api';
import { get } from 'lodash'; import { get } from 'lodash';
import { parseVersion } from 'soapbox/utils/features'; import { parseVersion } from 'soapbox/utils/features';
import { getAuthUserUrl } from 'soapbox/utils/auth'; 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_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_SUCCESS = 'NODEINFO_FETCH_SUCCESS';
export const NODEINFO_FETCH_FAIL = 'NODEINFO_FETCH_FAIL'; export const NODEINFO_FETCH_FAIL = 'NODEINFO_FETCH_FAIL';
@ -29,23 +34,32 @@ const getHost = state => {
export function rememberInstance(host) { export function rememberInstance(host) {
return (dispatch, getState) => { return (dispatch, getState) => {
return localforage.getItem(`instance:${host}`).then(instance => { dispatch({ type: INSTANCE_REMEMBER_REQUEST, host });
dispatch({ type: INSTANCE_REMEMBER_SUCCESS, instance }); return KVStore.getItemOrError(`instance:${host}`).then(instance => {
dispatch({ type: INSTANCE_REMEMBER_SUCCESS, host, instance });
return 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() { export function fetchInstance() {
return (dispatch, getState) => { return (dispatch, getState) => {
return api(getState).get('/api/v1/instance').then(response => { dispatch({ type: INSTANCE_FETCH_REQUEST });
dispatch(importInstance(response.data)); return api(getState).get('/api/v1/instance').then(({ data: instance }) => {
const v = parseVersion(get(response.data, 'version')); dispatch({ type: INSTANCE_FETCH_SUCCESS, instance });
if (v.software === 'Pleroma' && !get(response.data, ['pleroma', 'metadata'])) { if (needsNodeinfo(instance)) {
dispatch(fetchNodeinfo()); // Pleroma < 2.1 backwards compatibility dispatch(fetchNodeinfo()); // Pleroma < 2.1 backwards compatibility
} }
}).catch(error => { }).catch(error => {
dispatch(instanceFail(error)); dispatch({ type: INSTANCE_FETCH_FAIL, error, skipAlert: true });
}); });
}; };
} }
@ -55,7 +69,7 @@ export function loadInstance() {
return (dispatch, getState) => { return (dispatch, getState) => {
const host = getHost(getState()); const host = getHost(getState());
return dispatch(rememberInstance(host)).then(instance => { return dispatch(rememberInstance(host)).finally(instance => {
return dispatch(fetchInstance()); return dispatch(fetchInstance());
}); });
}; };
@ -63,40 +77,11 @@ export function loadInstance() {
export function fetchNodeinfo() { export function fetchNodeinfo() {
return (dispatch, getState) => { return (dispatch, getState) => {
api(getState).get('/nodeinfo/2.1.json').then(response => { dispatch({ type: NODEINFO_FETCH_REQUEST });
dispatch(importNodeinfo(response.data)); api(getState).get('/nodeinfo/2.1.json').then(({ data: nodeinfo }) => {
dispatch({ type: NODEINFO_FETCH_SUCCESS, nodeinfo });
}).catch(error => { }).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,
};
}

View file

@ -10,7 +10,6 @@ import ReactDOM from 'react-dom';
import * as OfflinePluginRuntime from '@lcdp/offline-plugin/runtime'; import * as OfflinePluginRuntime from '@lcdp/offline-plugin/runtime';
import * as perf from './performance'; import * as perf from './performance';
import * as monitoring from './monitoring'; import * as monitoring from './monitoring';
import localforage from 'localforage';
import ready from './ready'; import ready from './ready';
import { NODE_ENV } from 'soapbox/build_config'; import { NODE_ENV } from 'soapbox/build_config';
@ -20,15 +19,6 @@ function main() {
// Sentry // Sentry
monitoring.start(); 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(() => { ready(() => {
const mountNode = document.getElementById('soapbox'); const mountNode = document.getElementById('soapbox');

View file

@ -13,6 +13,7 @@ import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
import { validId, isURL } from 'soapbox/utils/auth'; import { validId, isURL } from 'soapbox/utils/auth';
import { trim } from 'lodash'; import { trim } from 'lodash';
import { FE_SUBDIRECTORY } from 'soapbox/build_config'; import { FE_SUBDIRECTORY } from 'soapbox/build_config';
import KVStore from 'soapbox/storage/kv_store';
const defaultState = ImmutableMap({ const defaultState = ImmutableMap({
app: 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) => { const reducer = (state, action) => {
switch(action.type) { switch(action.type) {
case AUTH_APP_CREATED: case AUTH_APP_CREATED:
@ -265,6 +272,7 @@ const reducer = (state, action) => {
case AUTH_LOGGED_OUT: case AUTH_LOGGED_OUT:
return deleteUser(state, action.account); return deleteUser(state, action.account);
case VERIFY_CREDENTIALS_SUCCESS: case VERIFY_CREDENTIALS_SUCCESS:
persistAuthAccount(action.account);
return importCredentials(state, action.token, action.account); return importCredentials(state, action.token, action.account);
case VERIFY_CREDENTIALS_FAIL: case VERIFY_CREDENTIALS_FAIL:
return [401, 403].includes(action.error.response.status) ? deleteToken(state, action.token) : state; return [401, 403].includes(action.error.response.status) ? deleteToken(state, action.token) : state;

View file

@ -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 { ADMIN_CONFIG_UPDATE_REQUEST, ADMIN_CONFIG_UPDATE_SUCCESS } from 'soapbox/actions/admin';
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
import { ConfigDB } from 'soapbox/utils/config_db'; import { ConfigDB } from 'soapbox/utils/config_db';
import localforage from 'localforage'; import KVStore from 'soapbox/storage/kv_store';
const nodeinfoToInstance = nodeinfo => { const nodeinfoToInstance = nodeinfo => {
// Match Pleroma's develop branch // Match Pleroma's develop branch
@ -106,7 +106,7 @@ const persistInstance = instance => {
const host = getHost(instance); const host = getHost(instance);
if (host) { if (host) {
localforage.setItem(`instance:${host}`, instance); KVStore.setItem(`instance:${host}`, instance).catch(console.error);
} }
}; };

View file

@ -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;