upgrade packages + improve typing

This commit is contained in:
seavor 2026-04-14 11:34:29 -05:00
parent fd55f4fb7f
commit 19f5eefdd2
138 changed files with 4504 additions and 11015 deletions

View file

@ -1,24 +1,16 @@
import React from 'react';
import { connect } from 'react-redux';
import { Navigate } from 'react-router-dom';
import { ServerSelectors } from 'store';
import { RouteEnum } from 'types';
import { useAppSelector } from 'store/store';
import { AuthenticationService } from 'api';
const AuthGuard = ({ state }: AuthGuardProps) => {
const AuthGuard = () => {
const state = useAppSelector(s => ServerSelectors.getState(s));
return !AuthenticationService.isConnected(state)
? <Navigate to={RouteEnum.LOGIN} />
: <div></div>;
};
interface AuthGuardProps {
state: number;
}
const mapStateToProps = state => ({
state: ServerSelectors.getState(state),
});
export default connect(mapStateToProps)(AuthGuard);
export default AuthGuard;

View file

@ -1,27 +1,16 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import React from 'react';
import { Navigate } from 'react-router-dom';
import { ServerSelectors } from 'store';
import { User } from 'types';
import { AuthenticationService } from 'api';
import { RouteEnum } from 'types';
import { useAppSelector } from 'store/store';
class ModGuard extends Component<ModGuardProps> {
render() {
return !AuthenticationService.isModerator(this.props.user)
? <Navigate to={RouteEnum.SERVER} />
: '';
}
const ModGuard = () => {
const user = useAppSelector(state => ServerSelectors.getUser(state));
return !AuthenticationService.isModerator(user)
? <Navigate to={RouteEnum.SERVER} />
: <></>;
};
interface ModGuardProps {
user: User;
}
const mapStateToProps = state => ({
user: ServerSelectors.getUser(state),
});
export default connect(mapStateToProps)(ModGuard);
export default ModGuard;

View file

@ -6,7 +6,7 @@ import { InputField } from 'components';
import './InputAction.css';
const InputAction = ({ action, label, name, validate, disabled }) => (
const InputAction = ({ action, label, name, validate = () => false, disabled = false }) => (
<div className="input-action">
<div className="input-action__item">
<Field label={label} name={name} component={InputField} validate={validate} />
@ -19,9 +19,4 @@ const InputAction = ({ action, label, name, validate, disabled }) => (
</div>
);
InputAction.defaultProps = {
disabled: false,
validate: () => false,
}
export default InputAction;

View file

@ -99,7 +99,7 @@ const KnownHosts = (props) => {
}, [loadKnownHosts]);
useEffect(() => {
const { hosts, selectedHost } = hostsState;
const { selectedHost } = hostsState;
if (selectedHost?.id) {
updateLastSelectedHost(selectedHost.id).then(() => {
@ -255,7 +255,7 @@ const KnownHosts = (props) => {
</div>
{ host.editable && (
<IconButton className='KnownHosts-item__edit' size='small' color='primary' onClick={(e) => {
<IconButton className='KnownHosts-item__edit' size='small' color='primary' onClick={() => {
openEditKnownHostDialog(hostsState.hosts[index]);
}}>
<EditRoundedIcon fontSize='small' />

View file

@ -1,9 +1,8 @@
// eslint-disable-next-line
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Select, MenuItem } from '@mui/material';
import FormControl from '@mui/material/FormControl';
import InputLabel from '@mui/material/InputLabel';
import { Images } from 'images/Images';
import { Language, LanguageCountry, LanguageNative } from 'types';

View file

@ -1,4 +1,4 @@
// eslint-disable-next-line
import React, { useMemo, useState } from 'react';
import { styled } from '@mui/material/styles';
import Popover from '@mui/material/Popover';
@ -17,7 +17,7 @@ const classes = {
popoverContent: `${PREFIX}-popoverContent`
};
const Root = styled('span')(({ theme }) => ({
const Root = styled('span')(() => ({
[`& .${classes.popover}`]: {
pointerEvents: 'none',
},

View file

@ -15,7 +15,7 @@ import {
import CardCallout from './CardCallout';
import './Message.css';
const Message = ({ message: { message, messageType, timeOf, timeReceived } }) => (
const Message = ({ message: { message } }) => (
<div className='message'>
<div className='message__detail'>
<ParsedMessage message={message} />

View file

@ -1,7 +1,5 @@
import { Component, CElement } from 'react';
import { connect } from 'react-redux';
import Grid from '@mui/material/Grid';
import Hidden from '@mui/material/Hidden';
import './ThreePaneLayout.css';
@ -12,25 +10,23 @@ class ThreePaneLayout extends Component<ThreePaneLayoutProps> {
return (
<div className="three-pane-layout">
<Grid container rowSpacing={0} columnSpacing={2} className="grid">
<Grid item xs={12} md={9} lg={10} className="grid-main">
<Grid item className={
<Grid size={{ xs: 12, md: 9, lg: 10 }} className="grid-main">
<Grid className={
'grid-main__top'
+ (this.props.fixedHeight ? ' fixedHeight' : '')
}>
{this.props.top}
</Grid>
<Grid item className={
<Grid className={
'grid-main__bottom'
+ (this.props.fixedHeight ? ' fixedHeight' : '')
}>
{this.props.bottom}
</Grid>
</Grid>
<Hidden mdDown>
<Grid item md={3} lg={2} className="grid-side">
{this.props.side}
</Grid>
</Hidden>
<Grid size={{ md: 3, lg: 2 }} sx={{ display: { xs: 'none', md: 'block' } }} className="grid-side">
{this.props.side}
</Grid>
</Grid>
</div>
);
@ -44,6 +40,4 @@ interface ThreePaneLayoutProps {
fixedHeight?: boolean,
}
const mapStateToProps = state => ({});
export default connect(mapStateToProps)(ThreePaneLayout);
export default ThreePaneLayout;

View file

@ -1,9 +1,9 @@
import * as React from 'react'
import ReactDOM from 'react-dom'
import Alert, { AlertProps } from '@mui/material/Alert';
import Alert from '@mui/material/Alert';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import Slide, { SlideProps } from '@mui/material/Slide';
import Slide from '@mui/material/Slide';
import Snackbar from '@mui/material/Snackbar';
const iconMapping = {
@ -11,7 +11,7 @@ const iconMapping = {
}
function Toast(props) {
const { open, onClose, severity, autoHideDuration, children } = props
const { open, onClose, severity = 'success', autoHideDuration = 10000, children } = props
const rootElemRef = React.useRef(document.createElement('div'));
@ -37,9 +37,9 @@ function Toast(props) {
TransitionComponent={TransitionLeft}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
>
<Alert onClose={handleClose} severity={severity} iconMapping={iconMapping}>
{children}
</Alert>
<Alert onClose={handleClose} severity={severity} iconMapping={iconMapping}
slotProps={{ message: { children } }}
/>
</Snackbar>
)
if (!rootElemRef.current) {
@ -52,12 +52,6 @@ function Toast(props) {
);
}
Toast.defaultProps = {
severity: 'success',
// 10s wait before automatically dismissing the Toast.
autoHideDuration: 10000,
}
function TransitionLeft(props) {
return <Slide {...props} direction="left" />;
}

View file

@ -1,4 +1,4 @@
import { createContext, FC, PropsWithChildren, ReactChild, ReactNode, useContext, useEffect, useReducer, ContextType, Context } from 'react'
import { createContext, FC, PropsWithChildren, ReactChild, ReactNode, useContext, useEffect, useReducer, Context } from 'react'
import { ACTIONS, initialState, reducer } from './reducer';
import Toast from './Toast'
@ -18,10 +18,10 @@ interface ToastState {
const ToastContext: Context<any> = createContext<ToastState>({
toasts: new Map<string, ToastEntry>(),
addToast: (key, children) => {},
openToast: (key) => {},
closeToast: (key) => {},
removeToast: (key) => {},
addToast: (_key, _children) => {},
openToast: (_key) => {},
closeToast: (_key) => {},
removeToast: (_key) => {},
});
export const ToastProvider: FC<PropsWithChildren> = (props) => {
@ -56,7 +56,7 @@ export interface ToastHookOptions {
children: ReactNode
}
export function useToast<ToastHookOptions>({ key, children }) {
export function useToast({ key, children }) {
const { addToast, openToast, closeToast, removeToast } = useContext(ToastContext)
useEffect(() => {

View file

@ -1,6 +1,5 @@
// eslint-disable-next-line
import React, { Component } from "react";
import { connect } from 'react-redux';
import React, { useState } from 'react';
import { NavLink, generatePath } from 'react-router-dom';
import Menu from '@mui/material/Menu';
@ -10,141 +9,85 @@ import { Images } from 'images/Images';
import { SessionService } from 'api';
import { ServerSelectors } from 'store';
import { RouteEnum, User } from 'types';
import { useAppSelector } from 'store/store';
import './UserDisplay.css';
class UserDisplay extends Component<UserDisplayProps, UserDisplayState> {
constructor(props) {
super(props);
const UserDisplay = ({ user }: UserDisplayProps) => {
const buddyList = useAppSelector(state => ServerSelectors.getBuddyList(state));
const ignoreList = useAppSelector(state => ServerSelectors.getIgnoreList(state));
const [position, setPosition] = useState<{ x: number; y: number } | null>(null);
this.handleClick = this.handleClick.bind(this);
this.handleClose = this.handleClose.bind(this);
this.navigateToUserProfile = this.navigateToUserProfile.bind(this);
this.addToBuddyList = this.addToBuddyList.bind(this);
this.removeFromBuddyList = this.removeFromBuddyList.bind(this);
this.addToIgnoreList = this.addToIgnoreList.bind(this);
this.removeFromIgnoreList = this.removeFromIgnoreList.bind(this);
const { name, country } = user;
this.isABuddy = this.isABuddy.bind(this);
this.isIgnored = this.isIgnored.bind(this);
this.state = {
position: null
};
}
handleClick(event) {
const handleClick = (event) => {
event.preventDefault();
setPosition({ x: event.clientX + 2, y: event.clientY + 4 });
};
this.setState({
position: {
x: event.clientX + 2,
y: event.clientY + 4,
}
});
}
const handleClose = () => setPosition(null);
handleClose() {
this.setState({
position: null
});
}
const isABuddy = buddyList.filter(u => u.name === user.name).length;
const isIgnored = ignoreList.filter(u => u.name === user.name).length;
navigateToUserProfile() {
this.handleClose();
}
const onAddBuddy = () => {
SessionService.addToBuddyList(user.name);
handleClose();
};
const onRemoveBuddy = () => {
SessionService.removeFromBuddyList(user.name);
handleClose();
};
const onAddIgnore = () => {
SessionService.addToIgnoreList(user.name);
handleClose();
};
const onRemoveIgnore = () => {
SessionService.removeFromIgnoreList(user.name);
handleClose();
};
addToBuddyList() {
SessionService.addToBuddyList(this.props.user.name);
this.handleClose();
}
removeFromBuddyList() {
SessionService.removeFromBuddyList(this.props.user.name);
this.handleClose();
}
addToIgnoreList() {
SessionService.addToIgnoreList(this.props.user.name);
this.handleClose();
}
removeFromIgnoreList() {
SessionService.removeFromIgnoreList(this.props.user.name);
this.handleClose();
}
isABuddy() {
return this.props.buddyList.filter(user => user.name === this.props.user.name).length;
}
isIgnored() {
return this.props.ignoreList.filter(user => user.name === this.props.user.name).length;
}
render() {
const { user } = this.props;
const { position } = this.state;
const { name, country } = user;
const isABuddy = this.isABuddy();
const isIgnored = this.isIgnored();
// console.log('user', name, !!isABuddy, !!isIgnored);
return (
<div className="user-display">
<NavLink to={generatePath(RouteEnum.PLAYER, { name })} className="plain-link">
<div className="user-display__details" onContextMenu={this.handleClick}>
<img className="user-display__country" src={Images.Countries[country]} alt={country}></img>
<div className="user-display__name single-line-ellipsis">{name}</div>
</div>
</NavLink>
<div className="user-display__menu">
<Menu
open={Boolean(position)}
onClose={this.handleClose}
anchorReference='anchorPosition'
anchorPosition={
position !== null
? { top: position.y, left: position.x }
: undefined
}
>
<NavLink to={generatePath(RouteEnum.PLAYER, { name })} className="user-display__link plain-link">
<MenuItem dense>Chat</MenuItem>
</NavLink>
{
!isABuddy
? (<MenuItem dense onClick={this.addToBuddyList}>Add to Buddy List</MenuItem>)
: (<MenuItem dense onClick={this.removeFromBuddyList}>Remove From Buddy List</MenuItem>)
}
{
!isIgnored
? (<MenuItem dense onClick={this.addToIgnoreList}>Add to Ignore List</MenuItem>)
: (<MenuItem dense onClick={this.removeFromIgnoreList}>Remove From Ignore List</MenuItem>)
}
</Menu>
return (
<div className="user-display">
<NavLink to={generatePath(RouteEnum.PLAYER, { name })} className="plain-link">
<div className="user-display__details" onContextMenu={handleClick}>
<img className="user-display__country" src={Images.Countries[country]} alt={country}></img>
<div className="user-display__name single-line-ellipsis">{name}</div>
</div>
</NavLink>
<div className="user-display__menu">
<Menu
open={Boolean(position)}
onClose={handleClose}
anchorReference='anchorPosition'
anchorPosition={
position !== null
? { top: position.y, left: position.x }
: undefined
}
>
<NavLink to={generatePath(RouteEnum.PLAYER, { name })} className="user-display__link plain-link">
<MenuItem dense>Chat</MenuItem>
</NavLink>
{
!isABuddy
? (<MenuItem dense onClick={onAddBuddy}>Add to Buddy List</MenuItem>)
: (<MenuItem dense onClick={onRemoveBuddy}>Remove From Buddy List</MenuItem>)
}
{
!isIgnored
? (<MenuItem dense onClick={onAddIgnore}>Add to Ignore List</MenuItem>)
: (<MenuItem dense onClick={onRemoveIgnore}>Remove From Ignore List</MenuItem>)
}
</Menu>
</div>
);
}
}
</div>
);
};
interface UserDisplayProps {
user: User;
buddyList: User[];
ignoreList: User[];
}
interface UserDisplayState {
position: any;
}
const mapStateToProps = (state) => ({
buddyList: ServerSelectors.getBuddyList(state),
ignoreList: ServerSelectors.getIgnoreList(state)
});
export default connect(mapStateToProps)(UserDisplay);
export default UserDisplay;

View file

@ -1,34 +1,29 @@
// eslint-disable-next-line
import React from "react";
import { FixedSizeList as List } from 'react-window';
import AutoSizer from 'react-virtualized-auto-sizer';
import { List, RowComponentProps } from 'react-window';
import './VirtualList.css';
const VirtualList = ({ items, itemKey, className = {}, size = 30 }) => (
<div className="virtual-list">
<AutoSizer>
{({ height, width }) => (
<List
className={`virtual-list__list ${className}`}
height={height}
width={width}
itemData={items}
itemCount={items.length}
itemSize={size}
itemKey={itemKey}
>
{Row}
</List>
)}
</AutoSizer>
interface RowData {
items: any[];
}
const Row = ({ index, style, items }: RowComponentProps<RowData>) => (
<div style={style}>
{items[index]}
</div>
);
const Row = ({ data, index, style }) => (
<div style={style}>
{data[index]}
const VirtualList = ({ items, className = {}, size = 30 }) => (
<div className="virtual-list">
<List<RowData>
className={`virtual-list__list ${className}`}
rowCount={items.length}
rowHeight={size}
rowComponent={Row}
rowProps={{ items }}
/>
</div>
);