From ea67a2f64af7f58447de0ac2db7a690b46f44b5a Mon Sep 17 00:00:00 2001 From: Harshith Mohan <26010946+harshithmohan@users.noreply.github.com> Date: Wed, 14 Aug 2024 19:32:19 +0530 Subject: [PATCH] Renamer and other fixes, allow renaming from unrecognized util and file search (#982) * Fix logout from error boundary * Allow renaming from unrecognized util and file search * Make monaco-editor work without internet * Remove old css classes * Stop default login backdrop from loading if not required --- package.json | 4 +- pnpm-lock.yaml | 66 +++++++++++++++++++ src/components/ErrorBoundary.tsx | 3 +- .../Utilities/Renamer/RenamerEditor.tsx | 22 +++++++ .../Utilities/Renamer/RenamerScript.tsx | 28 +++++--- src/core/monaco.ts | 26 ++++++++ src/css/modals.css | 17 ----- src/pages/SentryErrorBoundaryWrapper.tsx | 4 +- src/pages/login/LoginPage.tsx | 5 +- src/pages/utilities/FileSearch.tsx | 14 +++- .../UnrecognizedTab.tsx | 14 +++- vite.config.mjs | 3 +- 12 files changed, 173 insertions(+), 33 deletions(-) create mode 100644 src/components/Utilities/Renamer/RenamerEditor.tsx create mode 100644 src/core/monaco.ts diff --git a/package.json b/package.json index 9b2324bde..7de41fd36 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "format-thousands": "^2.0.0", "immer": "^10.1.1", "lodash": "^4.17.21", + "monaco-editor": "^0.50.0", "pretty-bytes": "^6.1.1", "react": "^18.3.1", "react-animate-height": "^3.2.3", @@ -95,7 +96,8 @@ "tailwindcss": "^3.4.3", "tailwindcss-text-fill-stroke": "2.0.0-beta.1", "typescript": "5.4.5", - "vite": "^5.2.11" + "vite": "^5.2.11", + "vite-plugin-webpackchunkname": "^1.0.3" }, "scripts": { "eslint": "eslint --cache --format=node_modules/eslint-formatter-pretty src", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b720e4dd2..abe87187c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,6 +74,9 @@ importers: lodash: specifier: ^4.17.21 version: 4.17.21 + monaco-editor: + specifier: ^0.50.0 + version: 0.50.0 pretty-bytes: specifier: ^6.1.1 version: 6.1.1 @@ -267,6 +270,9 @@ importers: vite: specifier: ^5.2.11 version: 5.2.11(@types/node@20.12.12) + vite-plugin-webpackchunkname: + specifier: ^1.0.3 + version: 1.0.3(rollup@4.14.3) packages: @@ -1006,6 +1012,24 @@ packages: resolution: {integrity: sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==} engines: {node: '>=14.0.0'} + '@rollup/plugin-alias@5.1.0': + resolution: {integrity: sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@5.1.0': + resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + '@rollup/rollup-android-arm-eabi@4.14.3': resolution: {integrity: sha512-X9alQ3XM6I9IlSlmC8ddAvMSyG1WuHk5oUnXGw+yUBs3BFoTizmG1La/Gr8fVJvDWAq+zlYTZ9DBgrlKRVY06g==} cpu: [arm] @@ -1868,6 +1892,9 @@ packages: resolution: {integrity: sha512-scxAJaewsahbqTYrGKJihhViaM6DDZDDoucfvzNbK0pOren1g/daDQ3IAhzn+1G14rBG7w+i5N+qul60++zlKA==} engines: {node: '>= 0.4'} + es-module-lexer@1.5.4: + resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} + es-object-atoms@1.0.0: resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} engines: {node: '>= 0.4'} @@ -2036,6 +2063,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -3400,6 +3430,10 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + slash@4.0.0: + resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} + engines: {node: '>=12'} + slice-ansi@4.0.0: resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} engines: {node: '>=10'} @@ -3679,6 +3713,9 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + vite-plugin-webpackchunkname@1.0.3: + resolution: {integrity: sha512-88lt6IrgCumnf4Up8eyaSJbmo4V0ZIaR4M94fbZvGGmK2aWMmPGVsiFBszYE7Kq04I9tGjLFnyremn+KEgEGyw==} + vite@5.2.11: resolution: {integrity: sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==} engines: {node: ^18.0.0 || >=20.0.0} @@ -4531,6 +4568,20 @@ snapshots: '@remix-run/router@1.16.1': {} + '@rollup/plugin-alias@5.1.0(rollup@4.14.3)': + dependencies: + slash: 4.0.0 + optionalDependencies: + rollup: 4.14.3 + + '@rollup/pluginutils@5.1.0(rollup@4.14.3)': + dependencies: + '@types/estree': 1.0.5 + estree-walker: 2.0.2 + picomatch: 2.3.1 + optionalDependencies: + rollup: 4.14.3 + '@rollup/rollup-android-arm-eabi@4.14.3': optional: true @@ -5524,6 +5575,8 @@ snapshots: iterator.prototype: 1.1.2 safe-array-concat: 1.1.2 + es-module-lexer@1.5.4: {} + es-object-atoms@1.0.0: dependencies: es-errors: 1.3.0 @@ -5808,6 +5861,8 @@ snapshots: estraverse@5.3.0: {} + estree-walker@2.0.2: {} + esutils@2.0.3: {} event-target-shim@5.0.1: {} @@ -7157,6 +7212,8 @@ snapshots: slash@3.0.0: {} + slash@4.0.0: {} + slice-ansi@4.0.0: dependencies: ansi-styles: 4.3.0 @@ -7551,6 +7608,15 @@ snapshots: util-deprecate@1.0.2: {} + vite-plugin-webpackchunkname@1.0.3(rollup@4.14.3): + dependencies: + '@rollup/plugin-alias': 5.1.0(rollup@4.14.3) + '@rollup/pluginutils': 5.1.0(rollup@4.14.3) + es-module-lexer: 1.5.4 + magic-string: 0.30.8 + transitivePeerDependencies: + - rollup + vite@5.2.11(@types/node@20.12.12): dependencies: esbuild: 0.20.2 diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx index bdf09a581..89e6e110a 100644 --- a/src/components/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary.tsx @@ -16,7 +16,7 @@ type RouteError = { error: Error; }; -const ErrorBoundary = ({ error }: { error?: Error }) => { +const ErrorBoundary = ({ error, resetError }: { error?: Error, resetError?: () => void }) => { const dispatch = useDispatch(); const routeError = useRouteError() as RouteError; const navigate = useNavigate(); @@ -28,6 +28,7 @@ const ErrorBoundary = ({ error }: { error?: Error }) => { const handleLogout = useEventCallback(() => { dispatch({ type: Events.AUTH_LOGOUT }); + if (resetError) resetError(); }); const handleWebUiUpdate = useEventCallback((channel: 'Stable' | 'Dev') => { diff --git a/src/components/Utilities/Renamer/RenamerEditor.tsx b/src/components/Utilities/Renamer/RenamerEditor.tsx new file mode 100644 index 000000000..2cabb4ea5 --- /dev/null +++ b/src/components/Utilities/Renamer/RenamerEditor.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { Editor } from '@monaco-editor/react'; + +// To import custom monaco config +import '@/core/monaco'; + +type Props = { + defaultLanguage: string; + value: string; + onChange: (value: string) => void; +}; + +const RenamerEditor = ({ defaultLanguage, onChange, value }: Props) => ( + +); + +export default RenamerEditor; diff --git a/src/components/Utilities/Renamer/RenamerScript.tsx b/src/components/Utilities/Renamer/RenamerScript.tsx index fb889aa33..6d7476438 100644 --- a/src/components/Utilities/Renamer/RenamerScript.tsx +++ b/src/components/Utilities/Renamer/RenamerScript.tsx @@ -1,5 +1,6 @@ -import React from 'react'; -import { Editor } from '@monaco-editor/react'; +import React, { Suspense, lazy } from 'react'; +import { mdiLoading } from '@mdi/js'; +import { Icon } from '@mdi/react'; import { findKey } from 'lodash'; import useEventCallback from '@/hooks/useEventCallback'; @@ -7,6 +8,10 @@ import useEventCallback from '@/hooks/useEventCallback'; import type { RenamerConfigSettingsType, RenamerSettingsType } from '@/core/react-query/renamer/types'; import type { Updater } from 'use-immer'; +const RenamerEditor = lazy( + () => import('@/components/Utilities/Renamer/RenamerEditor'), +); + type Props = { newConfig: Record; setNewConfig: Updater>; @@ -35,12 +40,19 @@ const RenamerScript = ({ newConfig, setNewConfig, settingsModel }: Props) => { } return ( - + + + + } + > + + ); }; diff --git a/src/core/monaco.ts b/src/core/monaco.ts new file mode 100644 index 000000000..a187957dd --- /dev/null +++ b/src/core/monaco.ts @@ -0,0 +1,26 @@ +/* eslint-disable new-cap */ +import { loader } from '@monaco-editor/react'; +import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; +import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'; +// import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'; +// import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'; +import 'monaco-editor/esm/vs/basic-languages/lua/lua.contribution'; +import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution'; +import 'monaco-editor/esm/vs/basic-languages/typescript/typescript.contribution'; +import 'monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution'; + +// eslint-disable-next-line no-restricted-globals +self.MonacoEnvironment = { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getWorker(_, label) { + // if (label === 'json') { + // return new jsonWorker(); + // } + // if (label === 'typescript' || label === 'javascript') { + // return new tsWorker(); + // } + return new editorWorker(); + }, +}; + +loader.config({ monaco }); diff --git a/src/css/modals.css b/src/css/modals.css index 9ea4fd029..83fdaa232 100644 --- a/src/css/modals.css +++ b/src/css/modals.css @@ -1,20 +1,3 @@ -.profile-modal { - width: 42rem; -} - -.profile-modal-image { - background-image: url('/images/OnePiece.png'); - background-repeat: no-repeat; - background-position: center; - background-size: cover; - width: 22rem; -} - -.profile-modal-image-alpha { - color: theme('colors.panel-background'); - background-color: theme('colors.panel-background-transparent'); -} - .modal-transition-left { transition: transform 300ms ease-in-out; transform: translateX(-30rem); diff --git a/src/pages/SentryErrorBoundaryWrapper.tsx b/src/pages/SentryErrorBoundaryWrapper.tsx index ec63a0577..28ec1a705 100644 --- a/src/pages/SentryErrorBoundaryWrapper.tsx +++ b/src/pages/SentryErrorBoundaryWrapper.tsx @@ -18,7 +18,9 @@ const SentryErrorBoundaryWrapper = () => { }, [versionQuery.data]); return ( - }> + } + > ); diff --git a/src/pages/login/LoginPage.tsx b/src/pages/login/LoginPage.tsx index bd5ce991a..35e477828 100644 --- a/src/pages/login/LoginPage.tsx +++ b/src/pages/login/LoginPage.tsx @@ -40,7 +40,7 @@ function LoginPage() { const [rememberUser, setRememberUser] = useState(false); const [pollingInterval, setPollingInterval] = useState(500); const [{ imageUrl, seriesId, seriesName }, setLoginImage] = useState(() => ({ - imageUrl: 'default', + imageUrl: '', seriesName: '', seriesId: 0, })); @@ -56,6 +56,7 @@ function LoginPage() { }; useEffect(() => { + if (imageMetadataQuery.isPending) return; if (!imageMetadataQuery.isSuccess || !imageMetadataQuery.data.Type) { setLoginImage({ imageUrl: 'default', seriesName: 'One Piece', seriesId: 0 }); return; @@ -66,7 +67,7 @@ function LoginPage() { seriesName: Series?.Name ?? '', seriesId: Series?.ID ?? 0, }); - }, [imageMetadataQuery.isSuccess, imageMetadataQuery.data]); + }, [imageMetadataQuery.isSuccess, imageMetadataQuery.isPending, imageMetadataQuery.data]); useEffect(() => { if (!serverStatusQuery.data) setPollingInterval(500); diff --git a/src/pages/utilities/FileSearch.tsx b/src/pages/utilities/FileSearch.tsx index 0c6f24b9e..1e6a850cc 100644 --- a/src/pages/utilities/FileSearch.tsx +++ b/src/pages/utilities/FileSearch.tsx @@ -1,11 +1,13 @@ import React, { useMemo, useState } from 'react'; -import { Link } from 'react-router-dom'; +import { useDispatch } from 'react-redux'; +import { Link, useNavigate } from 'react-router-dom'; import { mdiChevronLeft, mdiChevronRight, mdiCloseCircleOutline, mdiDatabaseSearchOutline, mdiDatabaseSyncOutline, + mdiFileDocumentEditOutline, mdiLoading, mdiMagnify, mdiMinusCircleOutline, @@ -38,6 +40,7 @@ import { import { useFileQuery, useFilesInfiniteQuery } from '@/core/react-query/file/queries'; import { invalidateQueries } from '@/core/react-query/queryClient'; import { useSeriesQuery } from '@/core/react-query/series/queries'; +import { addFiles } from '@/core/slices/utilities/renamer'; import { FileSortCriteriaEnum } from '@/core/types/api/file'; import useEventCallback from '@/hooks/useEventCallback'; import useFlattenListResult from '@/hooks/useFlattenListResult'; @@ -58,6 +61,9 @@ const Menu = ( setSelectedRows, } = props; + const dispatch = useDispatch(); + const navigate = useNavigate(); + const [showConfirmModal, setShowConfirmModal] = useState(false); const { mutate: deleteFiles } = useDeleteFilesMutation(); @@ -121,6 +127,11 @@ const Menu = ( if (failedFiles) toast.error(`Rescan failed for ${failedFiles} files!`); }); + const handleRename = useEventCallback(() => { + dispatch(addFiles(selectedRows)); + navigate('/webui/utilities/renamer'); + }); + return (
@@ -136,6 +147,7 @@ const Menu = (
+ setSelectedRows([])} diff --git a/src/pages/utilities/UnrecognizedUtilityTabs/UnrecognizedTab.tsx b/src/pages/utilities/UnrecognizedUtilityTabs/UnrecognizedTab.tsx index 75a095986..2d43094d5 100644 --- a/src/pages/utilities/UnrecognizedUtilityTabs/UnrecognizedTab.tsx +++ b/src/pages/utilities/UnrecognizedUtilityTabs/UnrecognizedTab.tsx @@ -1,5 +1,5 @@ import React, { useMemo, useState } from 'react'; -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { useNavigate } from 'react-router-dom'; import useMeasure from 'react-use-measure'; import { @@ -8,6 +8,7 @@ import { mdiDatabaseSyncOutline, mdiDumpTruck, mdiEyeOffOutline, + mdiFileDocumentEditOutline, mdiFileDocumentOutline, mdiLoading, mdiMagnify, @@ -44,6 +45,7 @@ import { import { useFilesInfiniteQuery } from '@/core/react-query/file/queries'; import { useImportFoldersQuery } from '@/core/react-query/import-folder/queries'; import { invalidateQueries } from '@/core/react-query/queryClient'; +import { addFiles } from '@/core/slices/utilities/renamer'; import { FileSortCriteriaEnum } from '@/core/types/api/file'; import useEventCallback from '@/hooks/useEventCallback'; import useFlattenListResult from '@/hooks/useFlattenListResult'; @@ -67,6 +69,9 @@ const Menu = ( setSeriesSelectModal, } = props; + const dispatch = useDispatch(); + const navigate = useNavigate(); + const [showConfirmModal, setShowConfirmModal] = useState(false); const { mutate: deleteFiles } = useDeleteFilesMutation(); @@ -148,6 +153,11 @@ const Menu = ( .catch(console.error); }); + const handleRename = useEventCallback(() => { + dispatch(addFiles(selectedRows)); + navigate('/webui/utilities/renamer'); + }); + const renderSelectedRowActions = useMemo(() => ( <>
@@ -166,6 +176,7 @@ const Menu = ( setSeriesSelectModal(true)} icon={mdiFileDocumentOutline} name="Add To AniDB" /> + ), [ + handleRename, ignoreFiles, rehashFiles, rescanFiles, diff --git a/vite.config.mjs b/vite.config.mjs index 7c6b346b9..481b3193c 100644 --- a/vite.config.mjs +++ b/vite.config.mjs @@ -6,6 +6,7 @@ import pkg from './package.json'; import { defineConfig } from 'vite'; import { sentryVitePlugin } from '@sentry/vite-plugin'; import react from '@vitejs/plugin-react'; +import { manualChunksPlugin } from 'vite-plugin-webpackchunkname' export default defineConfig(async () => { const isDebug = process.env.NODE_ENV !== 'production'; @@ -47,7 +48,7 @@ export default defineConfig(async () => { sourcemap: true, chunkSizeWarningLimit: 2000 }, - plugins: [react(), sentryPlugin], + plugins: [react(), sentryPlugin, manualChunksPlugin()], base: "/webui/" }; });