From 7259ed58fb3e532f5aa49bd076b0607c66fab82c Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 15 Nov 2021 16:56:33 -0600 Subject: [PATCH] Offline: persist Soapbox config (eg frontend_configurations or soapbox.json) --- app/soapbox/actions/instance.js | 2 +- app/soapbox/actions/soapbox.js | 49 +++++++++++++++++++++++++------ app/soapbox/containers/soapbox.js | 4 +-- app/soapbox/reducers/soapbox.js | 14 ++++++++- app/soapbox/utils/instance.js | 11 +++++++ 5 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 app/soapbox/utils/instance.js diff --git a/app/soapbox/actions/instance.js b/app/soapbox/actions/instance.js index ec77742b2..bc3b95758 100644 --- a/app/soapbox/actions/instance.js +++ b/app/soapbox/actions/instance.js @@ -22,7 +22,7 @@ const getMeUrl = state => { }; // Figure out the appropriate instance to fetch depending on the state -const getHost = state => { +export const getHost = state => { const accountUrl = getMeUrl(state) || getAuthUserUrl(state); try { diff --git a/app/soapbox/actions/soapbox.js b/app/soapbox/actions/soapbox.js index 64fbe1891..4358e9f1d 100644 --- a/app/soapbox/actions/soapbox.js +++ b/app/soapbox/actions/soapbox.js @@ -2,10 +2,16 @@ import api, { staticClient } from '../api'; import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import { getFeatures } from 'soapbox/utils/features'; import { createSelector } from 'reselect'; +import { getHost } from 'soapbox/actions/instance'; +import KVStore from 'soapbox/storage/kv_store'; export const SOAPBOX_CONFIG_REQUEST_SUCCESS = 'SOAPBOX_CONFIG_REQUEST_SUCCESS'; export const SOAPBOX_CONFIG_REQUEST_FAIL = 'SOAPBOX_CONFIG_REQUEST_FAIL'; +export const SOAPBOX_CONFIG_REMEMBER_REQUEST = 'SOAPBOX_CONFIG_REMEMBER_REQUEST'; +export const SOAPBOX_CONFIG_REMEMBER_SUCCESS = 'SOAPBOX_CONFIG_REMEMBER_SUCCESS'; +export const SOAPBOX_CONFIG_REMEMBER_FAIL = 'SOAPBOX_CONFIG_REMEMBER_FAIL'; + const allowedEmoji = ImmutableList([ '👍', '❤', @@ -61,46 +67,71 @@ export const getSoapboxConfig = createSelector([ return makeDefaultConfig(features).merge(soapbox); }); -export function fetchSoapboxConfig() { +export function rememberSoapboxConfig(host) { + return (dispatch, getState) => { + dispatch({ type: SOAPBOX_CONFIG_REMEMBER_REQUEST, host }); + return KVStore.getItemOrError(`soapbox_config:${host}`).then(soapboxConfig => { + dispatch({ type: SOAPBOX_CONFIG_REMEMBER_SUCCESS, host, soapboxConfig }); + return soapboxConfig; + }).catch(error => { + dispatch({ type: SOAPBOX_CONFIG_REMEMBER_FAIL, host, error, skipAlert: true }); + }); + }; +} + +export function fetchSoapboxConfig(host) { return (dispatch, getState) => { api(getState).get('/api/pleroma/frontend_configurations').then(response => { if (response.data.soapbox_fe) { - dispatch(importSoapboxConfig(response.data.soapbox_fe)); + dispatch(importSoapboxConfig(response.data.soapbox_fe, host)); } else { - dispatch(fetchSoapboxJson()); + dispatch(fetchSoapboxJson(host)); } }).catch(error => { - dispatch(fetchSoapboxJson()); + dispatch(fetchSoapboxJson(host)); }); }; } -export function fetchSoapboxJson() { +// Tries to remember the config from browser storage before fetching it +export function loadSoapboxConfig() { + return (dispatch, getState) => { + const host = getHost(getState()); + + return dispatch(rememberSoapboxConfig(host)).finally(() => { + return dispatch(fetchSoapboxConfig(host)); + }); + }; +} + +export function fetchSoapboxJson(host) { return (dispatch, getState) => { staticClient.get('/instance/soapbox.json').then(({ data }) => { if (!isObject(data)) throw 'soapbox.json failed'; - dispatch(importSoapboxConfig(data)); + dispatch(importSoapboxConfig(data, host)); }).catch(error => { - dispatch(soapboxConfigFail(error)); + dispatch(soapboxConfigFail(error, host)); }); }; } -export function importSoapboxConfig(soapboxConfig) { +export function importSoapboxConfig(soapboxConfig, host) { if (!soapboxConfig.brandColor) { soapboxConfig.brandColor = '#0482d8'; } return { type: SOAPBOX_CONFIG_REQUEST_SUCCESS, soapboxConfig, + host, }; } -export function soapboxConfigFail(error) { +export function soapboxConfigFail(error, host) { return { type: SOAPBOX_CONFIG_REQUEST_FAIL, error, skipAlert: true, + host, }; } diff --git a/app/soapbox/containers/soapbox.js b/app/soapbox/containers/soapbox.js index c011d0f0d..a4e48c587 100644 --- a/app/soapbox/containers/soapbox.js +++ b/app/soapbox/containers/soapbox.js @@ -17,7 +17,7 @@ import { preload } from '../actions/preload'; import { IntlProvider } from 'react-intl'; import ErrorBoundary from '../components/error_boundary'; import { loadInstance } from 'soapbox/actions/instance'; -import { fetchSoapboxConfig } from 'soapbox/actions/soapbox'; +import { loadSoapboxConfig } from 'soapbox/actions/soapbox'; import { fetchMe } from 'soapbox/actions/me'; import PublicLayout from 'soapbox/features/public_layout'; import { getSettings } from 'soapbox/actions/settings'; @@ -43,7 +43,7 @@ store.dispatch(fetchMe()) .then(() => { // Postpone for authenticated fetch store.dispatch(loadInstance()); - store.dispatch(fetchSoapboxConfig()); + store.dispatch(loadSoapboxConfig()); }) .catch(() => {}); diff --git a/app/soapbox/reducers/soapbox.js b/app/soapbox/reducers/soapbox.js index 60e339786..c1ab95d3e 100644 --- a/app/soapbox/reducers/soapbox.js +++ b/app/soapbox/reducers/soapbox.js @@ -6,6 +6,7 @@ import { import { PLEROMA_PRELOAD_IMPORT } from 'soapbox/actions/preload'; import { Map as ImmutableMap, fromJS } from 'immutable'; import { ConfigDB } from 'soapbox/utils/config_db'; +import KVStore from 'soapbox/storage/kv_store'; const initialState = ImmutableMap(); @@ -36,12 +37,23 @@ const preloadImport = (state, action) => { } }; +const persistSoapboxConfig = (soapboxConfig, host) => { + if (host) { + KVStore.setItem(`soapbox_config:${host}`, soapboxConfig.toJS()).catch(console.error); + } +}; + +const importSoapboxConfig = (state, soapboxConfig, host) => { + persistSoapboxConfig(soapboxConfig, host); + return soapboxConfig; +}; + export default function soapbox(state = initialState, action) { switch(action.type) { case PLEROMA_PRELOAD_IMPORT: return preloadImport(state, action); case SOAPBOX_CONFIG_REQUEST_SUCCESS: - return fromJS(action.soapboxConfig); + return importSoapboxConfig(state, fromJS(action.soapboxConfig), action.host); case SOAPBOX_CONFIG_REQUEST_FAIL: return fallbackState.mergeDeep(state); case ADMIN_CONFIG_UPDATE_SUCCESS: diff --git a/app/soapbox/utils/instance.js b/app/soapbox/utils/instance.js new file mode 100644 index 000000000..af9d69665 --- /dev/null +++ b/app/soapbox/utils/instance.js @@ -0,0 +1,11 @@ +export const getHost = instance => { + try { + return new URL(instance.get('uri')).host; + } catch { + try { + return new URL(`https://${instance.get('uri')}`).host; + } catch { + return null; + } + } +};