mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 12:24:38 +03:00
Library updates (#792)
* Upgrade Typescript to 4.0 * Update i18n-iso-countries to 6.0 * Update react-intl to 5.8.0 * Update jimp to 0.16.1 * Update apollo and graphql libraries * Update various libraries and fix linting/type errors * Refactor cache invalidation * Codegen refetch queries
This commit is contained in:
@@ -1,4 +1,10 @@
|
|||||||
{
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"Mousetrap": "readonly"
|
||||||
|
},
|
||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@typescript-eslint/parser",
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"project": "./tsconfig.json"
|
"project": "./tsconfig.json"
|
||||||
@@ -16,11 +22,18 @@
|
|||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/explicit-function-return-type": "off",
|
"@typescript-eslint/explicit-function-return-type": "off",
|
||||||
"@typescript-eslint/no-explicit-any": 2,
|
"@typescript-eslint/no-explicit-any": 2,
|
||||||
"lines-between-class-members": "off",
|
"@typescript-eslint/naming-convention": [
|
||||||
"@typescript-eslint/interface-name-prefix": [
|
"error",
|
||||||
"warn",
|
{
|
||||||
{ "prefixWithI": "always" }
|
"selector": "interface",
|
||||||
|
"format": ["PascalCase"],
|
||||||
|
"custom": {
|
||||||
|
"regex": "^I[A-Z]",
|
||||||
|
"match": true
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
|
"lines-between-class-members": "off",
|
||||||
"import/extensions": [
|
"import/extensions": [
|
||||||
"error",
|
"error",
|
||||||
"ignorePackages",
|
"ignorePackages",
|
||||||
@@ -46,6 +59,7 @@
|
|||||||
"@typescript-eslint/indent": "off",
|
"@typescript-eslint/indent": "off",
|
||||||
"react/prop-types": "off",
|
"react/prop-types": "off",
|
||||||
"react/destructuring-assignment": "off",
|
"react/destructuring-assignment": "off",
|
||||||
|
"react/require-default-props": "off",
|
||||||
"react/jsx-props-no-spreading": "off",
|
"react/jsx-props-no-spreading": "off",
|
||||||
"react/style-prop-object": ["error", {
|
"react/style-prop-object": ["error", {
|
||||||
"allow": ["FormattedNumber"]
|
"allow": ["FormattedNumber"]
|
||||||
|
|||||||
@@ -3,13 +3,12 @@ schema: "../../graphql/schema/**/*.graphql"
|
|||||||
documents: "../../graphql/documents/**/*.graphql"
|
documents: "../../graphql/documents/**/*.graphql"
|
||||||
generates:
|
generates:
|
||||||
src/core/generated-graphql.tsx:
|
src/core/generated-graphql.tsx:
|
||||||
config:
|
|
||||||
withHooks: true
|
|
||||||
withHOC: false
|
|
||||||
withComponents: false
|
|
||||||
plugins:
|
plugins:
|
||||||
- add: "/* eslint-disable */"
|
- add:
|
||||||
|
content: "/* eslint-disable */"
|
||||||
- time
|
- time
|
||||||
- typescript
|
- typescript
|
||||||
- typescript-operations
|
- typescript-operations
|
||||||
- typescript-react-apollo
|
- typescript-react-apollo
|
||||||
|
config:
|
||||||
|
withRefetchFn: true
|
||||||
|
|||||||
@@ -25,66 +25,56 @@
|
|||||||
"not op_mini all"
|
"not op_mini all"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/react-hooks": "^3.1.5",
|
"@apollo/client": "^3.1.4",
|
||||||
"@formatjs/intl-numberformat": "^4.2.1",
|
"@formatjs/intl-numberformat": "^5.6.0",
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.28",
|
"@fortawesome/fontawesome-svg-core": "^1.2.30",
|
||||||
"@fortawesome/free-regular-svg-icons": "^5.13.0",
|
"@fortawesome/free-regular-svg-icons": "^5.14.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "^5.13.0",
|
"@fortawesome/free-solid-svg-icons": "^5.14.0",
|
||||||
"@fortawesome/react-fontawesome": "^0.1.9",
|
"@fortawesome/react-fontawesome": "^0.1.11",
|
||||||
"@types/mousetrap": "^1.6.3",
|
"@types/mousetrap": "^1.6.3",
|
||||||
"apollo-cache": "^1.3.4",
|
"axios": "0.20.0",
|
||||||
"apollo-cache-inmemory": "^1.6.5",
|
"bootstrap": "^4.5.2",
|
||||||
"apollo-client": "^2.6.8",
|
|
||||||
"apollo-link": "^1.2.14",
|
|
||||||
"apollo-link-error": "^1.1.13",
|
|
||||||
"apollo-link-http": "^1.5.17",
|
|
||||||
"apollo-link-ws": "^1.0.20",
|
|
||||||
"apollo-utilities": "^1.3.3",
|
|
||||||
"axios": "0.19.2",
|
|
||||||
"bootstrap": "^4.4.1",
|
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"flag-icon-css": "^3.4.6",
|
"flag-icon-css": "^3.5.0",
|
||||||
"formik": "^2.1.4",
|
"formik": "^2.1.5",
|
||||||
"graphql": "^14.5.8",
|
"graphql": "^15.3.0",
|
||||||
"graphql-tag": "^2.10.3",
|
"graphql-tag": "^2.11.0",
|
||||||
"i18n-iso-countries": "^5.2.0",
|
"i18n-iso-countries": "^6.0.0",
|
||||||
"jimp": "^0.12.1",
|
"jimp": "^0.16.1",
|
||||||
"localforage": "1.7.3",
|
"localforage": "1.9.0",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.20",
|
||||||
"mousetrap": "^1.6.5",
|
"mousetrap": "^1.6.5",
|
||||||
"query-string": "6.12.1",
|
"query-string": "6.13.1",
|
||||||
"react": "16.13.1",
|
"react": "16.13.1",
|
||||||
"react-apollo": "^3.1.5",
|
"react-bootstrap": "1.3.0",
|
||||||
"react-bootstrap": "1.0.1",
|
|
||||||
"react-dom": "16.13.1",
|
"react-dom": "16.13.1",
|
||||||
"react-images": "0.5.19",
|
"react-images": "0.5.19",
|
||||||
"react-intl": "^4.5.1",
|
"react-intl": "^5.8.0",
|
||||||
"react-jw-player": "1.19.1",
|
"react-jw-player": "1.19.1",
|
||||||
"react-markdown": "^4.3.1",
|
"react-markdown": "^4.3.1",
|
||||||
"react-photo-gallery": "^8.0.0",
|
"react-photo-gallery": "^8.0.0",
|
||||||
"react-router-bootstrap": "^0.25.0",
|
"react-router-bootstrap": "^0.25.0",
|
||||||
"react-router-dom": "^5.1.2",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-select": "^3.1.0",
|
"react-select": "^3.1.0",
|
||||||
"subscriptions-transport-ws": "^0.9.16",
|
"subscriptions-transport-ws": "^0.9.18",
|
||||||
"universal-cookie": "^4.0.3"
|
"universal-cookie": "^4.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@graphql-codegen/add": "^1.13.5",
|
"@graphql-codegen/add": "^2.0.1",
|
||||||
"@graphql-codegen/cli": "^1.13.5",
|
"@graphql-codegen/cli": "^1.17.8",
|
||||||
"@graphql-codegen/time": "^1.13.5",
|
"@graphql-codegen/time": "^2.0.1",
|
||||||
"@graphql-codegen/typescript": "^1.13.5",
|
"@graphql-codegen/typescript": "^1.17.9",
|
||||||
"@graphql-codegen/typescript-compatibility": "^1.13.5",
|
"@graphql-codegen/typescript-operations": "^1.17.8",
|
||||||
"@graphql-codegen/typescript-operations": "^1.13.5",
|
"@graphql-codegen/typescript-react-apollo": "^2.0.6",
|
||||||
"@graphql-codegen/typescript-react-apollo": "^1.13.5",
|
|
||||||
"@types/classnames": "^2.2.10",
|
"@types/classnames": "^2.2.10",
|
||||||
"@types/lodash": "^4.14.150",
|
"@types/lodash": "^4.14.161",
|
||||||
"@types/node": "13.13.4",
|
"@types/node": "14.6.4",
|
||||||
"@types/react": "16.9.34",
|
"@types/react": "16.9.43",
|
||||||
"@types/react-dom": "^16.9.7",
|
"@types/react-dom": "^16.9.8",
|
||||||
"@types/react-images": "^0.5.1",
|
"@types/react-images": "^0.5.3",
|
||||||
"@types/react-router-bootstrap": "^0.24.5",
|
"@types/react-router-bootstrap": "^0.24.5",
|
||||||
"@types/react-router-dom": "5.1.5",
|
"@types/react-router-dom": "5.1.5",
|
||||||
"@types/react-select": "^3.0.12",
|
"@types/react-select": "3.0.19",
|
||||||
"@typescript-eslint/eslint-plugin": "^2.30.0",
|
"@typescript-eslint/eslint-plugin": "^2.30.0",
|
||||||
"@typescript-eslint/parser": "^2.30.0",
|
"@typescript-eslint/parser": "^2.30.0",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
@@ -95,13 +85,13 @@
|
|||||||
"eslint-plugin-react": "^7.19.0",
|
"eslint-plugin-react": "^7.19.0",
|
||||||
"eslint-plugin-react-hooks": "^4.0.0",
|
"eslint-plugin-react-hooks": "^4.0.0",
|
||||||
"extract-react-intl-messages": "^4.1.1",
|
"extract-react-intl-messages": "^4.1.1",
|
||||||
"node-sass": "4.14.0",
|
"node-sass": "4.14.1",
|
||||||
"postcss-safe-parser": "^4.0.2",
|
"postcss-safe-parser": "^4.0.2",
|
||||||
"prettier": "2.0.5",
|
"prettier": "2.1.1",
|
||||||
"react-scripts": "^3.4.1",
|
"react-scripts": "^3.4.3",
|
||||||
"stylelint": "^13.3.3",
|
"stylelint": "^13.3.3",
|
||||||
"stylelint-config-prettier": "^8.0.1",
|
"stylelint-config-prettier": "^8.0.1",
|
||||||
"stylelint-order": "^4.0.0",
|
"stylelint-order": "^4.0.0",
|
||||||
"typescript": "^3.8.3"
|
"typescript": "^3.9.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { ToastProvider } from "src/hooks/Toast";
|
|||||||
import { library } from "@fortawesome/fontawesome-svg-core";
|
import { library } from "@fortawesome/fontawesome-svg-core";
|
||||||
import { fas } from "@fortawesome/free-solid-svg-icons";
|
import { fas } from "@fortawesome/free-solid-svg-icons";
|
||||||
import "@formatjs/intl-numberformat/polyfill";
|
import "@formatjs/intl-numberformat/polyfill";
|
||||||
import "@formatjs/intl-numberformat/dist/locale-data/en";
|
import "@formatjs/intl-numberformat/locale-data/en";
|
||||||
import "@formatjs/intl-numberformat/dist/locale-data/en-GB";
|
import "@formatjs/intl-numberformat/locale-data/en-GB";
|
||||||
|
|
||||||
import locales from "src/locale";
|
import locales from "src/locale";
|
||||||
import { useConfiguration } from "src/core/StashService";
|
import { useConfiguration } from "src/core/StashService";
|
||||||
|
|||||||
@@ -4,8 +4,12 @@ import { useFindGallery } from "src/core/StashService";
|
|||||||
import { LoadingIndicator } from "src/components/Shared";
|
import { LoadingIndicator } from "src/components/Shared";
|
||||||
import { GalleryViewer } from "./GalleryViewer";
|
import { GalleryViewer } from "./GalleryViewer";
|
||||||
|
|
||||||
|
interface IGalleryParams {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const Gallery: React.FC = () => {
|
export const Gallery: React.FC = () => {
|
||||||
const { id = "" } = useParams();
|
const { id } = useParams<IGalleryParams>();
|
||||||
|
|
||||||
const { data, error, loading } = useFindGallery(id);
|
const { data, error, loading } = useFindGallery(id);
|
||||||
const gallery = data?.findGallery;
|
const gallery = data?.findGallery;
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ export const Manual: React.FC<IManualProps> = ({ show, onClose }) => {
|
|||||||
<Container className="manual-container">
|
<Container className="manual-container">
|
||||||
<Tab.Container
|
<Tab.Container
|
||||||
activeKey={activeTab}
|
activeKey={activeTab}
|
||||||
onSelect={(k) => setActiveTab(k)}
|
onSelect={(k) => k && setActiveTab(k)}
|
||||||
id="manual-tabs"
|
id="manual-tabs"
|
||||||
>
|
>
|
||||||
<Row>
|
<Row>
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
Form,
|
Form,
|
||||||
OverlayTrigger,
|
OverlayTrigger,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
SafeAnchorProps,
|
|
||||||
InputGroup,
|
InputGroup,
|
||||||
FormControl,
|
FormControl,
|
||||||
ButtonToolbar,
|
ButtonToolbar,
|
||||||
@@ -160,11 +159,9 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
props.onFilterUpdate(newFilter);
|
props.onFilterUpdate(newFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChangeSortBy(event: React.MouseEvent<SafeAnchorProps>) {
|
function onChangeSortBy(event: React.MouseEvent<HTMLAnchorElement>) {
|
||||||
const target = event.currentTarget as HTMLAnchorElement;
|
|
||||||
|
|
||||||
const newFilter = _.cloneDeep(props.filter);
|
const newFilter = _.cloneDeep(props.filter);
|
||||||
newFilter.sortBy = target.text;
|
newFilter.sortBy = event.currentTarget.text;
|
||||||
newFilter.currentPage = 1;
|
newFilter.currentPage = 1;
|
||||||
props.onFilterUpdate(newFilter);
|
props.onFilterUpdate(newFilter);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,10 +30,14 @@ import { RatingStars } from "src/components/Scenes/SceneDetails/RatingStars";
|
|||||||
import { MovieScenesPanel } from "./MovieScenesPanel";
|
import { MovieScenesPanel } from "./MovieScenesPanel";
|
||||||
import { MovieScrapeDialog } from "./MovieScrapeDialog";
|
import { MovieScrapeDialog } from "./MovieScrapeDialog";
|
||||||
|
|
||||||
|
interface IMovieParams {
|
||||||
|
id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const Movie: React.FC = () => {
|
export const Movie: React.FC = () => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const Toast = useToast();
|
const Toast = useToast();
|
||||||
const { id = "new" } = useParams();
|
const { id = "new" } = useParams<IMovieParams>();
|
||||||
const isNew = id === "new";
|
const isNew = id === "new";
|
||||||
|
|
||||||
// Editing state
|
// Editing state
|
||||||
|
|||||||
@@ -17,10 +17,15 @@ import { PerformerDetailsPanel } from "./PerformerDetailsPanel";
|
|||||||
import { PerformerOperationsPanel } from "./PerformerOperationsPanel";
|
import { PerformerOperationsPanel } from "./PerformerOperationsPanel";
|
||||||
import { PerformerScenesPanel } from "./PerformerScenesPanel";
|
import { PerformerScenesPanel } from "./PerformerScenesPanel";
|
||||||
|
|
||||||
|
interface IPerformerParams {
|
||||||
|
id?: string;
|
||||||
|
tab?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const Performer: React.FC = () => {
|
export const Performer: React.FC = () => {
|
||||||
const Toast = useToast();
|
const Toast = useToast();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { tab = "details", id = "new" } = useParams();
|
const { tab = "details", id = "new" } = useParams<IPerformerParams>();
|
||||||
const isNew = id === "new";
|
const isNew = id === "new";
|
||||||
|
|
||||||
// Performer state
|
// Performer state
|
||||||
@@ -51,7 +56,7 @@ export const Performer: React.FC = () => {
|
|||||||
tab === "scenes" || tab === "edit" || tab === "operations"
|
tab === "scenes" || tab === "edit" || tab === "operations"
|
||||||
? tab
|
? tab
|
||||||
: "details";
|
: "details";
|
||||||
const setActiveTabKey = (newTab: string) => {
|
const setActiveTabKey = (newTab: string | null) => {
|
||||||
if (tab !== newTab) {
|
if (tab !== newTab) {
|
||||||
const tabParam = newTab === "details" ? "" : `/${newTab}`;
|
const tabParam = newTab === "details" ? "" : `/${newTab}`;
|
||||||
history.replace(`/performers/${id}${tabParam}`);
|
history.replace(`/performers/${id}${tabParam}`);
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ export const SceneFilenameParser: React.FC = () => {
|
|||||||
|
|
||||||
queryParseSceneFilenames(parserFilter, parserInputData)
|
queryParseSceneFilenames(parserFilter, parserInputData)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const result = response.data.parseSceneFilenames;
|
const result = response?.data?.parseSceneFilenames;
|
||||||
if (result) {
|
if (result) {
|
||||||
parseResults(result.results);
|
parseResults(result.results);
|
||||||
setTotalItems(result.count);
|
setTotalItems(result.count);
|
||||||
|
|||||||
@@ -101,7 +101,6 @@ export class SceneParserResult {
|
|||||||
interface ISceneParserFieldProps<T> {
|
interface ISceneParserFieldProps<T> {
|
||||||
parserResult: ParserResult<T>;
|
parserResult: ParserResult<T>;
|
||||||
className?: string;
|
className?: string;
|
||||||
fieldName: string;
|
|
||||||
onSetChanged: (isSet: boolean) => void;
|
onSetChanged: (isSet: boolean) => void;
|
||||||
onValueChanged: (value: T) => void;
|
onValueChanged: (value: T) => void;
|
||||||
originalParserResult?: ParserResult<T>;
|
originalParserResult?: ParserResult<T>;
|
||||||
@@ -372,7 +371,6 @@ export const SceneParserRow = (props: ISceneParserRowProps) => {
|
|||||||
{props.showFields.get("Title") && (
|
{props.showFields.get("Title") && (
|
||||||
<SceneParserStringField
|
<SceneParserStringField
|
||||||
key="title"
|
key="title"
|
||||||
fieldName="Title"
|
|
||||||
className="parser-field-title input-control text-input"
|
className="parser-field-title input-control text-input"
|
||||||
parserResult={props.scene.title}
|
parserResult={props.scene.title}
|
||||||
onSetChanged={(isSet) =>
|
onSetChanged={(isSet) =>
|
||||||
@@ -386,7 +384,6 @@ export const SceneParserRow = (props: ISceneParserRowProps) => {
|
|||||||
{props.showFields.get("Date") && (
|
{props.showFields.get("Date") && (
|
||||||
<SceneParserStringField
|
<SceneParserStringField
|
||||||
key="date"
|
key="date"
|
||||||
fieldName="Date"
|
|
||||||
className="parser-field-date input-control text-input"
|
className="parser-field-date input-control text-input"
|
||||||
parserResult={props.scene.date}
|
parserResult={props.scene.date}
|
||||||
onSetChanged={(isSet) =>
|
onSetChanged={(isSet) =>
|
||||||
@@ -400,7 +397,6 @@ export const SceneParserRow = (props: ISceneParserRowProps) => {
|
|||||||
{props.showFields.get("Rating") && (
|
{props.showFields.get("Rating") && (
|
||||||
<SceneParserRatingField
|
<SceneParserRatingField
|
||||||
key="rating"
|
key="rating"
|
||||||
fieldName="Rating"
|
|
||||||
className="parser-field-rating input-control text-input"
|
className="parser-field-rating input-control text-input"
|
||||||
parserResult={props.scene.rating}
|
parserResult={props.scene.rating}
|
||||||
onSetChanged={(isSet) =>
|
onSetChanged={(isSet) =>
|
||||||
@@ -414,7 +410,6 @@ export const SceneParserRow = (props: ISceneParserRowProps) => {
|
|||||||
{props.showFields.get("Performers") && (
|
{props.showFields.get("Performers") && (
|
||||||
<SceneParserPerformerField
|
<SceneParserPerformerField
|
||||||
key="performers"
|
key="performers"
|
||||||
fieldName="Performers"
|
|
||||||
className="parser-field-performers input-control text-input"
|
className="parser-field-performers input-control text-input"
|
||||||
parserResult={props.scene.performers}
|
parserResult={props.scene.performers}
|
||||||
originalParserResult={props.scene.performers}
|
originalParserResult={props.scene.performers}
|
||||||
@@ -429,7 +424,6 @@ export const SceneParserRow = (props: ISceneParserRowProps) => {
|
|||||||
{props.showFields.get("Tags") && (
|
{props.showFields.get("Tags") && (
|
||||||
<SceneParserTagField
|
<SceneParserTagField
|
||||||
key="tags"
|
key="tags"
|
||||||
fieldName="Tags"
|
|
||||||
className="parser-field-tags input-control text-input"
|
className="parser-field-tags input-control text-input"
|
||||||
parserResult={props.scene.tags}
|
parserResult={props.scene.tags}
|
||||||
originalParserResult={props.scene.tags}
|
originalParserResult={props.scene.tags}
|
||||||
@@ -444,7 +438,6 @@ export const SceneParserRow = (props: ISceneParserRowProps) => {
|
|||||||
{props.showFields.get("Studio") && (
|
{props.showFields.get("Studio") && (
|
||||||
<SceneParserStudioField
|
<SceneParserStudioField
|
||||||
key="studio"
|
key="studio"
|
||||||
fieldName="Studio"
|
|
||||||
className="parser-field-studio input-control text-input"
|
className="parser-field-studio input-control text-input"
|
||||||
parserResult={props.scene.studio}
|
parserResult={props.scene.studio}
|
||||||
originalParserResult={props.scene.studio}
|
originalParserResult={props.scene.studio}
|
||||||
|
|||||||
@@ -26,8 +26,12 @@ import { SceneMoviePanel } from "./SceneMoviePanel";
|
|||||||
import { DeleteScenesDialog } from "../DeleteScenesDialog";
|
import { DeleteScenesDialog } from "../DeleteScenesDialog";
|
||||||
import { SceneGenerateDialog } from "../SceneGenerateDialog";
|
import { SceneGenerateDialog } from "../SceneGenerateDialog";
|
||||||
|
|
||||||
|
interface ISceneParams {
|
||||||
|
id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const Scene: React.FC = () => {
|
export const Scene: React.FC = () => {
|
||||||
const { id = "new" } = useParams();
|
const { id = "new" } = useParams<ISceneParams>();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const Toast = useToast();
|
const Toast = useToast();
|
||||||
@@ -210,7 +214,7 @@ export const Scene: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<Tab.Container
|
<Tab.Container
|
||||||
activeKey={activeTabKey}
|
activeKey={activeTabKey}
|
||||||
onSelect={(k) => setActiveTabKey(k)}
|
onSelect={(k) => k && setActiveTabKey(k)}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<Nav variant="tabs" className="mr-auto">
|
<Nav variant="tabs" className="mr-auto">
|
||||||
|
|||||||
@@ -26,15 +26,14 @@ export const SceneListTable: React.FC<ISceneListTableProps> = (
|
|||||||
</Link>
|
</Link>
|
||||||
));
|
));
|
||||||
|
|
||||||
const renderMovies = (movies: Partial<GQL.SceneMovie>[]) => {
|
const renderMovies = (scene: GQL.SlimSceneDataFragment) =>
|
||||||
return movies.map((sceneMovie) =>
|
scene.movies.map((sceneMovie) =>
|
||||||
!sceneMovie.movie ? undefined : (
|
!sceneMovie.movie ? undefined : (
|
||||||
<Link to={NavUtils.makeMovieScenesUrl(sceneMovie.movie)}>
|
<Link to={NavUtils.makeMovieScenesUrl(sceneMovie.movie)}>
|
||||||
<h6>{sceneMovie.movie.name}</h6>
|
<h6>{sceneMovie.movie.name}</h6>
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
const renderSceneRow = (scene: GQL.SlimSceneDataFragment) => (
|
const renderSceneRow = (scene: GQL.SlimSceneDataFragment) => (
|
||||||
<tr key={scene.id}>
|
<tr key={scene.id}>
|
||||||
@@ -68,7 +67,7 @@ export const SceneListTable: React.FC<ISceneListTableProps> = (
|
|||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td>{renderMovies(scene.movies)}</td>
|
<td>{renderMovies(scene)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export const Settings: React.FC = () => {
|
|||||||
<Tab.Container
|
<Tab.Container
|
||||||
defaultActiveKey={defaultTab}
|
defaultActiveKey={defaultTab}
|
||||||
id="configuration-tabs"
|
id="configuration-tabs"
|
||||||
onSelect={onSelect}
|
onSelect={(tab) => tab && onSelect(tab)}
|
||||||
>
|
>
|
||||||
<Row>
|
<Row>
|
||||||
<Col sm={3} md={2}>
|
<Col sm={3} md={2}>
|
||||||
|
|||||||
@@ -1,45 +1,24 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React from "react";
|
||||||
import { Button } from "react-bootstrap";
|
import { Button } from "react-bootstrap";
|
||||||
import { mutateReloadPlugins, usePlugins } from "src/core/StashService";
|
import { mutateReloadPlugins, usePlugins } from "src/core/StashService";
|
||||||
import { useToast } from "src/hooks";
|
import { useToast } from "src/hooks";
|
||||||
import * as GQL from "src/core/generated-graphql";
|
|
||||||
import { TextUtils } from "src/utils";
|
import { TextUtils } from "src/utils";
|
||||||
import { Icon, LoadingIndicator } from "../Shared";
|
import { Icon, LoadingIndicator } from "src/components/Shared";
|
||||||
|
|
||||||
export const SettingsPluginsPanel: React.FC = () => {
|
export const SettingsPluginsPanel: React.FC = () => {
|
||||||
const Toast = useToast();
|
const Toast = useToast();
|
||||||
|
const { data, loading } = usePlugins();
|
||||||
const plugins = usePlugins();
|
|
||||||
|
|
||||||
// Network state
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (plugins) {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
}, [plugins]);
|
|
||||||
|
|
||||||
async function onReloadPlugins() {
|
async function onReloadPlugins() {
|
||||||
setIsLoading(true);
|
await mutateReloadPlugins().catch((e) => Toast.error(e));
|
||||||
try {
|
|
||||||
await mutateReloadPlugins();
|
|
||||||
|
|
||||||
// reload the performer scrapers
|
|
||||||
await plugins.refetch();
|
|
||||||
} catch (e) {
|
|
||||||
Toast.error(e);
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderLink(plugin: GQL.Plugin) {
|
function renderLink(url?: string) {
|
||||||
if (plugin.url) {
|
if (url) {
|
||||||
return (
|
return (
|
||||||
<Button className="minimal">
|
<Button className="minimal">
|
||||||
<a
|
<a
|
||||||
href={TextUtils.sanitiseURL(plugin.url)}
|
href={TextUtils.sanitiseURL(url)}
|
||||||
className="link"
|
className="link"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
@@ -51,30 +30,24 @@ export const SettingsPluginsPanel: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderPlugin(plugin: GQL.Plugin) {
|
function renderPlugins() {
|
||||||
return (
|
const elements = (data?.plugins ?? []).map((plugin) => (
|
||||||
<div key={plugin.id}>
|
<div key={plugin.id}>
|
||||||
<h5>
|
<h5>
|
||||||
{plugin.name} {plugin.version ? `(${plugin.version})` : undefined}{" "}
|
{plugin.name} {plugin.version ? `(${plugin.version})` : undefined}{" "}
|
||||||
{renderLink(plugin)}
|
{renderLink(plugin.url ?? undefined)}
|
||||||
</h5>
|
</h5>
|
||||||
{plugin.description ? (
|
{plugin.description ? (
|
||||||
<small className="text-muted">{plugin.description}</small>
|
<small className="text-muted">{plugin.description}</small>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
<hr />
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
);
|
));
|
||||||
|
|
||||||
|
return <div>{elements}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderPlugins() {
|
if (loading) return <LoadingIndicator />;
|
||||||
if (!plugins.data || !plugins.data.plugins) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div>{plugins.data?.plugins.map(renderPlugin)}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isLoading) return <LoadingIndicator />;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -15,10 +15,13 @@ import {
|
|||||||
mutateRunPluginTask,
|
mutateRunPluginTask,
|
||||||
} from "src/core/StashService";
|
} from "src/core/StashService";
|
||||||
import { useToast } from "src/hooks";
|
import { useToast } from "src/hooks";
|
||||||
|
import * as GQL from "src/core/generated-graphql";
|
||||||
import { Modal } from "src/components/Shared";
|
import { Modal } from "src/components/Shared";
|
||||||
import { Plugin, PluginTask } from "src/core/generated-graphql";
|
|
||||||
import { GenerateButton } from "./GenerateButton";
|
import { GenerateButton } from "./GenerateButton";
|
||||||
|
|
||||||
|
type Plugin = Pick<GQL.Plugin, "id">;
|
||||||
|
type PluginTask = Pick<GQL.PluginTask, "name" | "description">;
|
||||||
|
|
||||||
export const SettingsTasksPanel: React.FC = () => {
|
export const SettingsTasksPanel: React.FC = () => {
|
||||||
const Toast = useToast();
|
const Toast = useToast();
|
||||||
const [isImportAlertOpen, setIsImportAlertOpen] = useState<boolean>(false);
|
const [isImportAlertOpen, setIsImportAlertOpen] = useState<boolean>(false);
|
||||||
@@ -199,17 +202,11 @@ export const SettingsTasksPanel: React.FC = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onPluginTaskClicked(
|
async function onPluginTaskClicked(plugin: Plugin, operation: PluginTask) {
|
||||||
plugin: Partial<Plugin>,
|
await mutateRunPluginTask(plugin.id, operation.name);
|
||||||
operation: Partial<PluginTask>
|
|
||||||
) {
|
|
||||||
await mutateRunPluginTask(plugin.id!, operation.name!);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderPluginTasks(
|
function renderPluginTasks(plugin: Plugin, pluginTasks: PluginTask[]) {
|
||||||
plugin: Partial<Plugin>,
|
|
||||||
pluginTasks: Partial<PluginTask>[] | undefined
|
|
||||||
) {
|
|
||||||
if (!pluginTasks) {
|
if (!pluginTasks) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState, CSSProperties } from "react";
|
import React, { useState, CSSProperties } from "react";
|
||||||
import Select, { ValueType } from "react-select";
|
import Select, { ValueType, Props } from "react-select";
|
||||||
import CreatableSelect from "react-select/creatable";
|
import CreatableSelect from "react-select/creatable";
|
||||||
import { debounce } from "lodash";
|
import { debounce } from "lodash";
|
||||||
|
|
||||||
@@ -388,15 +388,13 @@ const SelectComponent: React.FC<ISelectProps & ITypeProps> = ({
|
|||||||
...base,
|
...base,
|
||||||
color: "#000",
|
color: "#000",
|
||||||
}),
|
}),
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
container: (base: CSSProperties, props: Props) => ({
|
||||||
container: (base: CSSProperties, state: any) => ({
|
|
||||||
...base,
|
...base,
|
||||||
zIndex: state.isFocused ? 10 : base.zIndex,
|
zIndex: props.isFocused ? 10 : base.zIndex,
|
||||||
}),
|
}),
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
multiValueRemove: (base: CSSProperties, props: Props) => ({
|
||||||
multiValueRemove: (base: CSSProperties, state: any) => ({
|
|
||||||
...base,
|
...base,
|
||||||
color: state.isFocused ? base.color : "#333333",
|
color: props.isFocused ? base.color : "#333333",
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,15 @@ import { useToast } from "src/hooks";
|
|||||||
import { StudioScenesPanel } from "./StudioScenesPanel";
|
import { StudioScenesPanel } from "./StudioScenesPanel";
|
||||||
import { StudioChildrenPanel } from "./StudioChildrenPanel";
|
import { StudioChildrenPanel } from "./StudioChildrenPanel";
|
||||||
|
|
||||||
|
interface IStudioParams {
|
||||||
|
id?: string;
|
||||||
|
tab?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const Studio: React.FC = () => {
|
export const Studio: React.FC = () => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const Toast = useToast();
|
const Toast = useToast();
|
||||||
const { tab = "details", id = "new" } = useParams();
|
const { tab = "details", id = "new" } = useParams<IStudioParams>();
|
||||||
const isNew = id === "new";
|
const isNew = id === "new";
|
||||||
|
|
||||||
// Editing state
|
// Editing state
|
||||||
@@ -191,7 +196,7 @@ export const Studio: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const activeTabKey = tab === "childstudios" ? tab : "scenes";
|
const activeTabKey = tab === "childstudios" ? tab : "scenes";
|
||||||
const setActiveTabKey = (newTab: string) => {
|
const setActiveTabKey = (newTab: string | null) => {
|
||||||
if (tab !== newTab) {
|
if (tab !== newTab) {
|
||||||
const tabParam = newTab === "scenes" ? "" : `/${newTab}`;
|
const tabParam = newTab === "scenes" ? "" : `/${newTab}`;
|
||||||
history.replace(`/studios/${id}${tabParam}`);
|
history.replace(`/studios/${id}${tabParam}`);
|
||||||
|
|||||||
@@ -21,10 +21,15 @@ import { useToast } from "src/hooks";
|
|||||||
import { TagScenesPanel } from "./TagScenesPanel";
|
import { TagScenesPanel } from "./TagScenesPanel";
|
||||||
import { TagMarkersPanel } from "./TagMarkersPanel";
|
import { TagMarkersPanel } from "./TagMarkersPanel";
|
||||||
|
|
||||||
|
interface ITabParams {
|
||||||
|
id?: string;
|
||||||
|
tab?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const Tag: React.FC = () => {
|
export const Tag: React.FC = () => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const Toast = useToast();
|
const Toast = useToast();
|
||||||
const { tab = "scenes", id = "new" } = useParams();
|
const { tab = "scenes", id = "new" } = useParams<ITabParams>();
|
||||||
const isNew = id === "new";
|
const isNew = id === "new";
|
||||||
|
|
||||||
// Editing state
|
// Editing state
|
||||||
@@ -45,7 +50,7 @@ export const Tag: React.FC = () => {
|
|||||||
const [deleteTag] = useTagDestroy(getTagInput() as GQL.TagUpdateInput);
|
const [deleteTag] = useTagDestroy(getTagInput() as GQL.TagUpdateInput);
|
||||||
|
|
||||||
const activeTabKey = tab === "markers" ? tab : "scenes";
|
const activeTabKey = tab === "markers" ? tab : "scenes";
|
||||||
const setActiveTabKey = (newTab: string) => {
|
const setActiveTabKey = (newTab: string | null) => {
|
||||||
if (tab !== newTab) {
|
if (tab !== newTab) {
|
||||||
const tabParam = newTab === "scenes" ? "" : `/${newTab}`;
|
const tabParam = newTab === "scenes" ? "" : `/${newTab}`;
|
||||||
history.replace(`/tags/${id}${tabParam}`);
|
history.replace(`/tags/${id}${tabParam}`);
|
||||||
|
|||||||
@@ -1,32 +1,45 @@
|
|||||||
|
import { ApolloCache, DocumentNode } from "@apollo/client";
|
||||||
|
import {
|
||||||
|
isField,
|
||||||
|
resultKeyNameFromField,
|
||||||
|
getQueryDefinition,
|
||||||
|
getOperationName,
|
||||||
|
} from "@apollo/client/utilities";
|
||||||
import { ListFilterModel } from "../models/list-filter/filter";
|
import { ListFilterModel } from "../models/list-filter/filter";
|
||||||
import * as GQL from "./generated-graphql";
|
import * as GQL from "./generated-graphql";
|
||||||
|
|
||||||
import { createClient } from "./createClient";
|
import { createClient } from "./createClient";
|
||||||
|
|
||||||
const { client, cache } = createClient();
|
const { client } = createClient();
|
||||||
|
|
||||||
export const getClient = () => client;
|
export const getClient = () => client;
|
||||||
|
|
||||||
// TODO: Invalidation should happen through apollo client, rather than rewriting cache directly
|
const getQueryNames = (queries: DocumentNode[]): string[] =>
|
||||||
const invalidateQueries = (queries: string[]) => {
|
queries.map((q) => getOperationName(q)).filter((n) => n !== null) as string[];
|
||||||
if (cache) {
|
|
||||||
const keyMatchers = queries.map((query) => {
|
|
||||||
return new RegExp(`^${query}`, "i");
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: Hack to invalidate, manipulating private data
|
// Will delete the entire cache for any queries passed in
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
const deleteCache = (queries: DocumentNode[]) => {
|
||||||
const rootQuery = (cache as any).data.data.ROOT_QUERY;
|
const fields = queries
|
||||||
Object.keys(rootQuery).forEach((key) => {
|
.map((q) => {
|
||||||
if (
|
const field = getQueryDefinition(q).selectionSet.selections[0];
|
||||||
keyMatchers.some((matcher) => {
|
return isField(field) ? resultKeyNameFromField(field) : "";
|
||||||
return !!key.match(matcher);
|
})
|
||||||
})
|
.filter((name) => name !== "")
|
||||||
) {
|
.reduce(
|
||||||
delete rootQuery[key];
|
(prevFields, name) => ({
|
||||||
}
|
...prevFields,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
[name]: (_items: any, { DELETE }: any) => DELETE,
|
||||||
|
}),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
return (cache: ApolloCache<any>) =>
|
||||||
|
cache.modify({
|
||||||
|
id: "ROOT_QUERY",
|
||||||
|
fields,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useFindGalleries = (filter: ListFilterModel) =>
|
export const useFindGalleries = (filter: ListFilterModel) =>
|
||||||
@@ -136,20 +149,28 @@ export const useFindTag = (id: string) => {
|
|||||||
return GQL.useFindTagQuery({ variables: { id }, skip });
|
return GQL.useFindTagQuery({ variables: { id }, skip });
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO - scene marker manipulation functions are handled differently
|
const sceneMarkerMutationImpactedQueries = [
|
||||||
export const sceneMarkerMutationImpactedQueries = [
|
GQL.FindSceneDocument,
|
||||||
"findSceneMarkers",
|
GQL.FindScenesDocument,
|
||||||
"findScenes",
|
GQL.FindSceneMarkersDocument,
|
||||||
"markerStrings",
|
GQL.MarkerStringsDocument,
|
||||||
"sceneMarkerTags",
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export const useSceneMarkerCreate = () =>
|
export const useSceneMarkerCreate = () =>
|
||||||
GQL.useSceneMarkerCreateMutation({ refetchQueries: ["FindScene"] });
|
GQL.useSceneMarkerCreateMutation({
|
||||||
|
refetchQueries: getQueryNames([GQL.FindSceneDocument]),
|
||||||
|
update: deleteCache(sceneMarkerMutationImpactedQueries),
|
||||||
|
});
|
||||||
export const useSceneMarkerUpdate = () =>
|
export const useSceneMarkerUpdate = () =>
|
||||||
GQL.useSceneMarkerUpdateMutation({ refetchQueries: ["FindScene"] });
|
GQL.useSceneMarkerUpdateMutation({
|
||||||
|
refetchQueries: getQueryNames([GQL.FindSceneDocument]),
|
||||||
|
update: deleteCache(sceneMarkerMutationImpactedQueries),
|
||||||
|
});
|
||||||
export const useSceneMarkerDestroy = () =>
|
export const useSceneMarkerDestroy = () =>
|
||||||
GQL.useSceneMarkerDestroyMutation({ refetchQueries: ["FindScene"] });
|
GQL.useSceneMarkerDestroyMutation({
|
||||||
|
refetchQueries: getQueryNames([GQL.FindSceneDocument]),
|
||||||
|
update: deleteCache(sceneMarkerMutationImpactedQueries),
|
||||||
|
});
|
||||||
|
|
||||||
export const useListPerformerScrapers = () =>
|
export const useListPerformerScrapers = () =>
|
||||||
GQL.useListPerformerScrapersQuery();
|
GQL.useListPerformerScrapersQuery();
|
||||||
@@ -199,61 +220,61 @@ export const useConfiguration = () => GQL.useConfigurationQuery();
|
|||||||
export const useDirectory = (path?: string) =>
|
export const useDirectory = (path?: string) =>
|
||||||
GQL.useDirectoryQuery({ variables: { path } });
|
GQL.useDirectoryQuery({ variables: { path } });
|
||||||
|
|
||||||
export const performerMutationImpactedQueries = [
|
const performerMutationImpactedQueries = [
|
||||||
"FindPerformers",
|
GQL.FindPerformersDocument,
|
||||||
"FindScenes",
|
GQL.FindSceneDocument,
|
||||||
"FindSceneMarkers",
|
GQL.FindScenesDocument,
|
||||||
"AllPerformers",
|
GQL.AllPerformersForFilterDocument,
|
||||||
"AllPerformersForFilter",
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export const usePerformerCreate = () =>
|
export const usePerformerCreate = () =>
|
||||||
GQL.usePerformerCreateMutation({
|
GQL.usePerformerCreateMutation({
|
||||||
refetchQueries: performerMutationImpactedQueries,
|
refetchQueries: getQueryNames([
|
||||||
update: () => invalidateQueries(performerMutationImpactedQueries),
|
GQL.FindPerformersDocument,
|
||||||
|
GQL.AllPerformersForFilterDocument,
|
||||||
|
]),
|
||||||
|
update: deleteCache([
|
||||||
|
GQL.FindPerformersDocument,
|
||||||
|
GQL.AllPerformersForFilterDocument,
|
||||||
|
]),
|
||||||
});
|
});
|
||||||
export const usePerformerUpdate = () =>
|
export const usePerformerUpdate = () =>
|
||||||
GQL.usePerformerUpdateMutation({
|
GQL.usePerformerUpdateMutation({
|
||||||
refetchQueries: performerMutationImpactedQueries,
|
update: deleteCache(performerMutationImpactedQueries),
|
||||||
update: () => invalidateQueries(performerMutationImpactedQueries),
|
|
||||||
});
|
});
|
||||||
export const usePerformerDestroy = () =>
|
export const usePerformerDestroy = () =>
|
||||||
GQL.usePerformerDestroyMutation({
|
GQL.usePerformerDestroyMutation({
|
||||||
refetchQueries: performerMutationImpactedQueries,
|
refetchQueries: getQueryNames([
|
||||||
update: () => invalidateQueries(performerMutationImpactedQueries),
|
GQL.FindPerformersDocument,
|
||||||
|
GQL.AllPerformersForFilterDocument,
|
||||||
|
]),
|
||||||
|
update: deleteCache(performerMutationImpactedQueries),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const sceneMutationImpactedQueries = [
|
const sceneMutationImpactedQueries = [
|
||||||
"findPerformers",
|
GQL.FindPerformerDocument,
|
||||||
"findScenes",
|
GQL.FindPerformersDocument,
|
||||||
"findSceneMarkers",
|
GQL.FindScenesDocument,
|
||||||
"findStudios",
|
GQL.FindSceneMarkersDocument,
|
||||||
"findMovies",
|
GQL.FindStudioDocument,
|
||||||
"allTags",
|
GQL.FindStudiosDocument,
|
||||||
// TODO - add "findTags" when it is implemented
|
GQL.FindMovieDocument,
|
||||||
|
GQL.FindMoviesDocument,
|
||||||
|
GQL.FindTagDocument,
|
||||||
|
GQL.FindTagsDocument,
|
||||||
|
GQL.AllTagsDocument,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const useSceneUpdate = (input: GQL.SceneUpdateInput) =>
|
export const useSceneUpdate = (input: GQL.SceneUpdateInput) =>
|
||||||
GQL.useSceneUpdateMutation({
|
GQL.useSceneUpdateMutation({
|
||||||
variables: input,
|
variables: input,
|
||||||
update: () => invalidateQueries(sceneMutationImpactedQueries),
|
update: deleteCache(sceneMutationImpactedQueries),
|
||||||
refetchQueries: ["AllTagsForFilter"],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// remove findScenes for bulk scene update so that we don't lose
|
|
||||||
// existing results
|
|
||||||
export const sceneBulkMutationImpactedQueries = [
|
|
||||||
"findPerformers",
|
|
||||||
"findSceneMarkers",
|
|
||||||
"findStudios",
|
|
||||||
"findMovies",
|
|
||||||
"allTags",
|
|
||||||
];
|
|
||||||
|
|
||||||
export const useBulkSceneUpdate = (input: GQL.BulkSceneUpdateInput) =>
|
export const useBulkSceneUpdate = (input: GQL.BulkSceneUpdateInput) =>
|
||||||
GQL.useBulkSceneUpdateMutation({
|
GQL.useBulkSceneUpdateMutation({
|
||||||
variables: input,
|
variables: input,
|
||||||
update: () => invalidateQueries(sceneBulkMutationImpactedQueries),
|
update: deleteCache(sceneMutationImpactedQueries),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useScenesUpdate = (input: GQL.SceneUpdateInput[]) =>
|
export const useScenesUpdate = (input: GQL.SceneUpdateInput[]) =>
|
||||||
@@ -277,107 +298,123 @@ export const useSceneResetO = (id: string) =>
|
|||||||
export const useSceneDestroy = (input: GQL.SceneDestroyInput) =>
|
export const useSceneDestroy = (input: GQL.SceneDestroyInput) =>
|
||||||
GQL.useSceneDestroyMutation({
|
GQL.useSceneDestroyMutation({
|
||||||
variables: input,
|
variables: input,
|
||||||
update: () => invalidateQueries(sceneMutationImpactedQueries),
|
update: deleteCache(sceneMutationImpactedQueries),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useScenesDestroy = (input: GQL.ScenesDestroyInput) =>
|
export const useScenesDestroy = (input: GQL.ScenesDestroyInput) =>
|
||||||
GQL.useScenesDestroyMutation({
|
GQL.useScenesDestroyMutation({
|
||||||
variables: input,
|
variables: input,
|
||||||
update: () => invalidateQueries(sceneMutationImpactedQueries),
|
update: deleteCache(sceneMutationImpactedQueries),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useSceneGenerateScreenshot = () =>
|
export const useSceneGenerateScreenshot = () =>
|
||||||
GQL.useSceneGenerateScreenshotMutation({
|
GQL.useSceneGenerateScreenshotMutation({
|
||||||
update: () => invalidateQueries(["findScenes"]),
|
update: deleteCache([GQL.FindScenesDocument]),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const studioMutationImpactedQueries = [
|
export const studioMutationImpactedQueries = [
|
||||||
"FindStudios",
|
GQL.FindStudiosDocument,
|
||||||
"FindScenes",
|
GQL.FindSceneDocument,
|
||||||
"AllStudios",
|
GQL.FindScenesDocument,
|
||||||
"AllStudiosForFilter",
|
GQL.AllStudiosForFilterDocument,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const useStudioCreate = (input: GQL.StudioCreateInput) =>
|
export const useStudioCreate = (input: GQL.StudioCreateInput) =>
|
||||||
GQL.useStudioCreateMutation({
|
GQL.useStudioCreateMutation({
|
||||||
variables: input,
|
variables: input,
|
||||||
refetchQueries: studioMutationImpactedQueries,
|
refetchQueries: getQueryNames([GQL.AllStudiosForFilterDocument]),
|
||||||
update: () => invalidateQueries(studioMutationImpactedQueries),
|
update: deleteCache([
|
||||||
|
GQL.FindStudiosDocument,
|
||||||
|
GQL.AllStudiosForFilterDocument,
|
||||||
|
]),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useStudioUpdate = (input: GQL.StudioUpdateInput) =>
|
export const useStudioUpdate = (input: GQL.StudioUpdateInput) =>
|
||||||
GQL.useStudioUpdateMutation({
|
GQL.useStudioUpdateMutation({
|
||||||
variables: input,
|
variables: input,
|
||||||
update: () => invalidateQueries(studioMutationImpactedQueries),
|
update: deleteCache(studioMutationImpactedQueries),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useStudioDestroy = (input: GQL.StudioDestroyInput) =>
|
export const useStudioDestroy = (input: GQL.StudioDestroyInput) =>
|
||||||
GQL.useStudioDestroyMutation({
|
GQL.useStudioDestroyMutation({
|
||||||
variables: input,
|
variables: input,
|
||||||
update: () => invalidateQueries(studioMutationImpactedQueries),
|
update: deleteCache(studioMutationImpactedQueries),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const movieMutationImpactedQueries = [
|
export const movieMutationImpactedQueries = [
|
||||||
"findMovies",
|
GQL.FindSceneDocument,
|
||||||
"findScenes",
|
GQL.FindScenesDocument,
|
||||||
"allMovies",
|
GQL.FindMoviesDocument,
|
||||||
|
GQL.AllMoviesForFilterDocument,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const useMovieCreate = (input: GQL.MovieCreateInput) =>
|
export const useMovieCreate = (input: GQL.MovieCreateInput) =>
|
||||||
GQL.useMovieCreateMutation({
|
GQL.useMovieCreateMutation({
|
||||||
variables: input,
|
variables: input,
|
||||||
update: () => invalidateQueries(movieMutationImpactedQueries),
|
update: deleteCache([
|
||||||
|
GQL.FindMoviesDocument,
|
||||||
|
GQL.AllMoviesForFilterDocument,
|
||||||
|
]),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useMovieUpdate = (input: GQL.MovieUpdateInput) =>
|
export const useMovieUpdate = (input: GQL.MovieUpdateInput) =>
|
||||||
GQL.useMovieUpdateMutation({
|
GQL.useMovieUpdateMutation({
|
||||||
variables: input,
|
variables: input,
|
||||||
update: () => invalidateQueries(movieMutationImpactedQueries),
|
update: deleteCache(movieMutationImpactedQueries),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useMovieDestroy = (input: GQL.MovieDestroyInput) =>
|
export const useMovieDestroy = (input: GQL.MovieDestroyInput) =>
|
||||||
GQL.useMovieDestroyMutation({
|
GQL.useMovieDestroyMutation({
|
||||||
variables: input,
|
variables: input,
|
||||||
update: () => invalidateQueries(movieMutationImpactedQueries),
|
update: deleteCache(movieMutationImpactedQueries),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const tagMutationImpactedQueries = [
|
export const tagMutationImpactedQueries = [
|
||||||
"findScenes",
|
GQL.FindSceneDocument,
|
||||||
"findSceneMarkers",
|
GQL.FindScenesDocument,
|
||||||
"sceneMarkerTags",
|
GQL.FindSceneMarkersDocument,
|
||||||
"allTags",
|
GQL.AllTagsDocument,
|
||||||
"findTags",
|
GQL.AllTagsForFilterDocument,
|
||||||
|
GQL.FindTagsDocument,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const useTagCreate = (input: GQL.TagCreateInput) =>
|
export const useTagCreate = (input: GQL.TagCreateInput) =>
|
||||||
GQL.useTagCreateMutation({
|
GQL.useTagCreateMutation({
|
||||||
variables: input,
|
variables: input,
|
||||||
refetchQueries: ["AllTags", "AllTagsForFilter", "FindTags"],
|
refetchQueries: getQueryNames([
|
||||||
update: () => invalidateQueries(tagMutationImpactedQueries),
|
GQL.AllTagsDocument,
|
||||||
|
GQL.AllTagsForFilterDocument,
|
||||||
|
GQL.FindTagsDocument,
|
||||||
|
]),
|
||||||
|
update: deleteCache([
|
||||||
|
GQL.FindTagsDocument,
|
||||||
|
GQL.AllTagsDocument,
|
||||||
|
GQL.AllTagsForFilterDocument,
|
||||||
|
]),
|
||||||
});
|
});
|
||||||
export const useTagUpdate = (input: GQL.TagUpdateInput) =>
|
export const useTagUpdate = (input: GQL.TagUpdateInput) =>
|
||||||
GQL.useTagUpdateMutation({
|
GQL.useTagUpdateMutation({
|
||||||
variables: input,
|
variables: input,
|
||||||
refetchQueries: ["AllTags", "AllTagsForFilter", "FindTags"],
|
update: deleteCache(tagMutationImpactedQueries),
|
||||||
update: () => invalidateQueries(tagMutationImpactedQueries),
|
|
||||||
});
|
});
|
||||||
export const useTagDestroy = (input: GQL.TagDestroyInput) =>
|
export const useTagDestroy = (input: GQL.TagDestroyInput) =>
|
||||||
GQL.useTagDestroyMutation({
|
GQL.useTagDestroyMutation({
|
||||||
variables: input,
|
variables: input,
|
||||||
refetchQueries: ["AllTags", "AllTagsForFilter", "FindTags"],
|
update: deleteCache(tagMutationImpactedQueries),
|
||||||
update: () => invalidateQueries(tagMutationImpactedQueries),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useConfigureGeneral = (input: GQL.ConfigGeneralInput) =>
|
export const useConfigureGeneral = (input: GQL.ConfigGeneralInput) =>
|
||||||
GQL.useConfigureGeneralMutation({
|
GQL.useConfigureGeneralMutation({
|
||||||
variables: { input },
|
variables: { input },
|
||||||
refetchQueries: ["Configuration"],
|
refetchQueries: getQueryNames([GQL.ConfigurationDocument]),
|
||||||
|
update: deleteCache([GQL.ConfigurationDocument]),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useConfigureInterface = (input: GQL.ConfigInterfaceInput) =>
|
export const useConfigureInterface = (input: GQL.ConfigInterfaceInput) =>
|
||||||
GQL.useConfigureInterfaceMutation({
|
GQL.useConfigureInterfaceMutation({
|
||||||
variables: { input },
|
variables: { input },
|
||||||
refetchQueries: ["Configuration"],
|
refetchQueries: getQueryNames([GQL.ConfigurationDocument]),
|
||||||
|
update: deleteCache([GQL.ConfigurationDocument]),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useMetadataUpdate = () => GQL.useMetadataUpdateSubscription();
|
export const useMetadataUpdate = () => GQL.useMetadataUpdateSubscription();
|
||||||
@@ -466,12 +503,10 @@ export const mutateReloadScrapers = () =>
|
|||||||
mutation: GQL.ReloadScrapersDocument,
|
mutation: GQL.ReloadScrapersDocument,
|
||||||
});
|
});
|
||||||
|
|
||||||
const reloadPluginsMutationImpactedQueries = ["plugins", "pluginTasks"];
|
|
||||||
|
|
||||||
export const mutateReloadPlugins = () =>
|
export const mutateReloadPlugins = () =>
|
||||||
client.mutate<GQL.ReloadPluginsMutation>({
|
client.mutate<GQL.ReloadPluginsMutation>({
|
||||||
mutation: GQL.ReloadPluginsDocument,
|
mutation: GQL.ReloadPluginsDocument,
|
||||||
update: () => invalidateQueries(reloadPluginsMutationImpactedQueries),
|
refetchQueries: [GQL.refetchPluginsQuery(), GQL.refetchPluginTasksQuery()],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const mutateRunPluginTask = (
|
export const mutateRunPluginTask = (
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import ApolloClient from "apollo-client";
|
import {
|
||||||
import { InMemoryCache } from "apollo-cache-inmemory";
|
ApolloClient,
|
||||||
import { WebSocketLink } from "apollo-link-ws";
|
InMemoryCache,
|
||||||
import { HttpLink } from "apollo-link-http";
|
split,
|
||||||
import { onError } from "apollo-link-error";
|
from,
|
||||||
import { ServerError } from "apollo-link-http-common";
|
ServerError,
|
||||||
import { split, from } from "apollo-link";
|
HttpLink,
|
||||||
import { getMainDefinition } from "apollo-utilities";
|
} from "@apollo/client";
|
||||||
|
import { WebSocketLink } from "@apollo/client/link/ws";
|
||||||
|
import { onError } from "@apollo/client/link/error";
|
||||||
|
import { getMainDefinition } from "@apollo/client/utilities";
|
||||||
|
|
||||||
export const getPlatformURL = (ws?: boolean) => {
|
export const getPlatformURL = (ws?: boolean) => {
|
||||||
const platformUrl = new URL(window.location.origin);
|
const platformUrl = new URL(window.location.origin);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import React, { useCallback, useRef, useState, useEffect } from "react";
|
import React, { useCallback, useRef, useState, useEffect } from "react";
|
||||||
import { ApolloError } from "apollo-client";
|
import { ApolloError } from "@apollo/client";
|
||||||
import { useHistory, useLocation } from "react-router-dom";
|
import { useHistory, useLocation } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
SlimSceneDataFragment,
|
SlimSceneDataFragment,
|
||||||
@@ -58,7 +58,7 @@ const getSelectedData = <I extends IDataItem>(
|
|||||||
|
|
||||||
interface IListHookData {
|
interface IListHookData {
|
||||||
filter: ListFilterModel;
|
filter: ListFilterModel;
|
||||||
template: JSX.Element;
|
template: React.ReactElement;
|
||||||
onSelectChange: (id: string, selected: boolean, shiftKey: boolean) => void;
|
onSelectChange: (id: string, selected: boolean, shiftKey: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,15 +88,15 @@ interface IListHookOptions<T, E> {
|
|||||||
filter: ListFilterModel,
|
filter: ListFilterModel,
|
||||||
selectedIds: Set<string>,
|
selectedIds: Set<string>,
|
||||||
zoomIndex: number
|
zoomIndex: number
|
||||||
) => JSX.Element | undefined;
|
) => React.ReactNode;
|
||||||
renderEditDialog?: (
|
renderEditDialog?: (
|
||||||
selected: E[],
|
selected: E[],
|
||||||
onClose: (applied: boolean) => void
|
onClose: (applied: boolean) => void
|
||||||
) => JSX.Element | undefined;
|
) => React.ReactNode;
|
||||||
renderDeleteDialog?: (
|
renderDeleteDialog?: (
|
||||||
selected: E[],
|
selected: E[],
|
||||||
onClose: (confirmed: boolean) => void
|
onClose: (confirmed: boolean) => void
|
||||||
) => JSX.Element | undefined;
|
) => React.ReactNode;
|
||||||
addKeybinds?: (
|
addKeybinds?: (
|
||||||
result: T,
|
result: T,
|
||||||
filter: ListFilterModel,
|
filter: ListFilterModel,
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ function useLocalForage<T>(
|
|||||||
async function runAsync() {
|
async function runAsync() {
|
||||||
try {
|
try {
|
||||||
const serialized = await localForage.getItem<string>(key);
|
const serialized = await localForage.getItem<string>(key);
|
||||||
const parsed = JSON.parse(serialized);
|
const parsed = JSON.parse(serialized ?? "null");
|
||||||
if (!Object.is(parsed, null)) {
|
if (!Object.is(parsed, null)) {
|
||||||
setData(parsed);
|
setData(parsed);
|
||||||
Cache[key] = parsed;
|
Cache[key] = parsed;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Toast } from "react-bootstrap";
|
|||||||
|
|
||||||
interface IToast {
|
interface IToast {
|
||||||
header?: string;
|
header?: string;
|
||||||
content: JSX.Element | string;
|
content: React.ReactNode | string;
|
||||||
delay?: number;
|
delay?: number;
|
||||||
variant?: "success" | "danger" | "warning";
|
variant?: "success" | "danger" | "warning";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { ApolloProvider } from "react-apollo";
|
import { ApolloProvider } from "@apollo/client";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import { BrowserRouter } from "react-router-dom";
|
import { BrowserRouter } from "react-router-dom";
|
||||||
import { App } from "./App";
|
import { App } from "./App";
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const useFocus = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
return [htmlElRef, setFocus] as const;
|
return [htmlElRef, setFocus] as const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
3421
ui/v2.5/yarn.lock
3421
ui/v2.5/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user