diff --git a/ui/v2.5/package.json b/ui/v2.5/package.json index 67d128a9a..1793ccf36 100644 --- a/ui/v2.5/package.json +++ b/ui/v2.5/package.json @@ -48,7 +48,7 @@ "i18n-iso-countries": "^6.4.0", "intersection-observer": "^0.12.0", "localforage": "1.9.0", - "lodash": "^4.17.20", + "lodash-es": "^4.17.21", "mousetrap": "^1.6.5", "mousetrap-pause": "^1.0.0", "normalize-url": "^4.5.1", @@ -92,7 +92,7 @@ "@types/apollo-upload-client": "^14.1.0", "@types/classnames": "^2.2.11", "@types/fslightbox-react": "^1.4.0", - "@types/lodash": "^4.14.168", + "@types/lodash-es": "^4.17.6", "@types/mousetrap": "^1.6.5", "@types/node": "14.14.22", "@types/react": "17.0.31", diff --git a/ui/v2.5/src/App.tsx b/ui/v2.5/src/App.tsx index 7a3abdd2a..f008e1d97 100755 --- a/ui/v2.5/src/App.tsx +++ b/ui/v2.5/src/App.tsx @@ -1,12 +1,11 @@ -import React, { useEffect } from "react"; +import React, { lazy, Suspense, useEffect, useState } from "react"; import { Route, Switch, useRouteMatch } from "react-router-dom"; import { IntlProvider, CustomFormats } from "react-intl"; import { Helmet } from "react-helmet"; -import { mergeWith } from "lodash"; +import cloneDeep from "lodash-es/cloneDeep"; +import mergeWith from "lodash-es/mergeWith"; import { ToastProvider } from "src/hooks/Toast"; import LightboxProvider from "src/hooks/Lightbox/context"; -import { library } from "@fortawesome/fontawesome-svg-core"; -import { fas } from "@fortawesome/free-solid-svg-icons"; import { initPolyfills } from "src/polyfills"; import locales from "src/locales"; @@ -15,41 +14,48 @@ import { flattenMessages } from "src/utils"; import Mousetrap from "mousetrap"; import MousetrapPause from "mousetrap-pause"; import { ErrorBoundary } from "./components/ErrorBoundary"; -import Galleries from "./components/Galleries/Galleries"; import { MainNavbar } from "./components/MainNavbar"; import { PageNotFound } from "./components/PageNotFound"; -import Performers from "./components/Performers/Performers"; -import FrontPage from "./components/FrontPage/FrontPage"; -import Scenes from "./components/Scenes/Scenes"; -import { Settings } from "./components/Settings/Settings"; -import { Stats } from "./components/Stats"; -import Studios from "./components/Studios/Studios"; -import { SceneFilenameParser } from "./components/SceneFilenameParser/SceneFilenameParser"; -import { SceneDuplicateChecker } from "./components/SceneDuplicateChecker/SceneDuplicateChecker"; -import Movies from "./components/Movies/Movies"; -import Tags from "./components/Tags/Tags"; -import Images from "./components/Images/Images"; -import { Setup } from "./components/Setup/Setup"; -import { Migrate } from "./components/Setup/Migrate"; import * as GQL from "./core/generated-graphql"; import { LoadingIndicator, TITLE_SUFFIX } from "./components/Shared"; + import { ConfigurationProvider } from "./hooks/Config"; -import { ManualProvider } from "./components/Help/Manual"; +import { ManualProvider } from "./components/Help/context"; import { InteractiveProvider } from "./hooks/Interactive/context"; +const Performers = lazy(() => import("./components/Performers/Performers")); +const FrontPage = lazy(() => import("./components/FrontPage/FrontPage")); +const Scenes = lazy(() => import("./components/Scenes/Scenes")); +const Settings = lazy(() => import("./components/Settings/Settings")); +const Stats = lazy(() => import("./components/Stats")); +const Studios = lazy(() => import("./components/Studios/Studios")); +const Galleries = lazy(() => import("./components/Galleries/Galleries")); + +const Movies = lazy(() => import("./components/Movies/Movies")); +const Tags = lazy(() => import("./components/Tags/Tags")); +const Images = lazy(() => import("./components/Images/Images")); +const Setup = lazy(() => import("./components/Setup/Setup")); +const Migrate = lazy(() => import("./components/Setup/Migrate")); + +const SceneFilenameParser = lazy( + () => import("./components/SceneFilenameParser/SceneFilenameParser") +); +const SceneDuplicateChecker = lazy( + () => import("./components/SceneDuplicateChecker/SceneDuplicateChecker") +); + initPolyfills(); MousetrapPause(Mousetrap); -// Set fontawesome/free-solid-svg as default fontawesome icons -library.add(fas); - const intlFormats: CustomFormats = { date: { long: { year: "numeric", month: "long", day: "numeric" }, }, }; +const defaultLocale = "en-GB"; + function languageMessageString(language: string) { return language.replace(/-/, ""); } @@ -57,25 +63,32 @@ function languageMessageString(language: string) { export const App: React.FC = () => { const config = useConfiguration(); const { data: systemStatusData } = useSystemStatus(); - const defaultLocale = "en-GB"; + const language = config.data?.configuration?.interface?.language ?? defaultLocale; - const defaultMessageLanguage = languageMessageString(defaultLocale); - const messageLanguage = languageMessageString(language); // use en-GB as default messages if any messages aren't found in the chosen language - const mergedMessages = mergeWith( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (locales as any)[defaultMessageLanguage], - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (locales as any)[messageLanguage], - (objVal, srcVal) => { - if (srcVal === "") { - return objVal; - } - } - ); - const messages = flattenMessages(mergedMessages); + const [messages, setMessages] = useState<{}>(); + + useEffect(() => { + const setLocale = async () => { + const defaultMessageLanguage = languageMessageString(defaultLocale); + const messageLanguage = languageMessageString(language); + + const defaultMessages = await locales[defaultMessageLanguage](); + const mergedMessages = cloneDeep(Object.assign({}, defaultMessages)); + const chosenMessages = await locales[messageLanguage](); + mergeWith(mergedMessages, chosenMessages, (objVal, srcVal) => { + if (srcVal === "") { + return objVal; + } + }); + + setMessages(flattenMessages(mergedMessages)); + }; + + setLocale(); + }, [language]); const setupMatch = useRouteMatch(["/setup", "/migrate"]); @@ -118,52 +131,64 @@ export const App: React.FC = () => { } return ( - - - - - - - - - - - - - - - - - + }> + + + + + + + + + + + + + + + + + + ); } return ( - - - - - - - - {maybeRenderNavbar()} -
{renderContent()}
-
-
-
-
-
-
+ + + }> + + + + + {maybeRenderNavbar()} +
+ {renderContent()} +
+
+
+
+
+
+
+ + ) : null}
); }; diff --git a/ui/v2.5/src/components/Changelog/Version.tsx b/ui/v2.5/src/components/Changelog/Version.tsx index b274a7c3d..cd5b99442 100644 --- a/ui/v2.5/src/components/Changelog/Version.tsx +++ b/ui/v2.5/src/components/Changelog/Version.tsx @@ -1,3 +1,4 @@ +import { faAngleDown, faAngleUp } from "@fortawesome/free-solid-svg-icons"; import React, { useState } from "react"; import { Button, Card, Collapse } from "react-bootstrap"; import { FormattedDate, FormattedMessage } from "react-intl"; @@ -33,7 +34,7 @@ const Version: React.FC = ({

} > @@ -205,3 +206,5 @@ export const GenerateDialog: React.FC = ({ ); }; + +export default GenerateDialog; diff --git a/ui/v2.5/src/components/Dialogs/IdentifyDialog/FieldOptions.tsx b/ui/v2.5/src/components/Dialogs/IdentifyDialog/FieldOptions.tsx index fad0ff7a0..8b04da91b 100644 --- a/ui/v2.5/src/components/Dialogs/IdentifyDialog/FieldOptions.tsx +++ b/ui/v2.5/src/components/Dialogs/IdentifyDialog/FieldOptions.tsx @@ -5,6 +5,11 @@ import * as GQL from "src/core/generated-graphql"; import { FormattedMessage, useIntl } from "react-intl"; import { multiValueSceneFields, SceneField, sceneFields } from "./constants"; import { ThreeStateBoolean } from "./ThreeStateBoolean"; +import { + faCheck, + faPencilAlt, + faTimes, +} from "@fortawesome/free-solid-svg-icons"; interface IFieldOptionsEditor { options: GQL.IdentifyFieldOptions | undefined; @@ -148,10 +153,10 @@ const FieldOptionsEditor: React.FC = ({ return intl.formatMessage({ id: "actions.use_default" }); } if (value) { - return ; + return ; } - return ; + return ; } const defaultVal = defaultOptions?.fieldOptions?.find( @@ -212,7 +217,7 @@ const FieldOptionsEditor: React.FC = ({ className="minimal text-success" onClick={() => onEditOptions()} > - + ) : ( <> )} diff --git a/ui/v2.5/src/components/Dialogs/IdentifyDialog/IdentifyDialog.tsx b/ui/v2.5/src/components/Dialogs/IdentifyDialog/IdentifyDialog.tsx index 49714e51c..f5964ee97 100644 --- a/ui/v2.5/src/components/Dialogs/IdentifyDialog/IdentifyDialog.tsx +++ b/ui/v2.5/src/components/Dialogs/IdentifyDialog/IdentifyDialog.tsx @@ -20,6 +20,11 @@ import { Manual } from "src/components/Help/Manual"; import { IScraperSource } from "./constants"; import { OptionsEditor } from "./Options"; import { SourcesEditor, SourcesList } from "./Sources"; +import { + faCogs, + faFolderOpen, + faQuestionCircle, +} from "@fortawesome/free-solid-svg-icons"; const autoTagScraperID = "builtin_autotag"; @@ -167,7 +172,7 @@ export const IdentifyDialog: React.FC = ({ title={intl.formatMessage({ id: "actions.select_folders" })} onClick={() => onClick()} > - + @@ -403,7 +408,7 @@ export const IdentifyDialog: React.FC = ({ = ({ className="minimal help-button" onClick={() => onShowManual()} > - + } > diff --git a/ui/v2.5/src/components/Dialogs/IdentifyDialog/Sources.tsx b/ui/v2.5/src/components/Dialogs/IdentifyDialog/Sources.tsx index 81d213115..9cc0c6a51 100644 --- a/ui/v2.5/src/components/Dialogs/IdentifyDialog/Sources.tsx +++ b/ui/v2.5/src/components/Dialogs/IdentifyDialog/Sources.tsx @@ -5,6 +5,13 @@ import { FormattedMessage, useIntl } from "react-intl"; import * as GQL from "src/core/generated-graphql"; import { IScraperSource } from "./constants"; import { OptionsEditor } from "./Options"; +import { + faCog, + faGripVertical, + faMinus, + faPencilAlt, + faPlus, +} from "@fortawesome/free-solid-svg-icons"; interface ISourceEditor { isNew: boolean; @@ -50,7 +57,7 @@ export const SourcesEditor: React.FC = ({ dialogClassName="identify-source-editor" modalProps={{ animation: false, size: "lg" }} show - icon={isNew ? "plus" : "pencil-alt"} + icon={isNew ? faPlus : faPencilAlt} header={intl.formatMessage( { id: headerMsgId }, { @@ -184,19 +191,19 @@ export const SourcesList: React.FC = ({ onMouseEnter={() => setMouseOverIndex(index)} onMouseLeave={() => setMouseOverIndex(undefined)} > - + {s.displayName}
@@ -208,7 +215,7 @@ export const SourcesList: React.FC = ({ className="minimal add-scraper-source-button" onClick={() => editSource()} > - + )} diff --git a/ui/v2.5/src/components/Dialogs/SubmitDraft.tsx b/ui/v2.5/src/components/Dialogs/SubmitDraft.tsx index 6fb6d81fa..78f433ea9 100644 --- a/ui/v2.5/src/components/Dialogs/SubmitDraft.tsx +++ b/ui/v2.5/src/components/Dialogs/SubmitDraft.tsx @@ -5,6 +5,7 @@ import * as GQL from "src/core/generated-graphql"; import { Modal } from "src/components/Shared"; import { getStashboxBase } from "src/utils"; import { FormattedMessage, useIntl } from "react-intl"; +import { faPaperPlane } from "@fortawesome/free-solid-svg-icons"; interface IProps { show: boolean; @@ -72,7 +73,7 @@ export const SubmitStashBoxDraft: React.FC = ({ return ( = ({ ); }; + +export default SubmitStashBoxDraft; diff --git a/ui/v2.5/src/components/Galleries/DeleteGalleriesDialog.tsx b/ui/v2.5/src/components/Galleries/DeleteGalleriesDialog.tsx index bff3dd5f7..d4122be78 100644 --- a/ui/v2.5/src/components/Galleries/DeleteGalleriesDialog.tsx +++ b/ui/v2.5/src/components/Galleries/DeleteGalleriesDialog.tsx @@ -6,6 +6,7 @@ import { Modal } from "src/components/Shared"; import { useToast } from "src/hooks"; import { ConfigurationContext } from "src/hooks/Config"; import { FormattedMessage, useIntl } from "react-intl"; +import { faTrashAlt } from "@fortawesome/free-solid-svg-icons"; interface IDeleteGalleryDialogProps { selected: GQL.SlimGalleryDataFragment[]; @@ -114,7 +115,7 @@ export const DeleteGalleriesDialog: React.FC = ( return ( = ( if (GalleriestudioID !== updateStudioID) { updateStudioID = undefined; } - if (!_.isEqual(galleryPerformerIDs, updatePerformerIds)) { + if (!isEqual(galleryPerformerIDs, updatePerformerIds)) { updatePerformerIds = []; } - if (!_.isEqual(galleryTagIDs, updateTagIds)) { + if (!isEqual(galleryTagIDs, updateTagIds)) { updateTagIds = []; } if (gallery.organized !== updateOrganized) { @@ -229,7 +230,7 @@ export const EditGalleriesDialog: React.FC = ( return ( = (props) => { content={popoverContent} > @@ -62,7 +63,7 @@ export const GalleryCard: React.FC = (props) => { content={popoverContent} > @@ -113,7 +114,7 @@ export const GalleryCard: React.FC = (props) => { return (
); diff --git a/ui/v2.5/src/components/Galleries/GalleryDetails/Gallery.tsx b/ui/v2.5/src/components/Galleries/GalleryDetails/Gallery.tsx index b9f2673dc..1ef62af37 100644 --- a/ui/v2.5/src/components/Galleries/GalleryDetails/Gallery.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryDetails/Gallery.tsx @@ -21,6 +21,7 @@ import { GalleryImagesPanel } from "./GalleryImagesPanel"; import { GalleryAddPanel } from "./GalleryAddPanel"; import { GalleryFileInfoPanel } from "./GalleryFileInfoPanel"; import { GalleryScenesPanel } from "./GalleryScenesPanel"; +import { faEllipsisV } from "@fortawesome/free-solid-svg-icons"; interface IProps { gallery: GQL.GalleryDataFragment; @@ -116,7 +117,7 @@ export const GalleryPage: React.FC = ({ gallery }) => { className="minimal" title={intl.formatMessage({ id: "operations" })} > - + {gallery.path ? ( diff --git a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryAddPanel.tsx b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryAddPanel.tsx index e256251e0..15343f7e4 100644 --- a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryAddPanel.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryAddPanel.tsx @@ -8,7 +8,7 @@ import { mutateAddGalleryImages } from "src/core/StashService"; import { useToast } from "src/hooks"; import { TextUtils } from "src/utils"; import { useIntl } from "react-intl"; -import { IconProp } from "@fortawesome/fontawesome-svg-core"; +import { faPlus } from "@fortawesome/free-solid-svg-icons"; interface IGalleryAddProps { gallery: GQL.GalleryDataFragment; @@ -88,7 +88,7 @@ export const GalleryAddPanel: React.FC = ({ gallery }) => { onClick: addImages, isDisplayed: showWhenSelected, postRefetch: true, - icon: "plus" as IconProp, + icon: faPlus, }, ]; diff --git a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx index b85f1bf7f..e6711da47 100644 --- a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx @@ -34,6 +34,7 @@ import { useFormik } from "formik"; import { FormUtils, TextUtils } from "src/utils"; import { RatingStars } from "src/components/Scenes/SceneDetails/RatingStars"; import { GalleryScrapeDialog } from "./GalleryScrapeDialog"; +import { faSyncAlt } from "@fortawesome/free-solid-svg-icons"; interface IProps { isVisible: boolean; @@ -314,7 +315,7 @@ export const GalleryEditPanel: React.FC< ))} onReloadScrapers()}> - + diff --git a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryImagesPanel.tsx b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryImagesPanel.tsx index e58bdb9c8..1aa7fa2a5 100644 --- a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryImagesPanel.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryImagesPanel.tsx @@ -8,7 +8,7 @@ import { showWhenSelected, PersistanceLevel } from "src/hooks/ListHook"; import { useToast } from "src/hooks"; import { TextUtils } from "src/utils"; import { useIntl } from "react-intl"; -import { IconProp } from "@fortawesome/fontawesome-svg-core"; +import { faMinus } from "@fortawesome/free-solid-svg-icons"; interface IGalleryDetailsProps { gallery: GQL.GalleryDataFragment; @@ -82,7 +82,7 @@ export const GalleryImagesPanel: React.FC = ({ onClick: removeImages, isDisplayed: showWhenSelected, postRefetch: true, - icon: "minus" as IconProp, + icon: faMinus, buttonVariant: "danger", }, ]; diff --git a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryScrapeDialog.tsx b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryScrapeDialog.tsx index a0b82a67d..917e72b28 100644 --- a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryScrapeDialog.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryScrapeDialog.tsx @@ -10,7 +10,7 @@ import { ScrapedInputGroupRow, ScrapedTextAreaRow, } from "src/components/Shared/ScrapeDialog"; -import _ from "lodash"; +import clone from "lodash-es/clone"; import { useStudioCreate, usePerformerCreate, @@ -235,7 +235,7 @@ export const GalleryScrapeDialog: React.FC = ( return; } - const ret = _.clone(idList); + const ret = clone(idList); // sort by id numerically ret.sort((a, b) => { return parseInt(a, 10) - parseInt(b, 10); diff --git a/ui/v2.5/src/components/Galleries/GalleryList.tsx b/ui/v2.5/src/components/Galleries/GalleryList.tsx index cb240f13d..83a8eef90 100644 --- a/ui/v2.5/src/components/Galleries/GalleryList.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryList.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { useIntl } from "react-intl"; -import _ from "lodash"; +import cloneDeep from "lodash-es/cloneDeep"; import { Table } from "react-bootstrap"; import { Link, useHistory } from "react-router-dom"; import Mousetrap from "mousetrap"; @@ -84,7 +84,7 @@ export const GalleryList: React.FC = ({ const { count } = result.data.findGalleries; const index = Math.floor(Math.random() * count); - const filterCopy = _.cloneDeep(filter); + const filterCopy = cloneDeep(filter); filterCopy.itemsPerPage = 1; filterCopy.currentPage = index + 1; const singleResult = await queryFindGalleries(filterCopy); diff --git a/ui/v2.5/src/components/Galleries/GalleryViewer.tsx b/ui/v2.5/src/components/Galleries/GalleryViewer.tsx index 6a9ac5136..7c69ede5b 100644 --- a/ui/v2.5/src/components/Galleries/GalleryViewer.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryViewer.tsx @@ -38,3 +38,5 @@ export const GalleryViewer: React.FC = ({ galleryId }) => { ); }; + +export default GalleryViewer; diff --git a/ui/v2.5/src/components/Help/Manual.tsx b/ui/v2.5/src/components/Help/Manual.tsx index 501563472..83f787c2f 100644 --- a/ui/v2.5/src/components/Help/Manual.tsx +++ b/ui/v2.5/src/components/Help/Manual.tsx @@ -1,4 +1,4 @@ -import React, { useState, PropsWithChildren, useEffect } from "react"; +import React, { useState, useEffect } from "react"; import { Modal, Container, Row, Col, Nav, Tab } from "react-bootstrap"; import Introduction from "src/docs/en/Introduction.md"; import Tasks from "src/docs/en/Tasks.md"; @@ -239,62 +239,4 @@ export const Manual: React.FC = ({ ); }; -interface IManualContextState { - openManual: (tab?: string) => void; -} - -export const ManualStateContext = React.createContext({ - openManual: () => {}, -}); - -export const ManualProvider: React.FC = ({ children }) => { - const [showManual, setShowManual] = useState(false); - const [manualLink, setManualLink] = useState(); - - function openManual(tab?: string) { - setManualLink(tab); - setShowManual(true); - } - - useEffect(() => { - if (manualLink) setManualLink(undefined); - }, [manualLink]); - - return ( - - setShowManual(false)} - defaultActiveTab={manualLink} - /> - {children} - - ); -}; - -interface IManualLink { - tab: string; -} - -export const ManualLink: React.FC> = ({ - tab, - children, -}) => { - const { openManual } = React.useContext(ManualStateContext); - - return ( - { - openManual(`${tab}.md`); - e.preventDefault(); - }} - > - {children} - - ); -}; +export default Manual; diff --git a/ui/v2.5/src/components/Help/context.tsx b/ui/v2.5/src/components/Help/context.tsx new file mode 100644 index 000000000..335838429 --- /dev/null +++ b/ui/v2.5/src/components/Help/context.tsx @@ -0,0 +1,73 @@ +import React, { + lazy, + PropsWithChildren, + Suspense, + useEffect, + useState, +} from "react"; + +const Manual = lazy(() => import("./Manual")); + +interface IManualContextState { + openManual: (tab?: string) => void; +} + +export const ManualStateContext = React.createContext({ + openManual: () => {}, +}); + +export const ManualProvider: React.FC = ({ children }) => { + const [showManual, setShowManual] = useState(false); + const [manualLink, setManualLink] = useState(); + + function openManual(tab?: string) { + setManualLink(tab); + setShowManual(true); + } + + useEffect(() => { + if (manualLink) setManualLink(undefined); + }, [manualLink]); + + return ( + + }> + {showManual && ( + setShowManual(false)} + defaultActiveTab={manualLink} + /> + )} + + {children} + + ); +}; + +interface IManualLink { + tab: string; +} + +export const ManualLink: React.FC> = ({ + tab, + children, +}) => { + const { openManual } = React.useContext(ManualStateContext); + + return ( + { + openManual(`${tab}.md`); + e.preventDefault(); + }} + > + {children} + + ); +}; diff --git a/ui/v2.5/src/components/Images/DeleteImagesDialog.tsx b/ui/v2.5/src/components/Images/DeleteImagesDialog.tsx index ee939ae9b..059be9f36 100644 --- a/ui/v2.5/src/components/Images/DeleteImagesDialog.tsx +++ b/ui/v2.5/src/components/Images/DeleteImagesDialog.tsx @@ -6,6 +6,7 @@ import { Modal } from "src/components/Shared"; import { useToast } from "src/hooks"; import { ConfigurationContext } from "src/hooks/Config"; import { FormattedMessage, useIntl } from "react-intl"; +import { faTrashAlt } from "@fortawesome/free-solid-svg-icons"; interface IDeleteImageDialogProps { selected: GQL.SlimImageDataFragment[]; @@ -106,7 +107,7 @@ export const DeleteImagesDialog: React.FC = ( return ( = ( if (imageStudioID !== updateStudioID) { updateStudioID = undefined; } - if (!_.isEqual(imagePerformerIDs, updatePerformerIds)) { + if (!isEqual(imagePerformerIDs, updatePerformerIds)) { updatePerformerIds = []; } - if (!_.isEqual(imageTagIDs, updateTagIds)) { + if (!isEqual(imageTagIDs, updateTagIds)) { updateTagIds = []; } if (image.organized !== updateOrganized) { @@ -219,7 +220,7 @@ export const EditImagesDialog: React.FC = ( return ( = ( content={popoverContent} > @@ -76,7 +82,7 @@ export const ImageCard: React.FC = ( content={popoverContent} > @@ -88,7 +94,7 @@ export const ImageCard: React.FC = ( return (
); @@ -146,7 +152,7 @@ export const ImageCard: React.FC = ( {props.onPreview ? (
) : undefined} diff --git a/ui/v2.5/src/components/Images/ImageDetails/Image.tsx b/ui/v2.5/src/components/Images/ImageDetails/Image.tsx index b4144c8f9..338e19e22 100644 --- a/ui/v2.5/src/components/Images/ImageDetails/Image.tsx +++ b/ui/v2.5/src/components/Images/ImageDetails/Image.tsx @@ -21,6 +21,7 @@ import { ImageFileInfoPanel } from "./ImageFileInfoPanel"; import { ImageEditPanel } from "./ImageEditPanel"; import { ImageDetailPanel } from "./ImageDetailPanel"; import { DeleteImagesDialog } from "../DeleteImagesDialog"; +import { faEllipsisV } from "@fortawesome/free-solid-svg-icons"; interface IImageParams { id?: string; @@ -132,7 +133,7 @@ export const Image: React.FC = () => { className="minimal" title="Operations" > - + = ({ const { count } = result.data.findImages; const index = Math.floor(Math.random() * count); - const filterCopy = _.cloneDeep(filter); + const filterCopy = cloneDeep(filter); filterCopy.itemsPerPage = 1; filterCopy.currentPage = index + 1; const singleResult = await queryFindImages(filterCopy); diff --git a/ui/v2.5/src/components/List/AddFilterDialog.tsx b/ui/v2.5/src/components/List/AddFilterDialog.tsx index a370fab76..d7c0325bc 100644 --- a/ui/v2.5/src/components/List/AddFilterDialog.tsx +++ b/ui/v2.5/src/components/List/AddFilterDialog.tsx @@ -1,4 +1,4 @@ -import _ from "lodash"; +import cloneDeep from "lodash-es/cloneDeep"; import React, { useEffect, useRef, useState } from "react"; import { Button, Form, Modal } from "react-bootstrap"; import { CriterionModifier } from "src/core/generated-graphql"; @@ -80,13 +80,13 @@ export const AddFilterDialog: React.FC = ({ function onChangedModifierSelect( event: React.ChangeEvent ) { - const newCriterion = _.cloneDeep(criterion); + const newCriterion = cloneDeep(criterion); newCriterion.modifier = event.target.value as CriterionModifier; setCriterion(newCriterion); } function onValueChanged(value: CriterionValue) { - const newCriterion = _.cloneDeep(criterion); + const newCriterion = cloneDeep(criterion); newCriterion.value = value; setCriterion(newCriterion); } diff --git a/ui/v2.5/src/components/List/FilterTags.tsx b/ui/v2.5/src/components/List/FilterTags.tsx index db7a7063e..48e79da98 100644 --- a/ui/v2.5/src/components/List/FilterTags.tsx +++ b/ui/v2.5/src/components/List/FilterTags.tsx @@ -6,6 +6,7 @@ import { } from "src/models/list-filter/criteria/criterion"; import { useIntl } from "react-intl"; import { Icon } from "../Shared"; +import { faTimes } from "@fortawesome/free-solid-svg-icons"; interface IFilterTagsProps { criteria: Criterion[]; @@ -48,7 +49,7 @@ export const FilterTags: React.FC = ({ variant="secondary" onClick={($event) => onRemoveCriterionTag(criterion, $event)} > - + )); diff --git a/ui/v2.5/src/components/List/ListFilter.tsx b/ui/v2.5/src/components/List/ListFilter.tsx index a1876d1f8..56e0a8a76 100644 --- a/ui/v2.5/src/components/List/ListFilter.tsx +++ b/ui/v2.5/src/components/List/ListFilter.tsx @@ -1,4 +1,5 @@ -import _, { debounce } from "lodash"; +import debounce from "lodash-es/debounce"; +import cloneDeep from "lodash-es/cloneDeep"; import React, { HTMLAttributes, useEffect, useRef, useState } from "react"; import cx from "classnames"; import Mousetrap from "mousetrap"; @@ -23,6 +24,15 @@ import { ListFilterOptions } from "src/models/list-filter/filter-options"; import { FormattedMessage, useIntl } from "react-intl"; import { PersistanceLevel } from "src/hooks/ListHook"; import { SavedFilterList } from "./SavedFilterList"; +import { + faBookmark, + faCaretDown, + faCaretUp, + faCheck, + faFilter, + faRandom, + faTimes, +} from "@fortawesome/free-solid-svg-icons"; const maxPageSize = 1000; interface IListFilterProps { @@ -53,7 +63,7 @@ export const ListFilter: React.FC = ({ const [perPageInput, perPageFocus] = useFocus(); const searchCallback = debounce((value: string) => { - const newFilter = _.cloneDeep(filter); + const newFilter = cloneDeep(filter); newFilter.searchTerm = value; newFilter.currentPage = 1; onFilterUpdate(newFilter); @@ -101,7 +111,7 @@ export const ListFilter: React.FC = ({ pp = maxPageSize; } - const newFilter = _.cloneDeep(filter); + const newFilter = cloneDeep(filter); newFilter.itemsPerPage = pp; newFilter.currentPage = 1; onFilterUpdate(newFilter); @@ -120,7 +130,7 @@ export const ListFilter: React.FC = ({ } function onChangeSortDirection() { - const newFilter = _.cloneDeep(filter); + const newFilter = cloneDeep(filter); if (filter.sortDirection === SortDirectionEnum.Asc) { newFilter.sortDirection = SortDirectionEnum.Desc; } else { @@ -131,14 +141,14 @@ export const ListFilter: React.FC = ({ } function onChangeSortBy(eventKey: string | null) { - const newFilter = _.cloneDeep(filter); + const newFilter = cloneDeep(filter); newFilter.sortBy = eventKey ?? undefined; newFilter.currentPage = 1; onFilterUpdate(newFilter); } function onReshuffleRandomSort() { - const newFilter = _.cloneDeep(filter); + const newFilter = cloneDeep(filter); newFilter.currentPage = 1; newFilter.randomSeed = -1; onFilterUpdate(newFilter); @@ -225,7 +235,7 @@ export const ListFilter: React.FC = ({ queryClearShowing ? "" : "d-none" )} > - + @@ -241,7 +251,7 @@ export const ListFilter: React.FC = ({ } > - + = ({ onClick={() => openFilterDialog()} active={filterDialogOpen} > - + @@ -291,8 +301,8 @@ export const ListFilter: React.FC = ({ @@ -306,7 +316,7 @@ export const ListFilter: React.FC = ({ } > )} @@ -362,7 +372,7 @@ export const ListFilter: React.FC = ({ ) } > - + diff --git a/ui/v2.5/src/components/List/ListOperationButtons.tsx b/ui/v2.5/src/components/List/ListOperationButtons.tsx index f16159818..efa0eefdc 100644 --- a/ui/v2.5/src/components/List/ListOperationButtons.tsx +++ b/ui/v2.5/src/components/List/ListOperationButtons.tsx @@ -8,14 +8,19 @@ import { } from "react-bootstrap"; import Mousetrap from "mousetrap"; import { FormattedMessage, useIntl } from "react-intl"; -import { IconProp } from "@fortawesome/fontawesome-svg-core"; +import { IconDefinition } from "@fortawesome/fontawesome-svg-core"; import { Icon } from "../Shared"; +import { + faEllipsisH, + faPencilAlt, + faTrash, +} from "@fortawesome/free-solid-svg-icons"; interface IListFilterOperation { text: string; onClick: () => void; isDisplayed?: () => boolean; - icon?: IconProp; + icon?: IconDefinition; buttonVariant?: string; } @@ -78,14 +83,14 @@ export const ListOperationButtons: React.FC = ({ if (itemsSelected) { if (onEdit) { buttons.push({ - icon: "pencil-alt", + icon: faPencilAlt, text: intl.formatMessage({ id: "actions.edit" }), onClick: onEdit, }); } if (onDelete) { buttons.push({ - icon: "trash", + icon: faTrash, text: intl.formatMessage({ id: "actions.delete" }), onClick: onDelete, buttonVariant: "danger", @@ -106,7 +111,7 @@ export const ListOperationButtons: React.FC = ({ variant={button.buttonVariant ?? "secondary"} onClick={button.onClick} > - + {button.icon ? : undefined} ); @@ -173,7 +178,7 @@ export const ListOperationButtons: React.FC = ({ return ( - + {options} diff --git a/ui/v2.5/src/components/List/ListViewOptions.tsx b/ui/v2.5/src/components/List/ListViewOptions.tsx index 8cf35673c..487b95ee0 100644 --- a/ui/v2.5/src/components/List/ListViewOptions.tsx +++ b/ui/v2.5/src/components/List/ListViewOptions.tsx @@ -10,6 +10,12 @@ import { import { DisplayMode } from "src/models/list-filter/types"; import { useIntl } from "react-intl"; import { Icon } from "../Shared"; +import { + faList, + faSquare, + faTags, + faThLarge, +} from "@fortawesome/free-solid-svg-icons"; interface IListViewOptionsProps { zoomIndex?: number; @@ -71,13 +77,13 @@ export const ListViewOptions: React.FC = ({ function getIcon(option: DisplayMode) { switch (option) { case DisplayMode.Grid: - return "th-large"; + return faThLarge; case DisplayMode.List: - return "list"; + return faList; case DisplayMode.Wall: - return "square"; + return faSquare; case DisplayMode.Tagger: - return "tags"; + return faTags; } } function getLabel(option: DisplayMode) { diff --git a/ui/v2.5/src/components/List/SavedFilterList.tsx b/ui/v2.5/src/components/List/SavedFilterList.tsx index 887eee360..a66a06fc8 100644 --- a/ui/v2.5/src/components/List/SavedFilterList.tsx +++ b/ui/v2.5/src/components/List/SavedFilterList.tsx @@ -22,6 +22,7 @@ import { LoadingIndicator } from "src/components/Shared"; import { PersistanceLevel } from "src/hooks/ListHook"; import { FormattedMessage, useIntl } from "react-intl"; import { Icon } from "../Shared"; +import { faSave, faTimes } from "@fortawesome/free-solid-svg-icons"; interface ISavedFilterListProps { filter: ListFilterModel; @@ -191,7 +192,7 @@ export const SavedFilterList: React.FC = ({ e.stopPropagation(); }} > - + @@ -344,7 +345,7 @@ export const SavedFilterList: React.FC = ({ onSaveFilter(filterName); }} > - + diff --git a/ui/v2.5/src/components/MainNavbar.tsx b/ui/v2.5/src/components/MainNavbar.tsx index c9a288466..95f170a25 100644 --- a/ui/v2.5/src/components/MainNavbar.tsx +++ b/ui/v2.5/src/components/MainNavbar.tsx @@ -6,22 +6,38 @@ import { useIntl, } from "react-intl"; import { Nav, Navbar, Button, Fade } from "react-bootstrap"; -import { IconName } from "@fortawesome/fontawesome-svg-core"; +import { IconDefinition } from "@fortawesome/fontawesome-svg-core"; import { LinkContainer } from "react-router-bootstrap"; import { Link, NavLink, useLocation, useHistory } from "react-router-dom"; import Mousetrap from "mousetrap"; import { SessionUtils } from "src/utils"; -import { Icon } from "src/components/Shared"; +import Icon from "src/components/Shared/Icon"; import { ConfigurationContext } from "src/hooks/Config"; -import { ManualStateContext } from "./Help/Manual"; +import { ManualStateContext } from "./Help/context"; import { SettingsButton } from "./SettingsButton"; +import { + faBars, + faChartBar, + faFilm, + faHeart, + faImage, + faImages, + faMapMarkerAlt, + faPlayCircle, + faQuestionCircle, + faSignOutAlt, + faTag, + faTimes, + faUser, + faVideo, +} from "@fortawesome/free-solid-svg-icons"; interface IMenuItem { name: string; message: MessageDescriptor; href: string; - icon: IconName; + icon: IconDefinition; hotkey: string; userCreatable?: boolean; } @@ -77,21 +93,21 @@ const allMenuItems: IMenuItem[] = [ name: "scenes", message: messages.scenes, href: "/scenes", - icon: "play-circle", + icon: faPlayCircle, hotkey: "g s", }, { name: "images", message: messages.images, href: "/images", - icon: "image", + icon: faImage, hotkey: "g i", }, { name: "movies", message: messages.movies, href: "/movies", - icon: "film", + icon: faFilm, hotkey: "g v", userCreatable: true, }, @@ -99,14 +115,14 @@ const allMenuItems: IMenuItem[] = [ name: "markers", message: messages.markers, href: "/scenes/markers", - icon: "map-marker-alt", + icon: faMapMarkerAlt, hotkey: "g k", }, { name: "galleries", message: messages.galleries, href: "/galleries", - icon: "images", + icon: faImages, hotkey: "g l", userCreatable: true, }, @@ -114,7 +130,7 @@ const allMenuItems: IMenuItem[] = [ name: "performers", message: messages.performers, href: "/performers", - icon: "user", + icon: faUser, hotkey: "g p", userCreatable: true, }, @@ -122,7 +138,7 @@ const allMenuItems: IMenuItem[] = [ name: "studios", message: messages.studios, href: "/studios", - icon: "video", + icon: faVideo, hotkey: "g u", userCreatable: true, }, @@ -130,7 +146,7 @@ const allMenuItems: IMenuItem[] = [ name: "tags", message: messages.tags, href: "/tags", - icon: "tag", + icon: faTag, hotkey: "g t", userCreatable: true, }, @@ -236,7 +252,7 @@ export const MainNavbar: React.FC = () => { href="/logout" title={intl.formatMessage({ id: "actions.logout" })} > - + ); } @@ -257,7 +273,7 @@ export const MainNavbar: React.FC = () => { className="minimal donate" title={intl.formatMessage({ id: "donate" })} > - + {intl.formatMessage(messages.donate)} @@ -273,7 +289,7 @@ export const MainNavbar: React.FC = () => { className="minimal d-flex align-items-center h-100" title={intl.formatMessage({ id: "statistics" })} > - + { onClick={() => openManual()} title={intl.formatMessage({ id: "help" })} > - + {maybeRenderLogout()} @@ -355,7 +371,7 @@ export const MainNavbar: React.FC = () => { )} {renderUtilityButtons()} - + diff --git a/ui/v2.5/src/components/Movies/EditMoviesDialog.tsx b/ui/v2.5/src/components/Movies/EditMoviesDialog.tsx index 713989f1f..b8b6ed196 100644 --- a/ui/v2.5/src/components/Movies/EditMoviesDialog.tsx +++ b/ui/v2.5/src/components/Movies/EditMoviesDialog.tsx @@ -12,6 +12,7 @@ import { getAggregateRating, getAggregateStudioId, } from "src/utils/bulkUpdate"; +import { faPencilAlt } from "@fortawesome/free-solid-svg-icons"; interface IListOperationProps { selected: GQL.MovieDataFragment[]; @@ -101,7 +102,7 @@ export const EditMoviesDialog: React.FC = ( return ( = (props: IProps) => { content={popoverContent} > diff --git a/ui/v2.5/src/components/Movies/MovieDetails/Movie.tsx b/ui/v2.5/src/components/Movies/MovieDetails/Movie.tsx index 2edd6d407..3984d4c33 100644 --- a/ui/v2.5/src/components/Movies/MovieDetails/Movie.tsx +++ b/ui/v2.5/src/components/Movies/MovieDetails/Movie.tsx @@ -19,6 +19,7 @@ import { useToast } from "src/hooks"; import { MovieScenesPanel } from "./MovieScenesPanel"; import { MovieDetailsPanel } from "./MovieDetailsPanel"; import { MovieEditPanel } from "./MovieEditPanel"; +import { faTrashAlt } from "@fortawesome/free-solid-svg-icons"; interface IProps { movie: GQL.MovieDataFragment; @@ -110,7 +111,7 @@ const MoviePage: React.FC = ({ movie }) => { return ( = ({ filterHook }) => { const { count } = result.data.findMovies; const index = Math.floor(Math.random() * count); - const filterCopy = _.cloneDeep(filter); + const filterCopy = cloneDeep(filter); filterCopy.itemsPerPage = 1; filterCopy.currentPage = index + 1; const singleResult = await queryFindMovies(filterCopy); diff --git a/ui/v2.5/src/components/Performers/EditPerformersDialog.tsx b/ui/v2.5/src/components/Performers/EditPerformersDialog.tsx index 3498af758..634b055c2 100644 --- a/ui/v2.5/src/components/Performers/EditPerformersDialog.tsx +++ b/ui/v2.5/src/components/Performers/EditPerformersDialog.tsx @@ -20,6 +20,7 @@ import { } from "src/utils/gender"; import { IndeterminateCheckbox } from "../Shared/IndeterminateCheckbox"; import { BulkUpdateTextInput } from "../Shared/BulkUpdateTextInput"; +import { faPencilAlt } from "@fortawesome/free-solid-svg-icons"; interface IListOperationProps { selected: GQL.SlimPerformerDataFragment[]; @@ -183,7 +184,7 @@ export const EditPerformersDialog: React.FC = ( return ( []; @@ -65,7 +66,7 @@ export const PerformerCard: React.FC = ({ } return (
- +
); } @@ -122,7 +123,7 @@ export const PerformerCard: React.FC = ({ return ( diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/Performer.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/Performer.tsx index 2a848e72d..27a22a31e 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/Performer.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/Performer.tsx @@ -30,6 +30,12 @@ import { PerformerImagesPanel } from "./PerformerImagesPanel"; import { PerformerEditPanel } from "./PerformerEditPanel"; import { PerformerSubmitButton } from "./PerformerSubmitButton"; import GenderIcon from "../GenderIcon"; +import { + faCamera, + faDove, + faHeart, + faLink, +} from "@fortawesome/free-solid-svg-icons"; interface IProps { performer: GQL.PerformerDataFragment; @@ -325,7 +331,7 @@ const PerformerPage: React.FC = ({ performer }) => { )} onClick={() => setFavorite(!performer.favorite)} > - + {performer.url && ( )} @@ -350,7 +356,7 @@ const PerformerPage: React.FC = ({ performer }) => { target="_blank" rel="noopener noreferrer" > - + )} @@ -365,7 +371,7 @@ const PerformerPage: React.FC = ({ performer }) => { target="_blank" rel="noopener noreferrer" > - + )} diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx index 348051d67..3be7d87e9 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx @@ -37,6 +37,11 @@ import { PerformerScrapeDialog } from "./PerformerScrapeDialog"; import PerformerScrapeModal from "./PerformerScrapeModal"; import PerformerStashBoxModal, { IStashBox } from "./PerformerStashBoxModal"; import cx from "classnames"; +import { + faPlus, + faSyncAlt, + faTrashAlt, +} from "@fortawesome/free-solid-svg-icons"; const isScraper = ( scraper: GQL.Scraper | GQL.StashBox @@ -192,7 +197,7 @@ export const PerformerEditPanel: React.FC = ({ > {t.name} ))} @@ -594,7 +599,7 @@ export const PerformerEditPanel: React.FC = ({ onClick={() => onReloadScrapers()} > - + @@ -781,7 +786,7 @@ export const PerformerEditPanel: React.FC = ({ title={intl.formatMessage({ id: "actions.delete_stashid" })} onClick={() => removeStashID(stashID)} > - + {link} diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx index 9225cb976..d18844b9e 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx @@ -13,7 +13,7 @@ import { useTagCreate } from "src/core/StashService"; import { Form } from "react-bootstrap"; import { TagSelect } from "src/components/Shared"; import { useToast } from "src/hooks"; -import _ from "lodash"; +import clone from "lodash-es/clone"; import { genderStrings, genderToString, @@ -285,7 +285,7 @@ export const PerformerScrapeDialog: React.FC = ( return; } - const ret = _.clone(idList); + const ret = clone(idList); // sort by id numerically ret.sort((a, b) => { return parseInt(a, 10) - parseInt(b, 10); diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeModal.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeModal.tsx index 5c02f1f42..5fd55cdc5 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeModal.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeModal.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState } from "react"; -import { debounce } from "lodash"; +import debounce from "lodash-es/debounce"; import { Button, Form } from "react-bootstrap"; import { useIntl } from "react-intl"; diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerStashBoxModal.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerStashBoxModal.tsx index e2cb93e50..acdca66a7 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerStashBoxModal.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerStashBoxModal.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState } from "react"; -import { debounce } from "lodash"; +import debounce from "lodash-es/debounce"; import { Button, Form } from "react-bootstrap"; import { useIntl } from "react-intl"; diff --git a/ui/v2.5/src/components/Performers/PerformerList.tsx b/ui/v2.5/src/components/Performers/PerformerList.tsx index cdff5c00a..0cd8902ae 100644 --- a/ui/v2.5/src/components/Performers/PerformerList.tsx +++ b/ui/v2.5/src/components/Performers/PerformerList.tsx @@ -1,4 +1,4 @@ -import _ from "lodash"; +import cloneDeep from "lodash-es/cloneDeep"; import React, { useState } from "react"; import { useIntl } from "react-intl"; import { useHistory } from "react-router-dom"; @@ -138,7 +138,7 @@ export const PerformerList: React.FC = ({ if (result.data?.findPerformers) { const { count } = result.data.findPerformers; const index = Math.floor(Math.random() * count); - const filterCopy = _.cloneDeep(filter); + const filterCopy = cloneDeep(filter); filterCopy.itemsPerPage = 1; filterCopy.currentPage = index + 1; const singleResult = await queryFindPerformers(filterCopy); diff --git a/ui/v2.5/src/components/Performers/PerformerListTable.tsx b/ui/v2.5/src/components/Performers/PerformerListTable.tsx index c96e4ce1b..36af0e841 100644 --- a/ui/v2.5/src/components/Performers/PerformerListTable.tsx +++ b/ui/v2.5/src/components/Performers/PerformerListTable.tsx @@ -7,6 +7,7 @@ import { Link } from "react-router-dom"; import * as GQL from "src/core/generated-graphql"; import { Icon } from "src/components/Shared"; import { NavUtils } from "src/utils"; +import { faHeart } from "@fortawesome/free-solid-svg-icons"; interface IPerformerListTableProps { performers: GQL.PerformerDataFragment[]; @@ -37,7 +38,7 @@ export const PerformerListTable: React.FC = ( {performer.favorite && ( )} diff --git a/ui/v2.5/src/components/SceneDuplicateChecker/SceneDuplicateChecker.tsx b/ui/v2.5/src/components/SceneDuplicateChecker/SceneDuplicateChecker.tsx index 32c5ea4b0..557ee43f7 100644 --- a/ui/v2.5/src/components/SceneDuplicateChecker/SceneDuplicateChecker.tsx +++ b/ui/v2.5/src/components/SceneDuplicateChecker/SceneDuplicateChecker.tsx @@ -28,6 +28,16 @@ import { TextUtils } from "src/utils"; import { DeleteScenesDialog } from "src/components/Scenes/DeleteScenesDialog"; import { EditScenesDialog } from "../Scenes/EditScenesDialog"; import { PerformerPopoverButton } from "../Shared/PerformerPopoverButton"; +import { + faBox, + faExclamationTriangle, + faFilm, + faImages, + faMapMarkerAlt, + faPencilAlt, + faTag, + faTrash, +} from "@fortawesome/free-solid-svg-icons"; const CLASSNAME = "duplicate-checker"; @@ -144,7 +154,7 @@ export const SceneDuplicateChecker: React.FC = () => { if (missingPhashes > 0) { return (

- + Missing phashes for {missingPhashes} scenes. Please run the phash generation task.

@@ -173,7 +183,7 @@ export const SceneDuplicateChecker: React.FC = () => { return ( @@ -216,7 +226,7 @@ export const SceneDuplicateChecker: React.FC = () => { className="tag-tooltip" > @@ -236,7 +246,7 @@ export const SceneDuplicateChecker: React.FC = () => { return ( @@ -268,7 +278,7 @@ export const SceneDuplicateChecker: React.FC = () => { return ( @@ -280,7 +290,7 @@ export const SceneDuplicateChecker: React.FC = () => { return (
); @@ -332,7 +342,7 @@ export const SceneDuplicateChecker: React.FC = () => { } > { } > @@ -550,3 +560,5 @@ export const SceneDuplicateChecker: React.FC = () => { ); }; + +export default SceneDuplicateChecker; diff --git a/ui/v2.5/src/components/SceneFilenameParser/SceneFilenameParser.tsx b/ui/v2.5/src/components/SceneFilenameParser/SceneFilenameParser.tsx index f70f0381f..6f1464af1 100644 --- a/ui/v2.5/src/components/SceneFilenameParser/SceneFilenameParser.tsx +++ b/ui/v2.5/src/components/SceneFilenameParser/SceneFilenameParser.tsx @@ -3,7 +3,7 @@ import React, { useEffect, useState, useCallback, useRef } from "react"; import { Button, Card, Form, Table } from "react-bootstrap"; import { FormattedMessage, useIntl } from "react-intl"; -import _ from "lodash"; +import clone from "lodash-es/clone"; import { queryParseSceneFilenames, useScenesUpdate, @@ -155,7 +155,7 @@ export const SceneFilenameParser: React.FC = () => { }, [parserInput, parseSceneFilenames, prevParserInput]); function onPageSizeChanged(newSize: number) { - const newInput = _.clone(parserInput); + const newInput = clone(parserInput); newInput.page = 1; newInput.pageSize = newSize; setParserInput(newInput); @@ -163,14 +163,14 @@ export const SceneFilenameParser: React.FC = () => { function onPageChanged(newPage: number) { if (newPage !== parserInput.page) { - const newInput = _.clone(parserInput); + const newInput = clone(parserInput); newInput.page = newPage; setParserInput(newInput); } } function onFindClicked(input: IParserInput) { - const newInput = _.clone(input); + const newInput = clone(input); newInput.page = 1; newInput.findClicked = true; setParserInput(newInput); @@ -423,3 +423,5 @@ export const SceneFilenameParser: React.FC = () => { ); }; + +export default SceneFilenameParser; diff --git a/ui/v2.5/src/components/SceneFilenameParser/SceneParserRow.tsx b/ui/v2.5/src/components/SceneFilenameParser/SceneParserRow.tsx index 35596ad0c..472b4beba 100644 --- a/ui/v2.5/src/components/SceneFilenameParser/SceneParserRow.tsx +++ b/ui/v2.5/src/components/SceneFilenameParser/SceneParserRow.tsx @@ -1,5 +1,6 @@ import React from "react"; -import _ from "lodash"; +import isEqual from "lodash-es/isEqual"; +import clone from "lodash-es/clone"; import { Form } from "react-bootstrap"; import { ParseSceneFilenamesQuery, @@ -26,7 +27,7 @@ class ParserResult { public setValue(value?: T) { if (value) { this.value = value; - this.isSet = !_.isEqual(this.value, this.originalValue); + this.isSet = !isEqual(this.value, this.originalValue); } } } @@ -332,44 +333,44 @@ interface ISceneParserRowProps { export const SceneParserRow = (props: ISceneParserRowProps) => { function changeParser(result: ParserResult, isSet: boolean, value?: T) { - const newParser = _.clone(result); + const newParser = clone(result); newParser.isSet = isSet; newParser.value = value; return newParser; } function onTitleChanged(set: boolean, value: string) { - const newResult = _.clone(props.scene); + const newResult = clone(props.scene); newResult.title = changeParser(newResult.title, set, value); props.onChange(newResult); } function onDateChanged(set: boolean, value: string) { - const newResult = _.clone(props.scene); + const newResult = clone(props.scene); newResult.date = changeParser(newResult.date, set, value); props.onChange(newResult); } function onRatingChanged(set: boolean, value?: number) { - const newResult = _.clone(props.scene); + const newResult = clone(props.scene); newResult.rating = changeParser(newResult.rating, set, value); props.onChange(newResult); } function onPerformerIdsChanged(set: boolean, value: string[]) { - const newResult = _.clone(props.scene); + const newResult = clone(props.scene); newResult.performers = changeParser(newResult.performers, set, value); props.onChange(newResult); } function onTagIdsChanged(set: boolean, value: string[]) { - const newResult = _.clone(props.scene); + const newResult = clone(props.scene); newResult.tags = changeParser(newResult.tags, set, value); props.onChange(newResult); } function onStudioIdChanged(set: boolean, value: string) { - const newResult = _.clone(props.scene); + const newResult = clone(props.scene); newResult.studio = changeParser(newResult.studio, set, value); props.onChange(newResult); } diff --git a/ui/v2.5/src/components/SceneFilenameParser/ShowFields.tsx b/ui/v2.5/src/components/SceneFilenameParser/ShowFields.tsx index 1e864125f..ea1fcccf9 100644 --- a/ui/v2.5/src/components/SceneFilenameParser/ShowFields.tsx +++ b/ui/v2.5/src/components/SceneFilenameParser/ShowFields.tsx @@ -1,3 +1,9 @@ +import { + faCheck, + faChevronDown, + faChevronRight, + faTimes, +} from "@fortawesome/free-solid-svg-icons"; import React, { useState } from "react"; import { Button, Collapse } from "react-bootstrap"; import { useIntl } from "react-intl"; @@ -26,7 +32,7 @@ export const ShowFields = (props: IShowFieldsProps) => { handleClick(label); }} > - + {label} )); @@ -34,7 +40,7 @@ export const ShowFields = (props: IShowFieldsProps) => { return (
@@ -214,7 +222,7 @@ export const SceneCard: React.FC = ( className="movie-count tag-tooltip" > @@ -236,7 +244,7 @@ export const SceneCard: React.FC = ( content={popoverContent} > @@ -272,7 +280,7 @@ export const SceneCard: React.FC = ( content={popoverContent} > @@ -284,7 +292,7 @@ export const SceneCard: React.FC = ( return (
); @@ -299,7 +307,7 @@ export const SceneCard: React.FC = ( href={NavUtils.makeScenesPHashMatchUrl(props.scene.phash)} className="minimal" > - +
); diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/ExternalPlayerButton.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/ExternalPlayerButton.tsx index 416c5ab73..dbe21790f 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/ExternalPlayerButton.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/ExternalPlayerButton.tsx @@ -1,9 +1,10 @@ +import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons"; import React from "react"; import { Button } from "react-bootstrap"; import { useIntl } from "react-intl"; -import { Icon } from "src/components/Shared"; +import Icon from "src/components/Shared/Icon"; import { SceneDataFragment } from "src/core/generated-graphql"; -import { TextUtils } from "src/utils"; +import TextUtils from "src/utils/text"; export interface IExternalPlayerButtonProps { scene: SceneDataFragment; @@ -49,8 +50,10 @@ export const ExternalPlayerButton: React.FC = ({ title={intl.formatMessage({ id: "actions.open_in_external_player" })} > - + ); }; + +export default ExternalPlayerButton; diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/OCounterButton.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/OCounterButton.tsx index f9bc95162..19d9e337d 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/OCounterButton.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/OCounterButton.tsx @@ -1,3 +1,4 @@ +import { faBan, faMinus } from "@fortawesome/free-solid-svg-icons"; import React, { useState } from "react"; import { Button, ButtonGroup, Dropdown, DropdownButton } from "react-bootstrap"; import { useIntl } from "react-intl"; @@ -58,11 +59,11 @@ export const OCounterButton: React.FC = ( className="pl-0 show-carat" > - + Decrement - + Reset diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/OrganizedButton.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/OrganizedButton.tsx index 84a4abae2..63e82f9d2 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/OrganizedButton.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/OrganizedButton.tsx @@ -1,8 +1,9 @@ import React from "react"; import cx from "classnames"; import { Button, Spinner } from "react-bootstrap"; -import { Icon } from "src/components/Shared"; +import Icon from "src/components/Shared/Icon"; import { defineMessages, useIntl } from "react-intl"; +import { faBox } from "@fortawesome/free-solid-svg-icons"; export interface IOrganizedButtonProps { loading: boolean; @@ -34,7 +35,7 @@ export const OrganizedButton: React.FC = ( )} onClick={props.onClick} > - + ); }; diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/PrimaryTags.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/PrimaryTags.tsx index 2832431fb..6cbca4ae0 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/PrimaryTags.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/PrimaryTags.tsx @@ -2,7 +2,7 @@ import React from "react"; import { FormattedMessage } from "react-intl"; import * as GQL from "src/core/generated-graphql"; import { Button, Badge, Card } from "react-bootstrap"; -import { TextUtils } from "src/utils"; +import TextUtils from "src/utils/text"; interface IPrimaryTags { sceneMarkers: GQL.SceneMarkerDataFragment[]; diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/QueueViewer.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/QueueViewer.tsx index 07197a720..bebcf3f7c 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/QueueViewer.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/QueueViewer.tsx @@ -2,10 +2,17 @@ import React, { useEffect, useState } from "react"; import { Link } from "react-router-dom"; import cx from "classnames"; import * as GQL from "src/core/generated-graphql"; -import { TextUtils } from "src/utils"; +import TextUtils from "src/utils/text"; import { Button, Form, Spinner } from "react-bootstrap"; -import { Icon } from "src/components/Shared"; +import Icon from "src/components/Shared/Icon"; import { useIntl } from "react-intl"; +import { + faChevronDown, + faChevronUp, + faRandom, + faStepBackward, + faStepForward, +} from "@fortawesome/free-solid-svg-icons"; export interface IPlaylistViewer { scenes?: GQL.SlimSceneDataFragment[]; @@ -113,7 +120,7 @@ export const QueueViewer: React.FC = ({ variant="secondary" onClick={() => onPrevious()} > - + ) : ( "" @@ -124,7 +131,7 @@ export const QueueViewer: React.FC = ({ variant="secondary" onClick={() => onNext()} > - + ) : ( "" @@ -134,7 +141,7 @@ export const QueueViewer: React.FC = ({ variant="secondary" onClick={() => onRandom()} > - + @@ -143,7 +150,7 @@ export const QueueViewer: React.FC = ({
); }; + +export default QueueViewer; diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/RatingStars.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/RatingStars.tsx index bdec9167e..010c20c96 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/RatingStars.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/RatingStars.tsx @@ -1,6 +1,8 @@ import React, { useState } from "react"; import { Button } from "react-bootstrap"; -import { Icon } from "src/components/Shared"; +import Icon from "src/components/Shared/Icon"; +import { faStar as fasStar } from "@fortawesome/free-solid-svg-icons"; +import { faStar as farStar } from "@fortawesome/free-regular-svg-icons"; export interface IRatingStarsProps { value?: number; @@ -33,20 +35,20 @@ export const RatingStars: React.FC = ( props.onSetRating(newRating); } - function getIconPrefix(rating: number) { + function getIcon(rating: number) { if (hoverRating && hoverRating >= rating) { if (hoverRating === props.value) { - return "far"; + return farStar; } - return "fas"; + return fasStar; } if (!hoverRating && props.value && props.value >= rating) { - return "fas"; + return fasStar; } - return "far"; + return farStar; } function onMouseOver(rating: number) { @@ -101,10 +103,7 @@ export const RatingStars: React.FC = ( title={getTooltip(rating)} key={`star-${rating}`} > - + ); diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/Scene.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/Scene.tsx index 67a36991e..b036b2b17 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/Scene.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/Scene.tsx @@ -1,6 +1,6 @@ import { Tab, Nav, Dropdown, Button, ButtonGroup } from "react-bootstrap"; import queryString from "query-string"; -import React, { useEffect, useState, useMemo, useContext } from "react"; +import React, { useEffect, useState, useMemo, useContext, lazy } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { useParams, useLocation, useHistory, Link } from "react-router-dom"; import { Helmet } from "react-helmet"; @@ -16,29 +16,41 @@ import { queryFindScenes, queryFindScenesByID, } from "src/core/StashService"; -import { GalleryViewer } from "src/components/Galleries/GalleryViewer"; -import { Icon } from "src/components/Shared"; + +import Icon from "src/components/Shared/Icon"; import { useToast } from "src/hooks"; -import { SubmitStashBoxDraft } from "src/components/Dialogs/SubmitDraft"; -import { ScenePlayer, getPlayerPosition } from "src/components/ScenePlayer"; +import SceneQueue from "src/models/sceneQueue"; import { ListFilterModel } from "src/models/list-filter/filter"; -import { TextUtils } from "src/utils"; +import TextUtils from "src/utils/text"; import Mousetrap from "mousetrap"; -import { SceneQueue } from "src/models/sceneQueue"; -import { QueueViewer } from "./QueueViewer"; -import { SceneMarkersPanel } from "./SceneMarkersPanel"; -import { SceneFileInfoPanel } from "./SceneFileInfoPanel"; -import { SceneEditPanel } from "./SceneEditPanel"; -import { SceneDetailPanel } from "./SceneDetailPanel"; import { OCounterButton } from "./OCounterButton"; -import { ExternalPlayerButton } from "./ExternalPlayerButton"; -import { SceneMoviePanel } from "./SceneMoviePanel"; -import { SceneGalleriesPanel } from "./SceneGalleriesPanel"; -import { DeleteScenesDialog } from "../DeleteScenesDialog"; -import { GenerateDialog } from "../../Dialogs/GenerateDialog"; -import { SceneVideoFilterPanel } from "./SceneVideoFilterPanel"; import { OrganizedButton } from "./OrganizedButton"; import { ConfigurationContext } from "src/hooks/Config"; +import { getPlayerPosition } from "src/components/ScenePlayer/util"; +import { faEllipsisV } from "@fortawesome/free-solid-svg-icons"; + +const SubmitStashBoxDraft = lazy( + () => import("src/components/Dialogs/SubmitDraft") +); +const ScenePlayer = lazy( + () => import("src/components/ScenePlayer/ScenePlayer") +); + +const GalleryViewer = lazy( + () => import("src/components/Galleries/GalleryViewer") +); +const ExternalPlayerButton = lazy(() => import("./ExternalPlayerButton")); + +const QueueViewer = lazy(() => import("./QueueViewer")); +const SceneMarkersPanel = lazy(() => import("./SceneMarkersPanel")); +const SceneFileInfoPanel = lazy(() => import("./SceneFileInfoPanel")); +const SceneEditPanel = lazy(() => import("./SceneEditPanel")); +const SceneDetailPanel = lazy(() => import("./SceneDetailPanel")); +const SceneMoviePanel = lazy(() => import("./SceneMoviePanel")); +const SceneGalleriesPanel = lazy(() => import("./SceneGalleriesPanel")); +const DeleteScenesDialog = lazy(() => import("../DeleteScenesDialog")); +const GenerateDialog = lazy(() => import("../../Dialogs/GenerateDialog")); +const SceneVideoFilterPanel = lazy(() => import("./SceneVideoFilterPanel")); interface IProps { scene: GQL.SceneDataFragment; @@ -237,7 +249,7 @@ const ScenePage: React.FC = ({ className="minimal" title={intl.formatMessage({ id: "operations" })} > - + = (props) => { ); }; + +export default SceneDetailPanel; diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx index 48887aa13..94d9687b8 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useMemo } from "react"; +import React, { useEffect, useState, useMemo, lazy } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { Button, @@ -30,7 +30,7 @@ import { ImageInput, URLField, } from "src/components/Shared"; -import { useToast } from "src/hooks"; +import useToast from "src/hooks/Toast"; import { ImageUtils, FormUtils, TextUtils, getStashIDs } from "src/utils"; import { MovieSelect } from "src/components/Shared/Select"; import { useFormik } from "formik"; @@ -39,8 +39,14 @@ import { ConfigurationContext } from "src/hooks/Config"; import { stashboxDisplayName } from "src/utils/stashbox"; import { SceneMovieTable } from "./SceneMovieTable"; import { RatingStars } from "./RatingStars"; -import { SceneScrapeDialog } from "./SceneScrapeDialog"; -import { SceneQueryModal } from "./SceneQueryModal"; +import { + faSearch, + faSyncAlt, + faTrashAlt, +} from "@fortawesome/free-solid-svg-icons"; + +const SceneScrapeDialog = lazy(() => import("./SceneScrapeDialog")); +const SceneQueryModal = lazy(() => import("./SceneQueryModal")); interface IProps { scene: GQL.SceneDataFragment; @@ -401,7 +407,7 @@ export const SceneEditPanel: React.FC = ({ return ( - + @@ -428,7 +434,7 @@ export const SceneEditPanel: React.FC = ({ ))} onReloadScrapers()}> - + @@ -500,7 +506,7 @@ export const SceneEditPanel: React.FC = ({ ))} onReloadScrapers()}> - + @@ -857,7 +863,7 @@ export const SceneEditPanel: React.FC = ({ )} onClick={() => removeStashID(stashID)} > - + {link} @@ -908,3 +914,5 @@ export const SceneEditPanel: React.FC = ({ ); }; + +export default SceneEditPanel; diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx index a6d62f20f..f8c1c3063 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx @@ -175,3 +175,5 @@ export const SceneFileInfoPanel: React.FC = ( ); }; + +export default SceneFileInfoPanel; diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneGalleriesPanel.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneGalleriesPanel.tsx index 30b8ba83d..4a8ca38f6 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneGalleriesPanel.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneGalleriesPanel.tsx @@ -15,3 +15,5 @@ export const SceneGalleriesPanel: React.FC = ({ return
{cards}
; }; + +export default SceneGalleriesPanel; diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneMarkerForm.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneMarkerForm.tsx index 352d42e36..2d24dfb8a 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneMarkerForm.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneMarkerForm.tsx @@ -13,8 +13,8 @@ import { TagSelect, MarkerTitleSuggest, } from "src/components/Shared"; -import { getPlayerPosition } from "src/components/ScenePlayer"; -import { useToast } from "src/hooks"; +import { getPlayerPosition } from "src/components/ScenePlayer/util"; +import useToast from "src/hooks/Toast"; interface IFormFields { title: string; diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneMarkersPanel.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneMarkersPanel.tsx index ec2e6653d..10b8228b6 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneMarkersPanel.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneMarkersPanel.tsx @@ -89,3 +89,5 @@ export const SceneMarkersPanel: React.FC = ( ); }; + +export default SceneMarkersPanel; diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneMoviePanel.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneMoviePanel.tsx index e3df55308..6961c4761 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneMoviePanel.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneMoviePanel.tsx @@ -23,3 +23,5 @@ export const SceneMoviePanel: FunctionComponent = ( ); }; + +export default SceneMoviePanel; diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneQueryModal.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneQueryModal.tsx index 560ffae31..1dfd4deed 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneQueryModal.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneQueryModal.tsx @@ -10,7 +10,8 @@ import { Icon, } from "src/components/Shared"; import { queryScrapeSceneQuery } from "src/core/StashService"; -import { useToast } from "src/hooks"; +import useToast from "src/hooks/Toast"; +import { faSearch } from "@fortawesome/free-solid-svg-icons"; interface ISceneSearchResultDetailsProps { scene: GQL.ScrapedSceneDataFragment; @@ -219,7 +220,7 @@ export const SceneQueryModal: React.FC = ({ variant="primary" title={intl.formatMessage({ id: "actions.search" })} > - + @@ -235,3 +236,5 @@ export const SceneQueryModal: React.FC = ({
); }; + +export default SceneQueryModal; diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneScrapeDialog.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneScrapeDialog.tsx index 96e462682..a3515940d 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneScrapeDialog.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneScrapeDialog.tsx @@ -10,7 +10,7 @@ import { ScrapedTextAreaRow, ScrapedImageRow, } from "src/components/Shared/ScrapeDialog"; -import _ from "lodash"; +import clone from "lodash-es/clone"; import { useStudioCreate, usePerformerCreate, @@ -18,8 +18,8 @@ import { useTagCreate, makePerformerCreateInput, } from "src/core/StashService"; -import { useToast } from "src/hooks"; -import { DurationUtils } from "src/utils"; +import useToast from "src/hooks/Toast"; +import DurationUtils from "src/utils/duration"; import { useIntl } from "react-intl"; function renderScrapedStudio( @@ -297,7 +297,7 @@ export const SceneScrapeDialog: React.FC = ({ return; } - const ret = _.clone(idList); + const ret = clone(idList); // sort by id numerically ret.sort((a, b) => { return parseInt(a, 10) - parseInt(b, 10); @@ -634,3 +634,5 @@ export const SceneScrapeDialog: React.FC = ({ /> ); }; + +export default SceneScrapeDialog; diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneVideoFilterPanel.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneVideoFilterPanel.tsx index af3beafac..f45c2bfae 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneVideoFilterPanel.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneVideoFilterPanel.tsx @@ -1,8 +1,8 @@ import React, { useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { Button, Form } from "react-bootstrap"; -import { TruncatedText } from "src/components/Shared"; -import { VIDEO_PLAYER_ID } from "src/components/ScenePlayer"; +import TruncatedText from "src/components/Shared/TruncatedText"; +import { VIDEO_PLAYER_ID } from "src/components/ScenePlayer/util"; import * as GQL from "src/core/generated-graphql"; interface ISceneVideoFilterPanelProps { @@ -670,3 +670,5 @@ export const SceneVideoFilterPanel: React.FC = ( ); }; + +export default SceneVideoFilterPanel; diff --git a/ui/v2.5/src/components/Scenes/SceneList.tsx b/ui/v2.5/src/components/Scenes/SceneList.tsx index 143d552a0..7fe17d46a 100644 --- a/ui/v2.5/src/components/Scenes/SceneList.tsx +++ b/ui/v2.5/src/components/Scenes/SceneList.tsx @@ -1,9 +1,8 @@ import React, { useState } from "react"; -import _ from "lodash"; +import cloneDeep from "lodash-es/cloneDeep"; import { useIntl } from "react-intl"; import { useHistory } from "react-router-dom"; import Mousetrap from "mousetrap"; -import { IconProp } from "@fortawesome/fontawesome-svg-core"; import { FindScenesQueryResult, SlimSceneDataFragment, @@ -25,6 +24,7 @@ import { SceneCardsGrid } from "./SceneCardsGrid"; import { TaggerContext } from "../Tagger/context"; import { IdentifyDialog } from "../Dialogs/IdentifyDialog/IdentifyDialog"; import { ConfigurationContext } from "src/hooks/Config"; +import { faPlay } from "@fortawesome/free-solid-svg-icons"; interface ISceneList { filterHook?: (filter: ListFilterModel) => ListFilterModel; @@ -50,7 +50,7 @@ export const SceneList: React.FC = ({ text: intl.formatMessage({ id: "actions.play_selected" }), onClick: playSelected, isDisplayed: showWhenSelected, - icon: "play" as IconProp, + icon: faPlay, }, { text: intl.formatMessage({ id: "actions.play_random" }), @@ -137,7 +137,7 @@ export const SceneList: React.FC = ({ const indexMax = filter.itemsPerPage < count ? filter.itemsPerPage : count; const index = Math.floor(Math.random() * indexMax); - const filterCopy = _.cloneDeep(filter); + const filterCopy = cloneDeep(filter); filterCopy.currentPage = page; filterCopy.sortBy = "random"; const queryResults = await queryFindScenes(filterCopy); @@ -300,3 +300,5 @@ export const SceneList: React.FC = ({ return {listData.template}; }; + +export default SceneList; diff --git a/ui/v2.5/src/components/Scenes/SceneListTable.tsx b/ui/v2.5/src/components/Scenes/SceneListTable.tsx index d5b8f8f02..b4b7803b0 100644 --- a/ui/v2.5/src/components/Scenes/SceneListTable.tsx +++ b/ui/v2.5/src/components/Scenes/SceneListTable.tsx @@ -102,7 +102,7 @@ export const SceneListTable: React.FC = ( {scene.gallery && ( )} diff --git a/ui/v2.5/src/components/Scenes/SceneMarkerList.tsx b/ui/v2.5/src/components/Scenes/SceneMarkerList.tsx index 97638338d..9958b0432 100644 --- a/ui/v2.5/src/components/Scenes/SceneMarkerList.tsx +++ b/ui/v2.5/src/components/Scenes/SceneMarkerList.tsx @@ -1,4 +1,4 @@ -import _ from "lodash"; +import cloneDeep from "lodash-es/cloneDeep"; import React from "react"; import { useHistory } from "react-router-dom"; import { useIntl } from "react-intl"; @@ -58,7 +58,7 @@ export const SceneMarkerList: React.FC = ({ filterHook }) => { const { count } = result.data.findSceneMarkers; const index = Math.floor(Math.random() * count); - const filterCopy = _.cloneDeep(filter); + const filterCopy = cloneDeep(filter); filterCopy.itemsPerPage = 1; filterCopy.currentPage = index + 1; const singleResult = await queryFindSceneMarkers(filterCopy); @@ -98,3 +98,5 @@ export const SceneMarkerList: React.FC = ({ filterHook }) => { ); }; + +export default SceneMarkerList; diff --git a/ui/v2.5/src/components/Scenes/Scenes.tsx b/ui/v2.5/src/components/Scenes/Scenes.tsx index b49d5296c..ea2a7befe 100644 --- a/ui/v2.5/src/components/Scenes/Scenes.tsx +++ b/ui/v2.5/src/components/Scenes/Scenes.tsx @@ -1,12 +1,13 @@ -import React from "react"; +import React, { lazy } from "react"; import { Route, Switch } from "react-router-dom"; import { useIntl } from "react-intl"; import { Helmet } from "react-helmet"; import { TITLE_SUFFIX } from "src/components/Shared"; import { PersistanceLevel } from "src/hooks/ListHook"; -import Scene from "./SceneDetails/Scene"; -import { SceneList } from "./SceneList"; -import { SceneMarkerList } from "./SceneMarkerList"; + +const SceneList = lazy(() => import("./SceneList")); +const SceneMarkerList = lazy(() => import("./SceneMarkerList")); +const Scene = lazy(() => import("./SceneDetails/Scene")); const Scenes: React.FC = () => { const intl = useIntl(); diff --git a/ui/v2.5/src/components/Settings/Inputs.tsx b/ui/v2.5/src/components/Settings/Inputs.tsx index a4c322f46..8e70b095e 100644 --- a/ui/v2.5/src/components/Settings/Inputs.tsx +++ b/ui/v2.5/src/components/Settings/Inputs.tsx @@ -1,3 +1,4 @@ +import { faChevronDown, faChevronUp } from "@fortawesome/free-solid-svg-icons"; import React, { useState } from "react"; import { Button, Collapse, Form, Modal, ModalProps } from "react-bootstrap"; import { FormattedMessage, useIntl } from "react-intl"; @@ -94,7 +95,7 @@ export const SettingGroup: React.FC> = ({ variant="minimal" onClick={() => setOpen(!open)} > - + ); } diff --git a/ui/v2.5/src/components/Settings/Settings.tsx b/ui/v2.5/src/components/Settings/Settings.tsx index bd2defc19..56d526bd7 100644 --- a/ui/v2.5/src/components/Settings/Settings.tsx +++ b/ui/v2.5/src/components/Settings/Settings.tsx @@ -148,3 +148,5 @@ export const Settings: React.FC = () => { ); }; + +export default Settings; diff --git a/ui/v2.5/src/components/Settings/SettingsLibraryPanel.tsx b/ui/v2.5/src/components/Settings/SettingsLibraryPanel.tsx index 55aaa6f48..a7a35237e 100644 --- a/ui/v2.5/src/components/Settings/SettingsLibraryPanel.tsx +++ b/ui/v2.5/src/components/Settings/SettingsLibraryPanel.tsx @@ -5,6 +5,7 @@ import { SettingSection } from "./SettingSection"; import { BooleanSetting, StringListSetting, StringSetting } from "./Inputs"; import { SettingStateContext } from "./context"; import { useIntl } from "react-intl"; +import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons"; export const SettingsLibraryPanel: React.FC = () => { const intl = useIntl(); @@ -85,7 +86,7 @@ export const SettingsLibraryPanel: React.FC = () => { rel="noopener noreferrer" target="_blank" > - +
} @@ -107,7 +108,7 @@ export const SettingsLibraryPanel: React.FC = () => { rel="noopener noreferrer" target="_blank" > - + } diff --git a/ui/v2.5/src/components/Settings/SettingsPluginsPanel.tsx b/ui/v2.5/src/components/Settings/SettingsPluginsPanel.tsx index afb38b3f3..274c82b1c 100644 --- a/ui/v2.5/src/components/Settings/SettingsPluginsPanel.tsx +++ b/ui/v2.5/src/components/Settings/SettingsPluginsPanel.tsx @@ -8,6 +8,7 @@ import { TextUtils } from "src/utils"; import { CollapseButton, Icon, LoadingIndicator } from "src/components/Shared"; import { SettingSection } from "./SettingSection"; import { Setting, SettingGroup } from "./Inputs"; +import { faLink, faSyncAlt } from "@fortawesome/free-solid-svg-icons"; export const SettingsPluginsPanel: React.FC = () => { const Toast = useToast(); @@ -30,7 +31,7 @@ export const SettingsPluginsPanel: React.FC = () => { target="_blank" rel="noopener noreferrer" > - + ); @@ -105,7 +106,7 @@ export const SettingsPluginsPanel: React.FC = () => { @@ -364,7 +369,7 @@ export const SettingsServicesPanel: React.FC = () => { title={intl.formatMessage({ id: "actions.allow_temporarily" })} onClick={() => setTempIP(a)} > - + @@ -386,7 +391,7 @@ export const SettingsServicesPanel: React.FC = () => { onClick={() => setTempIP(ipEntry)} disabled={!ipEntry} > - + diff --git a/ui/v2.5/src/components/Settings/StashConfiguration.tsx b/ui/v2.5/src/components/Settings/StashConfiguration.tsx index 04b484b1e..57676b5fc 100644 --- a/ui/v2.5/src/components/Settings/StashConfiguration.tsx +++ b/ui/v2.5/src/components/Settings/StashConfiguration.tsx @@ -1,3 +1,4 @@ +import { faEllipsisV } from "@fortawesome/free-solid-svg-icons"; import React, { useState } from "react"; import { Button, Form, Row, Col, Dropdown } from "react-bootstrap"; import { FormattedMessage } from "react-intl"; @@ -72,7 +73,7 @@ const Stash: React.FC = ({ id={`stash-menu-${index}`} className="minimal" > - + onEdit()}> diff --git a/ui/v2.5/src/components/Settings/Tasks/DataManagementTasks.tsx b/ui/v2.5/src/components/Settings/Tasks/DataManagementTasks.tsx index 684aec6d3..663eaa77c 100644 --- a/ui/v2.5/src/components/Settings/Tasks/DataManagementTasks.tsx +++ b/ui/v2.5/src/components/Settings/Tasks/DataManagementTasks.tsx @@ -15,10 +15,16 @@ import { ImportDialog } from "./ImportDialog"; import * as GQL from "src/core/generated-graphql"; import { SettingSection } from "../SettingSection"; import { BooleanSetting, Setting } from "../Inputs"; -import { ManualLink } from "src/components/Help/Manual"; +import { ManualLink } from "src/components/Help/context"; import { Icon } from "src/components/Shared"; import { ConfigurationContext } from "src/hooks/Config"; import { FolderSelect } from "src/components/Shared/FolderSelect/FolderSelect"; +import { + faMinus, + faPlus, + faQuestionCircle, + faTrashAlt, +} from "@fortawesome/free-solid-svg-icons"; interface ICleanDialog { pathSelection?: boolean; @@ -63,7 +69,7 @@ const CleanDialog: React.FC = ({ return ( = ({ title={intl.formatMessage({ id: "actions.delete" })} onClick={() => removePath(p)} > - + @@ -103,7 +109,7 @@ const CleanDialog: React.FC = ({ variant="secondary" onClick={() => addPath(currentDirectory)} > - + } /> @@ -188,7 +194,7 @@ export const DataManagementTasks: React.FC = ({ return ( = ({ <> - + } diff --git a/ui/v2.5/src/components/Settings/Tasks/DirectorySelectionDialog.tsx b/ui/v2.5/src/components/Settings/Tasks/DirectorySelectionDialog.tsx index a3ba44c03..84b1e0d5a 100644 --- a/ui/v2.5/src/components/Settings/Tasks/DirectorySelectionDialog.tsx +++ b/ui/v2.5/src/components/Settings/Tasks/DirectorySelectionDialog.tsx @@ -1,3 +1,8 @@ +import { + faMinus, + faPencilAlt, + faPlus, +} from "@fortawesome/free-solid-svg-icons"; import React, { useState } from "react"; import { Button, Col, Form, Row } from "react-bootstrap"; import { useIntl } from "react-intl"; @@ -41,7 +46,7 @@ export const DirectorySelectionDialog: React.FC show modalProps={{ animation }} disabled={!allowEmpty && paths.length === 0} - icon="pencil-alt" + icon={faPencilAlt} header={intl.formatMessage({ id: "actions.select_folders" })} accept={{ onClick: () => { @@ -69,7 +74,7 @@ export const DirectorySelectionDialog: React.FC title={intl.formatMessage({ id: "actions.delete" })} onClick={() => removePath(p)} > - + @@ -84,7 +89,7 @@ export const DirectorySelectionDialog: React.FC variant="secondary" onClick={() => addPath(currentDirectory)} > - + } /> diff --git a/ui/v2.5/src/components/Settings/Tasks/ImportDialog.tsx b/ui/v2.5/src/components/Settings/Tasks/ImportDialog.tsx index 9a73dc693..ff5025089 100644 --- a/ui/v2.5/src/components/Settings/Tasks/ImportDialog.tsx +++ b/ui/v2.5/src/components/Settings/Tasks/ImportDialog.tsx @@ -5,6 +5,7 @@ import { Modal } from "src/components/Shared"; import * as GQL from "src/core/generated-graphql"; import { useToast } from "src/hooks"; import { useIntl } from "react-intl"; +import { faPencilAlt } from "@fortawesome/free-solid-svg-icons"; interface IImportDialogProps { onClose: () => void; @@ -115,7 +116,7 @@ export const ImportDialog: React.FC = ( return ( { diff --git a/ui/v2.5/src/components/Settings/Tasks/JobTable.tsx b/ui/v2.5/src/components/Settings/Tasks/JobTable.tsx index ba6a04b80..b88107434 100644 --- a/ui/v2.5/src/components/Settings/Tasks/JobTable.tsx +++ b/ui/v2.5/src/components/Settings/Tasks/JobTable.tsx @@ -7,8 +7,15 @@ import { } from "src/core/StashService"; import * as GQL from "src/core/generated-graphql"; import { Icon } from "src/components/Shared"; -import { IconProp } from "@fortawesome/fontawesome-svg-core"; import { useIntl } from "react-intl"; +import { + faBan, + faCheck, + faCircle, + faCog, + faHourglassStart, + faTimes, +} from "@fortawesome/free-solid-svg-icons"; type JobFragment = Pick< GQL.Job, @@ -68,25 +75,25 @@ const Task: React.FC = ({ job }) => { } function getStatusIcon() { - let icon: IconProp = "circle"; + let icon = faCircle; let iconClass = ""; switch (job.status) { case GQL.JobStatus.Ready: - icon = "hourglass-start"; + icon = faHourglassStart; break; case GQL.JobStatus.Running: - icon = "cog"; + icon = faCog; iconClass = "fa-spin"; break; case GQL.JobStatus.Stopping: - icon = "cog"; + icon = faCog; iconClass = "fa-spin"; break; case GQL.JobStatus.Finished: - icon = "check"; + icon = faCheck; break; case GQL.JobStatus.Cancelled: - icon = "ban"; + icon = faBan; break; } @@ -138,7 +145,7 @@ const Task: React.FC = ({ job }) => { onClick={() => stopJob()} disabled={!canStop()} > - +
diff --git a/ui/v2.5/src/components/Settings/Tasks/LibraryTasks.tsx b/ui/v2.5/src/components/Settings/Tasks/LibraryTasks.tsx index eb9045336..82c4c3b17 100644 --- a/ui/v2.5/src/components/Settings/Tasks/LibraryTasks.tsx +++ b/ui/v2.5/src/components/Settings/Tasks/LibraryTasks.tsx @@ -17,8 +17,9 @@ import { useToast } from "src/hooks"; import { GenerateOptions } from "./GenerateOptions"; import { SettingSection } from "../SettingSection"; import { BooleanSetting, Setting, SettingGroup } from "../Inputs"; -import { ManualLink } from "src/components/Help/Manual"; +import { ManualLink } from "src/components/Help/context"; import { Icon } from "src/components/Shared"; +import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons"; interface IAutoTagOptions { options: GQL.AutoTagMetadataInput; @@ -296,7 +297,7 @@ export const LibraryTasks: React.FC = () => { <> - + ), @@ -335,7 +336,7 @@ export const LibraryTasks: React.FC = () => { <> - + } @@ -358,7 +359,7 @@ export const LibraryTasks: React.FC = () => { <> - + ), @@ -399,7 +400,7 @@ export const LibraryTasks: React.FC = () => { <> - + ), diff --git a/ui/v2.5/src/components/Settings/context.tsx b/ui/v2.5/src/components/Settings/context.tsx index 2104c71f7..757d548a0 100644 --- a/ui/v2.5/src/components/Settings/context.tsx +++ b/ui/v2.5/src/components/Settings/context.tsx @@ -1,5 +1,9 @@ import { ApolloError } from "@apollo/client/errors"; -import { debounce } from "lodash"; +import { + faCheckCircle, + faTimesCircle, +} from "@fortawesome/free-solid-svg-icons"; +import debounce from "lodash-es/debounce"; import React, { useState, useEffect, @@ -452,7 +456,7 @@ export const SettingsContext: React.FC = ({ children }) => { if (updateSuccess === false) { return (
- +
); } @@ -477,7 +481,7 @@ export const SettingsContext: React.FC = ({ children }) => { if (updateSuccess) { return (
- +
); } diff --git a/ui/v2.5/src/components/SettingsButton.tsx b/ui/v2.5/src/components/SettingsButton.tsx index a15203da5..b8f9e2a5a 100644 --- a/ui/v2.5/src/components/SettingsButton.tsx +++ b/ui/v2.5/src/components/SettingsButton.tsx @@ -4,6 +4,7 @@ import { Button } from "react-bootstrap"; import { useJobQueue, useJobsSubscribe } from "src/core/StashService"; import * as GQL from "src/core/generated-graphql"; import { useIntl } from "react-intl"; +import { faCog } from "@fortawesome/free-solid-svg-icons"; type JobFragment = Pick< GQL.Job, @@ -59,7 +60,7 @@ export const SettingsButton: React.FC = () => { className="minimal d-flex align-items-center h-100" title={intl.formatMessage({ id: "settings" })} > - 0} /> + 0} /> ); }; diff --git a/ui/v2.5/src/components/Setup/Migrate.tsx b/ui/v2.5/src/components/Setup/Migrate.tsx index 788481ca8..6dd6851ec 100644 --- a/ui/v2.5/src/components/Setup/Migrate.tsx +++ b/ui/v2.5/src/components/Setup/Migrate.tsx @@ -180,3 +180,5 @@ export const Migrate: React.FC = () => { ); }; + +export default Migrate; diff --git a/ui/v2.5/src/components/Setup/Setup.tsx b/ui/v2.5/src/components/Setup/Setup.tsx index 066c24047..16a10fcd5 100644 --- a/ui/v2.5/src/components/Setup/Setup.tsx +++ b/ui/v2.5/src/components/Setup/Setup.tsx @@ -15,6 +15,11 @@ import { ConfigurationContext } from "src/hooks/Config"; import StashConfiguration from "../Settings/StashConfiguration"; import { Icon, LoadingIndicator, Modal } from "../Shared"; import { FolderSelectDialog } from "../Shared/FolderSelect/FolderSelectDialog"; +import { + faEllipsisH, + faExclamationTriangle, + faQuestionCircle, +} from "@fortawesome/free-solid-svg-icons"; export const Setup: React.FC = () => { const { configuration, loading: configLoading } = useContext( @@ -108,7 +113,7 @@ export const Setup: React.FC = () => { return ( { className="text-input" onClick={() => setShowGeneratedDialog(true)} > - + @@ -528,7 +533,7 @@ export const Setup: React.FC = () => {

}} + values={{ icon: }} />

@@ -640,3 +645,5 @@ export const Setup: React.FC = () => { ); }; + +export default Setup; diff --git a/ui/v2.5/src/components/Shared/BulkUpdateTextInput.tsx b/ui/v2.5/src/components/Shared/BulkUpdateTextInput.tsx index ba292d1a7..a64754d61 100644 --- a/ui/v2.5/src/components/Shared/BulkUpdateTextInput.tsx +++ b/ui/v2.5/src/components/Shared/BulkUpdateTextInput.tsx @@ -1,7 +1,8 @@ +import { faBan } from "@fortawesome/free-solid-svg-icons"; import React from "react"; import { Button, Form, FormControlProps, InputGroup } from "react-bootstrap"; import { useIntl } from "react-intl"; -import { Icon } from "."; +import Icon from "./Icon"; interface IBulkUpdateTextInputProps extends FormControlProps { valueChanged: (value: string | undefined) => void; @@ -37,7 +38,7 @@ export const BulkUpdateTextInput: React.FC = ({ onClick={() => valueChanged(undefined)} title={intl.formatMessage({ id: "actions.unset" })} > - + ) : undefined} diff --git a/ui/v2.5/src/components/Shared/CollapseButton.tsx b/ui/v2.5/src/components/Shared/CollapseButton.tsx index e29097821..2216121de 100644 --- a/ui/v2.5/src/components/Shared/CollapseButton.tsx +++ b/ui/v2.5/src/components/Shared/CollapseButton.tsx @@ -1,6 +1,10 @@ +import { + faChevronDown, + faChevronRight, +} from "@fortawesome/free-solid-svg-icons"; import React, { useState } from "react"; import { Button, Collapse } from "react-bootstrap"; -import { Icon } from "src/components/Shared"; +import Icon from "src/components/Shared/Icon"; interface IProps { text: string; @@ -17,7 +21,7 @@ export const CollapseButton: React.FC> = ( onClick={() => setOpen(!open)} className="minimal collapse-button" > - + {props.text} diff --git a/ui/v2.5/src/components/Shared/DeleteEntityDialog.tsx b/ui/v2.5/src/components/Shared/DeleteEntityDialog.tsx index fe5cc4922..cf6284982 100644 --- a/ui/v2.5/src/components/Shared/DeleteEntityDialog.tsx +++ b/ui/v2.5/src/components/Shared/DeleteEntityDialog.tsx @@ -2,8 +2,9 @@ import React, { useState } from "react"; import { defineMessages, FormattedMessage, useIntl } from "react-intl"; import { FetchResult } from "@apollo/client"; -import { Modal } from "src/components/Shared"; +import Modal from "src/components/Shared/Modal"; import { useToast } from "src/hooks"; +import { faTrashAlt } from "@fortawesome/free-solid-svg-icons"; interface IDeletionEntity { id: string; @@ -78,7 +79,7 @@ const DeleteEntityDialog: React.FC = ({ return ( = (props: IProps) => { disabled={props.disabled} onClick={() => increment()} > - + ); @@ -86,7 +91,7 @@ export const DurationInput: React.FC = (props: IProps) => { if (props.onReset) { return ( ); } diff --git a/ui/v2.5/src/components/Shared/ExportDialog.tsx b/ui/v2.5/src/components/Shared/ExportDialog.tsx index 6e49b740a..3c0ad5b7c 100644 --- a/ui/v2.5/src/components/Shared/ExportDialog.tsx +++ b/ui/v2.5/src/components/Shared/ExportDialog.tsx @@ -1,11 +1,12 @@ import React, { useState } from "react"; import { Form } from "react-bootstrap"; import { mutateExportObjects } from "src/core/StashService"; -import { Modal } from "src/components/Shared"; -import { useToast } from "src/hooks"; -import { downloadFile } from "src/utils"; +import Modal from "src/components/Shared/Modal"; +import useToast from "src/hooks/Toast"; +import downloadFile from "src/utils/download"; import { ExportObjectsInput } from "src/core/generated-graphql"; import { useIntl } from "react-intl"; +import { faCogs } from "@fortawesome/free-solid-svg-icons"; interface IExportDialogProps { exportInput: ExportObjectsInput; @@ -47,7 +48,7 @@ export const ExportDialog: React.FC = ( return ( = ({ {loading ? ( ) : ( - + )} ) : undefined} diff --git a/ui/v2.5/src/components/Shared/Icon.tsx b/ui/v2.5/src/components/Shared/Icon.tsx index 533477575..d1f2616f6 100644 --- a/ui/v2.5/src/components/Shared/Icon.tsx +++ b/ui/v2.5/src/components/Shared/Icon.tsx @@ -1,17 +1,9 @@ import React from "react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { IconProp, SizeProp, library } from "@fortawesome/fontawesome-svg-core"; -import { faStar as fasStar } from "@fortawesome/free-solid-svg-icons"; -import { - faCheckCircle as farCheckCircle, - faStar as farStar, -} from "@fortawesome/free-regular-svg-icons"; - -// need these to use far and fas styles of stars -library.add(fasStar, farStar, farCheckCircle); +import { IconDefinition, SizeProp } from "@fortawesome/fontawesome-svg-core"; interface IIcon { - icon: IconProp; + icon: IconDefinition; className?: string; color?: string; size?: SizeProp; diff --git a/ui/v2.5/src/components/Shared/ImageInput.tsx b/ui/v2.5/src/components/Shared/ImageInput.tsx index 4b6183779..f1ace6f83 100644 --- a/ui/v2.5/src/components/Shared/ImageInput.tsx +++ b/ui/v2.5/src/components/Shared/ImageInput.tsx @@ -8,8 +8,9 @@ import { Row, } from "react-bootstrap"; import { useIntl } from "react-intl"; -import { Modal } from "."; +import Modal from "./Modal"; import Icon from "./Icon"; +import { faFile, faLink } from "@fortawesome/free-solid-svg-icons"; interface IImageInput { isEditing: boolean; @@ -100,7 +101,7 @@ export const ImageInput: React.FC = ({

= ({
diff --git a/ui/v2.5/src/components/Shared/Modal.tsx b/ui/v2.5/src/components/Shared/Modal.tsx index 1ff9a1a50..8cf3b8029 100644 --- a/ui/v2.5/src/components/Shared/Modal.tsx +++ b/ui/v2.5/src/components/Shared/Modal.tsx @@ -1,7 +1,7 @@ import React from "react"; import { Button, Modal, Spinner, ModalProps } from "react-bootstrap"; -import { Icon } from "src/components/Shared"; -import { IconName } from "@fortawesome/fontawesome-svg-core"; +import Icon from "src/components/Shared/Icon"; +import { IconDefinition } from "@fortawesome/fontawesome-svg-core"; import { FormattedMessage } from "react-intl"; interface IButton { @@ -14,7 +14,7 @@ interface IModal { show: boolean; onHide?: () => void; header?: string; - icon?: IconName; + icon?: IconDefinition; cancel?: IButton; accept?: IButton; isRunning?: boolean; diff --git a/ui/v2.5/src/components/Shared/OperationButton.tsx b/ui/v2.5/src/components/Shared/OperationButton.tsx index a7c23dcf2..c71d9e139 100644 --- a/ui/v2.5/src/components/Shared/OperationButton.tsx +++ b/ui/v2.5/src/components/Shared/OperationButton.tsx @@ -1,6 +1,6 @@ import React, { useState, useRef, useEffect } from "react"; import { Button, ButtonProps } from "react-bootstrap"; -import { LoadingIndicator } from "src/components/Shared"; +import LoadingIndicator from "src/components/Shared/LoadingIndicator"; interface IOperationButton extends ButtonProps { operation?: () => Promise; diff --git a/ui/v2.5/src/components/Shared/PerformerPopoverButton.tsx b/ui/v2.5/src/components/Shared/PerformerPopoverButton.tsx index f669281a8..d12da10d3 100644 --- a/ui/v2.5/src/components/Shared/PerformerPopoverButton.tsx +++ b/ui/v2.5/src/components/Shared/PerformerPopoverButton.tsx @@ -1,3 +1,4 @@ +import { faUser } from "@fortawesome/free-solid-svg-icons"; import React from "react"; import { Button } from "react-bootstrap"; import { Link } from "react-router-dom"; @@ -36,7 +37,7 @@ export const PerformerPopoverButton: React.FC = ({ performers }) => { content={popoverContent} > diff --git a/ui/v2.5/src/components/Shared/PopoverCountButton.tsx b/ui/v2.5/src/components/Shared/PopoverCountButton.tsx index d7e3904d1..3eb1c731f 100644 --- a/ui/v2.5/src/components/Shared/PopoverCountButton.tsx +++ b/ui/v2.5/src/components/Shared/PopoverCountButton.tsx @@ -1,3 +1,9 @@ +import { + faFilm, + faImage, + faImages, + faPlayCircle, +} from "@fortawesome/free-solid-svg-icons"; import React from "react"; import { Button } from "react-bootstrap"; import { useIntl } from "react-intl"; @@ -24,13 +30,13 @@ export const PopoverCountButton: React.FC = ({ function getIcon() { switch (type) { case "scene": - return "play-circle"; + return faPlayCircle; case "image": - return "image"; + return faImage; case "gallery": - return "images"; + return faImages; case "movie": - return "film"; + return faFilm; } } diff --git a/ui/v2.5/src/components/Shared/RatingStars.tsx b/ui/v2.5/src/components/Shared/RatingStars.tsx index 0847dba0b..3b26fc96e 100644 --- a/ui/v2.5/src/components/Shared/RatingStars.tsx +++ b/ui/v2.5/src/components/Shared/RatingStars.tsx @@ -1,3 +1,5 @@ +import { faStar as fasStar } from "@fortawesome/free-solid-svg-icons"; +import { faStar as farStar } from "@fortawesome/free-regular-svg-icons"; import React from "react"; import Icon from "./Icon"; @@ -12,21 +14,21 @@ interface IProps { export const RatingStars: React.FC = ({ rating }) => rating ? (
- + = 2 ? "fas" : "far", "star"]} + icon={rating >= 2 ? fasStar : farStar} className={rating >= 2 ? CLASSNAME_FILLED : CLASSNAME_UNFILLED} /> = 3 ? "fas" : "far", "star"]} + icon={rating >= 3 ? fasStar : farStar} className={rating >= 3 ? CLASSNAME_FILLED : CLASSNAME_UNFILLED} /> = 4 ? "fas" : "far", "star"]} + icon={rating >= 4 ? fasStar : farStar} className={rating >= 4 ? CLASSNAME_FILLED : CLASSNAME_UNFILLED} />
diff --git a/ui/v2.5/src/components/Shared/ScrapeDialog.tsx b/ui/v2.5/src/components/Shared/ScrapeDialog.tsx index ecaa3bf00..0fb9c8ef5 100644 --- a/ui/v2.5/src/components/Shared/ScrapeDialog.tsx +++ b/ui/v2.5/src/components/Shared/ScrapeDialog.tsx @@ -8,9 +8,18 @@ import { FormControl, Badge, } from "react-bootstrap"; -import { CollapseButton, Icon, Modal } from "src/components/Shared"; -import _ from "lodash"; +import { CollapseButton } from "src/components/Shared/CollapseButton"; +import Icon from "src/components/Shared/Icon"; +import Modal from "src/components/Shared/Modal"; +import isEqual from "lodash-es/isEqual"; +import clone from "lodash-es/clone"; import { FormattedMessage, useIntl } from "react-intl"; +import { + faCheck, + faPencilAlt, + faPlus, + faTimes, +} from "@fortawesome/free-solid-svg-icons"; export class ScrapeResult { public newValue?: T; @@ -22,7 +31,7 @@ export class ScrapeResult { this.originalValue = originalValue ?? undefined; this.newValue = newValue ?? undefined; - const valuesEqual = _.isEqual(originalValue, newValue); + const valuesEqual = isEqual(originalValue, newValue); this.useNewValue = !!this.newValue && !valuesEqual; this.scraped = this.useNewValue; } @@ -33,10 +42,10 @@ export class ScrapeResult { } public cloneWithValue(value?: T) { - const ret = _.clone(this); + const ret = clone(this); ret.newValue = value; - ret.useNewValue = !_.isEqual(ret.newValue, ret.originalValue); + ret.useNewValue = isEqual(ret.newValue, ret.originalValue); ret.scraped = ret.useNewValue; return ret; @@ -73,7 +82,7 @@ function renderButtonIcon(selected: boolean) { return ( ); } @@ -82,7 +91,7 @@ export const ScrapeDialogRow = ( props: IScrapedRowProps ) => { function handleSelectClick(isNew: boolean) { - const ret = _.clone(props.result); + const ret = clone(props.result); ret.useNewValue = isNew; props.onChange(ret); } @@ -111,7 +120,7 @@ export const ScrapeDialogRow = ( > {t.name} ))} @@ -344,7 +353,7 @@ export const ScrapeDialog: React.FC = ( return ( { diff --git a/ui/v2.5/src/components/Shared/Select.tsx b/ui/v2.5/src/components/Shared/Select.tsx index 64790341d..bc494cfc9 100644 --- a/ui/v2.5/src/components/Shared/Select.tsx +++ b/ui/v2.5/src/components/Shared/Select.tsx @@ -8,7 +8,7 @@ import Select, { OptionsType, } from "react-select"; import CreatableSelect from "react-select/creatable"; -import { debounce } from "lodash"; +import debounce from "lodash-es/debounce"; import * as GQL from "src/core/generated-graphql"; import { diff --git a/ui/v2.5/src/components/Shared/StringListInput.tsx b/ui/v2.5/src/components/Shared/StringListInput.tsx index 1c4e7937d..95c4bac50 100644 --- a/ui/v2.5/src/components/Shared/StringListInput.tsx +++ b/ui/v2.5/src/components/Shared/StringListInput.tsx @@ -1,6 +1,7 @@ +import { faMinus, faPlus } from "@fortawesome/free-solid-svg-icons"; import React from "react"; import { Button, Form, InputGroup } from "react-bootstrap"; -import { Icon } from "src/components/Shared"; +import Icon from "src/components/Shared/Icon"; interface IStringListInputProps { value: string[]; @@ -49,7 +50,7 @@ export const StringListInput: React.FC = (props) => { /> @@ -57,7 +58,7 @@ export const StringListInput: React.FC = (props) => { )}
{props.errors}
diff --git a/ui/v2.5/src/components/Shared/SuccessIcon.tsx b/ui/v2.5/src/components/Shared/SuccessIcon.tsx index 292dae0ed..79d56d979 100644 --- a/ui/v2.5/src/components/Shared/SuccessIcon.tsx +++ b/ui/v2.5/src/components/Shared/SuccessIcon.tsx @@ -1,12 +1,13 @@ +import { faCheckCircle } from "@fortawesome/free-regular-svg-icons"; import React from "react"; -import { Icon } from "src/components/Shared"; +import Icon from "src/components/Shared/Icon"; interface ISuccessIconProps { className?: string; } const SuccessIcon: React.FC = ({ className }) => ( - + ); export default SuccessIcon; diff --git a/ui/v2.5/src/components/Shared/TagLink.tsx b/ui/v2.5/src/components/Shared/TagLink.tsx index 8b28e646b..e5e7d8fb7 100644 --- a/ui/v2.5/src/components/Shared/TagLink.tsx +++ b/ui/v2.5/src/components/Shared/TagLink.tsx @@ -10,7 +10,8 @@ import { SceneDataFragment, GalleryDataFragment, } from "src/core/generated-graphql"; -import { NavUtils, TextUtils } from "src/utils"; +import NavUtils from "src/utils/navigation"; +import TextUtils from "src/utils/text"; interface IProps { tag?: Partial; diff --git a/ui/v2.5/src/components/Shared/ThreeStateCheckbox.tsx b/ui/v2.5/src/components/Shared/ThreeStateCheckbox.tsx index 9340b4114..3e7cb8ac5 100644 --- a/ui/v2.5/src/components/Shared/ThreeStateCheckbox.tsx +++ b/ui/v2.5/src/components/Shared/ThreeStateCheckbox.tsx @@ -1,6 +1,7 @@ +import { faCheck, faMinus, faTimes } from "@fortawesome/free-solid-svg-icons"; import React from "react"; import { Button } from "react-bootstrap"; -import { Icon } from "."; +import Icon from "./Icon"; interface IThreeStateCheckbox { value: boolean | undefined; @@ -28,7 +29,7 @@ export const ThreeStateCheckbox: React.FC = ({ return true; } - const icon = value === undefined ? "minus" : value ? "check" : "times"; + const icon = value === undefined ? faMinus : value ? faCheck : faTimes; const labelClassName = value === undefined ? "unset" : value ? "checked" : "not-checked"; diff --git a/ui/v2.5/src/components/Shared/TruncatedText.tsx b/ui/v2.5/src/components/Shared/TruncatedText.tsx index 4c450403c..fd0c925b9 100644 --- a/ui/v2.5/src/components/Shared/TruncatedText.tsx +++ b/ui/v2.5/src/components/Shared/TruncatedText.tsx @@ -1,7 +1,7 @@ import React, { useRef, useState } from "react"; import { Overlay, Tooltip } from "react-bootstrap"; import { Placement } from "react-bootstrap/Overlay"; -import { debounce } from "lodash"; +import debounce from "lodash-es/debounce"; import cx from "classnames"; const CLASSNAME = "TruncatedText"; diff --git a/ui/v2.5/src/components/Shared/URLField.tsx b/ui/v2.5/src/components/Shared/URLField.tsx index cc233f9d9..d9818ddef 100644 --- a/ui/v2.5/src/components/Shared/URLField.tsx +++ b/ui/v2.5/src/components/Shared/URLField.tsx @@ -1,8 +1,9 @@ import React from "react"; import { useIntl } from "react-intl"; import { Button, InputGroup, Form } from "react-bootstrap"; -import { Icon } from "src/components/Shared"; +import Icon from "src/components/Shared/Icon"; import { FormikHandlers } from "formik"; +import { faFileDownload } from "@fortawesome/free-solid-svg-icons"; interface IProps { value: string; @@ -36,7 +37,7 @@ export const URLField: React.FC = (props: IProps) => { disabled={!props.value || !props.urlScrapable(props.value)} title={intl.formatMessage({ id: "actions.scrape" })} > - + diff --git a/ui/v2.5/src/components/Stats.tsx b/ui/v2.5/src/components/Stats.tsx index bfc1ff01b..69007f122 100644 --- a/ui/v2.5/src/components/Stats.tsx +++ b/ui/v2.5/src/components/Stats.tsx @@ -121,3 +121,5 @@ export const Stats: React.FC = () => {
); }; + +export default Stats; diff --git a/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx b/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx index 56875b40c..6a43ce54e 100644 --- a/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx +++ b/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx @@ -28,6 +28,7 @@ import { StudioPerformersPanel } from "./StudioPerformersPanel"; import { StudioEditPanel } from "./StudioEditPanel"; import { StudioDetailsPanel } from "./StudioDetailsPanel"; import { StudioMoviesPanel } from "./StudioMoviesPanel"; +import { faTrashAlt } from "@fortawesome/free-solid-svg-icons"; interface IProps { studio: GQL.StudioDataFragment; @@ -112,7 +113,7 @@ const StudioPage: React.FC = ({ studio }) => { return ( ; @@ -197,7 +198,7 @@ export const StudioEditPanel: React.FC = ({ )} onClick={() => removeStashID(stashID)} > - + {link} diff --git a/ui/v2.5/src/components/Studios/StudioList.tsx b/ui/v2.5/src/components/Studios/StudioList.tsx index 794ac1046..7e14190df 100644 --- a/ui/v2.5/src/components/Studios/StudioList.tsx +++ b/ui/v2.5/src/components/Studios/StudioList.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { useIntl } from "react-intl"; -import _ from "lodash"; +import cloneDeep from "lodash-es/cloneDeep"; import { useHistory } from "react-router-dom"; import Mousetrap from "mousetrap"; import { @@ -67,7 +67,7 @@ export const StudioList: React.FC = ({ const { count } = result.data.findStudios; const index = Math.floor(Math.random() * count); - const filterCopy = _.cloneDeep(filter); + const filterCopy = cloneDeep(filter); filterCopy.itemsPerPage = 1; filterCopy.currentPage = index + 1; const singleResult = await queryFindStudios(filterCopy); diff --git a/ui/v2.5/src/components/Tagger/IncludeButton.tsx b/ui/v2.5/src/components/Tagger/IncludeButton.tsx index bc0e12ab6..b60e7e12d 100644 --- a/ui/v2.5/src/components/Tagger/IncludeButton.tsx +++ b/ui/v2.5/src/components/Tagger/IncludeButton.tsx @@ -1,3 +1,4 @@ +import { faCheck, faTimes } from "@fortawesome/free-solid-svg-icons"; import React from "react"; import { Button } from "react-bootstrap"; import { Icon } from "../Shared"; @@ -21,7 +22,7 @@ export const IncludeExcludeButton: React.FC = ({ exclude ? "text-danger" : "text-success" } include-exclude-button`} > - + ); diff --git a/ui/v2.5/src/components/Tagger/PerformerFieldSelector.tsx b/ui/v2.5/src/components/Tagger/PerformerFieldSelector.tsx index c7635529f..3862ea7ac 100644 --- a/ui/v2.5/src/components/Tagger/PerformerFieldSelector.tsx +++ b/ui/v2.5/src/components/Tagger/PerformerFieldSelector.tsx @@ -1,3 +1,4 @@ +import { faCheck, faList, faTimes } from "@fortawesome/free-solid-svg-icons"; import React, { useState } from "react"; import { Button, Row, Col } from "react-bootstrap"; import { useIntl } from "react-intl"; @@ -36,7 +37,7 @@ const PerformerFieldSelect: React.FC = ({ variant="secondary" className={excluded[name] ? "text-muted" : "text-success"} > - + {TextUtils.capitalize(name)} @@ -45,7 +46,7 @@ const PerformerFieldSelect: React.FC = ({ return ( void; excludedPerformerFields?: string[]; header: string; - icon: IconName; + icon: IconDefinition; create?: boolean; endpoint?: string; } @@ -91,7 +98,7 @@ const PerformerModal: React.FC = ({ variant="secondary" className={excluded[name] ? "text-muted" : "text-success"} > - + )} @@ -219,7 +226,7 @@ const PerformerModal: React.FC = ({
Stash-Box Source - +
)} @@ -236,7 +243,7 @@ const PerformerModal: React.FC = ({ excluded.image ? "text-muted" : "text-success" )} > - + )} = ({
Select performer image @@ -265,7 +272,7 @@ const PerformerModal: React.FC = ({ {imageIndex + 1} of {images.length}
diff --git a/ui/v2.5/src/components/Tagger/performers/PerformerTagger.tsx b/ui/v2.5/src/components/Tagger/performers/PerformerTagger.tsx index 2d3f8a27e..2a1a1f4b7 100755 --- a/ui/v2.5/src/components/Tagger/performers/PerformerTagger.tsx +++ b/ui/v2.5/src/components/Tagger/performers/PerformerTagger.tsx @@ -20,6 +20,7 @@ import PerformerConfig from "./Config"; import { LOCAL_FORAGE_KEY, ITaggerConfig, initialConfig } from "../constants"; import PerformerModal from "../PerformerModal"; import { useUpdatePerformer } from "../queries"; +import { faStar, faTags } from "@fortawesome/free-solid-svg-icons"; type JobFragment = Pick< GQL.Job, @@ -352,7 +353,7 @@ const PerformerTaggerList: React.FC = ({ performer={modalPerformer} onSave={handlePerformerUpdate} excludedPerformerFields={config.excludedPerformerFields} - icon="tags" + icon={faTags} header={intl.formatMessage({ id: "performer_tagger.update_performer", })} @@ -381,7 +382,7 @@ const PerformerTaggerList: React.FC = ({ = ({ = ({ modalVisible={modalPerformer !== undefined} performer={modalPerformer} onSave={handleSave} - icon="tags" + icon={faTags} header="Update Performer" excludedPerformerFields={excludedPerformerFields} endpoint={endpoint} diff --git a/ui/v2.5/src/components/Tagger/queries.ts b/ui/v2.5/src/components/Tagger/queries.ts index 9daa3b1b5..69bf6d116 100644 --- a/ui/v2.5/src/components/Tagger/queries.ts +++ b/ui/v2.5/src/components/Tagger/queries.ts @@ -1,5 +1,5 @@ import * as GQL from "src/core/generated-graphql"; -import { sortBy } from "lodash"; +import sortBy from "lodash-es/sortBy"; export const useUpdatePerformerStashID = () => { const [updatePerformer] = GQL.usePerformerUpdateMutation({ diff --git a/ui/v2.5/src/components/Tagger/scenes/Config.tsx b/ui/v2.5/src/components/Tagger/scenes/Config.tsx index 1c1702687..2eaa94a43 100644 --- a/ui/v2.5/src/components/Tagger/scenes/Config.tsx +++ b/ui/v2.5/src/components/Tagger/scenes/Config.tsx @@ -1,3 +1,4 @@ +import { faTimes } from "@fortawesome/free-solid-svg-icons"; import React, { useRef, useContext } from "react"; import { Badge, @@ -205,7 +206,7 @@ const Config: React.FC = ({ show }) => { className="minimal ml-2" onClick={() => removeBlacklist(index)} > - + ))} diff --git a/ui/v2.5/src/components/Tagger/scenes/PerformerResult.tsx b/ui/v2.5/src/components/Tagger/scenes/PerformerResult.tsx index dc0ca049a..12bbef5d9 100755 --- a/ui/v2.5/src/components/Tagger/scenes/PerformerResult.tsx +++ b/ui/v2.5/src/components/Tagger/scenes/PerformerResult.tsx @@ -11,6 +11,7 @@ import { ValidTypes, } from "src/components/Shared"; import { OptionalField } from "../IncludeButton"; +import { faSave } from "@fortawesome/free-solid-svg-icons"; interface IPerformerResultProps { performer: GQL.ScrapedPerformer; @@ -91,7 +92,7 @@ const PerformerResult: React.FC = ({ operation={onLink} hideChildrenWhenLoading > - + ); } diff --git a/ui/v2.5/src/components/Tagger/scenes/SceneTagger.tsx b/ui/v2.5/src/components/Tagger/scenes/SceneTagger.tsx index 1f26bad31..e793bb63a 100755 --- a/ui/v2.5/src/components/Tagger/scenes/SceneTagger.tsx +++ b/ui/v2.5/src/components/Tagger/scenes/SceneTagger.tsx @@ -12,6 +12,7 @@ import { TaggerScene } from "./TaggerScene"; import { SceneTaggerModals } from "./sceneTaggerModals"; import { SceneSearchResults } from "./StashSearchResult"; import { ConfigurationContext } from "src/hooks/Config"; +import { faCog } from "@fortawesome/free-solid-svg-icons"; interface ITaggerProps { scenes: GQL.SlimSceneDataFragment[]; @@ -83,7 +84,7 @@ export const Tagger: React.FC = ({ scenes, queue }) => { return (
); diff --git a/ui/v2.5/src/components/Tagger/scenes/StashSearchResult.tsx b/ui/v2.5/src/components/Tagger/scenes/StashSearchResult.tsx index 7f39deac1..33b6887f9 100755 --- a/ui/v2.5/src/components/Tagger/scenes/StashSearchResult.tsx +++ b/ui/v2.5/src/components/Tagger/scenes/StashSearchResult.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect, useCallback, useMemo } from "react"; import cx from "classnames"; import { Badge, Button, Col, Form, Row } from "react-bootstrap"; import { FormattedMessage, useIntl } from "react-intl"; -import { uniq } from "lodash"; +import uniq from "lodash-es/uniq"; import { blobToBase64 } from "base64-blob"; import { distance } from "src/utils/hamming"; @@ -24,6 +24,7 @@ import { SceneTaggerModalsState } from "./sceneTaggerModals"; import PerformerResult from "./PerformerResult"; import StudioResult from "./StudioResult"; import { useInitialState } from "src/hooks/state"; +import { faPlus } from "@fortawesome/free-solid-svg-icons"; const getDurationStatus = ( scene: IScrapedScene, @@ -612,7 +613,7 @@ const StashSearchResult: React.FC = ({ > {t.name} ))} diff --git a/ui/v2.5/src/components/Tagger/scenes/StudioModal.tsx b/ui/v2.5/src/components/Tagger/scenes/StudioModal.tsx index 35edf62c2..4ce707297 100644 --- a/ui/v2.5/src/components/Tagger/scenes/StudioModal.tsx +++ b/ui/v2.5/src/components/Tagger/scenes/StudioModal.tsx @@ -1,10 +1,11 @@ import React, { useContext } from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import { IconName } from "@fortawesome/fontawesome-svg-core"; +import { IconDefinition } from "@fortawesome/fontawesome-svg-core"; import * as GQL from "src/core/generated-graphql"; import { Icon, Modal, TruncatedText } from "src/components/Shared"; import { TaggerStateContext } from "../context"; +import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons"; interface IStudioModalProps { studio: GQL.ScrapedSceneStudioDataFragment; @@ -12,7 +13,7 @@ interface IStudioModalProps { closeModal: () => void; handleStudioCreate: (input: GQL.StudioCreateInput) => void; header: string; - icon: IconName; + icon: IconDefinition; } const StudioModal: React.FC = ({ @@ -93,7 +94,7 @@ const StudioModal: React.FC = ({
Stash-Box Source - +
)} diff --git a/ui/v2.5/src/components/Tagger/scenes/StudioResult.tsx b/ui/v2.5/src/components/Tagger/scenes/StudioResult.tsx index 0c0d898bb..25a97c112 100755 --- a/ui/v2.5/src/components/Tagger/scenes/StudioResult.tsx +++ b/ui/v2.5/src/components/Tagger/scenes/StudioResult.tsx @@ -12,6 +12,7 @@ import { import * as GQL from "src/core/generated-graphql"; import { OptionalField } from "../IncludeButton"; +import { faSave } from "@fortawesome/free-solid-svg-icons"; interface IStudioResultProps { studio: GQL.ScrapedStudio; @@ -89,7 +90,7 @@ const StudioResult: React.FC = ({ operation={onLink} hideChildrenWhenLoading > - + ); } diff --git a/ui/v2.5/src/components/Tagger/scenes/TaggerScene.tsx b/ui/v2.5/src/components/Tagger/scenes/TaggerScene.tsx index 137b62f18..87755c3b5 100644 --- a/ui/v2.5/src/components/Tagger/scenes/TaggerScene.tsx +++ b/ui/v2.5/src/components/Tagger/scenes/TaggerScene.tsx @@ -14,6 +14,7 @@ import { import { parsePath, prepareQueryString } from "src/components/Tagger/utils"; import { ScenePreview } from "src/components/Scenes/SceneCard"; import { TaggerStateContext } from "../context"; +import { faChevronDown, faChevronUp } from "@fortawesome/free-solid-svg-icons"; interface ITaggerSceneDetails { scene: GQL.SlimSceneDataFragment; @@ -71,7 +72,7 @@ const TaggerSceneDetails: React.FC = ({ scene }) => { className="minimal collapse-button" size="lg" > - + ); diff --git a/ui/v2.5/src/components/Tagger/scenes/sceneTaggerModals.tsx b/ui/v2.5/src/components/Tagger/scenes/sceneTaggerModals.tsx index 0e40d408a..fe670e32b 100644 --- a/ui/v2.5/src/components/Tagger/scenes/sceneTaggerModals.tsx +++ b/ui/v2.5/src/components/Tagger/scenes/sceneTaggerModals.tsx @@ -5,6 +5,7 @@ import StudioModal from "./StudioModal"; import PerformerModal from "../PerformerModal"; import { TaggerStateContext } from "../context"; import { useIntl } from "react-intl"; +import { faTags } from "@fortawesome/free-solid-svg-icons"; type PerformerModalCallback = (toCreate?: GQL.PerformerCreateInput) => void; type StudioModalCallback = (toCreate?: GQL.StudioCreateInput) => void; @@ -112,7 +113,7 @@ export const SceneTaggerModals: React.FC = ({ children }) => { modalVisible performer={performerToCreate} onSave={handlePerformerSave} - icon="tags" + icon={faTags} header={intl.formatMessage( { id: "actions.create_entity" }, { entityType: intl.formatMessage({ id: "performer" }) } @@ -127,7 +128,7 @@ export const SceneTaggerModals: React.FC = ({ children }) => { modalVisible studio={studioToCreate} handleStudioCreate={handleStudioSave} - icon="tags" + icon={faTags} header={intl.formatMessage( { id: "actions.create_entity" }, { entityType: intl.formatMessage({ id: "studio" }) } diff --git a/ui/v2.5/src/components/Tags/TagCard.tsx b/ui/v2.5/src/components/Tags/TagCard.tsx index 8daa7bfcf..3bb2418ec 100644 --- a/ui/v2.5/src/components/Tags/TagCard.tsx +++ b/ui/v2.5/src/components/Tags/TagCard.tsx @@ -7,6 +7,7 @@ import { FormattedMessage } from "react-intl"; import { Icon } from "../Shared"; import { GridCard } from "../Shared/GridCard"; import { PopoverCountButton } from "../Shared/PopoverCountButton"; +import { faMapMarkerAlt, faUser } from "@fortawesome/free-solid-svg-icons"; interface IProps { tag: GQL.TagDataFragment; @@ -102,7 +103,7 @@ export const TagCard: React.FC = ({ return ( @@ -141,7 +142,7 @@ export const TagCard: React.FC = ({ return ( diff --git a/ui/v2.5/src/components/Tags/TagDetails/Tag.tsx b/ui/v2.5/src/components/Tags/TagDetails/Tag.tsx index 47bb279f8..b5dc4618b 100644 --- a/ui/v2.5/src/components/Tags/TagDetails/Tag.tsx +++ b/ui/v2.5/src/components/Tags/TagDetails/Tag.tsx @@ -30,6 +30,11 @@ import { TagGalleriesPanel } from "./TagGalleriesPanel"; import { TagDetailsPanel } from "./TagDetailsPanel"; import { TagEditPanel } from "./TagEditPanel"; import { TagMergeModal } from "./TagMergeDialog"; +import { + faSignInAlt, + faSignOutAlt, + faTrashAlt, +} from "@fortawesome/free-solid-svg-icons"; interface IProps { tag: GQL.TagDataFragment; @@ -165,7 +170,7 @@ const TagPage: React.FC = ({ tag }) => { return ( = ({ tag }) => { className="bg-secondary text-white" onClick={() => setMergeType("from")} > - + ...
@@ -227,7 +232,7 @@ const TagPage: React.FC = ({ tag }) => { className="bg-secondary text-white" onClick={() => setMergeType("into")} > - + ...
diff --git a/ui/v2.5/src/components/Tags/TagDetails/TagMergeDialog.tsx b/ui/v2.5/src/components/Tags/TagDetails/TagMergeDialog.tsx index 891c2d228..a2c51383f 100644 --- a/ui/v2.5/src/components/Tags/TagDetails/TagMergeDialog.tsx +++ b/ui/v2.5/src/components/Tags/TagDetails/TagMergeDialog.tsx @@ -7,6 +7,7 @@ import { useTagsMerge } from "src/core/StashService"; import { useIntl } from "react-intl"; import { useToast } from "src/hooks"; import { useHistory } from "react-router-dom"; +import { faSignInAlt, faSignOutAlt } from "@fortawesome/free-solid-svg-icons"; interface ITagMergeModalProps { show: boolean; @@ -74,7 +75,7 @@ export const TagMergeModal: React.FC = ({ onMerge(), diff --git a/ui/v2.5/src/components/Tags/TagList.tsx b/ui/v2.5/src/components/Tags/TagList.tsx index 88e804a82..e6201064f 100644 --- a/ui/v2.5/src/components/Tags/TagList.tsx +++ b/ui/v2.5/src/components/Tags/TagList.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import _ from "lodash"; +import cloneDeep from "lodash-es/cloneDeep"; import Mousetrap from "mousetrap"; import { FindTagsQueryResult } from "src/core/generated-graphql"; import { ListFilterModel } from "src/models/list-filter/filter"; @@ -25,6 +25,7 @@ import { Icon, Modal, DeleteEntityDialog } from "src/components/Shared"; import { TagCard } from "./TagCard"; import { ExportDialog } from "../Shared/ExportDialog"; import { tagRelationHook } from "../../core/tags"; +import { faTrashAlt } from "@fortawesome/free-solid-svg-icons"; interface ITagList { filterHook?: (filter: ListFilterModel) => ListFilterModel; @@ -82,7 +83,7 @@ export const TagList: React.FC = ({ filterHook }) => { const { count } = result.data.findTags; const index = Math.floor(Math.random() * count); - const filterCopy = _.cloneDeep(filter); + const filterCopy = cloneDeep(filter); filterCopy.itemsPerPage = 1; filterCopy.currentPage = index + 1; const singleResult = await queryFindTags(filterCopy); @@ -240,7 +241,7 @@ export const TagList: React.FC = ({ filterHook }) => { {}} show={!!deletingTag} - icon="trash-alt" + icon={faTrashAlt} accept={{ onClick: onDelete, variant: "danger", @@ -338,7 +339,7 @@ export const TagList: React.FC = ({ filterHook }) => { /> diff --git a/ui/v2.5/src/core/StashService.ts b/ui/v2.5/src/core/StashService.ts index 0f1512ef7..1bb7734df 100644 --- a/ui/v2.5/src/core/StashService.ts +++ b/ui/v2.5/src/core/StashService.ts @@ -6,7 +6,7 @@ import { getOperationName, } from "@apollo/client/utilities"; import { stringToGender } from "src/utils/gender"; -import { filterData } from "../utils"; +import { filterData } from "../utils/data"; import { ListFilterModel } from "../models/list-filter/filter"; import * as GQL from "./generated-graphql"; diff --git a/ui/v2.5/src/hooks/Interactive/status.tsx b/ui/v2.5/src/hooks/Interactive/status.tsx index 268fca7b8..d630cbb9c 100644 --- a/ui/v2.5/src/hooks/Interactive/status.tsx +++ b/ui/v2.5/src/hooks/Interactive/status.tsx @@ -1,3 +1,4 @@ +import { faCircle } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import React from "react"; import { FormattedMessage } from "react-intl"; @@ -35,7 +36,7 @@ export const SceneInteractiveStatus: React.FC = ({}) => { return (
- + {error && : {error}} diff --git a/ui/v2.5/src/hooks/Interval.ts b/ui/v2.5/src/hooks/Interval.ts index 854747c2b..838ea9764 100644 --- a/ui/v2.5/src/hooks/Interval.ts +++ b/ui/v2.5/src/hooks/Interval.ts @@ -1,5 +1,5 @@ import { useEffect, useRef, useState } from "react"; -import noop from "lodash/noop"; +import noop from "lodash-es/noop"; const MIN_VALID_INTERVAL = 1000; diff --git a/ui/v2.5/src/hooks/Lightbox/Lightbox.tsx b/ui/v2.5/src/hooks/Lightbox/Lightbox.tsx index a82ef6f81..91fcffcc8 100644 --- a/ui/v2.5/src/hooks/Lightbox/Lightbox.tsx +++ b/ui/v2.5/src/hooks/Lightbox/Lightbox.tsx @@ -10,7 +10,7 @@ import { } from "react-bootstrap"; import cx from "classnames"; import Mousetrap from "mousetrap"; -import debounce from "lodash/debounce"; +import debounce from "lodash-es/debounce"; import { Icon, LoadingIndicator } from "src/components/Shared"; import { useInterval, usePageVisibility, useToast } from "src/hooks"; @@ -29,6 +29,19 @@ import { import * as GQL from "src/core/generated-graphql"; import { useInterfaceLocalForage } from "../LocalForage"; import { imageLightboxDisplayModeIntlMap } from "src/core/enums"; +import { ILightboxImage } from "./types"; +import { + faArrowLeft, + faArrowRight, + faChevronLeft, + faChevronRight, + faCog, + faExpand, + faPause, + faPlay, + faSearchMinus, + faTimes, +} from "@fortawesome/free-solid-svg-icons"; const CLASSNAME = "Lightbox"; const CLASSNAME_HEADER = `${CLASSNAME}-header`; @@ -53,18 +66,6 @@ const DEFAULT_SLIDESHOW_DELAY = 5000; const SECONDS_TO_MS = 1000; const MIN_VALID_INTERVAL_SECONDS = 1; -interface IImagePaths { - image?: GQL.Maybe; - thumbnail?: GQL.Maybe; -} -export interface ILightboxImage { - id?: string; - title?: GQL.Maybe; - rating?: GQL.Maybe; - o_counter?: GQL.Maybe; - paths: IImagePaths; -} - interface IProps { images: ILightboxImage[]; isVisible: boolean; @@ -643,7 +644,7 @@ export const LightboxComponent: React.FC = ({ })} onClick={() => setShowOptions(!showOptions)} > - + = ({ onClick={toggleSlideshow} title="Toggle Slideshow" > - + )} {zoom !== 1 && ( @@ -691,7 +692,7 @@ export const LightboxComponent: React.FC = ({ }} title="Reset zoom" > - + )} {document.fullscreenEnabled && ( @@ -700,11 +701,11 @@ export const LightboxComponent: React.FC = ({ onClick={toggleFullscreen} title="Toggle Fullscreen" > - + )}
@@ -715,7 +716,7 @@ export const LightboxComponent: React.FC = ({ onClick={handleLeft} className={`${CLASSNAME_NAVBUTTON} d-none d-lg-block`} > - + )} @@ -757,7 +758,7 @@ export const LightboxComponent: React.FC = ({ onClick={handleRight} className={`${CLASSNAME_NAVBUTTON} d-none d-lg-block`} > - + )} @@ -768,7 +769,7 @@ export const LightboxComponent: React.FC = ({ onClick={() => setIndex(images.length - 1)} className={CLASSNAME_NAVBUTTON} > - + {navItems} )} @@ -813,3 +814,5 @@ export const LightboxComponent: React.FC = ({ ); }; + +export default LightboxComponent; diff --git a/ui/v2.5/src/hooks/Lightbox/context.tsx b/ui/v2.5/src/hooks/Lightbox/context.tsx index c8e9bc106..2f857d5fb 100644 --- a/ui/v2.5/src/hooks/Lightbox/context.tsx +++ b/ui/v2.5/src/hooks/Lightbox/context.tsx @@ -1,5 +1,7 @@ -import React, { useCallback, useState } from "react"; -import { ILightboxImage, LightboxComponent } from "./Lightbox"; +import React, { lazy, Suspense, useCallback, useState } from "react"; +import { ILightboxImage } from "./types"; + +const LightboxComponent = lazy(() => import("./Lightbox")); export interface IState { images: ILightboxImage[]; @@ -48,9 +50,11 @@ const Lightbox: React.FC = ({ children }) => { return ( {children} - {lightboxState.isVisible && ( - - )} + }> + {lightboxState.isVisible && ( + + )} + ); }; diff --git a/ui/v2.5/src/hooks/Lightbox/types.ts b/ui/v2.5/src/hooks/Lightbox/types.ts new file mode 100644 index 000000000..70bd16454 --- /dev/null +++ b/ui/v2.5/src/hooks/Lightbox/types.ts @@ -0,0 +1,14 @@ +import * as GQL from "src/core/generated-graphql"; + +interface IImagePaths { + image?: GQL.Maybe; + thumbnail?: GQL.Maybe; +} + +export interface ILightboxImage { + id?: string; + title?: GQL.Maybe; + rating?: GQL.Maybe; + o_counter?: GQL.Maybe; + paths: IImagePaths; +} diff --git a/ui/v2.5/src/hooks/ListHook.tsx b/ui/v2.5/src/hooks/ListHook.tsx index 5f95cf010..26e50bab5 100644 --- a/ui/v2.5/src/hooks/ListHook.tsx +++ b/ui/v2.5/src/hooks/ListHook.tsx @@ -1,4 +1,5 @@ -import _ from "lodash"; +import clone from "lodash-es/clone"; +import cloneDeep from "lodash-es/cloneDeep"; import queryString from "query-string"; import React, { useCallback, @@ -10,7 +11,7 @@ import React, { import { ApolloError } from "@apollo/client"; import { useHistory, useLocation } from "react-router-dom"; import Mousetrap from "mousetrap"; -import { IconProp } from "@fortawesome/fontawesome-svg-core"; +import { IconDefinition } from "@fortawesome/fontawesome-svg-core"; import { SlimSceneDataFragment, SceneMarkerDataFragment, @@ -99,7 +100,7 @@ export interface IListHookOperation { selectedIds: Set ) => boolean; postRefetch?: boolean; - icon?: IconProp; + icon?: IconDefinition; buttonVariant?: string; } @@ -268,7 +269,7 @@ const RenderList = < function singleSelect(id: string, selected: boolean) { setLastClickedId(id); - const newSelectedIds = _.clone(selectedIds); + const newSelectedIds = clone(selectedIds); if (selected) { newSelectedIds.add(id); } else { @@ -339,7 +340,7 @@ const RenderList = < } function onChangeZoom(newZoomIndex: number) { - const newFilter = _.cloneDeep(filter); + const newFilter = cloneDeep(filter); newFilter.zoomIndex = newZoomIndex; updateQueryParams(newFilter); } @@ -434,7 +435,7 @@ const RenderList = < } function onChangeDisplayMode(displayMode: DisplayMode) { - const newFilter = _.cloneDeep(filter); + const newFilter = cloneDeep(filter); newFilter.displayMode = displayMode; updateQueryParams(newFilter); } @@ -443,7 +444,7 @@ const RenderList = < criterion: Criterion, oldId?: string ) { - const newFilter = _.cloneDeep(filter); + const newFilter = cloneDeep(filter); // Find if we are editing an existing criteria, then modify that. Or create a new one. const existingIndex = newFilter.criteria.findIndex((c) => { @@ -469,7 +470,7 @@ const RenderList = < } function onRemoveCriterion(removedCriterion: Criterion) { - const newFilter = _.cloneDeep(filter); + const newFilter = cloneDeep(filter); newFilter.criteria = newFilter.criteria.filter( (criterion) => criterion.getId() !== removedCriterion.getId() ); @@ -478,7 +479,7 @@ const RenderList = < } function updateCriteria(c: Criterion[]) { - const newFilter = _.cloneDeep(filter); + const newFilter = cloneDeep(filter); newFilter.criteria = c.slice(); setNewCriterion(false); } @@ -713,7 +714,7 @@ const useList = ( const onChangePage = useCallback( (page: number) => { - const newFilter = _.cloneDeep(filter); + const newFilter = cloneDeep(filter); newFilter.currentPage = page; updateQueryParams(newFilter); window.scrollTo(0, 0); @@ -722,9 +723,7 @@ const useList = ( ); const renderFilter = useMemo(() => { - return !options.filterHook - ? filter - : options.filterHook(_.cloneDeep(filter)); + return !options.filterHook ? filter : options.filterHook(cloneDeep(filter)); }, [filter, options]); const { contentTemplate, onSelectChange } = RenderList({ diff --git a/ui/v2.5/src/hooks/LocalForage.ts b/ui/v2.5/src/hooks/LocalForage.ts index 5d47a4c50..b8e6aae9a 100644 --- a/ui/v2.5/src/hooks/LocalForage.ts +++ b/ui/v2.5/src/hooks/LocalForage.ts @@ -1,5 +1,5 @@ import localForage from "localforage"; -import _ from "lodash"; +import isEqual from "lodash-es/isEqual"; import React, { Dispatch, SetStateAction, useEffect } from "react"; import { ConfigImageLightboxInput } from "src/core/generated-graphql"; @@ -69,7 +69,7 @@ export function useLocalForage( }, [loading, key, defaultValue]); useEffect(() => { - if (!_.isEqual(Cache[key], data)) { + if (isEqual(Cache[key], data)) { Cache[key] = { ...Cache[key], ...data, diff --git a/ui/v2.5/src/locales/index.ts b/ui/v2.5/src/locales/index.ts index 0b03b1a64..bf6fd19b4 100644 --- a/ui/v2.5/src/locales/index.ts +++ b/ui/v2.5/src/locales/index.ts @@ -1,41 +1,24 @@ -import deDE from "./de-DE.json"; -import enGB from "./en-GB.json"; -import enUS from "./en-US.json"; -import esES from "./es-ES.json"; -import ptBR from "./pt-BR.json"; -import frFR from "./fr-FR.json"; -import itIT from "./it-IT.json"; -import fiFI from "./fi-FI.json"; -import svSE from "./sv-SE.json"; -import zhTW from "./zh-TW.json"; -import zhCN from "./zh-CN.json"; -import hrHR from "./hr-HR.json"; -import nlNL from "./nl-NL.json"; -import ruRU from "./ru-RU.json"; -import trTR from "./tr-TR.json"; -import jaJP from "./ja-JP.json"; -import plPL from "./pl-PL.json"; -import daDK from "./da-DK.json"; -import koKR from "./ko-KR.json"; +export const localeLoader = { + deDE: () => import("./de-DE.json"), + enGB: () => import("./en-GB.json"), + enUS: () => import("./en-US.json"), + esES: () => import("./es-ES.json"), + ptBR: () => import("./pt-BR.json"), + frFR: () => import("./fr-FR.json"), + itIT: () => import("./it-IT.json"), + fiFI: () => import("./fi-FI.json"), + svSE: () => import("./sv-SE.json"), + zhTW: () => import("./zh-TW.json"), + zhCN: () => import("./zh-CN.json"), + hrHR: () => import("./hr-HR.json"), + nlNL: () => import("./nl-NL.json"), + ruRU: () => import("./ru-RU.json"), + trTR: () => import("./tr-TR.json"), + jaJP: () => import("./ja-JP.json"), + plPL: () => import("./pl-PL.json"), + daDK: () => import("./da-DK.json"), + koKR: () => import("./ko-KR.json"), + // eslint-disable-next-line @typescript-eslint/no-explicit-any +} as { [key: string]: any }; -export default { - deDE, - enGB, - enUS, - esES, - ptBR, - frFR, - itIT, - fiFI, - svSE, - zhTW, - zhCN, - hrHR, - nlNL, - ruRU, - trTR, - jaJP, - plPL, - daDK, - koKR, -}; +export default localeLoader; diff --git a/ui/v2.5/src/models/sceneQueue.ts b/ui/v2.5/src/models/sceneQueue.ts index 948cebc72..3c558493a 100644 --- a/ui/v2.5/src/models/sceneQueue.ts +++ b/ui/v2.5/src/models/sceneQueue.ts @@ -129,3 +129,5 @@ export class SceneQueue { return `/scenes/${sceneID}${params.length ? "?" + params.join("&") : ""}`; } } + +export default SceneQueue; diff --git a/ui/v2.5/src/utils/bulkUpdate.ts b/ui/v2.5/src/utils/bulkUpdate.ts index c91175140..542a57337 100644 --- a/ui/v2.5/src/utils/bulkUpdate.ts +++ b/ui/v2.5/src/utils/bulkUpdate.ts @@ -1,5 +1,5 @@ import * as GQL from "src/core/generated-graphql"; -import _ from "lodash"; +import isEqual from "lodash-es/isEqual"; interface IHasRating { rating?: GQL.Maybe | undefined; @@ -63,7 +63,7 @@ export function getAggregatePerformerIds(state: IHasPerformers[]) { } else { const perfIds = o.performers ? o.performers.map((p) => p.id).sort() : []; - if (!_.isEqual(ret, perfIds)) { + if (isEqual(ret, perfIds)) { ret = []; } } @@ -87,7 +87,7 @@ export function getAggregateTagIds(state: IHasTags[]) { } else { const tIds = o.tags ? o.tags.map((t) => t.id).sort() : []; - if (!_.isEqual(ret, tIds)) { + if (isEqual(ret, tIds)) { ret = []; } } @@ -115,7 +115,7 @@ export function getAggregateMovieIds(state: IHasMovies[]) { } else { const mIds = o.movies ? o.movies.map((m) => m.movie.id).sort() : []; - if (!_.isEqual(ret, mIds)) { + if (isEqual(ret, mIds)) { ret = []; } } @@ -180,7 +180,7 @@ export function getAggregateState( newValue: T, first: boolean ) { - if (!first && !_.isEqual(currentValue, newValue)) { + if (!first && isEqual(currentValue, newValue)) { return undefined; } diff --git a/ui/v2.5/yarn.lock b/ui/v2.5/yarn.lock index a0a6a33fa..4a9dad558 100644 --- a/ui/v2.5/yarn.lock +++ b/ui/v2.5/yarn.lock @@ -1384,7 +1384,19 @@ dependencies: "@types/node" "*" -"@types/lodash@^4.14.165", "@types/lodash@^4.14.168": +"@types/lodash-es@^4.17.6": + version "4.17.6" + resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.6.tgz#c2ed4c8320ffa6f11b43eb89e9eaeec65966a0a0" + integrity sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.14.182" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2" + integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q== + +"@types/lodash@^4.14.165": version "4.14.168" resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.168.tgz" integrity sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q== @@ -2397,6 +2409,11 @@ chardet@^0.7.0: optionalDependencies: fsevents "~2.3.1" +classnames@^2.2.5: + version "2.3.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" + integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== + classnames@^2.2.6: version "2.2.6" resolved "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz" @@ -4904,7 +4921,7 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash-es@^4.17.14, lodash-es@^4.17.15, lodash-es@^4.17.20: +lodash-es@^4.17.14, lodash-es@^4.17.15, lodash-es@^4.17.20, lodash-es@^4.17.21: version "4.17.21" resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==