summaryrefslogtreecommitdiff
path: root/src/store
diff options
context:
space:
mode:
authorHaishan <[email protected]>2020-09-13 16:34:18 +0800
committerHaishan <[email protected]>2020-09-13 17:16:14 +0800
commit15bc0f69a8367a57fa1bf263e615285349ad4ab9 (patch)
treefbdd2a46303703822f7e7bc3462a70b4855fe4a1 /src/store
parenta8f0d3d4b4928caebf61c75fa9191a170b471035 (diff)
feat: multi backends management
Diffstat (limited to 'src/store')
-rw-r--r--src/store/app.js108
-rw-r--r--src/store/configs.js33
-rw-r--r--src/store/index.js4
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,
};