summaryrefslogtreecommitdiff
path: root/src/ducks/configs.js
blob: dd028c0c1c791b4a928e6bfd1ba62105dfed5e3a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import * as configsAPI from '../api/configs';
import * as trafficAPI from '../api/traffic';
import { openModal } from './modals';
import { getClashAPIConfig } from './app';

const CompletedFetchConfigs = 'configs/CompletedFetchConfigs';
const OptimisticUpdateConfigs = 'configs/OptimisticUpdateConfigs';
const MarkHaveFetchedConfig = 'configs/MarkHaveFetchedConfig';

export const getConfigs = s => s.configs;
export const getLogLevel = s => s.configs['log-level'];

export function fetchConfigs() {
  return async (dispatch, getState) => {
    let res;
    try {
      const apiSetup = getClashAPIConfig(getState());
      res = await configsAPI.fetchConfigs(apiSetup);
    } catch (err) {
      // FIXME
      // eslint-disable-next-line no-console
      console.log('Error fetch configs', err);
      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);
      }
      return;
    }

    const payload = await res.json();

    dispatch({
      type: CompletedFetchConfigs,
      payload
    });

    const configsCurr = getConfigs(getState());

    if (configsCurr.haveFetchedConfig) {
      // normally user will land on the "traffic chart" page first
      // calling this here will let the data start streaming
      // the traffic chart should already subscribed to the streaming
      const apiSetup = getClashAPIConfig(getState());
      trafficAPI.fetchData(apiSetup);
    } else {
      dispatch(markHaveFetchedConfig());
    }
  };
}

function markHaveFetchedConfig() {
  return {
    type: MarkHaveFetchedConfig,
    payload: {
      haveFetchedConfig: true
    }
  };
}

export function updateConfigs(partialConfg) {
  return async (dispatch, getState) => {
    const apiSetup = getClashAPIConfig(getState());
    configsAPI
      .updateConfigs(apiSetup, partialConfg)
      .then(
        res => {
          if (res.ok === false) {
            // eslint-disable-next-line no-console
            console.log('Error update configs', res.statusText);
          }
        },
        err => {
          // eslint-disable-next-line no-console
          console.log('Error update configs', err);
          throw err;
        }
      )
      .then(() => {
        // fetch updated configs to refresh to UI
        dispatch(fetchConfigs());
      });
    const configsCurr = getConfigs(getState());

    dispatch({
      type: OptimisticUpdateConfigs,
      payload: {
        ...configsCurr,
        ...partialConfg
      }
    });
  };
}

const initialState = {
  port: 7890,
  'socks-port': 7891,
  'redir-port': 0,
  'allow-lan': false,
  mode: 'Rule',
  'log-level': 'info',

  /////
  haveFetchedConfig: false
};

export default function reducer(state = initialState, { type, payload }) {
  switch (type) {
    case MarkHaveFetchedConfig:
    case OptimisticUpdateConfigs:
    case CompletedFetchConfigs: {
      return { ...state, ...payload };
    }

    default:
      return state;
  }
}