Feature/update dependency react to v18.x (#346)
* Update dependency "react" to v18.x Update to v18.2.0 * Update dependency "react" to v18.x - Fix some dependency version issue Something caused weird tsc issues, most likely a version mismatch since it got fixed by doing a clean install (deleting node_modules folder, yarn.lock and clearing the cache) of the dependencies * Update dependency "react" to v18.x - Fix "i18n" tsc issues * Update dependency "react" to v18.x - Fix tsc issue Typography can only have a single child * Update dependency "react" to v18.x - Prevent build warning TODO: migrate to Vite Gets used without being declared as a dependency. Since CRA is unmaintained this will not get fixed. * Update dependency "react" to v18.x - Use "createRoot" * Update dependency "react" to v18.x - Fix dnd in strict mode
This commit is contained in:
@@ -33,9 +33,9 @@
|
|||||||
"file-selector": "^0.6.0",
|
"file-selector": "^0.6.0",
|
||||||
"i18next": "^22.5.0",
|
"i18next": "^22.5.0",
|
||||||
"i18next-browser-languagedetector": "^7.0.2",
|
"i18next-browser-languagedetector": "^7.0.2",
|
||||||
"react": "^17.0.2",
|
"react": "^18.2.0",
|
||||||
"react-beautiful-dnd": "^13.1.1",
|
"react-beautiful-dnd": "^13.1.1",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^18.2.0",
|
||||||
"react-i18next": "^12.3.1",
|
"react-i18next": "^12.3.1",
|
||||||
"react-router-dom": "^6.11.2",
|
"react-router-dom": "^6.11.2",
|
||||||
"react-scripts": "^5.0.1",
|
"react-scripts": "^5.0.1",
|
||||||
@@ -45,9 +45,10 @@
|
|||||||
"web-vitals": "^3.3.2"
|
"web-vitals": "^3.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^17.0.2",
|
"@babel/plugin-proposal-private-property-in-object": "^7.21.10",
|
||||||
|
"@types/react": "^18.2.8",
|
||||||
"@types/react-beautiful-dnd": "^13.1.4",
|
"@types/react-beautiful-dnd": "^13.1.4",
|
||||||
"@types/react-dom": "^17.0.2",
|
"@types/react-dom": "^18.2.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.59.8",
|
"@typescript-eslint/eslint-plugin": "^5.59.8",
|
||||||
"@typescript-eslint/parser": "^5.59.8",
|
"@typescript-eslint/parser": "^5.59.8",
|
||||||
"eslint": "^8.35.0",
|
"eslint": "^8.35.0",
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ export default function ExtensionCard(props: IProps) {
|
|||||||
sx={{ color: installedState === InstalledState.OBSOLETE ? 'red' : 'inherit' }}
|
sx={{ color: installedState === InstalledState.OBSOLETE ? 'red' : 'inherit' }}
|
||||||
onClick={() => handleButtonClick()}
|
onClick={() => handleButtonClick()}
|
||||||
>
|
>
|
||||||
{t(INSTALLED_STATE_TO_TRANSLATION_KEY_MAP[installedState])}
|
{t(INSTALLED_STATE_TO_TRANSLATION_KEY_MAP[installedState]) as string}
|
||||||
</Button>
|
</Button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ const LibraryOptionsPanel: React.FC<IProps> = ({ open, onClose }) => {
|
|||||||
open={open}
|
open={open}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
tabs={['filter', 'sort', 'display']}
|
tabs={['filter', 'sort', 'display']}
|
||||||
tabTitle={(key) => t(TITLES[key])}
|
tabTitle={(key) => t(TITLES[key]) as string}
|
||||||
tabContent={(key) => {
|
tabContent={(key) => {
|
||||||
if (key === 'filter') {
|
if (key === 'filter') {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ const ChapterOptions: React.FC<IProps> = ({ open, onClose, options, optionsDispa
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
minHeight={150}
|
minHeight={150}
|
||||||
tabs={['filter', 'sort', 'display']}
|
tabs={['filter', 'sort', 'display']}
|
||||||
tabTitle={(key) => t(TITLES[key])}
|
tabTitle={(key) => t(TITLES[key]) as string}
|
||||||
tabContent={(key) => {
|
tabContent={(key) => {
|
||||||
if (key === 'filter') {
|
if (key === 'filter') {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -49,8 +49,10 @@ const DownloadStateIndicator: React.FC<DownloadStateIndicatorProps> = ({ downloa
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="caption" component="div" color="text.secondary">
|
<Typography variant="caption" component="div" color="text.secondary">
|
||||||
|
<>
|
||||||
{download.progress !== 0 && `${Math.round(download.progress * 100)}%`}
|
{download.progress !== 0 && `${Math.round(download.progress * 100)}%`}
|
||||||
{download.progress === 0 && t(DOWNLOAD_STATE_TO_TRANSLATION_KEY_MAP[download.state])}
|
{download.progress === 0 && t(DOWNLOAD_STATE_TO_TRANSLATION_KEY_MAP[download.state])}
|
||||||
|
</>
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export default function DesktopSideBar({ navBarItems }: IProps) {
|
|||||||
<Link to={path} style={{ color: 'inherit', textDecoration: 'none' }} key={path}>
|
<Link to={path} style={{ color: 'inherit', textDecoration: 'none' }} key={path}>
|
||||||
<ListItemButton disableRipple key={title}>
|
<ListItemButton disableRipple key={title}>
|
||||||
<ListItemIcon sx={{ minWidth: '0' }}>
|
<ListItemIcon sx={{ minWidth: '0' }}>
|
||||||
<Tooltip placement="right" title={t(title)}>
|
<Tooltip placement="right" title={t(title) as string}>
|
||||||
{iconFor(path, IconComponent, SelectedIconComponent)}
|
{iconFor(path, IconComponent, SelectedIconComponent)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export default function MobileBottomBar({ navBarItems }: IProps) {
|
|||||||
: 'grey.600',
|
: 'grey.600',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t(title)}
|
{t(title) as string}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
|
|||||||
@@ -7,11 +7,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import { Box } from '@mui/material';
|
import { Box } from '@mui/material';
|
||||||
import Page from 'components/reader/Page';
|
import Page from 'components/reader/Page';
|
||||||
import DoublePage from 'components/reader/DoublePage';
|
import DoublePage from 'components/reader/DoublePage';
|
||||||
import { IReaderProps } from 'typings';
|
import { IReaderProps } from 'typings';
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
const isSpreadPage = (image: HTMLImageElement): boolean => {
|
const isSpreadPage = (image: HTMLImageElement): boolean => {
|
||||||
const aspectRatio = image.height / image.width;
|
const aspectRatio = image.height / image.width;
|
||||||
@@ -58,8 +58,11 @@ export default function DoublePagedPager(props: IReaderProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function displayPages() {
|
function displayPages() {
|
||||||
|
const container = document.getElementById('display');
|
||||||
|
const root = createRoot(container!);
|
||||||
|
|
||||||
if (pagesDisplayed.current === 2) {
|
if (pagesDisplayed.current === 2) {
|
||||||
ReactDOM.render(
|
root.render(
|
||||||
<DoublePage
|
<DoublePage
|
||||||
key={curPage}
|
key={curPage}
|
||||||
index={curPage}
|
index={curPage}
|
||||||
@@ -67,10 +70,9 @@ export default function DoublePagedPager(props: IReaderProps) {
|
|||||||
image2src={pages[curPage + 1].src}
|
image2src={pages[curPage + 1].src}
|
||||||
settings={settings}
|
settings={settings}
|
||||||
/>,
|
/>,
|
||||||
document.getElementById('display'),
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
ReactDOM.render(
|
root.render(
|
||||||
<Page
|
<Page
|
||||||
key={curPage}
|
key={curPage}
|
||||||
index={curPage}
|
index={curPage}
|
||||||
@@ -78,7 +80,6 @@ export default function DoublePagedPager(props: IReaderProps) {
|
|||||||
onImageLoad={() => {}}
|
onImageLoad={() => {}}
|
||||||
settings={settings}
|
settings={settings}
|
||||||
/>,
|
/>,
|
||||||
document.getElementById('display'),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,16 +6,14 @@
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Slide, { SlideProps } from '@mui/material/Slide';
|
import Slide, { SlideProps } from '@mui/material/Slide';
|
||||||
import Snackbar from '@mui/material/Snackbar';
|
import Snackbar from '@mui/material/Snackbar';
|
||||||
import MuiAlert, { AlertColor as Severity } from '@mui/material/Alert';
|
import MuiAlert, { AlertColor as Severity } from '@mui/material/Alert';
|
||||||
|
import { createRoot, Root } from 'react-dom/client';
|
||||||
|
|
||||||
function removeToast(id: string) {
|
function removeToast(root: Root) {
|
||||||
const container = document.querySelector(`#${id}`)!!;
|
root.unmount();
|
||||||
ReactDOM.unmountComponentAtNode(container);
|
|
||||||
document.body.removeChild(container);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function Transition(props: SlideProps) {
|
function Transition(props: SlideProps) {
|
||||||
@@ -57,9 +55,10 @@ export default function makeToast(message: string, severity: Severity) {
|
|||||||
|
|
||||||
document.body.appendChild(container);
|
document.body.appendChild(container);
|
||||||
|
|
||||||
ReactDOM.render(<Toast message={message} severity={severity} />, container);
|
const root = createRoot(container!);
|
||||||
|
root.render(<Toast message={message} severity={severity} />);
|
||||||
|
|
||||||
setTimeout(() => removeToast(container.id), 3500);
|
setTimeout(() => removeToast(root), 3500);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeToaster([toasts, setToasts]: [React.ReactElement[], (arg0: React.ReactElement[]) => void]): [
|
export function makeToaster([toasts, setToasts]: [React.ReactElement[], (arg0: React.ReactElement[]) => void]): [
|
||||||
|
|||||||
@@ -7,15 +7,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import { createRoot } from 'react-dom/client';
|
||||||
import App from 'App';
|
import App from 'App';
|
||||||
import 'index.css';
|
import 'index.css';
|
||||||
// roboto font
|
// roboto font
|
||||||
import '@fontsource/roboto';
|
import '@fontsource/roboto';
|
||||||
|
|
||||||
ReactDOM.render(
|
const container = document.getElementById('root');
|
||||||
|
const root = createRoot(container!);
|
||||||
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
document.getElementById('root'),
|
|
||||||
);
|
);
|
||||||
|
|||||||
31
src/lib/StrictModeDroppable.tsx
Normal file
31
src/lib/StrictModeDroppable.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { Droppable, DroppableProps } from 'react-beautiful-dnd';
|
||||||
|
|
||||||
|
// issue: https://github.com/atlassian/react-beautiful-dnd/issues/2399
|
||||||
|
// credit for fix: https://github.com/atlassian/react-beautiful-dnd/issues/2399#issuecomment-1175638194
|
||||||
|
export default function StrictModeDroppable({ children, ...props }: DroppableProps) {
|
||||||
|
const [enabled, setEnabled] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const animation = requestAnimationFrame(() => setEnabled(true));
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelAnimationFrame(animation);
|
||||||
|
setEnabled(false);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Droppable {...props}>{children}</Droppable>;
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ import IconButton from '@mui/material/IconButton';
|
|||||||
import NavbarContext from 'components/context/NavbarContext';
|
import NavbarContext from 'components/context/NavbarContext';
|
||||||
import EmptyView from 'components/util/EmptyView';
|
import EmptyView from 'components/util/EmptyView';
|
||||||
import React, { useContext, useEffect } from 'react';
|
import React, { useContext, useEffect } from 'react';
|
||||||
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
|
import { DragDropContext, Draggable } from 'react-beautiful-dnd';
|
||||||
|
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import useSubscription from 'components/library/useSubscription';
|
import useSubscription from 'components/library/useSubscription';
|
||||||
@@ -27,6 +27,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { IChapter, IQueue } from 'typings';
|
import { IChapter, IQueue } from 'typings';
|
||||||
import makeToast from 'components/util/Toast';
|
import makeToast from 'components/util/Toast';
|
||||||
import requestManager from 'lib/RequestManager';
|
import requestManager from 'lib/RequestManager';
|
||||||
|
import StrictModeDroppable from 'lib/StrictModeDroppable';
|
||||||
|
|
||||||
const initialQueue = {
|
const initialQueue = {
|
||||||
status: 'Stopped',
|
status: 'Stopped',
|
||||||
@@ -95,7 +96,7 @@ const DownloadQueue: React.FC = () => {
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
</NavbarToolbar>
|
</NavbarToolbar>
|
||||||
<DragDropContext onDragEnd={onDragEnd}>
|
<DragDropContext onDragEnd={onDragEnd}>
|
||||||
<Droppable droppableId="droppable">
|
<StrictModeDroppable droppableId="droppable">
|
||||||
{(droppableProvided) => (
|
{(droppableProvided) => (
|
||||||
<Box ref={droppableProvided.innerRef} sx={{ pt: 1 }}>
|
<Box ref={droppableProvided.innerRef} sx={{ pt: 1 }}>
|
||||||
{queue.map((item, index) => (
|
{queue.map((item, index) => (
|
||||||
@@ -155,7 +156,7 @@ const DownloadQueue: React.FC = () => {
|
|||||||
{droppableProvided.placeholder}
|
{droppableProvided.placeholder}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Droppable>
|
</StrictModeDroppable>
|
||||||
</DragDropContext>
|
</DragDropContext>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,14 +8,7 @@
|
|||||||
|
|
||||||
import React, { useMemo, useState, useContext, useEffect } from 'react';
|
import React, { useMemo, useState, useContext, useEffect } from 'react';
|
||||||
import { List, ListItem, ListItemText, ListItemIcon, IconButton } from '@mui/material';
|
import { List, ListItem, ListItemText, ListItemIcon, IconButton } from '@mui/material';
|
||||||
import {
|
import { DragDropContext, Draggable, DropResult, DraggingStyle, NotDraggingStyle } from 'react-beautiful-dnd';
|
||||||
DragDropContext,
|
|
||||||
Droppable,
|
|
||||||
Draggable,
|
|
||||||
DropResult,
|
|
||||||
DraggingStyle,
|
|
||||||
NotDraggingStyle,
|
|
||||||
} from 'react-beautiful-dnd';
|
|
||||||
import DragHandleIcon from '@mui/icons-material/DragHandle';
|
import DragHandleIcon from '@mui/icons-material/DragHandle';
|
||||||
import EditIcon from '@mui/icons-material/Edit';
|
import EditIcon from '@mui/icons-material/Edit';
|
||||||
import { useTheme, Palette } from '@mui/material/styles';
|
import { useTheme, Palette } from '@mui/material/styles';
|
||||||
@@ -35,6 +28,7 @@ import { ICategory } from 'typings';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { DEFAULT_FULL_FAB_HEIGHT } from 'components/util/StyledFab';
|
import { DEFAULT_FULL_FAB_HEIGHT } from 'components/util/StyledFab';
|
||||||
import requestManager from 'lib/RequestManager';
|
import requestManager from 'lib/RequestManager';
|
||||||
|
import StrictModeDroppable from 'lib/StrictModeDroppable';
|
||||||
|
|
||||||
const getItemStyle = (
|
const getItemStyle = (
|
||||||
isDragging: boolean,
|
isDragging: boolean,
|
||||||
@@ -134,7 +128,7 @@ export default function Categories() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DragDropContext onDragEnd={onDragEnd}>
|
<DragDropContext onDragEnd={onDragEnd}>
|
||||||
<Droppable droppableId="droppable">
|
<StrictModeDroppable droppableId="droppable">
|
||||||
{(droppableProvided) => (
|
{(droppableProvided) => (
|
||||||
<List ref={droppableProvided.innerRef} sx={{ paddingBottom: DEFAULT_FULL_FAB_HEIGHT }}>
|
<List ref={droppableProvided.innerRef} sx={{ paddingBottom: DEFAULT_FULL_FAB_HEIGHT }}>
|
||||||
{categories.map((item, index) => (
|
{categories.map((item, index) => (
|
||||||
@@ -178,7 +172,7 @@ export default function Categories() {
|
|||||||
{droppableProvided.placeholder}
|
{droppableProvided.placeholder}
|
||||||
</List>
|
</List>
|
||||||
)}
|
)}
|
||||||
</Droppable>
|
</StrictModeDroppable>
|
||||||
</DragDropContext>
|
</DragDropContext>
|
||||||
<Fab
|
<Fab
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|||||||
Reference in New Issue
Block a user