Webatrice: update deps (#4700)

* save work

* fix reset styling

* fix toast reducer

* update non-react deps

* update react libraries

* remove jquery, use sanitize-html instead

* add missing change

* fix deps and dev deps

* update workflow to target Node 16

* run @mui/codemod to remove @mui/styles

* add default body font size

* update react 17 to 18

* declare enum before use

* add rel attr to links

* fix font sizing issue

* trailing commas

* refactor deep destructuring

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
This commit is contained in:
Jeremy Letto 2022-11-01 12:41:42 -05:00 committed by GitHub
parent 5854a635ca
commit 26d7fe2ff0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 26225 additions and 3552 deletions

View file

@ -28,7 +28,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
node_version: node_version:
- 12 - 16
- lts/* - lts/*
steps: steps:

1
webclient/.npmrc Normal file
View file

@ -0,0 +1 @@
legacy-peer-deps=true

29365
webclient/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -2,40 +2,6 @@
"name": "webclient", "name": "webclient",
"version": "1.0.0", "version": "1.0.0",
"private": true, "private": true,
"dependencies": {
"@emotion/react": "^11.8.2",
"@emotion/styled": "^11.8.1",
"@mui/icons-material": "^5.5.1",
"@mui/material": "^5.5.1",
"@mui/styles": "^5.5.1",
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.3",
"crypto-js": "^4.1.1",
"dexie": "^3.2.2",
"final-form": "^4.20.6",
"final-form-set-field-touched": "^1.0.1",
"i18next": "^21.6.13",
"i18next-browser-languagedetector": "^6.1.3",
"jquery": "^3.6.0",
"lodash": "^4.17.21",
"prop-types": "^15.8.1",
"protobufjs": "^6.11.3",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-final-form": "^6.5.8",
"react-final-form-listeners": "^1.0.3",
"react-i18next": "^11.15.6",
"react-redux": "^7.2.6",
"react-router-dom": "^6.2.2",
"react-scripts": "5.0.0",
"react-virtualized-auto-sizer": "^1.0.6",
"react-window": "^1.8.6",
"redux": "^4.1.2",
"redux-form": "^8.3.8",
"redux-thunk": "^2.4.1",
"rxjs": "^7.5.4",
"typescript": "^4.6.2"
},
"scripts": { "scripts": {
"prebuild": "node prebuild.js", "prebuild": "node prebuild.js",
"prestart": "node prebuild.js", "prestart": "node prebuild.js",
@ -50,6 +16,61 @@
"prepare": "cd .. && husky install", "prepare": "cd .. && husky install",
"translate": "node prebuild.js -i18nOnly" "translate": "node prebuild.js -i18nOnly"
}, },
"dependencies": {
"@emotion/react": "^11.8.2",
"@emotion/styled": "^11.8.1",
"@mui/icons-material": "^5.5.1",
"@mui/material": "^5.5.1",
"crypto-js": "^4.1.1",
"dexie": "^3.2.2",
"final-form": "^4.20.6",
"final-form-set-field-touched": "^1.0.1",
"i18next": "^22.0.4",
"i18next-browser-languagedetector": "^7.0.0",
"i18next-icu": "^2.0.3",
"intl-messageformat": "^10.2.1",
"lodash": "^4.17.21",
"prop-types": "^15.8.1",
"protobufjs": "^7.1.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-final-form": "^6.5.8",
"react-final-form-listeners": "^1.0.3",
"react-i18next": "^12.0.0",
"react-redux": "^8.0.4",
"react-router-dom": "^6.2.2",
"react-scripts": "5.0.1",
"react-virtualized-auto-sizer": "^1.0.6",
"react-window": "^1.8.6",
"redux": "^4.1.2",
"redux-form": "^8.3.8",
"redux-thunk": "^2.4.1",
"rxjs": "^7.5.4",
"sanitize-html": "^2.7.3"
},
"devDependencies": {
"@babel/core": "^7.17.5",
"@mui/types": "^7.1.3",
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^13.4.0",
"@types/jest": "29.2.0",
"@types/jquery": "^3.5.14",
"@types/lodash": "^4.14.179",
"@types/node": "18.11.7",
"@types/prop-types": "^15.7.4",
"@types/react": "18.0.24",
"@types/react-dom": "18.0.8",
"@types/react-redux": "^7.1.23",
"@types/react-router-dom": "^5.3.3",
"@types/react-virtualized-auto-sizer": "^1.0.1",
"@types/react-window": "^1.8.5",
"@types/redux-form": "^8.3.3",
"@typescript-eslint/eslint-plugin": "^5.14.0",
"@typescript-eslint/parser": "^5.14.0",
"fs-extra": "^10.0.1",
"husky": "^8.0.1",
"typescript": "^4.6.2"
},
"eslintConfig": { "eslintConfig": {
"extends": "react-app" "extends": "react-app"
}, },
@ -69,28 +90,5 @@
"moduleNameMapper": { "moduleNameMapper": {
"\\.(css|less)$": "identity-obj-proxy" "\\.(css|less)$": "identity-obj-proxy"
} }
},
"devDependencies": {
"@babel/core": "^7.17.5",
"@mui/types": "^7.1.3",
"@types/jest": "27.4.1",
"@types/jquery": "^3.5.14",
"@types/lodash": "^4.14.179",
"@types/node": "17.0.21",
"@types/prop-types": "^15.7.4",
"@types/react": "17.0.39",
"@types/react-dom": "17.0.13",
"@types/react-redux": "^7.1.23",
"@types/react-router-dom": "^5.3.3",
"@types/react-virtualized-auto-sizer": "^1.0.1",
"@types/react-window": "^1.8.5",
"@types/redux-form": "^8.3.3",
"@typescript-eslint/eslint-plugin": "^5.14.0",
"@typescript-eslint/parser": "^5.14.0",
"fs-extra": "^10.0.1",
"husky": "^7.0.4",
"i18next-icu": "^2.0.3",
"identity-obj-proxy": "^3.0.0",
"intl-messageformat": "^9.11.4"
} }
} }

View file

@ -1,27 +1,33 @@
import React from 'react'; import React from 'react';
import makeStyles from '@mui/styles/makeStyles'; import { styled } from '@mui/material/styles';
import TextField from '@mui/material/TextField'; import TextField from '@mui/material/TextField';
import ErrorOutlinedIcon from '@mui/icons-material/ErrorOutlined'; import ErrorOutlinedIcon from '@mui/icons-material/ErrorOutlined';
import './InputField.css'; import './InputField.css';
const useStyles = makeStyles(theme => ({ const PREFIX = 'InputField';
root: {
const classes = {
root: `${PREFIX}-root`
};
const Root = styled('div')(({ theme }) => ({
[`&.${classes.root}`]: {
'& .InputField-error': { '& .InputField-error': {
color: theme.palette.error.main color: theme.palette.error.main
}, },
'& .InputField-warning': { '& .InputField-warning': {
color: theme.palette.warning.main color: theme.palette.warning.main
} },
}, },
})); }));
const InputField = ({ input, meta: { touched, error, warning }, ...args }) => { const InputField = ({ input, meta, ...args }) => {
const classes = useStyles(); const { touched, error, warning } = meta;
return ( return (
<div className={'InputField ' + classes.root}> <Root className={'InputField ' + classes.root}>
{ touched && ( { touched && (
<div className="InputField-validation"> <div className="InputField-validation">
{ {
@ -47,7 +53,7 @@ const InputField = ({ input, meta: { touched, error, warning }, ...args }) => {
size="small" size="small"
fullWidth={true} fullWidth={true}
/> />
</div> </Root>
); );
}; };

View file

@ -1,4 +1,5 @@
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { styled } from '@mui/material/styles';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Select, MenuItem } from '@mui/material'; import { Select, MenuItem } from '@mui/material';
import Button from '@mui/material/Button'; import Button from '@mui/material/Button';
@ -7,7 +8,6 @@ import IconButton from '@mui/material/IconButton';
import WifiTetheringIcon from '@mui/icons-material/WifiTethering'; import WifiTetheringIcon from '@mui/icons-material/WifiTethering';
import PortableWifiOffIcon from '@mui/icons-material/PortableWifiOff'; import PortableWifiOffIcon from '@mui/icons-material/PortableWifiOff';
import InputLabel from '@mui/material/InputLabel'; import InputLabel from '@mui/material/InputLabel';
import makeStyles from '@mui/styles/makeStyles';
import Check from '@mui/icons-material/Check'; import Check from '@mui/icons-material/Check';
import AddIcon from '@mui/icons-material/Add'; import AddIcon from '@mui/icons-material/Add';
import EditRoundedIcon from '@mui/icons-material/Edit'; import EditRoundedIcon from '@mui/icons-material/Edit';
@ -29,8 +29,14 @@ enum TestConnection {
SUCCESS = 'success', SUCCESS = 'success',
} }
const useStyles = makeStyles(theme => ({ const PREFIX = 'KnownHosts';
root: {
const classes = {
root: `${PREFIX}-root`
};
const Root = styled('div')(({ theme }) => ({
[`&.${classes.root}`]: {
'& .KnownHosts-error': { '& .KnownHosts-error': {
color: theme.palette.error.main color: theme.palette.error.main
}, },
@ -50,13 +56,13 @@ const useStyles = makeStyles(theme => ({
color: theme.palette.success.main color: theme.palette.success.main
} }
} }
}, }
})); }));
const KnownHosts = (props) => { const KnownHosts = (props) => {
const { input: { onChange }, meta, disabled } = props; const { input: { onChange }, meta, disabled } = props;
const { touched, error, warning } = meta; const { touched, error, warning } = meta;
const classes = useStyles();
const { t } = useTranslation(); const { t } = useTranslation();
const [hostsState, setHostsState] = useState({ const [hostsState, setHostsState] = useState({
@ -195,7 +201,7 @@ const KnownHosts = (props) => {
} }
return ( return (
<div className={'KnownHosts ' + classes.root}> <Root className={'KnownHosts ' + classes.root}>
<FormControl className='KnownHosts-form' size='small' variant='outlined'> <FormControl className='KnownHosts-form' size='small' variant='outlined'>
{ touched && ( { touched && (
<div className='KnownHosts-validation'> <div className='KnownHosts-validation'>
@ -272,8 +278,8 @@ const KnownHosts = (props) => {
<Toast open={showCreateToast} onClose={() => setShowCreateToast(false)}>{ t('KnownHosts.toast', { mode: 'created' }) }</Toast> <Toast open={showCreateToast} onClose={() => setShowCreateToast(false)}>{ t('KnownHosts.toast', { mode: 'created' }) }</Toast>
<Toast open={showDeleteToast} onClose={() => setShowDeleteToast(false)}>{ t('KnownHosts.toast', { mode: 'deleted' }) }</Toast> <Toast open={showDeleteToast} onClose={() => setShowDeleteToast(false)}>{ t('KnownHosts.toast', { mode: 'deleted' }) }</Toast>
<Toast open={showEditToast} onClose={() => setShowEditToast(false)}>{ t('KnownHosts.toast', { mode: 'edited' }) }</Toast> <Toast open={showEditToast} onClose={() => setShowEditToast(false)}>{ t('KnownHosts.toast', { mode: 'edited' }) }</Toast>
</div> </Root>
) );
}; };
export default KnownHosts; export default KnownHosts;

View file

@ -1,6 +1,6 @@
// eslint-disable-next-line // eslint-disable-next-line
import React, { useMemo, useState } from 'react'; import React, { useMemo, useState } from 'react';
import makeStyles from '@mui/styles/makeStyles'; import { styled } from '@mui/material/styles';
import Popover from '@mui/material/Popover'; import Popover from '@mui/material/Popover';
import { CardDTO, TokenDTO } from 'services'; import { CardDTO, TokenDTO } from 'services';
@ -10,17 +10,24 @@ import TokenDetails from '../TokenDetails/TokenDetails';
import './CardCallout.css'; import './CardCallout.css';
const useStyles = makeStyles(theme => ({ const PREFIX = 'CardCallout';
popover: {
const classes = {
popover: `${PREFIX}-popover`,
popoverContent: `${PREFIX}-popoverContent`
};
const Root = styled('span')(({ theme }) => ({
[`& .${classes.popover}`]: {
pointerEvents: 'none', pointerEvents: 'none',
}, },
popoverContent: {
[`& .${classes.popoverContent}`]: {
pointerEvents: 'none', pointerEvents: 'none',
}, }
})); }));
const CardCallout = ({ name }) => { const CardCallout = ({ name }) => {
const classes = useStyles();
const [card, setCard] = useState<CardDTO>(null); const [card, setCard] = useState<CardDTO>(null);
const [token, setToken] = useState<TokenDTO>(null); const [token, setToken] = useState<TokenDTO>(null);
const [anchorEl, setAnchorEl] = useState<Element>(null); const [anchorEl, setAnchorEl] = useState<Element>(null);
@ -48,7 +55,7 @@ const CardCallout = ({ name }) => {
const open = Boolean(anchorEl); const open = Boolean(anchorEl);
return ( return (
<span className='callout'> <Root className='callout'>
<span <span
onMouseEnter={handlePopoverOpen} onMouseEnter={handlePopoverOpen}
onMouseLeave={handlePopoverClose} onMouseLeave={handlePopoverClose}
@ -80,7 +87,7 @@ const CardCallout = ({ name }) => {
</Popover> </Popover>
) )
} }
</span> </Root>
); );
}; };

View file

@ -1,4 +1,4 @@
import { createContext, FC, ReactChild, ReactNode, useContext, useEffect, useReducer, ContextType, Context } from 'react' import { createContext, FC, PropsWithChildren, ReactChild, ReactNode, useContext, useEffect, useReducer, ContextType, Context } from 'react'
import { ACTIONS, initialState, reducer } from './reducer'; import { ACTIONS, initialState, reducer } from './reducer';
import Toast from './Toast' import Toast from './Toast'
@ -24,15 +24,15 @@ const ToastContext: Context<any> = createContext<ToastState>({
removeToast: (key) => {}, removeToast: (key) => {},
}); });
export const ToastProvider: FC<ReactNode> = (props) => { export const ToastProvider: FC<PropsWithChildren> = (props) => {
const { children } = props const { children } = props
const [state, dispatch] = useReducer(reducer, initialState) const [state, dispatch] = useReducer(reducer, initialState)
const providerState = { const providerState = {
toasts: state.toasts, toasts: state.toasts,
addToast: (key, children) => dispatch({ type: ACTIONS.ADD_TOAST, payload: { key, children } }), addToast: (key, children) => dispatch({ type: ACTIONS.ADD_TOAST, payload: { key, children } }),
openToast: key => dispatch({ type: ACTIONS.OPEN_TOAST, payload: key }), openToast: key => dispatch({ type: ACTIONS.OPEN_TOAST, payload: { key } }),
closeToast: key => dispatch({ type: ACTIONS.CLOSE_TOAST, payload: key }), closeToast: key => dispatch({ type: ACTIONS.CLOSE_TOAST, payload: { key } }),
removeToast: key => dispatch({ type: ACTIONS.REMOVE_TOAST, payload: key }), removeToast: key => dispatch({ type: ACTIONS.REMOVE_TOAST, payload: { key } }),
} }
return ( return (
<ToastContext.Provider value={providerState}> <ToastContext.Provider value={providerState}>
@ -41,7 +41,7 @@ export const ToastProvider: FC<ReactNode> = (props) => {
{Array.from(state.toasts).map(([key, value]) => { {Array.from(state.toasts).map(([key, value]) => {
const { isOpen, children } = value; const { isOpen, children } = value;
return ( return (
<Toast key={key} open={isOpen} onClose={() => dispatch({ type: ACTIONS.CLOSE_TOAST, payload: key })}> <Toast key={key} open={isOpen} onClose={() => dispatch({ type: ACTIONS.CLOSE_TOAST, payload: { key } })}>
{children} {children}
</Toast> </Toast>
) )

View file

@ -6,41 +6,54 @@ export const ACTIONS = {
} }
export const initialState = { export const initialState = {
toasts: new Map() toasts: {}
} }
export function reducer(state, action) { export function reducer(state, { type, payload }) {
const { type, payload } = action const { key, children } = payload;
switch (type) { switch (type) {
case ACTIONS.ADD_TOAST: { case ACTIONS.ADD_TOAST: {
const newState = { ...state } return {
newState.toasts = new Map(Array.from(state.toasts)) ...state,
const { toasts } = newState; toasts: {
const { key, children } = payload ...state.toasts,
toasts.set(key, { isOpen: false, children }) [key]: {
return newState isOpen: false,
children,
},
},
};
} }
case ACTIONS.OPEN_TOAST: { case ACTIONS.OPEN_TOAST: {
const newState = { ...state } return {
newState.toasts = new Map(Array.from(state.toasts)) ...state,
const { toasts } = newState; toasts: {
const toast = toasts.get(payload) ...state.toasts,
toasts.set(payload, { isOpen: true, children: toast.children }) [key]: {
return newState ...state.toasts[key],
isOpen: true,
},
},
};
} }
case ACTIONS.CLOSE_TOAST: { case ACTIONS.CLOSE_TOAST: {
const newState = { ...state } return {
newState.toasts = new Map(Array.from(state.toasts)) ...state,
const { toasts } = newState; toasts: {
const toast = toasts.get(payload) ...state.toasts,
toasts.set(payload, { isOpen: false, children: toast.children }) [key]: {
return newState ...state.toasts[key],
isOpen: false,
},
},
};
} }
case ACTIONS.REMOVE_TOAST: { case ACTIONS.REMOVE_TOAST: {
const newState = { ...state } const newState = { ...state };
newState.toasts = new Map(Array.from(state.toasts)) delete newState.toasts[key];
newState.toasts.delete(payload)
return newState return newState;
} }
default: default:
throw Error('Please pick an available action') throw Error('Please pick an available action')

View file

@ -1,8 +1,8 @@
import { useState } from 'react'; import { useState } from 'react';
import { styled } from '@mui/material/styles';
import { useTranslation, Trans } from 'react-i18next'; import { useTranslation, Trans } from 'react-i18next';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Navigate } from 'react-router-dom'; import { Navigate } from 'react-router-dom';
import makeStyles from '@mui/styles/makeStyles';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import { Images } from 'images'; import { Images } from 'images';
@ -11,8 +11,14 @@ import { RouteEnum } from 'types';
import './Initialize.css'; import './Initialize.css';
const useStyles = makeStyles(theme => ({ const PREFIX = 'Initialize';
root: {
const classes = {
root: `${PREFIX}-root`
};
const Root = styled('div')(({ theme }) => ({
[`&.${classes.root}`]: {
'& .Initialize-graphics': { '& .Initialize-graphics': {
color: theme.palette.primary.contrastText, color: theme.palette.primary.contrastText,
}, },
@ -20,17 +26,16 @@ const useStyles = makeStyles(theme => ({
'& .Initialize-graphics__bar': { '& .Initialize-graphics__bar': {
backgroundColor: theme.palette.primary.contrastText, backgroundColor: theme.palette.primary.contrastText,
}, },
}, }
})); }));
const Initialize = ({ initialized }: InitializeProps) => { const Initialize = ({ initialized }: InitializeProps) => {
const classes = useStyles();
const { t } = useTranslation(); const { t } = useTranslation();
return initialized return initialized
? <Navigate to={RouteEnum.LOGIN} /> ? <Navigate to={RouteEnum.LOGIN} />
: ( : (
<div className={'Initialize ' + classes.root}> <Root className={'Initialize ' + classes.root}>
<div className='Initialize-content'> <div className='Initialize-content'>
<img src={Images.Logo} alt="logo" /> <img src={Images.Logo} alt="logo" />
<Typography variant="subtitle1" className='subtitle'>{ t('InitializeContainer.title') }</Typography> <Typography variant="subtitle1" className='subtitle'>{ t('InitializeContainer.title') }</Typography>
@ -48,7 +53,7 @@ const Initialize = ({ initialized }: InitializeProps) => {
<div className="topBar Initialize-graphics__bar" /> <div className="topBar Initialize-graphics__bar" />
<div className="bottomBar Initialize-graphics__bar" /> <div className="bottomBar Initialize-graphics__bar" />
</div> </div>
</div> </Root>
); );
} }

View file

@ -1,8 +1,8 @@
import { useState, useCallback } from 'react'; import { useState, useCallback } from 'react';
import { styled } from '@mui/material/styles';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Navigate } from 'react-router-dom'; import { Navigate } from 'react-router-dom';
import makeStyles from '@mui/styles/makeStyles';
import Button from '@mui/material/Button'; import Button from '@mui/material/Button';
import Paper from '@mui/material/Paper'; import Paper from '@mui/material/Paper';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
@ -20,8 +20,14 @@ import { ServerSelectors, ServerTypes } from 'store';
import './Login.css'; import './Login.css';
import { useToast } from 'components/Toast'; import { useToast } from 'components/Toast';
const useStyles = makeStyles(theme => ({ const PREFIX = 'Login';
root: {
const classes = {
root: `${PREFIX}-root`
};
const Root = styled('div')(({ theme }) => ({
[`&.${classes.root}`]: {
'& .login-content__header': { '& .login-content__header': {
color: theme.palette.success.light color: theme.palette.success.light
}, },
@ -54,11 +60,10 @@ const useStyles = makeStyles(theme => ({
display: 'flex', display: 'flex',
}, },
}, },
}, }
})); }));
const Login = ({ state, description, connectOptions }: LoginProps) => { const Login = ({ state, description, connectOptions }: LoginProps) => {
const classes = useStyles();
const { t } = useTranslation(); const { t } = useTranslation();
const isConnected = AuthenticationService.isConnected(state); const isConnected = AuthenticationService.isConnected(state);
@ -220,7 +225,7 @@ const Login = ({ state, description, connectOptions }: LoginProps) => {
}; };
return ( return (
<div className={'login overflow-scroll ' + classes.root}> <Root className={'login overflow-scroll ' + classes.root}>
{ isConnected && <Navigate to={RouteEnum.SERVER} />} { isConnected && <Navigate to={RouteEnum.SERVER} />}
<div className="login__wrapper"> <div className="login__wrapper">
@ -333,7 +338,7 @@ const Login = ({ state, description, connectOptions }: LoginProps) => {
onSubmit={handleAccountActivationDialogSubmit} onSubmit={handleAccountActivationDialogSubmit}
handleClose={closeActivateAccountDialog} handleClose={closeActivateAccountDialog}
/> />
</div> </Root>
); );
} }

View file

@ -1,9 +1,9 @@
import React from 'react'; import React from 'react';
import { styled } from '@mui/material/styles';
import Dialog from '@mui/material/Dialog'; import Dialog from '@mui/material/Dialog';
import DialogContent from '@mui/material/DialogContent'; import DialogContent from '@mui/material/DialogContent';
import DialogTitle from '@mui/material/DialogTitle'; import DialogTitle from '@mui/material/DialogTitle';
import IconButton from '@mui/material/IconButton'; import IconButton from '@mui/material/IconButton';
import makeStyles from '@mui/styles/makeStyles';
import AddIcon from '@mui/icons-material/Add'; import AddIcon from '@mui/icons-material/Add';
import CloseIcon from '@mui/icons-material/Close'; import CloseIcon from '@mui/icons-material/Close';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
@ -13,16 +13,21 @@ import { KnownHostForm } from 'forms';
import './KnownHostDialog.css'; import './KnownHostDialog.css';
const useStyles = makeStyles(theme => ({ const PREFIX = 'KnownHostDialog';
root: {
const classes = {
root: `${PREFIX}-root`
};
const StyledDialog = styled(Dialog)(({ theme }) => ({
[`&.${classes.root}`]: {
'& .dialog-title__wrapper': { '& .dialog-title__wrapper': {
borderColor: theme.palette.grey[300] borderColor: theme.palette.grey[300]
} }
}, }
})); }));
const KnownHostDialog = ({ handleClose, onRemove, onSubmit, isOpen, host }: any) => { const KnownHostDialog = ({ handleClose, onRemove, onSubmit, isOpen, host }: any) => {
const classes = useStyles();
const { t } = useTranslation(); const { t } = useTranslation();
const mode = host ? 'edit' : 'add'; const mode = host ? 'edit' : 'add';
@ -34,7 +39,7 @@ const KnownHostDialog = ({ handleClose, onRemove, onSubmit, isOpen, host }: any)
}; };
return ( return (
<Dialog className={'KnownHostDialog ' + classes.root} onClose={handleOnClose} open={isOpen}> <StyledDialog className={'KnownHostDialog ' + classes.root} onClose={handleOnClose} open={isOpen}>
<DialogTitle className='dialog-title'> <DialogTitle className='dialog-title'>
<div className='dialog-title__wrapper'> <div className='dialog-title__wrapper'>
<Typography variant='h2'>{ t('KnownHostDialog.title', { mode }) }</Typography> <Typography variant='h2'>{ t('KnownHostDialog.title', { mode }) }</Typography>
@ -52,7 +57,7 @@ const KnownHostDialog = ({ handleClose, onRemove, onSubmit, isOpen, host }: any)
</Typography> </Typography>
<KnownHostForm onRemove={onRemove} onSubmit={onSubmit} host={host}></KnownHostForm> <KnownHostForm onRemove={onRemove} onSubmit={onSubmit} host={host}></KnownHostForm>
</DialogContent> </DialogContent>
</Dialog> </StyledDialog>
); );
}; };

View file

@ -27,7 +27,7 @@ export function useReduxEffect(
const store = useStore(); const store = useStore();
const handleChange = (): void => { const handleChange = (): void => {
const state = store.getState(); const state: any = store.getState();
const action = state.action; const action = state.action;
const previousValue = currentValue.current; const previousValue = currentValue.current;
currentValue.current = action.count; currentValue.current = action.count;

View file

@ -17,7 +17,6 @@ body,
body { body {
margin: 0; margin: 0;
line-height: 1.4;
font-family: font-family:
-apple-system, -apple-system,
BlinkMacSystemFont, BlinkMacSystemFont,

View file

@ -1,26 +1,25 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { Theme, StyledEngineProvider } from '@mui/material'; import { Theme, StyledEngineProvider } from '@mui/material';
import { ThemeProvider } from '@mui/material/styles'; import { ThemeProvider } from '@mui/material/styles';
import React from 'react';
import ReactDOM from 'react-dom';
import { AppShell } from 'containers';
import { AppShell } from './containers';
import { materialTheme } from './material-theme'; import { materialTheme } from './material-theme';
import './i18n';
import './i18n';
import './index.css'; import './index.css';
declare module '@mui/styles/defaultTheme' {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface DefaultTheme extends Theme {}
}
const AppWithMaterialTheme = () => ( const AppWithMaterialTheme = () => (
<StyledEngineProvider injectFirst> <StrictMode>
<ThemeProvider theme={materialTheme}> <StyledEngineProvider injectFirst>
<AppShell /> <ThemeProvider theme={materialTheme}>
</ThemeProvider> <AppShell />
</StyledEngineProvider> </ThemeProvider>
</StyledEngineProvider>
</StrictMode>
); );
ReactDOM.render(<AppWithMaterialTheme />, document.getElementById('root')); const container = document.getElementById('root');
const root = createRoot(container!);
root.render(<AppWithMaterialTheme />);

View file

@ -215,7 +215,10 @@ export const materialTheme = createTheme({
lineHeight: 1.4, lineHeight: 1.4,
color: palette.grey[500], color: palette.grey[500],
}, },
// body1: {}, body1: {
fontSize: '.75rem',
lineHeight: 1.4,
},
// body2: {}, // body2: {},
// button: {}, // button: {},
// caption: {}, // caption: {},

View file

@ -1,52 +1,17 @@
import $ from 'jquery'; import sanitize from 'sanitize-html';
export function sanitizeHtml(msg: string): string { export function sanitizeHtml(msg: string): string {
const $div = $('<div>').html(msg); return sanitize(msg, {
const whitelist = { allowedTags: ['br', 'a', 'img', 'center', 'b', 'font'],
tags: 'br,a,img,center,b,font', allowedAttributes: {
attrs: ['href', 'color'], '*': ['href', 'color', 'rel', 'target'],
href: ['http://', 'https://', 'ftp://', '//'] },
}; allowedSchemes: ['http', 'https', 'ftp'],
transformTags: {
// remove all tags, attributes, and href protocols except some 'a': sanitize.simpleTransform('a', {
enforceTagWhitelist($div, whitelist.tags); target: '_blank',
enforceAttrWhitelist($div, whitelist.attrs); rel: 'noopener noreferrer',
enforceHrefWhitelist($div, whitelist.href); }),
return $div.html();
}
function enforceTagWhitelist($el: JQuery<HTMLElement>, tags: string): void {
$el.find('*').not(tags).each(function enforceTag() {
$(this).replaceWith(this.innerHTML);
});
}
function enforceAttrWhitelist($el: JQuery<HTMLElement>, attrs: string[]): void {
$el.find('*').each(function enforceAttribute() {
const attributes = this.attributes;
let i = attributes.length;
while (i--) {
const attr = attributes[i];
if ($.inArray(attr.name, attrs) === -1) {
this.removeAttributeNode(attr);
}
} }
}); });
} }
function enforceHrefWhitelist($el: JQuery<HTMLElement>, hrefs: string[]): void {
$el.find('[href]').each(function enforceHref() {
const $_el = $(this);
const attributeValue = $_el.attr('href');
for (let protocol in hrefs) {
if (attributeValue.indexOf(hrefs[protocol]) === 0) {
$_el.attr('target', '_blank');
return;
}
}
$_el.removeAttr('href');
});
}