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 |
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 |
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 |
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 |
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 |
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 |
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>
);
}