notistack#SnackbarMessage TypeScript Examples

The following examples show how to use notistack#SnackbarMessage. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: notifications.ts    From shadowsocks-electron with GNU General Public License v3.0 6 votes vote down vote up
enqueueSnackbar = (message: SnackbarMessage, notification: Notification) => {
    const key = notification?.key;

    return {
        type: ENQUEUE_SNACKBAR,
        notification: {
            ...notification,
            message,
            key: key || new Date().getTime() + Math.random(),
        },
    };
}
Example #2
Source File: ConfShareDialog.tsx    From shadowsocks-electron with GNU General Public License v3.0 5 votes vote down vote up
MediaCard: React.FC<MediaCard> = (props) => {
  const classes = useStyles();
  const dispatch = useDispatch();
  const { t } = useTranslation();

  const enqueueSnackbar = (message: SnackbarMessage, options: Notification) => {
    dispatch(enqueueSnackbarAction(message, options))
  };

  const downloadPicture = (dataLink: string) => {
    setTimeout(() => {
      props.onClose('share');
    }, 1e3);
    saveDataURLAsFile(dataLink, 'share');
  }

  const copyLink = (link: string) => {
    setTimeout(() => {
      props.onClose('share');
    }, 1e3);
    enqueueSnackbar(t('copied'), { variant: 'success' });
    clipboard.writeText(link, 'clipboard');
  }

  return (
    <StyledCard>
      <CardActionArea>
        <CardMedia
          component="img"
          className={classes.media}
          image={props.dataUrl}
        />
        <CardContent>
          <Typography gutterBottom variant="h5" component="h2"></Typography>
          <Typography variant="body2" color="textSecondary" component="span">
            <p className={classes.textOverflow}>
              {props.url}
            </p>
          </Typography>
        </CardContent>
      </CardActionArea>
      <Divider />
      <CardActions className={classes.action}>
        <Button
          className={classes.button}
          size="small" onClick={() => copyLink(props.url)}
          endIcon={<FileCopyIcon />}
        >
          {t('copy_link')}
        </Button>
        <Button
          className={classes.button}
          size="small" onClick={() => downloadPicture(props.dataUrl)}
          endIcon={<GetAppIcon />}
        >
          {t('save')}
        </Button>
      </CardActions>
    </StyledCard>
  );
}
Example #3
Source File: EditServerDialog.tsx    From shadowsocks-electron with GNU General Public License v3.0 4 votes vote down vote up
EditServerDialog: React.FC<EditServerDialogProps> = props => {
  const styles = useStyles();
  const { t } = useTranslation();
  const dispatch = useDispatch();

  const { open, onClose, defaultValues, onValues } = props;


  const theme = useTheme();
  const fullScreen = useMediaQuery(theme.breakpoints.down("sm"));

  const enqueueSnackbar = (message: SnackbarMessage, options: Notification) => {
    dispatch(enqueueSnackbarAction(message, options))
  };

  const [values, setValues] = useState<Partial<Config>>(
    defaultValues ?? {
      timeout: 60,
      encryptMethod: "none",
      type: 'ss'
    }
  );

  useLayoutEffect(() => {
    setValues(
      defaultValues ?? {
        timeout: 60,
        encryptMethod: "none",
        type: 'ss'
      }
    );
  }, [defaultValues]);

  const handleValueChange = (
    key: keyof Config,
    value: boolean | string | number
  ) => {
    setValues({
      ...values,
      // [key]: e.target[attr || 'value'].trim()
      [key]: value
    });
  };

  const handleCancel = () => {
    onValues(null);
  };

  const handleAdd = () => {
    if (!values.serverHost) {
      enqueueSnackbar(t("invalid_server_address"), { variant: "warning" });
      return;
    }
    if (
      !(
        values.serverPort &&
        values.serverPort > 0 &&
        values.serverPort <= 65535
      )
    ) {
      enqueueSnackbar(t("invalid_server_port"), { variant: "warning" });
      return;
    }
    if (!values.password) {
      enqueueSnackbar(t("invalid_password"), { variant: "warning" });
      return;
    }
    if (!values.timeout) {
      enqueueSnackbar(t("invalid_timeout"), { variant: "warning" });
      return;
    }

    onValues(values as Config);
  };

  const [showPassword, setShowPassword] = useState(false);

  const handleClickShowPassword = () => {
    setShowPassword(v => !v);
  };

  const handleMouseDownPassword = (
    event: React.MouseEvent<HTMLButtonElement>
  ) => {
    event.preventDefault();
  };

  const isSSR = values.type === 'ssr';
  const isSS = values.type === 'ss';

  return (
    <StyledDialog
      fullScreen={fullScreen}
      open={open}
      onClose={onClose}
    >
      <AdaptiveAppBar className={fullScreen ? styles.appBar : styles.appBarRelative}>
        <Toolbar>
          <IconButton edge="start" color="inherit" onClick={handleCancel}>
            <CloseIcon />
          </IconButton>
          <Typography variant="h6" className={styles.title}>
            { t('edit_server') }
          </Typography>
          <Button color="inherit" onClick={handleAdd}>
            { t('save') }
          </Button>
        </Toolbar>
      </AdaptiveAppBar>
      <Container className={`${styles.container}`}>
        {fullScreen && <div className={`${styles.toolbar}`} />}
        <InputLabel required style={{ marginBottom: 0 }}>
          {t('server_type')}
        </InputLabel>
        <Select
          required
          label={t('server_type')}
          displayEmpty
          fullWidth
          value={values.type ?? "ss"}
          onChange={(e: any) => handleValueChange("type", e.target.value.trim())}
        >
          {serverTypes.map(serverType => (
            <MenuItem key={serverType} value={serverType}>
              {serverType}
            </MenuItem>
          ))}
        </Select>
        <TextField
          fullWidth
          label={t('remark')}
          value={values.remark ?? ""}
          onChange={e => handleValueChange("remark", e.target.value.trim())}
        />
        <TextField
          required
          fullWidth
          label={t('server_address')}
          value={values.serverHost ?? ""}
          onChange={e => handleValueChange("serverHost", e.target.value.trim())}
        />
        <TextField
          required
          fullWidth
          type="number"
          label={t('server_port')}
          value={values.serverPort ?? ""}
          onChange={e => handleValueChange("serverPort", e.target.value.trim())}
        />
        <FormControl required fullWidth>
          <InputLabel htmlFor="password">{t('password')}</InputLabel>
          <Input
            id="password"
            type={showPassword ? "text" : "password"}
            value={values.password ?? ""}
            onChange={e => handleValueChange("password", e.target.value.trim())}
            endAdornment={
              <InputAdornment position="end">
                <IconButton
                  onClick={handleClickShowPassword}
                  onMouseDown={handleMouseDownPassword}
                >
                  {showPassword ? <Visibility /> : <VisibilityOff />}
                </IconButton>
              </InputAdornment>
            }
          />
        </FormControl>
        <InputLabel required style={{ marginBottom: 0 }}>
          {t('encryption')}
        </InputLabel>
        <Select
          required
          label={t('encryption')}
          displayEmpty
          fullWidth
          value={values.encryptMethod ?? "none"}
          onChange={(e: any) => handleValueChange("encryptMethod", e.target.value.trim())}
        >
          {encryptMethods.map(method => (
            <MenuItem key={method} value={method}>
              {method}
            </MenuItem>
          ))}
        </Select>
        {
          isSSR && (
            <>
              <InputLabel required style={{ marginBottom: 0 }}>
                {t('protocol')}
              </InputLabel>
              <Select
                required
                label={t('protocol')}
                displayEmpty
                fullWidth
                value={values.protocol ?? "origin"}
                onChange={(e: any) => handleValueChange("protocol", e.target.value.trim())}
              >
                {protocols.map(protocol => (
                  <MenuItem key={protocol} value={protocol}>
                    {protocol}
                  </MenuItem>
                ))}
              </Select>
              <TextField
                fullWidth
                label={t('protocolParam')}
                value={values.protocolParam ?? ""}
                onChange={e => handleValueChange("protocolParam", e.target.value.trim())}
              />
            </>
          )
        }
        {
          isSSR && (
            <>
              <InputLabel required style={{ marginBottom: 0 }}>
                {t('obfs')}
              </InputLabel>
              <Select
                required
                label={t('obfs')}
                displayEmpty
                fullWidth
                value={values.obfs ?? "plain"}
                onChange={(e: any) => handleValueChange("obfs", e.target.value.trim())}
              >
                {obfs.map(value => (
                  <MenuItem key={value} value={value}>
                    {value}
                  </MenuItem>
                ))}
              </Select>
              <TextField
                fullWidth
                label={t('obfsParam')}
                value={values.obfsParam ?? ""}
                onChange={e => handleValueChange("obfsParam", e.target.value.trim())}
              />
            </>
          )
        }
        <TextField
          required
          fullWidth
          label={t('timeout')}
          value={values.timeout ?? 60}
          onChange={e => handleValueChange("timeout", e.target.value)}
        />
        <List>
          <ListItem>
            <ListItemText primary="TCP Fast Open" />
            <ListItemSecondaryAction>
              <Switch checked={!!values.fastOpen} edge="end" color="primary" onChange={(e) => handleValueChange('fastOpen', e.target.checked)} />
            </ListItemSecondaryAction>
          </ListItem>
          {
            isSS && (
              <ListItem>
                <ListItemText primary="TCP No Delay" />
                <ListItemSecondaryAction>
                  <Switch checked={!!values.noDelay} edge="end" color="primary" onChange={(e) => handleValueChange('noDelay', e.target.checked)} />
                </ListItemSecondaryAction>
              </ListItem>
            )
          }
          <ListItem>
            <ListItemText primary="UDP Relay" />
            <ListItemSecondaryAction>
              <Switch checked={!!values.udp} edge="end" color="primary" onChange={(e) => handleValueChange('udp', e.target.checked)} />
            </ListItemSecondaryAction>
          </ListItem>
        </List>
        <InputLabel style={{ marginBottom: 0 }}><TextWithTooltip text={t('plugin')} tooltip={t('readme')} /></InputLabel>
        {
          isSS && (
            <>
              <Select
                label={t('plugin')}
                displayEmpty
                fullWidth
                value={values.plugin ?? ""}
                onChange={(e: any) => handleValueChange("plugin", e.target.value.trim())}
              >
                <MenuItem key="none" value="">
                  <em>{t('none')}</em>
                </MenuItem>
                {plugins.map(plugin => (
                  <MenuItem key={plugin.name} value={plugin.name}>
                    {plugin.name} {plugin.tips ? `(${t(plugin.tips)})` : ""}
                  </MenuItem>
                ))}
              </Select>
              <TextField
                fullWidth
                multiline
                label={t('plugin_options')}
                value={values.pluginOpts ?? ""}
                onChange={e => handleValueChange("pluginOpts", e.target.value.trim())}
              />
            </>
          )
        }
      </Container>
    </StyledDialog>
  );
}
Example #4
Source File: ServerListItemGroup.tsx    From shadowsocks-electron with GNU General Public License v3.0 4 votes vote down vote up
ServerListItemGroup: React.FC<ServerListItemGroupProps> = props => {
  // const styles = useStyles();
  const dispatch = useDispatch();
  const enqueueSnackbar = (message: SnackbarMessage, options: Notification) => {
    dispatch(enqueueSnackbarAction(message, options))
  };
  const { t } = useTranslation();

  const {
    item,
    selectedServer
  } = props;

  const [expanded, handleChange] = useState(!!item.servers?.find(server => server.id === selectedServer));
  const [ContextMenu, handleMenuOpen] = useContextMenu([
    { label: t('copy'), action: 'copy', icon: <CopyIcon fontSize="small" /> },
    { label: t('update'), action: 'update_subscription', icon: <Refresh fontSize="small" /> },
    { label: t('top'), action: 'top', icon: <VerticalAlignTopIcon fontSize="small" />},
    { label: t('move_up'), action: 'move_up', icon: <ArrowUpwardIcon fontSize="small" /> },
    { label: t('move_down'), action: 'move_down', icon: <ArrowDownwardIcon fontSize="small" /> },
    { label: t('delete'), action: 'delete', icon: <DeleteIcon fontSize="small" />}
  ]);

  useEffect(() => {
    handleChange(!!item.servers?.find(server => server.id === selectedServer));
  }, [selectedServer]);

  const handleRemoveButtonClick = () => {
    props.onRemove?.(item.id);
  };

  function onContextMenuClick (action: string) {
    switch (action) {
      case 'copy':
        clipboard.writeText(JSON.stringify(item));
        break;
      case 'update_subscription':
        if (item.url) {
          dispatch(updateSubscription(item.id, item.url, {
            success: t('subscription_updated'),
            error: t('failed_to_update_subscription')
          }));
        } else {
          enqueueSnackbar(t('server_url_not_set'), { variant: 'warning' });
        }
        break;
      case 'top':
        dispatch(top(item.id));
        break;
      case 'move_up':
        dispatch(moveUp(item.id));
        break;
      case 'move_down':
        dispatch(moveDown(item.id));
        break;
      case 'delete':
        handleRemoveButtonClick();
      default:
        break;
    }
  }

  const onContextMenu = (e: React.MouseEvent<HTMLElement>) => {
    e.preventDefault();
    e.stopPropagation();
    handleMenuOpen(e);
  };

  return (
    <div
    >
      <Accordion expanded={expanded} onChange={() => handleChange(!expanded)}>
        <StyledAccordionSummary
          expandIcon={<ExpandMore />}
          aria-controls="panel1bh-content"
          onContextMenu={onContextMenu}
        >
          { item.name }
        </StyledAccordionSummary>
        <StyledAccordionDetails>
          {
            item.servers.map(server => (
              <ServerListItemSingle
                selected={selectedServer === server.id}
                moveable={false}
                deleteable={false}
                topable={false}
                key={server.id}
                {...props}
                item={server}
              />
            ))
          }
        </StyledAccordionDetails>
      </Accordion>
      <ContextMenu onItemClick={onContextMenuClick} />
    </div>
  );
}
Example #5
Source File: HomePage.tsx    From shadowsocks-electron with GNU General Public License v3.0 4 votes vote down vote up
HomePage: React.FC = () => {
  const styles = useStyles();
  const { t } =  useTranslation();

  const dispatch = useDispatch();
  const config = useTypedSelector(state => state.config);
  const selectedServer = useTypedSelector(
    state => state.settings.selectedServer
  );
  const mode = useTypedSelector(state => state.settings.mode);
  const settings = useTypedSelector(state => state.settings);
  const connected = useTypedSelector(state => state.status.connected);
  const delay = useTypedSelector(state => state.status.delay);
  const loading = useTypedSelector(state => state.status.loading);

  const [DialogConfirm, showDialog, closeDialog] = useDialogConfirm();
  // const [BackDrop, setBackDrop] = useBackDrop();
  const [dialogOpen, setDialogOpen] = useState(false);
  const [shareDialogOpen, setShareDialogOpen] = useState(false);
  const [shareData, setShareData] = useState({
    url: '',
    dataUrl: ''
  });
  const [editServerDialogOpen, setEditServerDialogOpen] = useState(false);
  const [editingServerId, setEditingServerId] = useState<string | null>(null);
  const [removingServerId, setRemovingServerId] = useState<string | null>(null);

  {/* -------- functions ------- */}

  const enqueueSnackbar = (message: SnackbarMessage, options: Notification) => {
    dispatch(enqueueSnackbarAction(message, options))
  };

  const handleServerSelect = useCallback((id: string) => {
    dispatch({
      type: SET_SETTING,
      key: "selectedServer",
      value: id
    });
  }, []);

  const handleDialogClose = (selection?: CloseOptions) => {
    switch (selection) {
      case 'manual':
        setDialogOpen(false);
        setEditServerDialogOpen(true);
        break;
      case 'qrcode':
        // setBackDrop.current(true);
        dispatch(getQrCodeFromScreenResources({
          success: t('added_a_server'),
          error: t('no_qr_code_is_detected'),
        }));
        setDialogOpen(false);
        break;
      case 'url':
        setDialogOpen(false);
        // setBackDrop.current(true);
        dispatch(addConfigFromClipboard({
          success: t('added_a_server'),
          error: t('invalid_operation')
        }));
        break;
      case 'subscription':
        setDialogOpen(false);
        // setBackDrop.current(true);
        dispatch(addSubscriptionFromClipboard({
          success: t('added_a_server'),
          error: t('invalid_operation')
        }));
        break;
      case 'share':
        setShareDialogOpen(false);
        break;
      default:
        setDialogOpen(false);
        break;
    }
  };

  const handleEditServer = (values: Config | null) => {
    setEditServerDialogOpen(false);
    if (values) {
      if (!editingServerId) {
        const id = uuid();
        dispatch({ type: ADD_CONFIG, config: values, id });
        selectedServer === id && connectedToServer(config, id, values);
        enqueueSnackbar(t("added_a_server"), { variant: 'success' });
      } else {
        dispatch({
          type: EDIT_CONFIG,
          config: values,
          id: values.id
        });
        selectedServer === values.id && connectedToServer(config, values.id, values);
        enqueueSnackbar(t("edited_a_server"), { variant: 'success' });
      }
    }

    setEditingServerId(null);
  };

  const handleEditServerDialogClose = (event: {}, reason: "backdropClick" | "escapeKeyDown") => {
    if (reason !== 'backdropClick') {
      setEditServerDialogOpen(false);
      setEditingServerId(null);
    }
  };

  const handleServerConnect = useCallback(async (useValue?: string) => {
    const value = useValue === undefined ? selectedServer : useValue;
    if (value) {
      if (selectedServer) {
        if (connected) {
          await MessageChannel.invoke('main', 'service:main', {
            action: 'stopClient',
            params: {}
          });
        } else {
          findAndCallback(config, value, (conf: Config) => {
            dispatch(getConnectionDelay(conf.serverHost, conf.serverPort));
            dispatch(
              startClientAction(
                conf,
                settings,
                t('warning'),
                t('the_local_port_is_occupied')
              )
            );
          });
        }
        await new Promise(resolve => setTimeout(resolve, 1000));
      }
    }
  }, [selectedServer, connected, config, settings]);

  const handleShareButtonClick = useCallback((id: string) => {
      findAndCallback(config, id, (conf: Config) => {
        setShareDialogOpen(true);
        generateUrlFromConfig(conf)
          .then(rsp => {
            if (rsp.code === 200) {
              setShareData({
                url: rsp.result.url,
                dataUrl: rsp.result.dataUrl
              });
            }
          });
      });
    },
    [config]
  );

  const handleEditButtonClick = useCallback((id: string) => {
    setEditingServerId(id);
    setEditServerDialogOpen(true);
  }, []);

  const handleRemoveButtonClick = useCallback((id: string) => {
    if (id === selectedServer) {
      enqueueSnackbar(t('cannot_remove_selected_server'), { variant: 'warning' });
      return;
    }

    setRemovingServerId(id);
    showDialog(t('remove_this_server?'), t('this_action_cannot_be_undone'));
  }, [selectedServer]);

  const handleServerRemove = () => {
    dispatch({
      type: REMOVE_CONFIG,
      config: null as any,
      id: removingServerId!
    });
    enqueueSnackbar(t("removed_a_server"), { variant: 'success' });

    closeDialog();
    setRemovingServerId(null);
  };

  const handleAlertDialogClose = () => {
    closeDialog()
    setRemovingServerId(null);
  };

  const connectedToServer = (config: (Config | GroupConfig)[], selectedServer: string, useConfig?: Config) => {
    findAndCallback(config, selectedServer, (c: Config) => {
      const conf = useConfig || c;
      dispatch(getConnectionDelay(conf.serverHost, conf.serverPort));
      dispatch(
        startClientAction(
          conf,
          settings,
          t('warning'),
          t('the_local_port_is_occupied')
        )
      )});
  }

  {/* -------- hooks ------- */}

  useEffect(() => {
    setTimeout(() => {
      if (!connected && selectedServer) {
        connectedToServer(config, selectedServer);
      }

      if (settings.httpProxy.enable) {
        setHttpAndHttpsProxy({
          ...settings.httpProxy,
          type: 'http',
          proxyPort: settings.localPort
        });
      }

    }, 500);

  }, [])

  useEffect(() => {
    if (selectedServer && connected) {
      connectedToServer(config, selectedServer);
    }
  }, [selectedServer, settings]);

  return (
    <Container className={styles.container}>
      {/* -------- main ------- */}

      <ServerList
        config={config}
        selectedServer={selectedServer}
        connected={connected}
        handleShareButtonClick={handleShareButtonClick}
        handleEditButtonClick={handleEditButtonClick}
        handleRemoveButtonClick={handleRemoveButtonClick}
        handleServerSelect={handleServerSelect}
        handleServerConnect={handleServerConnect}
      />
      <FooterBar mode={mode} setDialogOpen={setDialogOpen} />

      <StatusBar
        left={[
          <SyncIcon
            key="status_bar_rotate"
            fontSize='small'
            className={`${styles['loading-icon']} ${loading ? 'rotate' : ''}`}
          />,
          <StatusBarNetwork key="status_bar_network" delay={delay}/>
        ]}
        right={[
          <StatusBarConnection
            key="status_bar_connection"
            status={connected ? 'online' : 'offline'}
          />
          // <span key="status_bar_mode" className={styles['statu-sbar_modeinfo']}>{t(mode.toLowerCase())}</span>
        ]}
      />

      {/* -------- dialog ------- */}

      <AddServerDialog open={dialogOpen} onClose={handleDialogClose} children={undefined} />
      <ConfShareDialog
        dataUrl={shareData.dataUrl}
        url={shareData.url}
        open={shareDialogOpen}
        onClose={handleDialogClose}
        children={undefined}
      />
      <EditServerDialog
        open={editServerDialogOpen}
        defaultValues={
          editingServerId ? findAndCallback(config, editingServerId) as Config : null
        }
        children={undefined}
        onClose={handleEditServerDialogClose}
        onValues={handleEditServer}
      />
      <DialogConfirm onClose={handleAlertDialogClose} onConfirm={handleServerRemove} />
    </Container>
  );
}
Example #6
Source File: SettingsPage.tsx    From shadowsocks-electron with GNU General Public License v3.0 4 votes vote down vote up
SettingsPage: React.FC = () => {
  const styles = useStyles();
  const { t } = useTranslation();

  const dispatch = useTypedDispatch();
  const [form] = Form.useForm();
  const settings = useTypedSelector(state => state.settings);
  const config = useTypedSelector(state => state.config);
  // const [aclVisible, setAclVisible] = useState(false);
  const inputFileRef = React.useRef<HTMLInputElement>(null);
  const [DialogConfirm, showDialog, closeDialog] = useDialogConfirm();
  const settingKeys = useRef(
    ['localPort', 'pacPort', 'gfwListUrl',
    'httpProxy', 'autoLaunch', 'fixedMenu',
    'darkMode', 'autoTheme', 'verbose', 'autoHide']
  );
  const cachedRef = useRef<any>(null);

  const enqueueSnackbar = (message: SnackbarMessage, options: Notification) => {
    dispatch(enqueueSnackbarAction(message, options))
  };

  useEffect(() => {
    dispatch<any>(getStartupOnBoot());
  }, [dispatch]);

  /* dark mode */
  useEffect(() => {
    if (
      (persistStore.get('darkMode') === 'true' && !settings.darkMode) ||
      (persistStore.get('darkMode') === 'false' && !!settings.darkMode) ||
      (persistStore.get('darkMode') === undefined && !!settings.darkMode)
    ) {
      dispatchEvent({
        type: 'theme:update',
        payload: {
          shouldUseDarkColors: !!settings.darkMode
        }
      });
    }
  }, [settings.darkMode]);

  /* restoreFromFile */
  useMemo(() => {
    const obj = {};
    if (cachedRef.current) {
      settingKeys.current.forEach(key => {
        if (cachedRef.current[key] !== (settings as any)[key]) {
          if (key === 'httpProxy') {
            Object.assign(obj, {
              httpProxy: settings.httpProxy.enable,
              httpProxyPort: settings.httpProxy.port,
            });
          } else {
            Object.assign(obj, { [key]: (settings as any)[key] });
          }
        }
      });
      form.setFieldsValue(obj);
    }
    cachedRef.current = settingKeys.current.reduce(
      (pre, cur) => Object.assign(pre, { [cur]: (settings as any)[cur] }),
      {}
    );
  }, settingKeys.current.map(key => (settings as any)[key]));

  const backupConfiguration = () => {
    return backupConfigurationToFile({
      config,
      settings
    });
  };

  const restoreConfiguration = () => {
    dispatch<any>(restoreConfigurationFromFile({
      success: t('the_recovery_is_successful'),
      error: {
        default: t('the_recovery_is_failed'),
        404: t('user_canceled')
      }
    }));
  }

  const checkPortValid = (parsedValue: number) => {
    if (!(parsedValue && parsedValue > 1024 && parsedValue <= 65535)) {
          return Promise.reject(t("invalid_port_range"));
    }
    return Promise.resolve();
  };

  const checkPortSame = () => {
    const localPort = +form.getFieldValue('localPort');
    const pacPort = +form.getFieldValue('pacPort');
    const httpPort = +form.getFieldValue('httpProxyPort');
    const num = localPort ^ pacPort ^ httpPort;
    if (num === localPort || num === pacPort || num === httpPort) {
      return Promise.reject(t("the_same_port_is_not_allowed"));
    }
    return Promise.resolve();
  };

  const handleOpenLog = async () => {
    await MessageChannel.invoke('main', 'service:desktop', {
      action: 'openLogDir',
      params: {}
    });
  };

  const handleOpenProcessManager = async () => {
    await MessageChannel.invoke('main', 'service:desktop', {
      action: 'openProcessManager',
      params: {}
    });
  };

  const handleReset = () => {
    dispatch({
      type: CLEAR_STORE
    } as any);
    closeDialog();
    MessageChannel.invoke('main', 'service:main', {
      action: 'stopClient',
      params: {}
    });
    enqueueSnackbar(t('cleared_all_data'), { variant: 'success' });
  };

  const handleAlertDialogOpen = () => {
    showDialog(t('reset_all_data'), t('reset_all_data_tips'));
  };

  const handleAlertDialogClose = () => {
    closeDialog();
  };

  const reGeneratePacFileWithFile = () => {
    inputFileRef.current?.click();
  }

  const reGeneratePacFileWithUrl = () => {
    reGeneratePacFile({
      url: settings.gfwListUrl
    });
  }

  const onGFWListFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file) {
      const reader = new FileReader();
      reader.onload = (e: any) => {
        const text = e.target.result;
        if (text) {
          reGeneratePacFile({
            text: text
          });
        }
      };
      reader.readAsText(file);
    }
  }

  const reGeneratePacFile = (params: { url?: string, text?: string }) => {
    dispatch<any>(setStatus('waiting', true));
    MessageChannel.invoke('main', 'service:main', {
      action: 'reGeneratePacFile',
      params
    }).then((rsp) => {
      setTimeout(() => { dispatch<any>(setStatus('waiting', false)); }, 1e3);
      if (rsp.code === 200) {
        enqueueSnackbar(t('successful_operation'), { variant: 'success' });
      } else {
        enqueueSnackbar(t('failed_to_download_file'), { variant: 'error' });
      }
    });
  }

  const onLangChange = (e: React.ChangeEvent<{ name?: string | undefined, value: unknown; }>) => {
    if (persistStore.get('lang') === e.target.value) return;
    persistStore.set('lang', e.target.value as string);
    MessageChannel.invoke('main', 'service:desktop', {
      action: 'reloadMainWindow',
      params: {}
    });
    MessageChannel.invoke('main', 'service:desktop', {
      action: 'setLocale',
      params: getFirstLanguage(e.target.value as string)
    });
  }

  const onAutoThemeChange = (e: React.ChangeEvent<{ name?: string | undefined, checked: boolean; }>) => {
    const checked = e.target.checked;
    MessageChannel.invoke('main', 'service:theme', {
      action: checked ? 'listenForUpdate' : 'unlistenForUpdate',
      params: {}
    }).then(rsp => {
      if (rsp.code === 200) {
        persistStore.set('autoTheme', checked ? 'true' : 'false');
      }
    });
    MessageChannel.invoke('main', 'service:theme', {
      action: 'getSystemThemeInfo',
      params: {}
    })
    .then(rsp => {
      if (rsp.code === 200) {
        dispatchEvent({
          type: 'theme:update',
          payload: rsp.result
        });
        if (!checked) {
          form.setFieldsValue({
            darkMode: rsp.result?.shouldUseDarkColors
          });
        }
      }
    });

  }

  const checkPortField = (rule: any, value: any) => {
    return Promise.all([checkPortSame(), checkPortValid(value)]);
  };

  const onFieldChange = (changedFields: { [key: string]: any }, allFields: { [key: string]: any }) => {
    const keys = Object.keys(changedFields);
    keys.forEach((key) => {
      let value = changedFields[key];
      form.validateFields([key]).then(() => {
        switch (key) {
          case 'httpProxy':
            value = {
              ...settings.httpProxy,
              enable: value
            };
            dispatch(setSetting<'httpProxy'>(key, value))
            setHttpAndHttpsProxy({ ...value, type: 'http', proxyPort: settings.localPort });
            return;
          case 'httpProxyPort':
            value = {
              ...settings.httpProxy,
              port: value
            };
            dispatch(setSetting<'httpProxy'>('httpProxy', value))
            setHttpAndHttpsProxy({ ...value, type: 'http', proxyPort: settings.localPort });
            return;
          case 'acl':
            dispatch(setSetting<'acl'>(key, {
              ...settings.acl,
              text: value
            }));
            return;
          case 'autoLaunch':
            dispatch<any>(setStartupOnBoot(value));
            return;
          case 'darkMode':
            dispatchEvent({
              type: 'theme:update',
              payload: {
                shouldUseDarkColors: value
              }
            });
            break;
          default:
            break;
        }

        dispatch(setSetting<any>(key, value));
      }).catch((reason: { errorFields: { errors: string[] }[] }) => {
        enqueueSnackbar(reason?.errorFields?.map(item => item.errors.join()).join(), { variant: 'error' });
      });
    });
  }

  return (
    <Container className={styles.container}>
      <Form
        form={form}
        initialValues={
          {
            localPort: settings.localPort,
            pacPort: settings.pacPort,
            gfwListUrl: settings.gfwListUrl,
            httpProxy: settings.httpProxy.enable,
            httpProxyPort: settings.httpProxy.port,
            autoLaunch: settings.autoLaunch,
            fixedMenu: settings.fixedMenu,
            darkMode: settings.darkMode,
            autoTheme: settings.autoTheme,
            verbose: settings.verbose,
            autoHide: settings.autoHide,
          }
        }
        onValuesChange={onFieldChange}
      >
        <Field
          name="localPort"
          rules={[
            { required: true, message: t('invalid_value') },
            { validator: checkPortField },
          ]}
          normalize={(value: string) => +(value.trim())}
          validateTrigger={false}
        >
          <TextField
            className={styles.textField}
            required
            fullWidth
            size="small"
            type="number"
            label={t('local_port')}
            placeholder={t('local_port_tips')}
          />
        </Field>
        <Field
          name="pacPort"
          rules={[
            { required: true, message: t('invalid_value') },
            { validator: checkPortField }
          ]}
          normalize={(value: string) => +(value.trim())}
          validateTrigger={false}
        >
          <TextField
            className={styles.textField}
            required
            fullWidth
            type="number"
            size="small"
            label={t('pac_port')}
            placeholder={t('pac_port_tips')}
          />
        </Field>
        <Field
          name="gfwListUrl"
          validateTrigger={false}
        >
          <TextField
          className={styles.textField}
          required
          fullWidth
          type="url"
          size="small"
          label={
            <TextWithTooltip
              text={t('gfwlist_url')}
              icon={
                <span>
                  <Tooltip arrow placement="top" title={t('recover_pac_file_with_link') as string}>
                    <RestorePage className={styles.cursorPointer} onClick={reGeneratePacFileWithUrl} />
                  </Tooltip>
                  <Tooltip arrow placement="top" title={t('recover_pac_file_with_file') as string}>
                    <NoteAdd className={styles.cursorPointer} onClick={reGeneratePacFileWithFile}/>
                  </Tooltip>
                </span>
              }
            />
          }
          placeholder={t('gfwlist_url_tips')}
        />
        </Field>
        <input onChange={onGFWListFileChange} ref={inputFileRef} type={'file'} multiple={false} style={{ display: 'none' }}></input>
        <List className={styles.list}>
          <ListItem>
              <ListItemText
                primary={t('http_proxy')}
              />
              <ListItemSecondaryAction>
                <Field name="httpProxy" valuePropName="checked">
                  <AdaptiveSwitch
                    edge="end"
                  />
                </Field>
              </ListItemSecondaryAction>
          </ListItem>
          {
            settings.httpProxy.enable && (
              <ListItem>
                <ListItemText
                  primary={t('http_proxy_port')}
                  secondary={t('restart_when_changed')}
                />
                <ListItemSecondaryAction>
                  <Field
                    name="httpProxyPort"
                    rules={[
                      { required: true, message: t('invalid_value') },
                      { validator: checkPortField }
                    ]}
                    normalize={(value: string) => +(value.trim())}
                    validateTrigger={false}
                  >
                    <TextField
                      className={`${styles.textField} ${styles.indentInput}`}
                      required
                      size="small"
                      type="number"
                      placeholder={t('http_proxy_port')}
                    />
                  </Field>
                </ListItemSecondaryAction>
              </ListItem>
            )
          }
          {/* <ListItem>
              <ListItemText
                primary={'ACL'}
                // secondary="Not applicable to Linux"
              />
              <ListItemSecondaryAction>
                <AdaptiveSwitch
                  edge="end"
                  checked={settings.acl.enable}
                  onChange={e => handleSwitchValueChange("acl", e)}
                />
              </ListItemSecondaryAction>
          </ListItem>
          {
            settings.acl.enable && (
              <ListItem>
                <ListItemText
                  primary={t('acl_content')}
                />
                <ListItemSecondaryAction>
                  <TextField
                    className={`${styles.textField} ${styles.indentInput}`}
                    style={{ width: '120px', textAlign: 'right' }}
                    required
                    size="small"
                    type="text"
                    placeholder={t('click_to_edit')}
                    onClick={() => setAclVisible(true)}
                    value={'*****'}
                  />
                </ListItemSecondaryAction>
              </ListItem>
            )
          } */}
          <ListItem>
            <ListItemText
              primary={t('launch_on_boot')}
              secondary={t('not_applicable_to_linux_snap_application')}
            />
            <ListItemSecondaryAction>
              <Field name="autoLaunch" valuePropName="checked">
                <AdaptiveSwitch
                  edge="end"
                />
              </Field>
            </ListItemSecondaryAction>
          </ListItem>
          <ListItem>
            <ListItemText
              primary={t('fixed_menu')}
            />
            <ListItemSecondaryAction>
              <Field name="fixedMenu" valuePropName="checked">
                <AdaptiveSwitch
                  edge="end"
                />
              </Field>
            </ListItemSecondaryAction>
          </ListItem>
          <ListItem>
            <ListItemText
              primary={t('auto_hide')}
              secondary={t('minimize_on_start')}
            />
            <ListItemSecondaryAction>
              <Field name="autoHide" valuePropName="checked">
                <AdaptiveSwitch
                  edge="end"
                />
              </Field>
            </ListItemSecondaryAction>
          </ListItem>
          <ListItem>
            <ListItemText
              primary={t('autoTheme')}
              secondary={t('autoThemeTips')}
            />
            <ListItemSecondaryAction>
              <Field name="autoTheme" valuePropName="checked">
                <AdaptiveSwitch
                  edge="end"
                  onChange={onAutoThemeChange}
                />
              </Field>
            </ListItemSecondaryAction>
          </ListItem>
          <ListItem>
            <ListItemText
              primary={t('darkMode')}
            />
            <ListItemSecondaryAction>
              <Field name="darkMode" valuePropName="checked">
                <AdaptiveSwitch
                  edge="end"
                  disabled={settings.autoTheme}
                />
              </Field>
            </ListItemSecondaryAction>
          </ListItem>
          <ListItem>
            <ListItemText
              primary={'Language'}
            />
            <ListItemSecondaryAction>
              <Select
                value={getDefaultLang()}
                onChange={onLangChange}
              >
              <MenuItem value={'en-US'}>English</MenuItem>
              <MenuItem value={'zh-CN'}>中文简体</MenuItem>
            </Select>
            </ListItemSecondaryAction>
          </ListItem>

          <ListItem button onClick={backupConfiguration}>
              <ListItemText primary={t('backup')} />
          </ListItem>
          <ListItem button onClick={() => restoreConfiguration()}>
              <ListItemText primary={t('restore')} />
          </ListItem>
          <ListItem button onClick={handleAlertDialogOpen}>
            <ListItemText primary={t('reset_data')} />
          </ListItem>

          <Divider className={styles.margin} />

          <ListSubheader>{t('debugging')}</ListSubheader>

          <ListItem>
            <ListItemText
              primary="Verbose"
              secondary={t('verbose_output')}
            />
            <ListItemSecondaryAction>
              <Field name="verbose" valuePropName="checked">
                <AdaptiveSwitch
                  edge="end"
                />
              </Field>
            </ListItemSecondaryAction>
          </ListItem>
          <ListItem button onClick={handleOpenLog}>
            <ListItemText primary={t('open_log_dir')} />
          </ListItem>
          <ListItem button onClick={handleOpenProcessManager}>
            <ListItemText primary={t('open_process_manager')} />
          </ListItem>
        </List>

      </Form>

      {/* dialog */}

      {/* <EditAclDialog
        open={aclVisible}
        onClose={() => setAclVisible(false)}
        children={undefined}
        onTextChange={handleValueChange}
      /> */}

      <DialogConfirm onClose={handleAlertDialogClose} onConfirm={handleReset} />
    </Container>
  );
}