diff options
| author | Haishan <[email protected]> | 2020-09-13 16:34:18 +0800 |
|---|---|---|
| committer | Haishan <[email protected]> | 2020-09-13 17:16:14 +0800 |
| commit | 15bc0f69a8367a57fa1bf263e615285349ad4ab9 (patch) | |
| tree | fbdd2a46303703822f7e7bc3462a70b4855fe4a1 /src/store | |
| parent | a8f0d3d4b4928caebf61c75fa9191a170b471035 (diff) | |
feat: multi backends management
Diffstat (limited to 'src/store')
| -rw-r--r-- | src/store/app.js | 108 | ||||
| -rw-r--r-- | src/store/configs.js | 33 | ||||
| -rw-r--r-- | src/store/index.js | 4 |
3 files changed, 99 insertions, 46 deletions
diff --git a/src/store/app.js b/src/store/app.js index dc0e269..84ef96b 100644 --- a/src/store/app.js +++ b/src/store/app.js @@ -1,4 +1,4 @@ -import { clearState, loadState, saveState } from '../misc/storage'; +import { loadState, saveState } from '../misc/storage'; import { debounce, trimTrailingSlash } from '../misc/utils'; import { fetchConfigs } from './configs'; import { closeModal } from './modals'; @@ -7,6 +7,9 @@ export const getClashAPIConfig = (s) => { const idx = s.app.selectedClashAPIConfigIndex; return s.app.clashAPIConfigs[idx]; }; +export const getSelectedClashAPIConfigIndex = (s) => + s.app.selectedClashAPIConfigIndex; +export const getClashAPIConfigs = (s) => s.app.clashAPIConfigs; export const getTheme = (s) => s.app.theme; export const getSelectedChartStyleIndex = (s) => s.app.selectedChartStyleIndex; export const getLatencyTestUrl = (s) => s.app.latencyTestUrl; @@ -17,6 +20,66 @@ export const getAutoCloseOldConns = (s) => s.app.autoCloseOldConns; const saveStateDebounced = debounce(saveState, 600); +function findClashAPIConfigIndex(getState, { baseURL, secret }) { + const arr = getClashAPIConfigs(getState()); + for (let i = 0; i < arr.length; i++) { + const x = arr[i]; + if (x.baseURL === baseURL && x.secret === secret) return i; + } +} + +export function addClashAPIConfig({ baseURL, secret }) { + return async (dispatch, getState) => { + const idx = findClashAPIConfigIndex(getState, { baseURL, secret }); + // already exists + if (idx) return; + + const clashAPIConfig = { baseURL, secret, addedAt: Date.now() }; + dispatch('addClashAPIConfig', (s) => { + s.app.clashAPIConfigs.push(clashAPIConfig); + }); + // side effect + saveState(getState().app); + }; +} + +export function removeClashAPIConfig({ baseURL, secret }) { + return async (dispatch, getState) => { + const idx = findClashAPIConfigIndex(getState, { baseURL, secret }); + dispatch('removeClashAPIConfig', (s) => { + s.app.clashAPIConfigs = [ + ...s.app.clashAPIConfigs.slice(0, idx), + ...s.app.clashAPIConfigs.slice(idx + 1), + ]; + }); + // side effect + saveState(getState().app); + }; +} + +export function selectClashAPIConfig({ baseURL, secret }) { + return async (dispatch, getState) => { + const idx = findClashAPIConfigIndex(getState, { baseURL, secret }); + const curr = getSelectedClashAPIConfigIndex(getState()); + if (curr !== idx) { + dispatch('selectClashAPIConfig', (s) => { + s.app.selectedClashAPIConfigIndex = idx; + }); + } + // side effect + saveState(getState().app); + + // manual clean up is too complex + // we just reload the app + try { + window.location.reload(); + } catch (err) { + // ignore + } + }; +} + +// unused export function updateClashAPIConfig({ baseURL, secret }) { return async (dispatch, getState) => { const clashAPIConfig = { baseURL, secret }; @@ -55,15 +118,6 @@ export function switchTheme() { }; } -export function clearStorage() { - clearState(); - try { - window.location.reload(); - } catch (err) { - // ignore - } -} - export function selectChartStyleIndex(selectedChartStyleIndex) { return (dispatch, getState) => { dispatch('appSelectChartStyleIndex', (s) => { @@ -97,6 +151,7 @@ export function updateCollapsibleIsOpen(prefix, name, v) { const defaultClashAPIConfig = { baseURL: 'http://127.0.0.1:7892', secret: '', + addedAt: 0, }; // type Theme = 'light' | 'dark'; const defaultState = { @@ -133,25 +188,24 @@ export function initialState() { const query = parseConfigQueryString(); const conf = s.clashAPIConfigs[s.selectedClashAPIConfigIndex]; - const url = new URL(conf.baseURL); - if (query.hostname) { - url.hostname = query.hostname; - } - if (query.port) { - url.port = query.port; - } - // url.href is a stringifier and it appends a trailing slash - // that is not we want - conf.baseURL = trimTrailingSlash(url.href); - - if (query.secret) { - conf.secret = query.secret; + if (conf) { + const url = new URL(conf.baseURL); + if (query.hostname) { + url.hostname = query.hostname; + } + if (query.port) { + url.port = query.port; + } + // url.href is a stringifier and it appends a trailing slash + // that is not we want + conf.baseURL = trimTrailingSlash(url.href); + if (query.secret) { + conf.secret = query.secret; + } } - if (query.theme) { - if (query.theme === 'dark' || query.theme === 'light') { - s.theme = query.theme; - } + if (query.theme === 'dark' || query.theme === 'light') { + s.theme = query.theme; } // set initial theme setTheme(s.theme); diff --git a/src/store/configs.js b/src/store/configs.js index f114cef..bcd4ac8 100644 --- a/src/store/configs.js +++ b/src/store/configs.js @@ -2,8 +2,8 @@ import * as configsAPI from '../api/configs'; import * as trafficAPI from '../api/traffic'; import { openModal } from './modals'; -export const getConfigs = s => s.configs.configs; -export const getLogLevel = s => s.configs.configs['log-level']; +export const getConfigs = (s) => s.configs.configs; +export const getLogLevel = (s) => s.configs.configs['log-level']; export function fetchConfigs(apiConfig) { return async (dispatch, getState) => { @@ -11,25 +11,20 @@ export function fetchConfigs(apiConfig) { try { res = await configsAPI.fetchConfigs(apiConfig); } catch (err) { - // eslint-disable-next-line no-console - console.log('Error fetch configs', err); + // TypeError and AbortError dispatch(openModal('apiConfig')); return; } if (!res.ok) { - if (res.status === 404 || res.status === 401) { - dispatch(openModal('apiConfig')); - } else { - // eslint-disable-next-line no-console - console.log('Error fetch configs', res.statusText); - } + console.log('Error fetch configs', res.statusText); + dispatch(openModal('apiConfig')); return; } const payload = await res.json(); - dispatch('store/configs#fetchConfigs', s => { + dispatch('store/configs#fetchConfigs', (s) => { s.configs.configs = payload; }); @@ -47,25 +42,25 @@ export function fetchConfigs(apiConfig) { } function markHaveFetchedConfig() { - return dispatch => { - dispatch('store/configs#markHaveFetchedConfig', s => { + return (dispatch) => { + dispatch('store/configs#markHaveFetchedConfig', (s) => { s.configs.haveFetchedConfig = true; }); }; } export function updateConfigs(apiConfig, partialConfg) { - return async dispatch => { + return async (dispatch) => { configsAPI .updateConfigs(apiConfig, partialConfg) .then( - res => { + (res) => { if (res.ok === false) { // eslint-disable-next-line no-console console.log('Error update configs', res.statusText); } }, - err => { + (err) => { // eslint-disable-next-line no-console console.log('Error update configs', err); throw err; @@ -75,7 +70,7 @@ export function updateConfigs(apiConfig, partialConfg) { dispatch(fetchConfigs(apiConfig)); }); - dispatch('storeConfigsOptimisticUpdateConfigs', s => { + dispatch('storeConfigsOptimisticUpdateConfigs', (s) => { s.configs.configs = { ...s.configs.configs, ...partialConfg }; }); }; @@ -88,7 +83,7 @@ export const initialState = { 'redir-port': 0, 'allow-lan': false, mode: 'Rule', - 'log-level': 'info' + 'log-level': 'info', }, - haveFetchedConfig: false + haveFetchedConfig: false, }; diff --git a/src/store/index.js b/src/store/index.js index bd4e7a9..4fc8e4c 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,6 +1,8 @@ import { initialState as app, + removeClashAPIConfig, selectChartStyleIndex, + selectClashAPIConfig, updateAppConfig, updateCollapsibleIsOpen, } from './app'; @@ -24,6 +26,8 @@ export const actions = { app: { updateCollapsibleIsOpen, updateAppConfig, + removeClashAPIConfig, + selectClashAPIConfig, }, proxies: proxiesActions, }; |
