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:
parent
5854a635ca
commit
26d7fe2ff0
17 changed files with 26225 additions and 3552 deletions
2
.github/workflows/web-build.yml
vendored
2
.github/workflows/web-build.yml
vendored
|
@ -28,7 +28,7 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
node_version:
|
||||
- 12
|
||||
- 16
|
||||
- lts/*
|
||||
|
||||
steps:
|
||||
|
|
1
webclient/.npmrc
Normal file
1
webclient/.npmrc
Normal file
|
@ -0,0 +1 @@
|
|||
legacy-peer-deps=true
|
29365
webclient/package-lock.json
generated
29365
webclient/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -2,40 +2,6 @@
|
|||
"name": "webclient",
|
||||
"version": "1.0.0",
|
||||
"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": {
|
||||
"prebuild": "node prebuild.js",
|
||||
"prestart": "node prebuild.js",
|
||||
|
@ -50,6 +16,61 @@
|
|||
"prepare": "cd .. && husky install",
|
||||
"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": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
|
@ -69,28 +90,5 @@
|
|||
"moduleNameMapper": {
|
||||
"\\.(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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,33 @@
|
|||
import React from 'react';
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import ErrorOutlinedIcon from '@mui/icons-material/ErrorOutlined';
|
||||
|
||||
import './InputField.css';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
const PREFIX = 'InputField';
|
||||
|
||||
const classes = {
|
||||
root: `${PREFIX}-root`
|
||||
};
|
||||
|
||||
const Root = styled('div')(({ theme }) => ({
|
||||
[`&.${classes.root}`]: {
|
||||
'& .InputField-error': {
|
||||
color: theme.palette.error.main
|
||||
},
|
||||
|
||||
'& .InputField-warning': {
|
||||
color: theme.palette.warning.main
|
||||
}
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const InputField = ({ input, meta: { touched, error, warning }, ...args }) => {
|
||||
const classes = useStyles();
|
||||
const InputField = ({ input, meta, ...args }) => {
|
||||
const { touched, error, warning } = meta;
|
||||
|
||||
return (
|
||||
<div className={'InputField ' + classes.root}>
|
||||
<Root className={'InputField ' + classes.root}>
|
||||
{ touched && (
|
||||
<div className="InputField-validation">
|
||||
{
|
||||
|
@ -47,7 +53,7 @@ const InputField = ({ input, meta: { touched, error, warning }, ...args }) => {
|
|||
size="small"
|
||||
fullWidth={true}
|
||||
/>
|
||||
</div>
|
||||
</Root>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Select, MenuItem } from '@mui/material';
|
||||
import Button from '@mui/material/Button';
|
||||
|
@ -7,7 +8,6 @@ import IconButton from '@mui/material/IconButton';
|
|||
import WifiTetheringIcon from '@mui/icons-material/WifiTethering';
|
||||
import PortableWifiOffIcon from '@mui/icons-material/PortableWifiOff';
|
||||
import InputLabel from '@mui/material/InputLabel';
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
import Check from '@mui/icons-material/Check';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import EditRoundedIcon from '@mui/icons-material/Edit';
|
||||
|
@ -29,8 +29,14 @@ enum TestConnection {
|
|||
SUCCESS = 'success',
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
const PREFIX = 'KnownHosts';
|
||||
|
||||
const classes = {
|
||||
root: `${PREFIX}-root`
|
||||
};
|
||||
|
||||
const Root = styled('div')(({ theme }) => ({
|
||||
[`&.${classes.root}`]: {
|
||||
'& .KnownHosts-error': {
|
||||
color: theme.palette.error.main
|
||||
},
|
||||
|
@ -50,13 +56,13 @@ const useStyles = makeStyles(theme => ({
|
|||
color: theme.palette.success.main
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}));
|
||||
|
||||
const KnownHosts = (props) => {
|
||||
const { input: { onChange }, meta, disabled } = props;
|
||||
const { touched, error, warning } = meta;
|
||||
const classes = useStyles();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [hostsState, setHostsState] = useState({
|
||||
|
@ -195,7 +201,7 @@ const KnownHosts = (props) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className={'KnownHosts ' + classes.root}>
|
||||
<Root className={'KnownHosts ' + classes.root}>
|
||||
<FormControl className='KnownHosts-form' size='small' variant='outlined'>
|
||||
{ touched && (
|
||||
<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={showDeleteToast} onClose={() => setShowDeleteToast(false)}>{ t('KnownHosts.toast', { mode: 'deleted' }) }</Toast>
|
||||
<Toast open={showEditToast} onClose={() => setShowEditToast(false)}>{ t('KnownHosts.toast', { mode: 'edited' }) }</Toast>
|
||||
</div>
|
||||
)
|
||||
</Root>
|
||||
);
|
||||
};
|
||||
|
||||
export default KnownHosts;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// eslint-disable-next-line
|
||||
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 { CardDTO, TokenDTO } from 'services';
|
||||
|
@ -10,17 +10,24 @@ import TokenDetails from '../TokenDetails/TokenDetails';
|
|||
|
||||
import './CardCallout.css';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
popover: {
|
||||
const PREFIX = 'CardCallout';
|
||||
|
||||
const classes = {
|
||||
popover: `${PREFIX}-popover`,
|
||||
popoverContent: `${PREFIX}-popoverContent`
|
||||
};
|
||||
|
||||
const Root = styled('span')(({ theme }) => ({
|
||||
[`& .${classes.popover}`]: {
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
popoverContent: {
|
||||
|
||||
[`& .${classes.popoverContent}`]: {
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
}
|
||||
}));
|
||||
|
||||
const CardCallout = ({ name }) => {
|
||||
const classes = useStyles();
|
||||
const [card, setCard] = useState<CardDTO>(null);
|
||||
const [token, setToken] = useState<TokenDTO>(null);
|
||||
const [anchorEl, setAnchorEl] = useState<Element>(null);
|
||||
|
@ -48,7 +55,7 @@ const CardCallout = ({ name }) => {
|
|||
const open = Boolean(anchorEl);
|
||||
|
||||
return (
|
||||
<span className='callout'>
|
||||
<Root className='callout'>
|
||||
<span
|
||||
onMouseEnter={handlePopoverOpen}
|
||||
onMouseLeave={handlePopoverClose}
|
||||
|
@ -80,7 +87,7 @@ const CardCallout = ({ name }) => {
|
|||
</Popover>
|
||||
)
|
||||
}
|
||||
</span>
|
||||
</Root>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -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 Toast from './Toast'
|
||||
|
@ -24,15 +24,15 @@ const ToastContext: Context<any> = createContext<ToastState>({
|
|||
removeToast: (key) => {},
|
||||
});
|
||||
|
||||
export const ToastProvider: FC<ReactNode> = (props) => {
|
||||
export const ToastProvider: FC<PropsWithChildren> = (props) => {
|
||||
const { children } = props
|
||||
const [state, dispatch] = useReducer(reducer, initialState)
|
||||
const providerState = {
|
||||
toasts: state.toasts,
|
||||
addToast: (key, children) => dispatch({ type: ACTIONS.ADD_TOAST, payload: { key, children } }),
|
||||
openToast: key => dispatch({ type: ACTIONS.OPEN_TOAST, payload: key }),
|
||||
closeToast: key => dispatch({ type: ACTIONS.CLOSE_TOAST, payload: key }),
|
||||
removeToast: key => dispatch({ type: ACTIONS.REMOVE_TOAST, payload: key }),
|
||||
openToast: key => dispatch({ type: ACTIONS.OPEN_TOAST, payload: { key } }),
|
||||
closeToast: key => dispatch({ type: ACTIONS.CLOSE_TOAST, payload: { key } }),
|
||||
removeToast: key => dispatch({ type: ACTIONS.REMOVE_TOAST, payload: { key } }),
|
||||
}
|
||||
return (
|
||||
<ToastContext.Provider value={providerState}>
|
||||
|
@ -41,7 +41,7 @@ export const ToastProvider: FC<ReactNode> = (props) => {
|
|||
{Array.from(state.toasts).map(([key, value]) => {
|
||||
const { isOpen, children } = value;
|
||||
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}
|
||||
</Toast>
|
||||
)
|
||||
|
|
|
@ -6,41 +6,54 @@ export const ACTIONS = {
|
|||
}
|
||||
|
||||
export const initialState = {
|
||||
toasts: new Map()
|
||||
toasts: {}
|
||||
}
|
||||
|
||||
export function reducer(state, action) {
|
||||
const { type, payload } = action
|
||||
export function reducer(state, { type, payload }) {
|
||||
const { key, children } = payload;
|
||||
|
||||
switch (type) {
|
||||
case ACTIONS.ADD_TOAST: {
|
||||
const newState = { ...state }
|
||||
newState.toasts = new Map(Array.from(state.toasts))
|
||||
const { toasts } = newState;
|
||||
const { key, children } = payload
|
||||
toasts.set(key, { isOpen: false, children })
|
||||
return newState
|
||||
return {
|
||||
...state,
|
||||
toasts: {
|
||||
...state.toasts,
|
||||
[key]: {
|
||||
isOpen: false,
|
||||
children,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
case ACTIONS.OPEN_TOAST: {
|
||||
const newState = { ...state }
|
||||
newState.toasts = new Map(Array.from(state.toasts))
|
||||
const { toasts } = newState;
|
||||
const toast = toasts.get(payload)
|
||||
toasts.set(payload, { isOpen: true, children: toast.children })
|
||||
return newState
|
||||
return {
|
||||
...state,
|
||||
toasts: {
|
||||
...state.toasts,
|
||||
[key]: {
|
||||
...state.toasts[key],
|
||||
isOpen: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
case ACTIONS.CLOSE_TOAST: {
|
||||
const newState = { ...state }
|
||||
newState.toasts = new Map(Array.from(state.toasts))
|
||||
const { toasts } = newState;
|
||||
const toast = toasts.get(payload)
|
||||
toasts.set(payload, { isOpen: false, children: toast.children })
|
||||
return newState
|
||||
return {
|
||||
...state,
|
||||
toasts: {
|
||||
...state.toasts,
|
||||
[key]: {
|
||||
...state.toasts[key],
|
||||
isOpen: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
case ACTIONS.REMOVE_TOAST: {
|
||||
const newState = { ...state }
|
||||
newState.toasts = new Map(Array.from(state.toasts))
|
||||
newState.toasts.delete(payload)
|
||||
return newState
|
||||
const newState = { ...state };
|
||||
delete newState.toasts[key];
|
||||
|
||||
return newState;
|
||||
}
|
||||
default:
|
||||
throw Error('Please pick an available action')
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { useState } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { useTranslation, Trans } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
import { Images } from 'images';
|
||||
|
@ -11,8 +11,14 @@ import { RouteEnum } from 'types';
|
|||
|
||||
import './Initialize.css';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
const PREFIX = 'Initialize';
|
||||
|
||||
const classes = {
|
||||
root: `${PREFIX}-root`
|
||||
};
|
||||
|
||||
const Root = styled('div')(({ theme }) => ({
|
||||
[`&.${classes.root}`]: {
|
||||
'& .Initialize-graphics': {
|
||||
color: theme.palette.primary.contrastText,
|
||||
},
|
||||
|
@ -20,17 +26,16 @@ const useStyles = makeStyles(theme => ({
|
|||
'& .Initialize-graphics__bar': {
|
||||
backgroundColor: theme.palette.primary.contrastText,
|
||||
},
|
||||
},
|
||||
}
|
||||
}));
|
||||
|
||||
const Initialize = ({ initialized }: InitializeProps) => {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return initialized
|
||||
? <Navigate to={RouteEnum.LOGIN} />
|
||||
: (
|
||||
<div className={'Initialize ' + classes.root}>
|
||||
<Root className={'Initialize ' + classes.root}>
|
||||
<div className='Initialize-content'>
|
||||
<img src={Images.Logo} alt="logo" />
|
||||
<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="bottomBar Initialize-graphics__bar" />
|
||||
</div>
|
||||
</div>
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { useState, useCallback } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
import Button from '@mui/material/Button';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
@ -20,8 +20,14 @@ import { ServerSelectors, ServerTypes } from 'store';
|
|||
import './Login.css';
|
||||
import { useToast } from 'components/Toast';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
const PREFIX = 'Login';
|
||||
|
||||
const classes = {
|
||||
root: `${PREFIX}-root`
|
||||
};
|
||||
|
||||
const Root = styled('div')(({ theme }) => ({
|
||||
[`&.${classes.root}`]: {
|
||||
'& .login-content__header': {
|
||||
color: theme.palette.success.light
|
||||
},
|
||||
|
@ -54,11 +60,10 @@ const useStyles = makeStyles(theme => ({
|
|||
display: 'flex',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}));
|
||||
|
||||
const Login = ({ state, description, connectOptions }: LoginProps) => {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const isConnected = AuthenticationService.isConnected(state);
|
||||
|
@ -220,7 +225,7 @@ const Login = ({ state, description, connectOptions }: LoginProps) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className={'login overflow-scroll ' + classes.root}>
|
||||
<Root className={'login overflow-scroll ' + classes.root}>
|
||||
{ isConnected && <Navigate to={RouteEnum.SERVER} />}
|
||||
|
||||
<div className="login__wrapper">
|
||||
|
@ -333,7 +338,7 @@ const Login = ({ state, description, connectOptions }: LoginProps) => {
|
|||
onSubmit={handleAccountActivationDialogSubmit}
|
||||
handleClose={closeActivateAccountDialog}
|
||||
/>
|
||||
</div>
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import Dialog from '@mui/material/Dialog';
|
||||
import DialogContent from '@mui/material/DialogContent';
|
||||
import DialogTitle from '@mui/material/DialogTitle';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
@ -13,16 +13,21 @@ import { KnownHostForm } from 'forms';
|
|||
|
||||
import './KnownHostDialog.css';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
const PREFIX = 'KnownHostDialog';
|
||||
|
||||
const classes = {
|
||||
root: `${PREFIX}-root`
|
||||
};
|
||||
|
||||
const StyledDialog = styled(Dialog)(({ theme }) => ({
|
||||
[`&.${classes.root}`]: {
|
||||
'& .dialog-title__wrapper': {
|
||||
borderColor: theme.palette.grey[300]
|
||||
}
|
||||
},
|
||||
}
|
||||
}));
|
||||
|
||||
const KnownHostDialog = ({ handleClose, onRemove, onSubmit, isOpen, host }: any) => {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const mode = host ? 'edit' : 'add';
|
||||
|
@ -34,7 +39,7 @@ const KnownHostDialog = ({ handleClose, onRemove, onSubmit, isOpen, host }: any)
|
|||
};
|
||||
|
||||
return (
|
||||
<Dialog className={'KnownHostDialog ' + classes.root} onClose={handleOnClose} open={isOpen}>
|
||||
<StyledDialog className={'KnownHostDialog ' + classes.root} onClose={handleOnClose} open={isOpen}>
|
||||
<DialogTitle className='dialog-title'>
|
||||
<div className='dialog-title__wrapper'>
|
||||
<Typography variant='h2'>{ t('KnownHostDialog.title', { mode }) }</Typography>
|
||||
|
@ -52,7 +57,7 @@ const KnownHostDialog = ({ handleClose, onRemove, onSubmit, isOpen, host }: any)
|
|||
</Typography>
|
||||
<KnownHostForm onRemove={onRemove} onSubmit={onSubmit} host={host}></KnownHostForm>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</StyledDialog>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ export function useReduxEffect(
|
|||
const store = useStore();
|
||||
|
||||
const handleChange = (): void => {
|
||||
const state = store.getState();
|
||||
const state: any = store.getState();
|
||||
const action = state.action;
|
||||
const previousValue = currentValue.current;
|
||||
currentValue.current = action.count;
|
||||
|
|
|
@ -17,7 +17,6 @@ body,
|
|||
|
||||
body {
|
||||
margin: 0;
|
||||
line-height: 1.4;
|
||||
font-family:
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
|
|
|
@ -1,26 +1,25 @@
|
|||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { Theme, StyledEngineProvider } from '@mui/material';
|
||||
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 './i18n';
|
||||
|
||||
import './i18n';
|
||||
import './index.css';
|
||||
|
||||
declare module '@mui/styles/defaultTheme' {
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
interface DefaultTheme extends Theme {}
|
||||
}
|
||||
|
||||
const AppWithMaterialTheme = () => (
|
||||
<StyledEngineProvider injectFirst>
|
||||
<ThemeProvider theme={materialTheme}>
|
||||
<AppShell />
|
||||
</ThemeProvider>
|
||||
</StyledEngineProvider>
|
||||
<StrictMode>
|
||||
<StyledEngineProvider injectFirst>
|
||||
<ThemeProvider theme={materialTheme}>
|
||||
<AppShell />
|
||||
</ThemeProvider>
|
||||
</StyledEngineProvider>
|
||||
</StrictMode>
|
||||
);
|
||||
|
||||
ReactDOM.render(<AppWithMaterialTheme />, document.getElementById('root'));
|
||||
const container = document.getElementById('root');
|
||||
const root = createRoot(container!);
|
||||
|
||||
root.render(<AppWithMaterialTheme />);
|
||||
|
|
|
@ -215,7 +215,10 @@ export const materialTheme = createTheme({
|
|||
lineHeight: 1.4,
|
||||
color: palette.grey[500],
|
||||
},
|
||||
// body1: {},
|
||||
body1: {
|
||||
fontSize: '.75rem',
|
||||
lineHeight: 1.4,
|
||||
},
|
||||
// body2: {},
|
||||
// button: {},
|
||||
// caption: {},
|
||||
|
|
|
@ -1,52 +1,17 @@
|
|||
import $ from 'jquery';
|
||||
import sanitize from 'sanitize-html';
|
||||
|
||||
export function sanitizeHtml(msg: string): string {
|
||||
const $div = $('<div>').html(msg);
|
||||
const whitelist = {
|
||||
tags: 'br,a,img,center,b,font',
|
||||
attrs: ['href', 'color'],
|
||||
href: ['http://', 'https://', 'ftp://', '//']
|
||||
};
|
||||
|
||||
// remove all tags, attributes, and href protocols except some
|
||||
enforceTagWhitelist($div, whitelist.tags);
|
||||
enforceAttrWhitelist($div, whitelist.attrs);
|
||||
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);
|
||||
}
|
||||
return sanitize(msg, {
|
||||
allowedTags: ['br', 'a', 'img', 'center', 'b', 'font'],
|
||||
allowedAttributes: {
|
||||
'*': ['href', 'color', 'rel', 'target'],
|
||||
},
|
||||
allowedSchemes: ['http', 'https', 'ftp'],
|
||||
transformTags: {
|
||||
'a': sanitize.simpleTransform('a', {
|
||||
target: '_blank',
|
||||
rel: 'noopener noreferrer',
|
||||
}),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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');
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue