diff --git a/ui/v2.5/src/components/MainNavbar.tsx b/ui/v2.5/src/components/MainNavbar.tsx
index 556577008..150ad31c4 100644
--- a/ui/v2.5/src/components/MainNavbar.tsx
+++ b/ui/v2.5/src/components/MainNavbar.tsx
@@ -1,72 +1,77 @@
import React from "react";
import { Nav, Navbar, Button } from "react-bootstrap";
-import { IconName } from '@fortawesome/fontawesome-svg-core';
-import { LinkContainer } from 'react-router-bootstrap';
+import { IconName } from "@fortawesome/fontawesome-svg-core";
+import { LinkContainer } from "react-router-bootstrap";
import { Link, useLocation } from "react-router-dom";
-import { Icon } from 'src/components/Shared'
+import { Icon } from "src/components/Shared";
interface IMenuItem {
- text: string;
- href: string;
- icon: IconName;
+ text: string;
+ href: string;
+ icon: IconName;
}
-const menuItems:IMenuItem[] = [
-{
- icon: "play-circle",
- text: "Scenes",
- href: "/scenes"
-},
-{
- href: "/scenes/markers",
- icon: "map-marker-alt",
- text: "Markers"
-},
-{
- href: "/galleries",
- icon: "image",
- text: "Galleries"
-},
-{
- href: "/performers",
- icon: "user",
- text: "Performers"
-},
-{
- href: "/studios",
- icon: "video",
- text: "Studios"
-},
-{
- href: "/tags",
- icon: "tag",
- text: "Tags"
-}
+const menuItems: IMenuItem[] = [
+ {
+ icon: "play-circle",
+ text: "Scenes",
+ href: "/scenes"
+ },
+ {
+ href: "/scenes/markers",
+ icon: "map-marker-alt",
+ text: "Markers"
+ },
+ {
+ href: "/galleries",
+ icon: "image",
+ text: "Galleries"
+ },
+ {
+ href: "/performers",
+ icon: "user",
+ text: "Performers"
+ },
+ {
+ href: "/studios",
+ icon: "video",
+ text: "Studios"
+ },
+ {
+ href: "/tags",
+ icon: "tag",
+ text: "Tags"
+ }
];
export const MainNavbar: React.FC = () => {
const location = useLocation();
- const path = location.pathname === '/performers'
- ? '/performers/new'
- : location.pathname === '/studios'
- ? '/studios/new' : null;
- const newButton = path === null ? '' : (
-
- New
-
- );
+ const path =
+ location.pathname === "/performers"
+ ? "/performers/new"
+ : location.pathname === "/studios"
+ ? "/studios/new"
+ : null;
+ const newButton =
+ path === null ? (
+ ""
+ ) : (
+
+ New
+
+ );
return (
- Stash
+ Stash
- {menuItems.map((i) => (
+ {menuItems.map(i => (
{
{newButton}
-
-
-
-
+
+
+
+
diff --git a/ui/v2.5/src/components/PageNotFound.tsx b/ui/v2.5/src/components/PageNotFound.tsx
index 8e7cfb32e..645709fcf 100644
--- a/ui/v2.5/src/components/PageNotFound.tsx
+++ b/ui/v2.5/src/components/PageNotFound.tsx
@@ -1,7 +1,5 @@
import React, { FunctionComponent } from "react";
export const PageNotFound: FunctionComponent = () => {
- return (
-
Page not found.
- );
+ return
Page not found. ;
};
diff --git a/ui/v2.5/src/components/Settings/Settings.tsx b/ui/v2.5/src/components/Settings/Settings.tsx
index f2f164cdc..e3e02686c 100644
--- a/ui/v2.5/src/components/Settings/Settings.tsx
+++ b/ui/v2.5/src/components/Settings/Settings.tsx
@@ -1,7 +1,7 @@
import React from "react";
import queryString from "query-string";
-import { Card, Tab, Nav, Row, Col } from 'react-bootstrap';
-import { useHistory, useLocation } from 'react-router-dom';
+import { Card, Tab, Nav, Row, Col } from "react-bootstrap";
+import { useHistory, useLocation } from "react-router-dom";
import { SettingsAboutPanel } from "./SettingsAboutPanel";
import { SettingsConfigurationPanel } from "./SettingsConfigurationPanel";
import { SettingsInterfacePanel } from "./SettingsInterfacePanel";
@@ -9,55 +9,59 @@ import { SettingsLogsPanel } from "./SettingsLogsPanel";
import { SettingsTasksPanel } from "./SettingsTasksPanel/SettingsTasksPanel";
export const Settings: React.FC = () => {
- const location = useLocation();
+ const location = useLocation();
const history = useHistory();
- const defaultTab = queryString.parse(location.search).tab ?? 'configuration';
+ const defaultTab = queryString.parse(location.search).tab ?? "configuration";
- const onSelect = ((val:string) => history.push(`?tab=${val}`));
+ const onSelect = (val: string) => history.push(`?tab=${val}`);
return (
-
-
-
-
-
+
+
+
+
+
Configuration
-
- Interface
-
-
- Tasks
-
-
- Logs
-
-
- About
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ Interface
+
+
+ Tasks
+
+
+ Logs
+
+
+ About
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
diff --git a/ui/v2.5/src/components/Settings/SettingsAboutPanel.tsx b/ui/v2.5/src/components/Settings/SettingsAboutPanel.tsx
index 70008e3e4..2e70f7abf 100644
--- a/ui/v2.5/src/components/Settings/SettingsAboutPanel.tsx
+++ b/ui/v2.5/src/components/Settings/SettingsAboutPanel.tsx
@@ -1,12 +1,14 @@
import React from "react";
-import { Table, Spinner } from 'react-bootstrap';
+import { Table, Spinner } from "react-bootstrap";
import { StashService } from "src/core/StashService";
export const SettingsAboutPanel: React.FC = () => {
const { data, error, loading } = StashService.useVersion();
function maybeRenderTag() {
- if (!data || !data.version || !data.version.version) { return; }
+ if (!data || !data.version || !data.version.version) {
+ return;
+ }
return (
Version:
@@ -16,30 +18,32 @@ export const SettingsAboutPanel: React.FC = () => {
}
function renderVersion() {
- if (!data || !data.version) { return; }
+ if (!data || !data.version) {
+ return;
+ }
return (
<>
-
-
- {maybeRenderTag()}
-
- Build hash:
- {data.version.hash}
-
-
- Build time:
- {data.version.build_time}
-
-
-
+
+
+ {maybeRenderTag()}
+
+ Build hash:
+ {data.version.hash}
+
+
+ Build time:
+ {data.version.build_time}
+
+
+
>
);
}
return (
<>
About
- {!data || loading ? : ''}
- {error ? error.message : ''}
+ {!data || loading ? : ""}
+ {error ? error.message : ""}
{renderVersion()}
>
);
diff --git a/ui/v2.5/src/components/Settings/SettingsConfigurationPanel.tsx b/ui/v2.5/src/components/Settings/SettingsConfigurationPanel.tsx
index b607bdbc2..eea36e1e4 100644
--- a/ui/v2.5/src/components/Settings/SettingsConfigurationPanel.tsx
+++ b/ui/v2.5/src/components/Settings/SettingsConfigurationPanel.tsx
@@ -1,26 +1,34 @@
import React, { useEffect, useState } from "react";
-import { Button, Form, InputGroup, Spinner } from 'react-bootstrap';
+import { Button, Form, InputGroup, Spinner } from "react-bootstrap";
import * as GQL from "src/core/generated-graphql";
import { StashService } from "src/core/StashService";
-import { useToast } from 'src/hooks';
-import { Icon } from 'src/components/Shared';
+import { useToast } from "src/hooks";
+import { Icon } from "src/components/Shared";
import { FolderSelect } from "src/components/Shared/FolderSelect/FolderSelect";
export const SettingsConfigurationPanel: React.FC = () => {
const Toast = useToast();
// Editing config state
const [stashes, setStashes] = useState([]);
- const [databasePath, setDatabasePath] = useState(undefined);
- const [generatedPath, setGeneratedPath] = useState(undefined);
- const [maxTranscodeSize, setMaxTranscodeSize] = useState(undefined);
- const [maxStreamingTranscodeSize, setMaxStreamingTranscodeSize] = useState(undefined);
+ const [databasePath, setDatabasePath] = useState(
+ undefined
+ );
+ const [generatedPath, setGeneratedPath] = useState(
+ undefined
+ );
+ const [maxTranscodeSize, setMaxTranscodeSize] = useState<
+ GQL.StreamingResolutionEnum | undefined
+ >(undefined);
+ const [maxStreamingTranscodeSize, setMaxStreamingTranscodeSize] = useState<
+ GQL.StreamingResolutionEnum | undefined
+ >(undefined);
const [username, setUsername] = useState(undefined);
const [password, setPassword] = useState(undefined);
const [logFile, setLogFile] = useState();
const [logOut, setLogOut] = useState(true);
const [logLevel, setLogLevel] = useState("Info");
const [logAccess, setLogAccess] = useState(true);
- const [excludes, setExcludes] = useState<(string)[]>([]);
+ const [excludes, setExcludes] = useState([]);
const { data, error, loading } = StashService.useConfiguration();
@@ -36,12 +44,11 @@ export const SettingsConfigurationPanel: React.FC = () => {
logOut,
logLevel,
logAccess,
- excludes,
+ excludes
});
useEffect(() => {
- if (!data?.configuration || error)
- return;
+ if (!data?.configuration || error) return;
const conf = data.configuration;
if (conf.general) {
@@ -65,27 +72,26 @@ export const SettingsConfigurationPanel: React.FC = () => {
}
function excludeRegexChanged(idx: number, value: string) {
- const newExcludes = excludes.map((regex, i)=> {
- const ret = ( idx !== i ) ? regex : value ;
- return ret
- })
+ const newExcludes = excludes.map((regex, i) => {
+ const ret = idx !== i ? regex : value;
+ return ret;
+ });
setExcludes(newExcludes);
}
function excludeRemoveRegex(idx: number) {
- const newExcludes = excludes.filter((_regex, i) => i !== idx );
+ const newExcludes = excludes.filter((_regex, i) => i !== idx);
setExcludes(newExcludes);
}
function excludeAddRegex() {
- const demo = "sample\\.mp4$"
+ const demo = "sample\\.mp4$";
const newExcludes = excludes.concat(demo);
setExcludes(newExcludes);
}
-
async function onSave() {
try {
const result = await updateGeneralConfig();
@@ -106,35 +112,46 @@ export const SettingsConfigurationPanel: React.FC = () => {
GQL.StreamingResolutionEnum.Original
].map(resolutionToString);
- function resolutionToString(r : GQL.StreamingResolutionEnum | undefined) {
+ function resolutionToString(r: GQL.StreamingResolutionEnum | undefined) {
switch (r) {
- case GQL.StreamingResolutionEnum.Low: return "240p";
- case GQL.StreamingResolutionEnum.Standard: return "480p";
- case GQL.StreamingResolutionEnum.StandardHd: return "720p";
- case GQL.StreamingResolutionEnum.FullHd: return "1080p";
- case GQL.StreamingResolutionEnum.FourK: return "4k";
- case GQL.StreamingResolutionEnum.Original: return "Original";
+ case GQL.StreamingResolutionEnum.Low:
+ return "240p";
+ case GQL.StreamingResolutionEnum.Standard:
+ return "480p";
+ case GQL.StreamingResolutionEnum.StandardHd:
+ return "720p";
+ case GQL.StreamingResolutionEnum.FullHd:
+ return "1080p";
+ case GQL.StreamingResolutionEnum.FourK:
+ return "4k";
+ case GQL.StreamingResolutionEnum.Original:
+ return "Original";
}
return "Original";
}
- function translateQuality(quality : string) {
+ function translateQuality(quality: string) {
switch (quality) {
- case "240p": return GQL.StreamingResolutionEnum.Low;
- case "480p": return GQL.StreamingResolutionEnum.Standard;
- case "720p": return GQL.StreamingResolutionEnum.StandardHd;
- case "1080p": return GQL.StreamingResolutionEnum.FullHd;
- case "4k": return GQL.StreamingResolutionEnum.FourK;
- case "Original": return GQL.StreamingResolutionEnum.Original;
+ case "240p":
+ return GQL.StreamingResolutionEnum.Low;
+ case "480p":
+ return GQL.StreamingResolutionEnum.Standard;
+ case "720p":
+ return GQL.StreamingResolutionEnum.StandardHd;
+ case "1080p":
+ return GQL.StreamingResolutionEnum.FullHd;
+ case "4k":
+ return GQL.StreamingResolutionEnum.FourK;
+ case "Original":
+ return GQL.StreamingResolutionEnum.Original;
}
return GQL.StreamingResolutionEnum.Original;
}
- if(error)
- return {error.message} ;
- if(!data?.configuration || loading)
+ if (error) return {error.message} ;
+ if (!data?.configuration || loading)
return ;
return (
@@ -147,37 +164,56 @@ export const SettingsConfigurationPanel: React.FC = () => {
directories={stashes}
onDirectoriesChanged={onStashesChanged}
/>
- Directory locations to your content
+
+ Directory locations to your content
+
Database Path
- setDatabasePath(e.target.value)} />
- File location for the SQLite database (requires restart)
+ setDatabasePath(e.target.value)}
+ />
+
+ File location for the SQLite database (requires restart)
+
Generated Path
- setGeneratedPath(e.target.value)} />
- Directory location for the generated files (scene markers, scene previews, sprites, etc)
+ setGeneratedPath(e.target.value)}
+ />
+
+ Directory location for the generated files (scene markers, scene
+ previews, sprites, etc)
+
Excluded Patterns
- { excludes ? excludes.map((regexp, i) => (
-
- excludeRegexChanged(i, e.target.value)}
- />
-
- excludeRemoveRegex(i)}>
-
-
-
-
- )) : ''
- }
+ {excludes
+ ? excludes.map((regexp, i) => (
+
+
+ excludeRegexChanged(i, e.target.value)
+ }
+ />
+
+ excludeRemoveRegex(i)}
+ >
+
+
+
+
+ ))
+ : ""}
excludeAddRegex()}>
@@ -189,7 +225,9 @@ export const SettingsConfigurationPanel: React.FC = () => {
rel="noopener noreferrer"
target="_blank"
>
- Regexps of files/paths to exclude from Scan and add to Clean
+
+ Regexps of files/paths to exclude from Scan and add to Clean
+
@@ -205,23 +243,41 @@ export const SettingsConfigurationPanel: React.FC = () => {
Maximum transcode size
) => setMaxTranscodeSize(translateQuality(event.currentTarget.value))}
+ onChange={(event: React.FormEvent) =>
+ setMaxTranscodeSize(translateQuality(event.currentTarget.value))
+ }
value={resolutionToString(maxTranscodeSize)}
>
- { transcodeQualities.map(q => ({q} ))}
+ {transcodeQualities.map(q => (
+
+ {q}
+
+ ))}
- Maximum size for generated transcodes
+
+ Maximum size for generated transcodes
+
Maximum streaming transcode size
) => setMaxStreamingTranscodeSize(translateQuality(event.currentTarget.value))}
+ onChange={(event: React.FormEvent) =>
+ setMaxStreamingTranscodeSize(
+ translateQuality(event.currentTarget.value)
+ )
+ }
value={resolutionToString(maxStreamingTranscodeSize)}
>
- { transcodeQualities.map(q => ({q} ))}
+ {transcodeQualities.map(q => (
+
+ {q}
+
+ ))}
- Maximum size for transcoded streams
+
+ Maximum size for transcoded streams
+
@@ -231,13 +287,28 @@ export const SettingsConfigurationPanel: React.FC = () => {
Authentication
Username
- ) => setUsername(e.currentTarget.value)} />
- Username to access Stash. Leave blank to disable user authentication
+ ) =>
+ setUsername(e.currentTarget.value)
+ }
+ />
+
+ Username to access Stash. Leave blank to disable user authentication
+
Password
- ) => setPassword(e.currentTarget.value)} />
- Password to access Stash. Leave blank to disable user authentication
+ ) =>
+ setPassword(e.currentTarget.value)
+ }
+ />
+
+ Password to access Stash. Leave blank to disable user authentication
+
@@ -246,8 +317,16 @@ export const SettingsConfigurationPanel: React.FC = () => {
Logging
Log file
- ) => setLogFile(e.currentTarget.value)} />
- Path to the file to output logging to. Blank to disable file logging. Requires restart.
+ ) =>
+ setLogFile(e.currentTarget.value)
+ }
+ />
+
+ Path to the file to output logging to. Blank to disable file logging.
+ Requires restart.
+
@@ -256,17 +335,26 @@ export const SettingsConfigurationPanel: React.FC = () => {
label="Log to terminal"
onChange={() => setLogOut(!logOut)}
/>
- Logs to the terminal in addition to a file. Always true if file logging is disabled. Requires restart.
+
+ Logs to the terminal in addition to a file. Always true if file
+ logging is disabled. Requires restart.
+
Log Level
) => setLogLevel(event.currentTarget.value)}
+ onChange={(event: React.FormEvent) =>
+ setLogLevel(event.currentTarget.value)
+ }
value={logLevel}
>
- { ["Debug", "Info", "Warning", "Error"].map(o => ({o} )) }
+ {["Debug", "Info", "Warning", "Error"].map(o => (
+
+ {o}
+
+ ))}
@@ -276,12 +364,16 @@ export const SettingsConfigurationPanel: React.FC = () => {
label="Log http access"
onChange={() => setLogAccess(!logAccess)}
/>
- Logs http access to the terminal. Requires restart.
+
+ Logs http access to the terminal. Requires restart.
+
- onSave()}>Save
+ onSave()}>
+ Save
+
>
);
};
diff --git a/ui/v2.5/src/components/Settings/SettingsInterfacePanel.tsx b/ui/v2.5/src/components/Settings/SettingsInterfacePanel.tsx
index ae44e934b..4fc8f7abd 100644
--- a/ui/v2.5/src/components/Settings/SettingsInterfacePanel.tsx
+++ b/ui/v2.5/src/components/Settings/SettingsInterfacePanel.tsx
@@ -1,7 +1,7 @@
import React, { useEffect, useState } from "react";
-import { Button, Form, Spinner } from 'react-bootstrap';
+import { Button, Form, Spinner } from "react-bootstrap";
import { StashService } from "src/core/StashService";
-import { useToast } from 'src/hooks';
+import { useToast } from "src/hooks";
export const SettingsInterfacePanel: React.FC = () => {
const Toast = useToast();
@@ -25,8 +25,7 @@ export const SettingsInterfacePanel: React.FC = () => {
});
useEffect(() => {
- if (config.error)
- return;
+ if (config.error) return;
const iCfg = config?.data?.configuration?.interface;
setSoundOnPreview(iCfg?.soundOnPreview ?? true);
@@ -51,8 +50,12 @@ export const SettingsInterfacePanel: React.FC = () => {
return (
<>
- {config.error ? {config.error.message} : ''}
- {(!config?.data?.configuration || config.loading) ? : ''}
+ {config.error ? {config.error.message} : ""}
+ {!config?.data?.configuration || config.loading ? (
+
+ ) : (
+ ""
+ )}
User Interface
Scene / Marker Wall
@@ -66,7 +69,9 @@ export const SettingsInterfacePanel: React.FC = () => {
label="Enable sound"
onChange={() => setSoundOnPreview(!soundOnPreview)}
/>
- Configuration for wall items
+
+ Configuration for wall items
+
@@ -75,31 +80,38 @@ export const SettingsInterfacePanel: React.FC = () => {
checked={showStudioAsText}
label="Show Studios as text"
onChange={() => {
- setShowStudioAsText(!showStudioAsText)
+ setShowStudioAsText(!showStudioAsText);
}}
/>
-
+
Scene Player
{
- setAutostartVideo(!autostartVideo)
+ setAutostartVideo(!autostartVideo);
}}
/>
-
+
Maximum loop duration
) => setMaximumLoopDuration(Number.parseInt(event.currentTarget.value, 10) ?? 0)}
+ onChange={(event: React.FormEvent) =>
+ setMaximumLoopDuration(
+ Number.parseInt(event.currentTarget.value, 10) ?? 0
+ )
+ }
min={0}
step={1}
/>
- Maximum scene duration - in seconds - where scene player will loop the video - 0 to disable
+
+ Maximum scene duration - in seconds - where scene player will loop
+ the video - 0 to disable
+
@@ -109,7 +121,7 @@ export const SettingsInterfacePanel: React.FC = () => {
checked={cssEnabled}
label="Custom CSS enabled"
onChange={() => {
- setCSSEnabled(!cssEnabled)
+ setCSSEnabled(!cssEnabled);
}}
/>
@@ -117,13 +129,17 @@ export const SettingsInterfacePanel: React.FC = () => {
as="textarea"
value={css}
onChange={(e: any) => setCSS(e.target.value)}
- rows={16}>
-
- Page must be reloaded for changes to take effect.
+ rows={16}
+ >
+
+ Page must be reloaded for changes to take effect.
+
- onSave()}>Save
+ onSave()}>
+ Save
+
>
);
};
diff --git a/ui/v2.5/src/components/Settings/SettingsLogsPanel.tsx b/ui/v2.5/src/components/Settings/SettingsLogsPanel.tsx
index aba2f4497..4a4d7d8bc 100644
--- a/ui/v2.5/src/components/Settings/SettingsLogsPanel.tsx
+++ b/ui/v2.5/src/components/Settings/SettingsLogsPanel.tsx
@@ -1,13 +1,13 @@
import React, { useState } from "react";
-import { Form, Col } from 'react-bootstrap';
+import { Form, Col } from "react-bootstrap";
import * as GQL from "src/core/generated-graphql";
import { StashService } from "src/core/StashService";
function convertTime(logEntry: GQL.LogEntryDataFragment) {
- function pad(val : number) {
+ function pad(val: number) {
let ret = val.toString();
if (val <= 9) {
- ret = `0${ ret}`;
+ ret = `0${ret}`;
}
return ret;
@@ -16,21 +16,23 @@ function convertTime(logEntry: GQL.LogEntryDataFragment) {
const date = new Date(logEntry.time);
const month = date.getMonth() + 1;
const day = date.getDate();
- let dateStr = `${date.getFullYear() }-${ pad(month) }-${ pad(day)}`;
- dateStr += ` ${ pad(date.getHours()) }:${ pad(date.getMinutes()) }:${ pad(date.getSeconds())}`;
+ let dateStr = `${date.getFullYear()}-${pad(month)}-${pad(day)}`;
+ dateStr += ` ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(
+ date.getSeconds()
+ )}`;
return dateStr;
}
-function levelClass(level : string) {
+function levelClass(level: string) {
return level.toLowerCase().trim();
}
interface ILogElementProps {
- logEntry : LogEntry
+ logEntry: LogEntry;
}
-const LogElement: React.FC = ({ logEntry }) => {
+const LogElement: React.FC = ({ logEntry }) => {
// pad to maximum length of level enum
const level = logEntry.level.padEnd(GQL.LogLevel.Progress.length);
@@ -39,11 +41,10 @@ const LogElement: React.FC = ({ logEntry }) => {
{logEntry.time}
{level}
{logEntry.message}
-
+
>
);
-}
-
+};
class LogEntry {
public time: string;
@@ -77,15 +78,17 @@ export const SettingsLogsPanel: React.FC = () => {
const newData = (data?.loggingSubscribe ?? []).map(e => new LogEntry(e));
const filteredLogEntries = [...newData.reverse(), ...oldData]
- .filter(filterByLogLevel).slice(0, MAX_LOG_ENTRIES);
+ .filter(filterByLogLevel)
+ .slice(0, MAX_LOG_ENTRIES);
- const maybeRenderError = error
- ? Error connecting to log server: {error.message}
- : '';
+ const maybeRenderError = error ? (
+ Error connecting to log server: {error.message}
+ ) : (
+ ""
+ );
- function filterByLogLevel(logEntry : LogEntry) {
- if (logLevel === "Debug")
- return true;
+ function filterByLogLevel(logEntry: LogEntry) {
+ if (logLevel === "Debug") return true;
const logLevelIndex = logLevels.indexOf(logLevel);
const levelIndex = logLevels.indexOf(logEntry.level);
@@ -104,17 +107,21 @@ export const SettingsLogsPanel: React.FC = () => {
setLogLevel(event.currentTarget.value)}
+ onChange={event => setLogLevel(event.currentTarget.value)}
>
- { logLevels.map(level => ({level} )) }
+ {logLevels.map(level => (
+
+ {level}
+
+ ))}
{maybeRenderError}
- {filteredLogEntries.map((logEntry) =>
-
- )}
+ {filteredLogEntries.map(logEntry => (
+
+ ))}
>
);
diff --git a/ui/v2.5/src/components/Settings/SettingsTasksPanel/GenerateButton.tsx b/ui/v2.5/src/components/Settings/SettingsTasksPanel/GenerateButton.tsx
index 21fcbe4e8..af280ce33 100644
--- a/ui/v2.5/src/components/Settings/SettingsTasksPanel/GenerateButton.tsx
+++ b/ui/v2.5/src/components/Settings/SettingsTasksPanel/GenerateButton.tsx
@@ -1,7 +1,7 @@
import React, { useState } from "react";
-import { Button, Form } from 'react-bootstrap';
+import { Button, Form } from "react-bootstrap";
import { StashService } from "src/core/StashService";
-import { useToast } from 'src/hooks';
+import { useToast } from "src/hooks";
export const GenerateButton: React.FC = () => {
const Toast = useToast();
@@ -12,7 +12,12 @@ export const GenerateButton: React.FC = () => {
async function onGenerate() {
try {
- await StashService.queryMetadataGenerate({sprites, previews, markers, transcodes});
+ await StashService.queryMetadataGenerate({
+ sprites,
+ previews,
+ markers,
+ transcodes
+ });
Toast.success({ content: "Started generating" });
} catch (e) {
Toast.error(e);
@@ -21,12 +26,36 @@ export const GenerateButton: React.FC = () => {
return (
- setSprites(!sprites)} />
- setPreviews(!previews)} />
- setMarkers(!markers)} />
- setTranscodes(!transcodes)} />
- onGenerate()}>Generate
- Generate supporting image, sprite, video, vtt and other files.
+ setSprites(!sprites)}
+ />
+ setPreviews(!previews)}
+ />
+ setMarkers(!markers)}
+ />
+ setTranscodes(!transcodes)}
+ />
+ onGenerate()}>
+ Generate
+
+
+ Generate supporting image, sprite, video, vtt and other files.
+
);
};
diff --git a/ui/v2.5/src/components/Settings/SettingsTasksPanel/SettingsTasksPanel.tsx b/ui/v2.5/src/components/Settings/SettingsTasksPanel/SettingsTasksPanel.tsx
index 7019141a4..b1a73a955 100644
--- a/ui/v2.5/src/components/Settings/SettingsTasksPanel/SettingsTasksPanel.tsx
+++ b/ui/v2.5/src/components/Settings/SettingsTasksPanel/SettingsTasksPanel.tsx
@@ -1,9 +1,9 @@
import React, { useState, useEffect } from "react";
-import { Button, Form, ProgressBar } from 'react-bootstrap';
+import { Button, Form, ProgressBar } from "react-bootstrap";
import { Link } from "react-router-dom";
import { StashService } from "src/core/StashService";
-import { useToast } from 'src/hooks';
-import { Modal } from 'src/components/Shared';
+import { useToast } from "src/hooks";
+import { Modal } from "src/components/Shared";
import { GenerateButton } from "./GenerateButton";
export const SettingsTasksPanel: React.FC = () => {
@@ -22,7 +22,7 @@ export const SettingsTasksPanel: React.FC = () => {
const metadataUpdate = StashService.useMetadataUpdate();
function statusToText(s: string) {
- switch(s) {
+ switch (s) {
case "Idle":
return "Idle";
case "Scan":
@@ -38,7 +38,7 @@ export const SettingsTasksPanel: React.FC = () => {
case "Auto Tag":
return "Auto tagging scenes";
default:
- return "Idle"
+ return "Idle";
}
}
@@ -68,7 +68,9 @@ export const SettingsTasksPanel: React.FC = () => {
function onImport() {
setIsImportAlertOpen(false);
- StashService.queryMetadataImport().then(() => { jobStatus.refetch()});
+ StashService.queryMetadataImport().then(() => {
+ jobStatus.refetch();
+ });
}
function renderImportAlert() {
@@ -76,12 +78,12 @@ export const SettingsTasksPanel: React.FC = () => {
setIsImportAlertOpen(false) }}
>
- Are you sure you want to import? This will delete the database and re-import from
- your exported metadata.
+ Are you sure you want to import? This will delete the database and
+ re-import from your exported metadata.
);
@@ -89,7 +91,9 @@ export const SettingsTasksPanel: React.FC = () => {
function onClean() {
setIsCleanAlertOpen(false);
- StashService.queryMetadataClean().then(() => { jobStatus.refetch()});
+ StashService.queryMetadataClean().then(() => {
+ jobStatus.refetch();
+ });
}
function renderCleanAlert() {
@@ -97,13 +101,13 @@ export const SettingsTasksPanel: React.FC = () => {
setIsCleanAlertOpen(false) }}
>
- Are you sure you want to Clean?
- This will delete db information and generated content
- for all scenes that are no longer found in the filesystem.
+ Are you sure you want to Clean? This will delete db information and
+ generated content for all scenes that are no longer found in the
+ filesystem.
);
@@ -111,7 +115,7 @@ export const SettingsTasksPanel: React.FC = () => {
async function onScan() {
try {
- await StashService.queryMetadataScan({useFileMetadata});
+ await StashService.queryMetadataScan({ useFileMetadata });
Toast.success({ content: "Started scan" });
jobStatus.refetch();
} catch (e) {
@@ -125,7 +129,7 @@ export const SettingsTasksPanel: React.FC = () => {
performers: autoTagPerformers ? wildcard : [],
studios: autoTagStudios ? wildcard : [],
tags: autoTagTags ? wildcard : []
- }
+ };
}
async function onAutoTag() {
@@ -145,7 +149,15 @@ export const SettingsTasksPanel: React.FC = () => {
return (
- StashService.queryStopJob().then(() => jobStatus.refetch())}>Stop
+
+ StashService.queryStopJob().then(() => jobStatus.refetch())
+ }
+ >
+ Stop
+
);
}
@@ -153,11 +165,15 @@ export const SettingsTasksPanel: React.FC = () => {
function renderJobStatus() {
return (
<>
-
- Status: {status}
- { status !== "Idle" ? : '' }
-
- {maybeRenderStop()}
+
+ Status: {status}
+ {status !== "Idle" ? (
+
+ ) : (
+ ""
+ )}
+
+ {maybeRenderStop()}
>
);
}
@@ -180,8 +196,12 @@ export const SettingsTasksPanel: React.FC = () => {
label="Set name, date, details from metadata (if present)"
onChange={() => setUseFileMetadata(!useFileMetadata)}
/>
- onScan()}>Scan
- Scan for new content and add it to the database.
+ onScan()}>
+ Scan
+
+
+ Scan for new content and add it to the database.
+
@@ -204,8 +224,12 @@ export const SettingsTasksPanel: React.FC = () => {
label="Tags"
onChange={() => setAutoTagTags(!autoTagTags)}
/>
- onAutoTag()}>Auto Tag
- Auto-tag content based on filenames.
+ onAutoTag()}>
+ Auto Tag
+
+
+ Auto-tag content based on filenames.
+
@@ -219,21 +243,50 @@ export const SettingsTasksPanel: React.FC = () => {
Generated Content
- setIsCleanAlertOpen(true)}>Clean
- Check for missing files and remove them from the database. This is a destructive action.
+ setIsCleanAlertOpen(true)}
+ >
+ Clean
+
+
+ Check for missing files and remove them from the database. This is a
+ destructive action.
+
Metadata
- StashService.queryMetadataExport().then(() => { jobStatus.refetch()})}>Export
- Export the database content into JSON format.
+
+ StashService.queryMetadataExport().then(() => {
+ jobStatus.refetch();
+ })
+ }
+ >
+ Export
+
+
+ Export the database content into JSON format.
+
- setIsImportAlertOpen(true)}>Import
- Import from exported JSON. This is a destructive action.
+ setIsImportAlertOpen(true)}
+ >
+ Import
+
+
+ Import from exported JSON. This is a destructive action.
+
>
);
diff --git a/ui/v2.5/src/components/Shared/DetailsEditNavbar.tsx b/ui/v2.5/src/components/Shared/DetailsEditNavbar.tsx
index 1d87e45d8..6db019597 100644
--- a/ui/v2.5/src/components/Shared/DetailsEditNavbar.tsx
+++ b/ui/v2.5/src/components/Shared/DetailsEditNavbar.tsx
@@ -1,4 +1,12 @@
-import { Button, Form, Modal, Nav, Navbar, OverlayTrigger, Popover } from 'react-bootstrap';
+import {
+ Button,
+ Form,
+ Modal,
+ Nav,
+ Navbar,
+ OverlayTrigger,
+ Popover
+} from "react-bootstrap";
import React, { useState } from "react";
import { Link } from "react-router-dom";
import * as GQL from "src/core/generated-graphql";
@@ -17,56 +25,85 @@ interface IProps {
// TODO: only for performers. make generic
scrapers?: GQL.ListPerformerScrapersListPerformerScrapers[];
- onDisplayScraperDialog?: (scraper: GQL.ListPerformerScrapersListPerformerScrapers) => void;
+ onDisplayScraperDialog?: (
+ scraper: GQL.ListPerformerScrapersListPerformerScrapers
+ ) => void;
}
export const DetailsEditNavbar: React.FC = (props: IProps) => {
const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState(false);
function renderEditButton() {
- if (props.isNew) { return; }
+ if (props.isNew) {
+ return;
+ }
return (
- props.onToggleEdit()}
- >
- { props.isEditing ? "Cancel" : "Edit"}
+ props.onToggleEdit()}>
+ {props.isEditing ? "Cancel" : "Edit"}
);
}
function renderSaveButton() {
- if (!props.isEditing) { return; }
- return props.onSave()}>Save ;
+ if (!props.isEditing) {
+ return;
+ }
+ return (
+ props.onSave()}>
+ Save
+
+ );
}
function renderDeleteButton() {
- if (props.isNew || props.isEditing) { return; }
- return setIsDeleteAlertOpen(true)}>Delete ;
+ if (props.isNew || props.isEditing) {
+ return;
+ }
+ return (
+ setIsDeleteAlertOpen(true)}>
+ Delete
+
+ );
}
function renderImageInput() {
- if (!props.isEditing) { return; }
- return (
-
- Choose image...
-
-
- )
+ if (!props.isEditing) {
+ return;
+ }
+ return (
+
+ Choose image...
+
+
+ );
}
function renderScraperMenu() {
- if (!props.performer || !props.isEditing) { return; }
+ if (!props.performer || !props.isEditing) {
+ return;
+ }
const popover = (
- { props.scrapers ? props.scrapers.map((s) => (
- props.onDisplayScraperDialog && props.onDisplayScraperDialog(s) }>
- {s.name}
-
- )) : ''}
+ {props.scrapers
+ ? props.scrapers.map(s => (
+
+ props.onDisplayScraperDialog &&
+ props.onDisplayScraperDialog(s)
+ }
+ >
+ {s.name}
+
+ ))
+ : ""}
@@ -80,53 +117,63 @@ export const DetailsEditNavbar: React.FC = (props: IProps) => {
}
function renderAutoTagButton() {
- if (props.isNew || props.isEditing) { return; }
+ if (props.isNew || props.isEditing) {
+ return;
+ }
if (props.onAutoTag) {
- return ( {
- if (props.onAutoTag) { props.onAutoTag() }
- }}>Auto Tag )
+ return (
+ {
+ if (props.onAutoTag) {
+ props.onAutoTag();
+ }
+ }}
+ >
+ Auto Tag
+
+ );
}
}
function renderScenesButton() {
- if (props.isEditing) { return; }
+ if (props.isEditing) {
+ return;
+ }
let linkSrc: string = "#";
if (props.performer) {
linkSrc = NavUtils.makePerformerScenesUrl(props.performer);
} else if (props.studio) {
linkSrc = NavUtils.makeStudioScenesUrl(props.studio);
}
- return (
-
- Scenes
-
- );
+ return Scenes;
}
function renderDeleteAlert() {
const name = props?.studio?.name ?? props?.performer?.name;
return (
-
-
- Are you sure you want to delete {name}?
-
+
+ Are you sure you want to delete {name}?
- Delete
- setIsDeleteAlertOpen(false)}>Cancel
+
+ Delete
+
+ setIsDeleteAlertOpen(false)}
+ >
+ Cancel
+
);
}
-
return (
<>
- {renderDeleteAlert()}
-
-
+ {renderDeleteAlert()}
+
+
{renderEditButton()}
{renderScraperMenu()}
{renderImageInput()}
@@ -135,8 +182,8 @@ export const DetailsEditNavbar: React.FC = (props: IProps) => {
{renderAutoTagButton()}
{renderScenesButton()}
{renderDeleteButton()}
-
-
+
+
>
);
};
diff --git a/ui/v2.5/src/components/Shared/DurationInput.tsx b/ui/v2.5/src/components/Shared/DurationInput.tsx
index cb2589799..2983d086c 100644
--- a/ui/v2.5/src/components/Shared/DurationInput.tsx
+++ b/ui/v2.5/src/components/Shared/DurationInput.tsx
@@ -1,23 +1,25 @@
import React, { useState, useEffect } from "react";
-import { Button, ButtonGroup, InputGroup, Form } from 'react-bootstrap';
-import { Icon } from 'src/components/Shared'
+import { Button, ButtonGroup, InputGroup, Form } from "react-bootstrap";
+import { Icon } from "src/components/Shared";
import { TextUtils } from "src/utils";
interface IProps {
- disabled?: boolean
- numericValue: number
- onValueChange(valueAsNumber: number): void
- onReset?(): void
+ disabled?: boolean;
+ numericValue: number;
+ onValueChange(valueAsNumber: number): void;
+ onReset?(): void;
}
export const DurationInput: React.FC = (props: IProps) => {
- const [value, setValue] = useState(secondsToString(props.numericValue));
+ const [value, setValue] = useState(
+ secondsToString(props.numericValue)
+ );
useEffect(() => {
setValue(secondsToString(props.numericValue));
}, [props.numericValue]);
- function secondsToString(seconds : number) {
+ function secondsToString(seconds: number) {
let ret = TextUtils.secondsToTimestamp(seconds);
if (ret.startsWith("00:")) {
@@ -31,7 +33,7 @@ export const DurationInput: React.FC = (props: IProps) => {
return ret;
}
- function stringToSeconds(v : string) {
+ function stringToSeconds(v: string) {
if (!v) {
return 0;
}
@@ -44,7 +46,7 @@ export const DurationInput: React.FC = (props: IProps) => {
let seconds = 0;
let factor = 1;
- while(splits.length > 0) {
+ while (splits.length > 0) {
const thisSplit = splits.pop();
if (thisSplit === undefined) {
return 0;
@@ -76,23 +78,15 @@ export const DurationInput: React.FC = (props: IProps) => {
function renderButtons() {
return (
-
- increment()}
- >
+
+ increment()}>
- decrement()}
- >
+ decrement()}>
- )
+ );
}
function onReset() {
@@ -104,12 +98,10 @@ export const DurationInput: React.FC = (props: IProps) => {
function maybeRenderReset() {
if (props.onReset) {
return (
- onReset()}
- >
+ onReset()}>
- )
+ );
}
}
@@ -119,15 +111,15 @@ export const DurationInput: React.FC = (props: IProps) => {
setValue(e.target.value)}
+ onChange={(e: any) => setValue(e.target.value)}
onBlur={() => props.onValueChange(stringToSeconds(value))}
placeholder="hh:mm:ss"
/>
- { maybeRenderReset() }
- { renderButtons() }
+ {maybeRenderReset()}
+ {renderButtons()}
- )
+ );
};
diff --git a/ui/v2.5/src/components/Shared/FolderSelect/FolderSelect.tsx b/ui/v2.5/src/components/Shared/FolderSelect/FolderSelect.tsx
index 3bd5c274f..7c605cbce 100644
--- a/ui/v2.5/src/components/Shared/FolderSelect/FolderSelect.tsx
+++ b/ui/v2.5/src/components/Shared/FolderSelect/FolderSelect.tsx
@@ -1,5 +1,5 @@
import React, { useEffect, useState } from "react";
-import { Button, InputGroup, Form, Modal, Spinner } from 'react-bootstrap';
+import { Button, InputGroup, Form, Modal, Spinner } from "react-bootstrap";
import { StashService } from "src/core/StashService";
interface IProps {
@@ -11,13 +11,15 @@ export const FolderSelect: React.FC = (props: IProps) => {
const [currentDirectory, setCurrentDirectory] = useState("");
const [isDisplayingDialog, setIsDisplayingDialog] = useState(false);
const [selectedDirectories, setSelectedDirectories] = useState([]);
- const { data, error, loading } = StashService.useDirectories(currentDirectory);
+ const { data, error, loading } = StashService.useDirectories(
+ currentDirectory
+ );
useEffect(() => {
setSelectedDirectories(props.directories);
}, [props.directories]);
- const selectableDirectories:string[] = data?.directories ?? [];
+ const selectableDirectories: string[] = data?.directories ?? [];
function onSelectDirectory() {
selectedDirectories.push(currentDirectory);
@@ -28,7 +30,9 @@ export const FolderSelect: React.FC = (props: IProps) => {
}
function onRemoveDirectory(directory: string) {
- const newSelectedDirectories = selectedDirectories.filter((dir) => dir !== directory);
+ const newSelectedDirectories = selectedDirectories.filter(
+ dir => dir !== directory
+ );
setSelectedDirectories(newSelectedDirectories);
props.onDirectoriesChanged(newSelectedDirectories);
}
@@ -40,9 +44,7 @@ export const FolderSelect: React.FC = (props: IProps) => {
onHide={() => setIsDisplayingDialog(false)}
title=""
>
-
- Select Directory
-
+ Select Directory
@@ -52,11 +54,23 @@ export const FolderSelect: React.FC = (props: IProps) => {
defaultValue={currentDirectory}
/>
- {(!data || !data.directories || loading) ? : ''}
+ {!data || !data.directories || loading ? (
+
+ ) : (
+ ""
+ )}
- {selectableDirectories.map((path) => {
- return setCurrentDirectory(path)}>{path} ;
+ {selectableDirectories.map(path => {
+ return (
+ setCurrentDirectory(path)}
+ >
+ {path}
+
+ );
})}
@@ -69,11 +83,18 @@ export const FolderSelect: React.FC = (props: IProps) => {
return (
<>
- {error ? {error.message} : ''}
+ {error ? {error.message} : ""}
{renderDialog()}
- {selectedDirectories.map((path) => {
- return {path} onRemoveDirectory(path)}>Remove
;
+ {selectedDirectories.map(path => {
+ return (
+
+ {path}{" "}
+ onRemoveDirectory(path)}>
+ Remove
+
+
+ );
})}
diff --git a/ui/v2.5/src/components/Shared/HoverPopover.tsx b/ui/v2.5/src/components/Shared/HoverPopover.tsx
index 566c64720..1941f4e4d 100644
--- a/ui/v2.5/src/components/Shared/HoverPopover.tsx
+++ b/ui/v2.5/src/components/Shared/HoverPopover.tsx
@@ -1,15 +1,22 @@
-import React, { useState, useCallback, useEffect, useRef } from 'react'
-import { Overlay, Popover, OverlayProps } from 'react-bootstrap'
+import React, { useState, useCallback, useEffect, useRef } from "react";
+import { Overlay, Popover, OverlayProps } from "react-bootstrap";
interface IHoverPopover {
- enterDelay?: number;
- leaveDelay?: number;
+ enterDelay?: number;
+ leaveDelay?: number;
content: JSX.Element[] | JSX.Element | string;
className?: string;
placement?: OverlayProps["placement"];
}
-export const HoverPopover: React.FC = ({ enterDelay = 0, leaveDelay = 400, content, children, className, placement = 'top' }) => {
+export const HoverPopover: React.FC = ({
+ enterDelay = 0,
+ leaveDelay = 400,
+ content,
+ children,
+ className,
+ placement = "top"
+}) => {
const [show, setShow] = useState(false);
const triggerRef = useRef(null);
const enterTimer = useRef();
@@ -25,33 +32,35 @@ export const HoverPopover: React.FC = ({ enterDelay = 0, leaveDel
leaveTimer.current = window.setTimeout(() => setShow(false), leaveDelay);
}, [leaveDelay]);
- useEffect(() => (
- () => {
- window.clearTimeout(enterTimer.current)
- window.clearTimeout(leaveTimer.current)
- }
- ), []);
+ useEffect(
+ () => () => {
+ window.clearTimeout(enterTimer.current);
+ window.clearTimeout(leaveTimer.current);
+ },
+ []
+ );
return (
<>
-
+
{children}
- { triggerRef.current &&
-
+ {triggerRef.current && (
+
{content}
- }
+ )}
>
);
-}
+};
diff --git a/ui/v2.5/src/components/Shared/Icon.tsx b/ui/v2.5/src/components/Shared/Icon.tsx
index f59238c54..5ce5bfe3f 100644
--- a/ui/v2.5/src/components/Shared/Icon.tsx
+++ b/ui/v2.5/src/components/Shared/Icon.tsx
@@ -1,6 +1,6 @@
-import React from 'react';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { IconName } from '@fortawesome/fontawesome-svg-core';
+import React from "react";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { IconName } from "@fortawesome/fontawesome-svg-core";
interface IIcon {
icon: IconName;
diff --git a/ui/v2.5/src/components/Shared/Modal.tsx b/ui/v2.5/src/components/Shared/Modal.tsx
index f90939b20..5fc9e2252 100644
--- a/ui/v2.5/src/components/Shared/Modal.tsx
+++ b/ui/v2.5/src/components/Shared/Modal.tsx
@@ -1,11 +1,11 @@
import React from "react";
-import { Button, Modal } from 'react-bootstrap';
-import { Icon } from 'src/components/Shared';
-import { IconName } from '@fortawesome/fontawesome-svg-core';
+import { Button, Modal } from "react-bootstrap";
+import { Icon } from "src/components/Shared";
+import { IconName } from "@fortawesome/fontawesome-svg-core";
interface IButton {
text?: string;
- variant?: 'danger'|'primary';
+ variant?: "danger" | "primary";
onClick?: () => void;
}
@@ -18,27 +18,42 @@ interface IModal {
accept?: IButton;
}
-const ModalComponent: React.FC = ({ children, show, icon, header, cancel, accept, onHide }) => ((
-
+const ModalComponent: React.FC = ({
+ children,
+ show,
+ icon,
+ header,
+ cancel,
+ accept,
+ onHide
+}) => (
+
- { icon ? : '' }
- { header ?? '' }
+ {icon ? : ""}
+ {header ?? ""}
{children}
- { cancel
- ? {cancel.text ?? 'Cancel'}
- : ''
- }
- {accept?.text ?? 'Close'}
+ {cancel ? (
+
+ {cancel.text ?? "Cancel"}
+
+ ) : (
+ ""
+ )}
+
+ {accept?.text ?? "Close"}
+
-));
+);
export default ModalComponent;
diff --git a/ui/v2.5/src/components/Shared/Select.tsx b/ui/v2.5/src/components/Shared/Select.tsx
index 9dd474ac7..b5fb3904a 100644
--- a/ui/v2.5/src/components/Shared/Select.tsx
+++ b/ui/v2.5/src/components/Shared/Select.tsx
@@ -1,20 +1,20 @@
import React, { useState, useCallback } from "react";
-import Select, { ValueType } from 'react-select';
-import CreatableSelect from 'react-select/creatable';
-import { debounce } from 'lodash';
+import Select, { ValueType } from "react-select";
+import CreatableSelect from "react-select/creatable";
+import { debounce } from "lodash";
import * as GQL from "src/core/generated-graphql";
import { StashService } from "src/core/StashService";
-import { useToast } from 'src/hooks';
+import { useToast } from "src/hooks";
type ValidTypes =
- GQL.AllPerformersForFilterAllPerformers |
- GQL.AllTagsForFilterAllTags |
- GQL.AllStudiosForFilterAllStudios;
-type Option = { value:string, label:string };
+ | GQL.AllPerformersForFilterAllPerformers
+ | GQL.AllTagsForFilterAllTags
+ | GQL.AllStudiosForFilterAllStudios;
+type Option = { value: string; label: string };
interface ITypeProps {
- type?: 'performers' | 'studios' | 'tags';
+ type?: "performers" | "studios" | "tags";
}
interface IFilterProps {
initialIds: string[];
@@ -40,131 +40,221 @@ interface ISelectProps {
interface ISceneGallerySelect {
initialId?: string;
sceneId: string;
- onSelect: (item: GQL.ValidGalleriesForSceneValidGalleriesForScene | undefined) => void;
+ onSelect: (
+ item: GQL.ValidGalleriesForSceneValidGalleriesForScene | undefined
+ ) => void;
}
-const getSelectedValues = (selectedItems:ValueType) => (
- (Array.isArray(selectedItems) ? selectedItems : [selectedItems])
- .map(item => item.value)
-);
+const getSelectedValues = (selectedItems: ValueType ) =>
+ (Array.isArray(selectedItems) ? selectedItems : [selectedItems]).map(
+ item => item.value
+ );
-export const SceneGallerySelect: React.FC = (props) => {
- const { data, loading } = StashService.useValidGalleriesForScene(props.sceneId);
+export const SceneGallerySelect: React.FC = props => {
+ const { data, loading } = StashService.useValidGalleriesForScene(
+ props.sceneId
+ );
const galleries = data?.validGalleriesForScene ?? [];
- const items = (galleries.length > 0 ? [{ path: 'None', id: '0' }, ...galleries] : [])
- .map(g => ({ label: g.path, value: g.id }));
+ const items = (galleries.length > 0
+ ? [{ path: "None", id: "0" }, ...galleries]
+ : []
+ ).map(g => ({ label: g.path, value: g.id }));
- const onChange = (selectedItems:ValueType) => {
+ const onChange = (selectedItems: ValueType ) => {
const selectedItem = getSelectedValues(selectedItems)[0];
props.onSelect(galleries.find(g => g.id === selectedItem.value));
};
const initialId = props.initialId ? [props.initialId] : [];
- return
+ return (
+
+ );
};
interface IScrapePerformerSuggestProps {
scraperId: string;
- onSelectPerformer: (query: GQL.ScrapePerformerListScrapePerformerList) => void;
+ onSelectPerformer: (
+ query: GQL.ScrapePerformerListScrapePerformerList
+ ) => void;
placeholder?: string;
}
-export const ScrapePerformerSuggest: React.FC = (props) => {
+export const ScrapePerformerSuggest: React.FC = props => {
const [query, setQuery] = React.useState("");
- const { data, loading } = StashService.useScrapePerformerList(props.scraperId, query);
-
- const onInputChange = useCallback(debounce((input:string) => { setQuery(input)}, 500), []);
- const onChange = (selectedItems:ValueType) => (
- props.onSelectPerformer(getSelectedValues(selectedItems)[0])
+ const { data, loading } = StashService.useScrapePerformerList(
+ props.scraperId,
+ query
);
+ const onInputChange = useCallback(
+ debounce((input: string) => {
+ setQuery(input);
+ }, 500),
+ []
+ );
+ const onChange = (selectedItems: ValueType ) =>
+ props.onSelectPerformer(getSelectedValues(selectedItems)[0]);
+
const performers = data?.scrapePerformerList ?? [];
- const items = performers.map(item => ({ label: item.name ?? '', value: item.name ?? '' }));
- return
-}
+ const items = performers.map(item => ({
+ label: item.name ?? "",
+ value: item.name ?? ""
+ }));
+ return (
+
+ );
+};
interface IMarkerSuggestProps {
initialMarkerTitle?: string;
- onChange: (title:string) => void;
+ onChange: (title: string) => void;
}
-export const MarkerTitleSuggest: React.FC = (props) => {
+export const MarkerTitleSuggest: React.FC = props => {
const { data, loading } = StashService.useMarkerStrings();
const suggestions = data?.markerStrings ?? [];
- const onChange = (selectedItems:ValueType) => (
- props.onChange(getSelectedValues(selectedItems)[0])
+ const onChange = (selectedItems: ValueType ) =>
+ props.onChange(getSelectedValues(selectedItems)[0]);
+
+ const items = suggestions.map(item => ({
+ label: item?.title ?? "",
+ value: item?.title ?? ""
+ }));
+ const initialIds = props.initialMarkerTitle ? [props.initialMarkerTitle] : [];
+ return (
+
+ );
+};
+
+export const FilterSelect: React.FC = props =>
+ props.type === "performers" ? (
+
+ ) : props.type === "studios" ? (
+
+ ) : (
+
);
- const items = suggestions.map(item => ({ label: item?.title ?? '', value: item?.title ?? '' }));
- const initialIds = props.initialMarkerTitle ? [props.initialMarkerTitle] : [];
- return
-}
-
-export const FilterSelect: React.FC = (props) => (
- props.type === 'performers' ?
- : props.type === 'studios' ?
- :
-);
-
-export const PerformerSelect: React.FC = (props) => {
+export const PerformerSelect: React.FC = props => {
const { data, loading } = StashService.useAllPerformersForFilter();
const normalizedData = data?.allPerformers ?? [];
- const items:Option[] = normalizedData.map(item => ({ value: item.id, label: item.name ?? '' }));
- const placeholder = props.noSelectionString ?? "Select performer..."
+ const items: Option[] = normalizedData.map(item => ({
+ value: item.id,
+ label: item.name ?? ""
+ }));
+ const placeholder = props.noSelectionString ?? "Select performer...";
- const onChange = (selectedItems:ValueType) => {
+ const onChange = (selectedItems: ValueType ) => {
const selectedIds = getSelectedValues(selectedItems);
- props.onSelect(normalizedData.filter(item => selectedIds.indexOf(item.id) !== -1));
+ props.onSelect(
+ normalizedData.filter(item => selectedIds.indexOf(item.id) !== -1)
+ );
};
- return
-}
+ return (
+
+ );
+};
-export const StudioSelect: React.FC = (props) => {
+export const StudioSelect: React.FC = props => {
const { data, loading } = StashService.useAllStudiosForFilter();
const normalizedData = data?.allStudios ?? [];
- const items:Option[] = normalizedData.map(item => ({ value: item.id, label: item.name }));
- const placeholder = props.noSelectionString ?? "Select studio..."
+ const items: Option[] = normalizedData.map(item => ({
+ value: item.id,
+ label: item.name
+ }));
+ const placeholder = props.noSelectionString ?? "Select studio...";
- const onChange = (selectedItems:ValueType) => {
+ const onChange = (selectedItems: ValueType ) => {
const selectedIds = getSelectedValues(selectedItems);
- props.onSelect(normalizedData.filter(item => selectedIds.indexOf(item.id) !== -1));
+ props.onSelect(
+ normalizedData.filter(item => selectedIds.indexOf(item.id) !== -1)
+ );
};
- return
-}
+ return (
+
+ );
+};
-export const TagSelect: React.FC = (props) => {
+export const TagSelect: React.FC = props => {
const [loading, setLoading] = useState(false);
const [selectedIds, setSelectedIds] = useState([]);
const { data, loading: dataLoading } = StashService.useAllTagsForFilter();
- const createTag = StashService.useTagCreate({name: ''});
+ const createTag = StashService.useTagCreate({ name: "" });
const Toast = useToast();
- const placeholder = props.noSelectionString ?? "Select tags..."
+ const placeholder = props.noSelectionString ?? "Select tags...";
const tags = data?.allTags ?? [];
- const selected = tags.filter(tag => selectedIds.indexOf(tag.id) !== -1).map(tag => ({value: tag.id, label: tag.name}));
- const items:Option[] = tags.map(item => ({ value: item.id, label: item.name }));
+ const selected = tags
+ .filter(tag => selectedIds.indexOf(tag.id) !== -1)
+ .map(tag => ({ value: tag.id, label: tag.name }));
+ const items: Option[] = tags.map(item => ({
+ value: item.id,
+ label: item.name
+ }));
const onCreate = async (tagName: string) => {
try {
setLoading(true);
const result = await createTag({
- variables: { name: tagName },
+ variables: { name: tagName }
});
setSelectedIds([...selectedIds, result.data.tagCreate.id]);
- props.onSelect([...tags, result.data.tagCreate].filter(item => selected.indexOf(item.id) !== -1));
+ props.onSelect(
+ [...tags, result.data.tagCreate].filter(
+ item => selected.indexOf(item.id) !== -1
+ )
+ );
setLoading(false);
- Toast.success({ content: (Created tag: {tagName} ) });
+ Toast.success({
+ content: (
+
+ Created tag: {tagName}
+
+ )
+ });
} catch (e) {
Toast.error(e);
}
};
- const onChange = (selectedItems:ValueType) => {
+ const onChange = (selectedItems: ValueType ) => {
const selectedValues = getSelectedValues(selectedItems);
setSelectedIds(selectedValues);
props.onSelect(tags.filter(item => selectedValues.indexOf(item.id) !== -1));
@@ -183,23 +273,24 @@ export const TagSelect: React.FC = (props) => {
selectedOptions={selected}
/>
);
-}
+};
const SelectComponent: React.FC = ({
- type,
- initialIds,
- onChange,
- className,
- items,
- selectedOptions,
- isLoading,
- onCreateOption,
- creatable = false,
- isMulti = false,
- onInputChange,
- placeholder
+ type,
+ initialIds,
+ onChange,
+ className,
+ items,
+ selectedOptions,
+ isLoading,
+ onCreateOption,
+ creatable = false,
+ isMulti = false,
+ onInputChange,
+ placeholder
}) => {
- const defaultValue = items.filter(item => initialIds?.indexOf(item.value) !== -1) ?? null;
+ const defaultValue =
+ items.filter(item => initialIds?.indexOf(item.value) !== -1) ?? null;
const props = {
className,
@@ -208,14 +299,19 @@ const SelectComponent: React.FC = ({
onChange,
isMulti,
defaultValue,
- noOptionsMessage: () => (type !== 'tags' ? 'None' : null),
+ noOptionsMessage: () => (type !== "tags" ? "None" : null),
placeholder,
onInputChange
- }
+ };
- return (
- creatable
- ?
- :
+ return creatable ? (
+
+ ) : (
+
);
};
diff --git a/ui/v2.5/src/components/Shared/TagLink.tsx b/ui/v2.5/src/components/Shared/TagLink.tsx
index b7ef37cc4..fc5143c3c 100644
--- a/ui/v2.5/src/components/Shared/TagLink.tsx
+++ b/ui/v2.5/src/components/Shared/TagLink.tsx
@@ -1,7 +1,11 @@
-import { Badge } from 'react-bootstrap';
+import { Badge } from "react-bootstrap";
import React from "react";
import { Link } from "react-router-dom";
-import { PerformerDataFragment, SceneMarkerDataFragment, TagDataFragment } from "src/core/generated-graphql";
+import {
+ PerformerDataFragment,
+ SceneMarkerDataFragment,
+ TagDataFragment
+} from "src/core/generated-graphql";
import { NavUtils, TextUtils } from "src/utils";
interface IProps {
@@ -21,13 +25,12 @@ export const TagLink: React.FC = (props: IProps) => {
title = props.performer.name || "";
} else if (props.marker) {
link = NavUtils.makeSceneMarkerUrl(props.marker);
- title = `${props.marker.title} - ${TextUtils.secondsToTimestamp(props.marker.seconds || 0)}`;
+ title = `${props.marker.title} - ${TextUtils.secondsToTimestamp(
+ props.marker.seconds || 0
+ )}`;
}
return (
-
+
{title}
);
diff --git a/ui/v2.5/src/components/Shared/index.ts b/ui/v2.5/src/components/Shared/index.ts
index f1a0f5916..3a56611f4 100644
--- a/ui/v2.5/src/components/Shared/index.ts
+++ b/ui/v2.5/src/components/Shared/index.ts
@@ -6,11 +6,11 @@ export {
PerformerSelect,
StudioSelect,
TagSelect
-} from './Select';
+} from "./Select";
-export { default as Icon } from './Icon';
-export { default as Modal } from './Modal';
-export { DetailsEditNavbar } from './DetailsEditNavbar';
-export { DurationInput } from './DurationInput';
-export { TagLink } from './TagLink';
-export { HoverPopover } from './HoverPopover';
+export { default as Icon } from "./Icon";
+export { default as Modal } from "./Modal";
+export { DetailsEditNavbar } from "./DetailsEditNavbar";
+export { DurationInput } from "./DurationInput";
+export { TagLink } from "./TagLink";
+export { HoverPopover } from "./HoverPopover";
diff --git a/ui/v2.5/src/components/Stats.tsx b/ui/v2.5/src/components/Stats.tsx
index adb60c050..7dcfb7d1c 100644
--- a/ui/v2.5/src/components/Stats.tsx
+++ b/ui/v2.5/src/components/Stats.tsx
@@ -1,4 +1,4 @@
-import { Spinner } from 'react-bootstrap';
+import { Spinner } from "react-bootstrap";
import React, { FunctionComponent } from "react";
import { StashService } from "../core/StashService";
@@ -6,7 +6,9 @@ export const Stats: FunctionComponent = () => {
const { data, error, loading } = StashService.useStats();
function renderStats() {
- if (!data || !data.stats) { return; }
+ if (!data || !data.stats) {
+ return;
+ }
return (
@@ -45,10 +47,13 @@ export const Stats: FunctionComponent = () => {
return (
- {!data || loading ?
-
- Loading...
- : undefined}
+ {!data || loading ? (
+
+ Loading...
+
+ ) : (
+ undefined
+ )}
{error ?
error.message : undefined}
{renderStats()}
diff --git a/ui/v2.5/src/components/Studios/StudioCard.tsx b/ui/v2.5/src/components/Studios/StudioCard.tsx
index af2c60a4f..095c6a564 100644
--- a/ui/v2.5/src/components/Studios/StudioCard.tsx
+++ b/ui/v2.5/src/components/Studios/StudioCard.tsx
@@ -1,4 +1,4 @@
-import { Card } from 'react-bootstrap';
+import { Card } from "react-bootstrap";
import React from "react";
import { Link } from "react-router-dom";
import * as GQL from "src/core/generated-graphql";
@@ -9,18 +9,14 @@ interface IProps {
export const StudioCard: React.FC
= ({ studio }) => {
return (
-
+
-
- {studio.name}
-
+ {studio.name}
{studio.scene_count} scenes.
diff --git a/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx b/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx
index 2909a5731..ac0e3941d 100644
--- a/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx
+++ b/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx
@@ -1,37 +1,45 @@
/* eslint-disable react/no-this-in-sfc */
-import { Form, Spinner, Table } from 'react-bootstrap';
+import { Form, Spinner, Table } from "react-bootstrap";
import React, { useEffect, useState } from "react";
-import { useParams, useHistory } from 'react-router-dom';
+import { useParams, useHistory } from "react-router-dom";
import * as GQL from "src/core/generated-graphql";
import { StashService } from "src/core/StashService";
import { ImageUtils, TableUtils } from "src/utils";
import { DetailsEditNavbar } from "src/components/Shared";
-import { useToast} from "src/hooks";
+import { useToast } from "src/hooks";
export const Studio: React.FC = () => {
const history = useHistory();
const Toast = useToast();
- const { id = 'new' } = useParams();
+ const { id = "new" } = useParams();
const isNew = id === "new";
// Editing state
const [isEditing, setIsEditing] = useState(isNew);
// Editing studio state
- const [image, setImage] = useState(undefined);
- const [name, setName] = useState(undefined);
- const [url, setUrl] = useState(undefined);
+ const [image, setImage] = useState(undefined);
+ const [name, setName] = useState(undefined);
+ const [url, setUrl] = useState(undefined);
// Studio state
const [studio, setStudio] = useState>({});
- const [imagePreview, setImagePreview] = useState(undefined);
+ const [imagePreview, setImagePreview] = useState(
+ undefined
+ );
const { data, error, loading } = StashService.useFindStudio(id);
- const updateStudio = StashService.useStudioUpdate(getStudioInput() as GQL.StudioUpdateInput);
- const createStudio = StashService.useStudioCreate(getStudioInput() as GQL.StudioCreateInput);
- const deleteStudio = StashService.useStudioDestroy(getStudioInput() as GQL.StudioDestroyInput);
+ const updateStudio = StashService.useStudioUpdate(
+ getStudioInput() as GQL.StudioUpdateInput
+ );
+ const createStudio = StashService.useStudioCreate(
+ getStudioInput() as GQL.StudioCreateInput
+ );
+ const deleteStudio = StashService.useStudioDestroy(
+ getStudioInput() as GQL.StudioDestroyInput
+ );
function updateStudioEditState(state: Partial) {
setName(state.name);
@@ -64,15 +72,14 @@ export const Studio: React.FC = () => {
if (!isNew && !isEditing) {
if (!data?.findStudio || loading)
return ;
- if (error)
- return {error.message}
;
+ if (error) return {error.message}
;
}
function getStudioInput() {
const input: Partial = {
name,
url,
- image,
+ image
};
if (!isNew) {
@@ -85,7 +92,7 @@ export const Studio: React.FC = () => {
try {
if (!isNew) {
const result = await updateStudio();
- updateStudioData(result.data.studioUpdate)
+ updateStudioData(result.data.studioUpdate);
setIsEditing(false);
} else {
const result = await createStudio();
@@ -101,7 +108,7 @@ export const Studio: React.FC = () => {
return;
}
try {
- await StashService.queryMetadataAutoTag({ studios: [studio.id]});
+ await StashService.queryMetadataAutoTag({ studios: [studio.id] });
Toast.success({ content: "Started auto tagging" });
} catch (e) {
Toast.error(e);
@@ -125,41 +132,50 @@ export const Studio: React.FC = () => {
// TODO: CSS class
return (
-
-
-
-
-
-
{ setIsEditing(!isEditing); updateStudioEditState(studio); }}
- onSave={onSave}
- onDelete={onDelete}
- onAutoTag={onAutoTag}
- onImageChange={onImageChangeHandler}
- />
-
- { !isEditing
- ? {studio.name}
- :
- Name
- setName(event.target.value)}
- />
-
- }
-
-
-
-
- {TableUtils.renderInputGroup({title: "URL", value: studio.url, isEditing, onChange: (val:string) => setUrl(val)})}
-
-
-
+
+
+
+
+
{
+ setIsEditing(!isEditing);
+ updateStudioEditState(studio);
+ }}
+ onSave={onSave}
+ onDelete={onDelete}
+ onAutoTag={onAutoTag}
+ onImageChange={onImageChangeHandler}
+ />
+
+ {!isEditing ? (
+ {studio.name}
+ ) : (
+
+ Name
+ setName(event.target.value)}
+ />
+
+ )}
+
+
+
+
+ {TableUtils.renderInputGroup({
+ title: "URL",
+ value: studio.url,
+ isEditing,
+ onChange: (val: string) => setUrl(val)
+ })}
+
+
+
+
);
};
diff --git a/ui/v2.5/src/components/Studios/StudioList.tsx b/ui/v2.5/src/components/Studios/StudioList.tsx
index 0760593e3..da62db1e0 100644
--- a/ui/v2.5/src/components/Studios/StudioList.tsx
+++ b/ui/v2.5/src/components/Studios/StudioList.tsx
@@ -1,6 +1,9 @@
import React from "react";
import { QueryHookResult } from "react-apollo-hooks";
-import { FindStudiosQuery, FindStudiosVariables } from "src/core/generated-graphql";
+import {
+ FindStudiosQuery,
+ FindStudiosVariables
+} from "src/core/generated-graphql";
import { useStudiosList } from "src/hooks";
import { ListFilterModel } from "src/models/list-filter/filter";
import { DisplayMode } from "src/models/list-filter/types";
@@ -8,20 +11,29 @@ import { StudioCard } from "./StudioCard";
export const StudioList: React.FC = () => {
const listData = useStudiosList({
- renderContent,
+ renderContent
});
- function renderContent(result: QueryHookResult
, filter: ListFilterModel) {
- if (!result.data || !result.data.findStudios) { return; }
+ function renderContent(
+ result: QueryHookResult,
+ filter: ListFilterModel
+ ) {
+ if (!result.data || !result.data.findStudios) {
+ return;
+ }
if (filter.displayMode === DisplayMode.Grid) {
return (
- {result.data.findStudios.studios.map((studio) => ( ))}
+ {result.data.findStudios.studios.map(studio => (
+
+ ))}
);
- } if (filter.displayMode === DisplayMode.List) {
+ }
+ if (filter.displayMode === DisplayMode.List) {
return TODO ;
- } if (filter.displayMode === DisplayMode.Wall) {
+ }
+ if (filter.displayMode === DisplayMode.Wall) {
return TODO ;
}
}
diff --git a/ui/v2.5/src/components/Tags/TagList.tsx b/ui/v2.5/src/components/Tags/TagList.tsx
index 7197b423d..af64f7d7d 100644
--- a/ui/v2.5/src/components/Tags/TagList.tsx
+++ b/ui/v2.5/src/components/Tags/TagList.tsx
@@ -1,23 +1,33 @@
import React, { useState } from "react";
-import { Button, Form, Spinner } from 'react-bootstrap';
+import { Button, Form, Spinner } from "react-bootstrap";
import { Link } from "react-router-dom";
import * as GQL from "src/core/generated-graphql";
import { StashService } from "src/core/StashService";
import { NavUtils } from "src/utils";
-import { Icon, Modal } from 'src/components/Shared';
-import { useToast } from 'src/hooks';
+import { Icon, Modal } from "src/components/Shared";
+import { useToast } from "src/hooks";
export const TagList: React.FC = () => {
const Toast = useToast();
// Editing / New state
- const [name, setName] = useState('');
- const [editingTag, setEditingTag] = useState | null>(null);
- const [deletingTag, setDeletingTag] = useState | null>(null);
+ const [name, setName] = useState("");
+ const [editingTag, setEditingTag] = useState | null>(null);
+ const [deletingTag, setDeletingTag] = useState | null>(null);
const { data, error } = StashService.useAllTags();
- const updateTag = StashService.useTagUpdate(getTagInput() as GQL.TagUpdateInput);
- const createTag = StashService.useTagCreate(getTagInput() as GQL.TagCreateInput);
- const deleteTag = StashService.useTagDestroy(getDeleteTagInput() as GQL.TagDestroyInput);
+ const updateTag = StashService.useTagUpdate(
+ getTagInput() as GQL.TagUpdateInput
+ );
+ const createTag = StashService.useTagCreate(
+ getTagInput() as GQL.TagCreateInput
+ );
+ const deleteTag = StashService.useTagDestroy(
+ getDeleteTagInput() as GQL.TagDestroyInput
+ );
function getTagInput() {
const tagInput: Partial = { name };
@@ -29,7 +39,7 @@ export const TagList: React.FC = () => {
function getDeleteTagInput() {
const tagInput: Partial = {};
if (deletingTag) {
- tagInput.id = deletingTag.id;
+ tagInput.id = deletingTag.id;
}
return tagInput;
}
@@ -49,11 +59,10 @@ export const TagList: React.FC = () => {
}
}
- async function onAutoTag(tag : GQL.TagDataFragment) {
- if (!tag)
- return;
+ async function onAutoTag(tag: GQL.TagDataFragment) {
+ if (!tag) return;
try {
- await StashService.queryMetadataAutoTag({ tags: [tag.id]});
+ await StashService.queryMetadataAutoTag({ tags: [tag.id] });
Toast.success({ content: "Started auto tagging" });
} catch (e) {
Toast.error(e);
@@ -75,55 +84,71 @@ export const TagList: React.FC = () => {
onHide={() => {}}
show={!!deletingTag}
icon="trash-alt"
- accept={{ onClick: onDelete, variant: 'danger', text: 'Delete' }}
+ accept={{ onClick: onDelete, variant: "danger", text: "Delete" }}
cancel={{ onClick: () => setDeletingTag(null) }}
>
- Are you sure you want to delete {deletingTag && deletingTag.name}?
+
+ Are you sure you want to delete {deletingTag && deletingTag.name}?
+
);
- if (!data?.allTags)
- return ;
- if (error)
- return {error.message}
;
+ if (!data?.allTags) return ;
+ if (error) return {error.message}
;
- const tagElements = data.allTags.map((tag) => {
+ const tagElements = data.allTags.map(tag => {
return (
<>
- {deleteAlert}
-
-
setEditingTag(tag)}>{tag.name}
-
-
onAutoTag(tag)}>Auto Tag
-
Scenes: {tag.scene_count}
-
- Markers: {tag.scene_marker_count}
-
-
Total: {(tag.scene_count || 0) + (tag.scene_marker_count || 0)}
-
setDeletingTag(tag)}>
-
+ {deleteAlert}
+
+
setEditingTag(tag)}>
+ {tag.name}
+
+ onAutoTag(tag)}>Auto Tag
+
+ Scenes: {tag.scene_count}
+
+
+ Markers: {tag.scene_marker_count}
+
+
+ Total: {(tag.scene_count || 0) + (tag.scene_marker_count || 0)}
+
+ setDeletingTag(tag)}>
+
+
+
-
>
);
});
return (
-
setEditingTag({})}>New Tag
+
setEditingTag({})}
+ >
+ New Tag
+
setEditingTag(null)}
- accept={{ onClick: onEdit, variant: 'danger', text: (editingTag?.id ? 'Update' : 'Create') }}
+ accept={{
+ onClick: onEdit,
+ variant: "danger",
+ text: editingTag?.id ? "Update" : "Create"
+ }}
>
Name
setName(newValue.target.value)}
- defaultValue={(editingTag && editingTag.name) || ''}
+ defaultValue={(editingTag && editingTag.name) || ""}
/>
diff --git a/ui/v2.5/src/components/Wall/WallItem.tsx b/ui/v2.5/src/components/Wall/WallItem.tsx
index 8c6272be6..872d6230b 100644
--- a/ui/v2.5/src/components/Wall/WallItem.tsx
+++ b/ui/v2.5/src/components/Wall/WallItem.tsx
@@ -11,18 +11,27 @@ interface IWallItemProps {
sceneMarker?: GQL.SceneMarkerDataFragment;
origin?: string;
onOverlay: (show: boolean) => void;
- clickHandler?: (item: GQL.SlimSceneDataFragment | GQL.SceneMarkerDataFragment) => void;
+ clickHandler?: (
+ item: GQL.SlimSceneDataFragment | GQL.SceneMarkerDataFragment
+ ) => void;
}
-export const WallItem: FunctionComponent
= (props: IWallItemProps) => {
+export const WallItem: FunctionComponent = (
+ props: IWallItemProps
+) => {
const [videoPath, setVideoPath] = useState(undefined);
const [previewPath, setPreviewPath] = useState("");
const [screenshotPath, setScreenshotPath] = useState("");
const [title, setTitle] = useState("");
const [tags, setTags] = useState([]);
const config = StashService.useConfiguration();
- const videoHoverHook = VideoHoverHook.useVideoHover({resetOnMouseLeave: true});
- const showTextContainer = !!config.data && !!config.data.configuration ? config.data.configuration.interface.wallShowTitle : true;
+ const videoHoverHook = VideoHoverHook.useVideoHover({
+ resetOnMouseLeave: true
+ });
+ const showTextContainer =
+ !!config.data && !!config.data.configuration
+ ? config.data.configuration.interface.wallShowTitle
+ : true;
function onMouseEnter() {
VideoHoverHook.onMouseEnter(videoHoverHook);
@@ -45,7 +54,9 @@ export const WallItem: FunctionComponent = (props: IWallItemProp
}
function onClick() {
- if (props.clickHandler === undefined) { return; }
+ if (props.clickHandler === undefined) {
+ return;
+ }
if (props.scene !== undefined) {
props.clickHandler(props.scene);
} else if (props.sceneMarker !== undefined) {
@@ -65,18 +76,28 @@ export const WallItem: FunctionComponent = (props: IWallItemProp
function onTransitionEnd(event: React.TransitionEvent) {
const target = event.currentTarget;
if (target.classList.contains("double-scale") && target.parentElement) {
- target.parentElement.style.zIndex = '10';
- } else if(target.parentElement) {
- target.parentElement.style.zIndex = '';
+ target.parentElement.style.zIndex = "10";
+ } else if (target.parentElement) {
+ target.parentElement.style.zIndex = "";
}
}
useEffect(() => {
if (props.sceneMarker) {
setPreviewPath(props.sceneMarker.preview);
- setTitle(`${props.sceneMarker!.title} - ${TextUtils.secondsToTimestamp(props.sceneMarker.seconds)}`);
- const thisTags = props.sceneMarker.tags.map((tag) => ({tag.name} ));
- thisTags.unshift({props.sceneMarker.primary_tag.name} );
+ setTitle(
+ `${props.sceneMarker!.title} - ${TextUtils.secondsToTimestamp(
+ props.sceneMarker.seconds
+ )}`
+ );
+ const thisTags = props.sceneMarker.tags.map(tag => (
+ {tag.name}
+ ));
+ thisTags.unshift(
+
+ {props.sceneMarker.primary_tag.name}
+
+ );
setTags(thisTags);
} else if (props.scene) {
setPreviewPath(props.scene.paths.webp || "");
@@ -93,9 +114,13 @@ export const WallItem: FunctionComponent = (props: IWallItemProp
}
const className = ["scene-wall-item-container"];
- if (videoHoverHook.isHovering.current) { className.push("double-scale"); }
+ if (videoHoverHook.isHovering.current) {
+ className.push("double-scale");
+ }
const style: React.CSSProperties = {};
- if (props.origin) { style.transformOrigin = props.origin; }
+ if (props.origin) {
+ style.transformOrigin = props.origin;
+ }
return (
= (props: IWallItemProp
-
previewNotFound()} />
- {showTextContainer ?
+
previewNotFound()}
+ />
+ {showTextContainer ? (
-
- {title}
-
+
{title}
{tags}
-
: ''
- }
+
+ ) : (
+ ""
+ )}
diff --git a/ui/v2.5/src/components/Wall/WallPanel.tsx b/ui/v2.5/src/components/Wall/WallPanel.tsx
index 5dbaec166..bed455ae0 100644
--- a/ui/v2.5/src/components/Wall/WallPanel.tsx
+++ b/ui/v2.5/src/components/Wall/WallPanel.tsx
@@ -6,10 +6,14 @@ import "./Wall.scss";
interface IWallPanelProps {
scenes?: GQL.SlimSceneDataFragment[];
sceneMarkers?: GQL.SceneMarkerDataFragment[];
- clickHandler?: (item: GQL.SlimSceneDataFragment | GQL.SceneMarkerDataFragment) => void;
+ clickHandler?: (
+ item: GQL.SlimSceneDataFragment | GQL.SceneMarkerDataFragment
+ ) => void;
}
-export const WallPanel: FunctionComponent
= (props: IWallPanelProps) => {
+export const WallPanel: FunctionComponent = (
+ props: IWallPanelProps
+) => {
const [showOverlay, setShowOverlay] = useState(false);
function onOverlay(show: boolean) {
@@ -22,25 +26,47 @@ export const WallPanel: FunctionComponent = (props: IWallPanelP
const endRemaining = total % rowSize;
// First row
- if (total === 1) { return "top"; }
- if (index === 0) { return "top left"; }
- if (index === rowSize - 1 || (total < rowSize && index === total - 1)) { return "top right"; }
- if (index < rowSize) { return "top"; }
+ if (total === 1) {
+ return "top";
+ }
+ if (index === 0) {
+ return "top left";
+ }
+ if (index === rowSize - 1 || (total < rowSize && index === total - 1)) {
+ return "top right";
+ }
+ if (index < rowSize) {
+ return "top";
+ }
// Bottom row
- if (isAtEnd && index === total - 1) { return "bottom right"; }
- if (isAtStart && index === total - rowSize) { return "bottom left"; }
- if (endRemaining !== 0 && index >= total - endRemaining) { return "bottom"; }
- if (endRemaining === 0 && index >= total - rowSize) { return "bottom"; }
+ if (isAtEnd && index === total - 1) {
+ return "bottom right";
+ }
+ if (isAtStart && index === total - rowSize) {
+ return "bottom left";
+ }
+ if (endRemaining !== 0 && index >= total - endRemaining) {
+ return "bottom";
+ }
+ if (endRemaining === 0 && index >= total - rowSize) {
+ return "bottom";
+ }
// Everything else
- if (isAtStart) { return "center left"; }
- if (isAtEnd) { return "center right"; }
+ if (isAtStart) {
+ return "center left";
+ }
+ if (isAtEnd) {
+ return "center right";
+ }
return "center";
}
function maybeRenderScenes() {
- if (props.scenes === undefined) { return; }
+ if (props.scenes === undefined) {
+ return;
+ }
return props.scenes.map((scene, index) => {
const origin = getOrigin(index, 5, props.scenes!.length);
return (
@@ -56,7 +82,9 @@ export const WallPanel: FunctionComponent = (props: IWallPanelP
}
function maybeRenderSceneMarkers() {
- if (props.sceneMarkers === undefined) { return; }
+ if (props.sceneMarkers === undefined) {
+ return;
+ }
return props.sceneMarkers.map((marker, index) => {
const origin = getOrigin(index, 5, props.sceneMarkers!.length);
return (
diff --git a/ui/v2.5/src/components/list/AddFilter.tsx b/ui/v2.5/src/components/list/AddFilter.tsx
index 12328b914..43369b516 100644
--- a/ui/v2.5/src/components/list/AddFilter.tsx
+++ b/ui/v2.5/src/components/list/AddFilter.tsx
@@ -1,9 +1,12 @@
import _ from "lodash";
import React, { useEffect, useRef, useState } from "react";
-import { Button, Form, Modal, OverlayTrigger, Tooltip } from 'react-bootstrap'
-import { Icon , FilterSelect } from 'src/components/Shared';
+import { Button, Form, Modal, OverlayTrigger, Tooltip } from "react-bootstrap";
+import { Icon, FilterSelect } from "src/components/Shared";
import { CriterionModifier } from "src/core/generated-graphql";
-import { Criterion, CriterionType } from "src/models/list-filter/criteria/criterion";
+import {
+ Criterion,
+ CriterionType
+} from "src/models/list-filter/criteria/criterion";
import { NoneCriterion } from "src/models/list-filter/criteria/none";
import { PerformersCriterion } from "src/models/list-filter/criteria/performers";
import { StudiosCriterion } from "src/models/list-filter/criteria/studios";
@@ -11,7 +14,6 @@ import { TagsCriterion } from "src/models/list-filter/criteria/tags";
import { makeCriteria } from "src/models/list-filter/criteria/utils";
import { ListFilterModel } from "src/models/list-filter/filter";
-
interface IAddFilterProps {
onAddCriterion: (criterion: Criterion, oldId?: string) => void;
onCancel: () => void;
@@ -19,17 +21,23 @@ interface IAddFilterProps {
editingCriterion?: Criterion;
}
-export const AddFilter: React.FC = (props: IAddFilterProps) => {
- const defaultValue= useRef();
+export const AddFilter: React.FC = (
+ props: IAddFilterProps
+) => {
+ const defaultValue = useRef();
const [isOpen, setIsOpen] = useState(false);
- const [criterion, setCriterion] = useState>(new NoneCriterion());
+ const [criterion, setCriterion] = useState>(
+ new NoneCriterion()
+ );
const valueStage = useRef(criterion.value);
// Configure if we are editing an existing criterion
useEffect(() => {
- if (!props.editingCriterion) { return; }
+ if (!props.editingCriterion) {
+ return;
+ }
setIsOpen(true);
setCriterion(props.editingCriterion);
}, [props.editingCriterion]);
@@ -40,7 +48,9 @@ export const AddFilter: React.FC = (props: IAddFilterProps) =>
setCriterion(newCriterion);
}
- function onChangedModifierSelect(event: React.ChangeEvent) {
+ function onChangedModifierSelect(
+ event: React.ChangeEvent
+ ) {
const newCriterion = _.cloneDeep(criterion);
newCriterion.modifier = event.target.value as any;
setCriterion(newCriterion);
@@ -65,7 +75,10 @@ export const AddFilter: React.FC = (props: IAddFilterProps) =>
function onAddFilter() {
if (!Array.isArray(criterion.value) && defaultValue.current) {
const value = defaultValue.current;
- if (criterion.options && (value === undefined || value === "" || typeof value === "number")) {
+ if (
+ criterion.options &&
+ (value === undefined || value === "" || typeof value === "number")
+ ) {
criterion.value = criterion.options[0];
} else if (typeof value === "number" && value === undefined) {
criterion.value = 0;
@@ -73,7 +86,9 @@ export const AddFilter: React.FC = (props: IAddFilterProps) =>
criterion.value = "";
}
}
- const oldId = props.editingCriterion ? props.editingCriterion.getId() : undefined;
+ const oldId = props.editingCriterion
+ ? props.editingCriterion.getId()
+ : undefined;
props.onAddCriterion(criterion, oldId);
onToggle();
}
@@ -87,10 +102,14 @@ export const AddFilter: React.FC = (props: IAddFilterProps) =>
}
const maybeRenderFilterPopoverContents = () => {
- if (criterion.type === "none") { return; }
+ if (criterion.type === "none") {
+ return;
+ }
function renderModifier() {
- if (criterion.modifierOptions.length === 0) { return; }
+ if (criterion.modifierOptions.length === 0) {
+ return;
+ }
return (
= (props: IAddFilterProps) =>
onChange={onChangedModifierSelect}
value={criterion.modifier}
>
- { criterion.modifierOptions.map(c => (
- {c.label}
+ {criterion.modifierOptions.map(c => (
+ {c.label}
))}
@@ -108,7 +127,10 @@ export const AddFilter: React.FC = (props: IAddFilterProps) =>
function renderSelect() {
// Hide the value select if the modifier is "IsNull" or "NotNull"
- if (criterion.modifier === CriterionModifier.IsNull || criterion.modifier === CriterionModifier.NotNull) {
+ if (
+ criterion.modifier === CriterionModifier.IsNull ||
+ criterion.modifier === CriterionModifier.NotNull
+ ) {
return;
}
@@ -127,61 +149,58 @@ export const AddFilter: React.FC = (props: IAddFilterProps) =>
return (
{
- criterion.value = items.map(i => ({id: i.id, label: i.name!})) }
- }
+ onSelect={items => {
+ criterion.value = items.map(i => ({ id: i.id, label: i.name! }));
+ }}
initialIds={criterion.value.map((labeled: any) => labeled.id)}
/>
);
}
- if (criterion.options) {
- defaultValue.current = criterion.value;
- return (
-
- { criterion.options.map(c => (
- {c}
- ))}
-
- );
- }
- return (
-
- )
-
-
+ if (criterion.options) {
+ defaultValue.current = criterion.value;
+ return (
+
+ {criterion.options.map(c => (
+ {c}
+ ))}
+
+ );
+ }
+ return (
+
+ );
}
return (
<>
-
- {renderModifier()}
-
-
- {renderSelect()}
-
+ {renderModifier()}
+ {renderSelect()}
>
);
};
function maybeRenderFilterSelect() {
- if (props.editingCriterion) { return; }
+ if (props.editingCriterion) {
+ return;
+ }
return (
Filter
- { props.filter.criterionOptions.map(c => (
- {c.label}
+ value={criterion.type}
+ >
+ {props.filter.criterionOptions.map(c => (
+ {c.label}
))}
@@ -195,17 +214,12 @@ export const AddFilter: React.FC = (props: IAddFilterProps) =>
placement="top"
overlay={Filter }
>
- onToggle()}
- active={isOpen}
- >
+ onToggle()} active={isOpen}>
- onToggle()}>
+ onToggle()}>
{title}
@@ -214,7 +228,9 @@ export const AddFilter: React.FC = (props: IAddFilterProps) =>
- {title}
+
+ {title}
+
>
diff --git a/ui/v2.5/src/components/list/ListFilter.tsx b/ui/v2.5/src/components/list/ListFilter.tsx
index d7b140d10..e816bcc60 100644
--- a/ui/v2.5/src/components/list/ListFilter.tsx
+++ b/ui/v2.5/src/components/list/ListFilter.tsx
@@ -1,8 +1,16 @@
import { debounce } from "lodash";
import React, { SyntheticEvent, useCallback, useState } from "react";
-import { Badge, Button, ButtonGroup, Dropdown, Form, OverlayTrigger, Tooltip } from 'react-bootstrap';
+import {
+ Badge,
+ Button,
+ ButtonGroup,
+ Dropdown,
+ Form,
+ OverlayTrigger,
+ Tooltip
+} from "react-bootstrap";
-import { Icon } from 'src/components/Shared';
+import { Icon } from "src/components/Shared";
import { Criterion } from "src/models/list-filter/criteria/criterion";
import { ListFilterModel } from "src/models/list-filter/filter";
import { DisplayMode } from "src/models/list-filter/types";
@@ -31,14 +39,19 @@ interface IListFilterProps {
const PAGE_SIZE_OPTIONS = ["20", "40", "60", "120"];
-export const ListFilter: React.FC = (props: IListFilterProps) => {
+export const ListFilter: React.FC = (
+ props: IListFilterProps
+) => {
const searchCallback = useCallback(
debounce((event: any) => {
props.onChangeQuery(event.target.value);
- }, 500), [props.onChangeQuery]
+ }, 500),
+ [props.onChangeQuery]
);
- const [editingCriterion, setEditingCriterion] = useState(undefined);
+ const [editingCriterion, setEditingCriterion] = useState<
+ Criterion | undefined
+ >(undefined);
function onChangePageSize(event: SyntheticEvent) {
const val = event!.currentTarget!.value;
@@ -76,39 +89,55 @@ export const ListFilter: React.FC = (props: IListFilterProps)
let removedCriterionId = "";
function onRemoveCriterionTag(criterion?: Criterion) {
- if (!criterion) { return; }
+ if (!criterion) {
+ return;
+ }
setEditingCriterion(undefined);
removedCriterionId = criterion.getId();
props.onRemoveCriterion(criterion);
}
function onClickCriterionTag(criterion?: Criterion) {
- if (!criterion || removedCriterionId !== "") { return; }
+ if (!criterion || removedCriterionId !== "") {
+ return;
+ }
setEditingCriterion(criterion);
}
function renderSortByOptions() {
- return props.filter.sortByOptions.map((option) => (
- {option}
+ return props.filter.sortByOptions.map(option => (
+
+ {option}
+
));
}
function renderDisplayModeOptions() {
function getIcon(option: DisplayMode) {
switch (option) {
- case DisplayMode.Grid: return "th-large";
- case DisplayMode.List: return "list";
- case DisplayMode.Wall: return "square";
+ case DisplayMode.Grid:
+ return "th-large";
+ case DisplayMode.List:
+ return "list";
+ case DisplayMode.Wall:
+ return "square";
}
}
function getLabel(option: DisplayMode) {
switch (option) {
- case DisplayMode.Grid: return "Grid";
- case DisplayMode.List: return "List";
- case DisplayMode.Wall: return "Wall";
+ case DisplayMode.Grid:
+ return "Grid";
+ case DisplayMode.List:
+ return "List";
+ case DisplayMode.Wall:
+ return "Wall";
}
}
- return props.filter.displayModeOptions.map((option) => (
- {getLabel(option)}}>
+ return props.filter.displayModeOptions.map(option => (
+ {getLabel(option)}
+ }
+ >
= (props: IListFilterProps)
}
function renderFilterTags() {
- return props.filter.criteria.map((criterion) => (
+ return props.filter.criteria.map(criterion => (
= (props: IListFilterProps)
function renderSelectAll() {
if (props.onSelectAll) {
- return onSelectAll()}>Select All ;
+ return (
+ onSelectAll()}>Select All
+ );
}
}
function renderSelectNone() {
if (props.onSelectNone) {
- return onSelectNone()}>Select None ;
+ return (
+ onSelectNone()}>
+ Select None
+
+ );
}
}
function renderMore() {
- const options = [
- renderSelectAll(),
- renderSelectNone()
- ];
+ const options = [renderSelectAll(), renderSelectNone()];
if (props.otherOperations) {
- props.otherOperations.forEach((o) => {
- options.push({o.text} );
+ props.otherOperations.forEach(o => {
+ options.push(
+ {o.text}
+ );
});
}
@@ -179,15 +213,13 @@ export const ListFilter: React.FC = (props: IListFilterProps)
-
- {options}
-
+ {options}
);
}
}
- function onChangeZoom(v : number) {
+ function onChangeZoom(v: number) {
if (props.onChangeZoom) {
props.onChangeZoom(v);
}
@@ -201,9 +233,11 @@ export const ListFilter: React.FC = (props: IListFilterProps)
type="range"
min={0}
max={3}
- onChange={(event: any) => onChangeZoom(Number.parseInt(event.target.value, 10))}
+ onChange={(event: any) =>
+ onChangeZoom(Number.parseInt(event.target.value, 10))
+ }
/>
-
+
);
}
}
@@ -224,23 +258,35 @@ export const ListFilter: React.FC = (props: IListFilterProps)
value={props.filter.itemsPerPage.toString()}
className="filter-item"
>
- { PAGE_SIZE_OPTIONS.map(s => {s} ) }
+ {PAGE_SIZE_OPTIONS.map(s => (
+ {s}
+ ))}
-
- {renderSortByOptions()}
-
+ {renderSortByOptions()}
- {props.filter.sortDirection === "asc" ? "Ascending" : "Descending"}
- }>
+
+ {props.filter.sortDirection === "asc"
+ ? "Ascending"
+ : "Descending"}
+
+ }
+ >
-
+
@@ -258,11 +304,15 @@ export const ListFilter: React.FC = (props: IListFilterProps)
{maybeRenderZoom()}
-
- {renderMore()}
-
+ {renderMore()}
-
+
{renderFilterTags()}
>
diff --git a/ui/v2.5/src/components/list/Pagination.tsx b/ui/v2.5/src/components/list/Pagination.tsx
index e58a91763..2cf03bef2 100644
--- a/ui/v2.5/src/components/list/Pagination.tsx
+++ b/ui/v2.5/src/components/list/Pagination.tsx
@@ -1,5 +1,5 @@
import React from "react";
-import { Button, ButtonGroup } from 'react-bootstrap';
+import { Button, ButtonGroup } from "react-bootstrap";
interface IPaginationProps {
itemsPerPage: number;
@@ -8,7 +8,12 @@ interface IPaginationProps {
onChangePage: (page: number) => void;
}
-export const Pagination: React.FC
= ({ itemsPerPage, currentPage, totalItems, onChangePage }) => {
+export const Pagination: React.FC = ({
+ itemsPerPage,
+ currentPage,
+ totalItems,
+ onChangePage
+}) => {
const totalPages = Math.ceil(totalItems / itemsPerPage);
let startPage: number;
@@ -28,35 +33,44 @@ export const Pagination: React.FC = ({ itemsPerPage, currentPa
endPage = currentPage + 4;
}
- const pages = [...Array((endPage + 1) - startPage).keys()].map((i) => startPage + i);
+ const pages = [...Array(endPage + 1 - startPage).keys()].map(
+ i => startPage + i
+ );
const pageButtons = pages.map((page: number) => (
onChangePage(page)}
- >{page}
+ >
+ {page}
+
));
return (
- onChangePage(1)}
- >First
+ onChangePage(1)}>
+ First
+
onChangePage(currentPage - 1)}
- >Previous
+ >
+ Previous
+
{pageButtons}
onChangePage(currentPage + 1)}
- >Next
+ >
+ Next
+
onChangePage(totalPages)}
- >Last
+ >
+ Last
+
);
-}
+};
diff --git a/ui/v2.5/src/components/performers/PerformerCard.tsx b/ui/v2.5/src/components/performers/PerformerCard.tsx
index 21227e064..bff4f8548 100644
--- a/ui/v2.5/src/components/performers/PerformerCard.tsx
+++ b/ui/v2.5/src/components/performers/PerformerCard.tsx
@@ -1,5 +1,5 @@
import React from "react";
-import { Card } from 'react-bootstrap';
+import { Card } from "react-bootstrap";
import { Link } from "react-router-dom";
import * as GQL from "src/core/generated-graphql";
import { NavUtils, TextUtils } from "src/utils";
@@ -9,17 +9,19 @@ interface IPerformerCardProps {
ageFromDate?: string;
}
-export const PerformerCard: React.FC = (props: IPerformerCardProps) => {
+export const PerformerCard: React.FC = (
+ props: IPerformerCardProps
+) => {
const age = TextUtils.age(props.performer.birthdate, props.ageFromDate);
- const ageString = `${age} years old${props.ageFromDate ? " in this scene." : "."}`;
+ const ageString = `${age} years old${
+ props.ageFromDate ? " in this scene." : "."
+ }`;
function maybeRenderFavoriteBanner() {
- if (props.performer.favorite === false) { return; }
- return (
-
- FAVORITE
-
- );
+ if (props.performer.favorite === false) {
+ return;
+ }
+ return FAVORITE
;
}
return (
@@ -27,16 +29,19 @@ export const PerformerCard: React.FC = (props: IPerformerCa
{maybeRenderFavoriteBanner()}
-
- {props.performer.name}
-
- {age !== 0 ?
{ageString}
: ''}
-
Stars in {props.performer.scene_count} scenes.
+ {props.performer.name}
+ {age !== 0 ? {ageString}
: ""}
+
+ Stars in {props.performer.scene_count}{" "}
+
+ scenes
+
+ .
diff --git a/ui/v2.5/src/components/performers/PerformerDetails/Performer.tsx b/ui/v2.5/src/components/performers/PerformerDetails/Performer.tsx
index 1b20a6eda..6bed0657c 100644
--- a/ui/v2.5/src/components/performers/PerformerDetails/Performer.tsx
+++ b/ui/v2.5/src/components/performers/PerformerDetails/Performer.tsx
@@ -1,24 +1,33 @@
/* eslint-disable react/no-this-in-sfc */
import React, { useEffect, useState } from "react";
-import { Button, Form, Spinner, Table } from 'react-bootstrap';
-import { useParams, useHistory } from 'react-router-dom';
+import { Button, Form, Spinner, Table } from "react-bootstrap";
+import { useParams, useHistory } from "react-router-dom";
import * as GQL from "src/core/generated-graphql";
import { StashService } from "src/core/StashService";
-import { DetailsEditNavbar, Icon, Modal, ScrapePerformerSuggest } from "src/components/Shared";
-import { ImageUtils, TableUtils } from 'src/utils'
-import { useToast } from 'src/hooks';
+import {
+ DetailsEditNavbar,
+ Icon,
+ Modal,
+ ScrapePerformerSuggest
+} from "src/components/Shared";
+import { ImageUtils, TableUtils } from "src/utils";
+import { useToast } from "src/hooks";
export const Performer: React.FC = () => {
const Toast = useToast();
const history = useHistory();
- const { id = 'new' } = useParams();
+ const { id = "new" } = useParams();
const isNew = id === "new";
// Editing state
const [isEditing, setIsEditing] = useState(isNew);
- const [isDisplayingScraperDialog, setIsDisplayingScraperDialog] = useState(undefined);
- const [scrapePerformerDetails, setScrapePerformerDetails] = useState(undefined);
+ const [isDisplayingScraperDialog, setIsDisplayingScraperDialog] = useState<
+ GQL.ListPerformerScrapersListPerformerScrapers | undefined
+ >(undefined);
+ const [scrapePerformerDetails, setScrapePerformerDetails] = useState<
+ GQL.ScrapePerformerListScrapePerformerList | undefined
+ >(undefined);
// Editing performer state
const [image, setImage] = useState(undefined);
@@ -30,9 +39,13 @@ export const Performer: React.FC = () => {
const [country, setCountry] = useState(undefined);
const [eyeColor, setEyeColor] = useState(undefined);
const [height, setHeight] = useState(undefined);
- const [measurements, setMeasurements] = useState(undefined);
+ const [measurements, setMeasurements] = useState(
+ undefined
+ );
const [fakeTits, setFakeTits] = useState(undefined);
- const [careerLength, setCareerLength] = useState(undefined);
+ const [careerLength, setCareerLength] = useState(
+ undefined
+ );
const [tattoos, setTattoos] = useState(undefined);
const [piercings, setPiercings] = useState(undefined);
const [url, setUrl] = useState(undefined);
@@ -40,21 +53,35 @@ export const Performer: React.FC = () => {
const [instagram, setInstagram] = useState(undefined);
// Performer state
- const [performer, setPerformer] = useState>({});
- const [imagePreview, setImagePreview] = useState(undefined);
+ const [performer, setPerformer] = useState<
+ Partial
+ >({});
+ const [imagePreview, setImagePreview] = useState(
+ undefined
+ );
// Network state
const [isLoading, setIsLoading] = useState(false);
const Scrapers = StashService.useListPerformerScrapers();
- const [queryableScrapers, setQueryableScrapers] = useState([]);
+ const [queryableScrapers, setQueryableScrapers] = useState<
+ GQL.ListPerformerScrapersListPerformerScrapers[]
+ >([]);
const { data, error } = StashService.useFindPerformer(id);
- const updatePerformer = StashService.usePerformerUpdate(getPerformerInput() as GQL.PerformerUpdateInput);
- const createPerformer = StashService.usePerformerCreate(getPerformerInput() as GQL.PerformerCreateInput);
- const deletePerformer = StashService.usePerformerDestroy(getPerformerInput() as GQL.PerformerDestroyInput);
+ const updatePerformer = StashService.usePerformerUpdate(
+ getPerformerInput() as GQL.PerformerUpdateInput
+ );
+ const createPerformer = StashService.usePerformerCreate(
+ getPerformerInput() as GQL.PerformerCreateInput
+ );
+ const deletePerformer = StashService.usePerformerDestroy(
+ getPerformerInput() as GQL.PerformerDestroyInput
+ );
- function updatePerformerEditState(state: Partial) {
+ function updatePerformerEditState(
+ state: Partial
+ ) {
if ((state as GQL.PerformerDataFragment).favorite !== undefined) {
setFavorite((state as GQL.PerformerDataFragment).favorite);
}
@@ -77,8 +104,7 @@ export const Performer: React.FC = () => {
useEffect(() => {
setIsLoading(false);
- if(data?.findPerformer)
- setPerformer(data.findPerformer);
+ if (data?.findPerformer) setPerformer(data.findPerformer);
}, [data]);
useEffect(() => {
@@ -96,25 +122,28 @@ export const Performer: React.FC = () => {
ImageUtils.usePasteImage(onImageLoad);
useEffect(() => {
- let newQueryableScrapers : GQL.ListPerformerScrapersListPerformerScrapers[] = [];
+ let newQueryableScrapers: GQL.ListPerformerScrapersListPerformerScrapers[] = [];
if (!!Scrapers.data && Scrapers.data.listPerformerScrapers) {
- newQueryableScrapers = Scrapers.data.listPerformerScrapers.filter((s) => {
- return s.performer && s.performer.supported_scrapes.includes(GQL.ScrapeType.Name);
+ newQueryableScrapers = Scrapers.data.listPerformerScrapers.filter(s => {
+ return (
+ s.performer &&
+ s.performer.supported_scrapes.includes(GQL.ScrapeType.Name)
+ );
});
}
setQueryableScrapers(newQueryableScrapers);
-
}, [Scrapers.data]);
if ((!isNew && !isEditing && !data?.findPerformer) || isLoading)
return ;
- if (error)
- return {error.message}
;
+ if (error) return {error.message}
;
function getPerformerInput() {
- const performerInput: Partial = {
+ const performerInput: Partial<
+ GQL.PerformerCreateInput | GQL.PerformerUpdateInput
+ > = {
name,
aliases,
favorite,
@@ -131,7 +160,7 @@ export const Performer: React.FC = () => {
url,
twitter,
instagram,
- image,
+ image
};
if (!isNew) {
@@ -167,7 +196,7 @@ export const Performer: React.FC = () => {
setIsLoading(false);
// redirect to performers page
- history.push('/performers');
+ history.push("/performers");
}
async function onAutoTag() {
@@ -175,7 +204,7 @@ export const Performer: React.FC = () => {
return;
}
try {
- await StashService.queryMetadataAutoTag({ performers: [performer.id]});
+ await StashService.queryMetadataAutoTag({ performers: [performer.id] });
Toast.success({ content: "Started auto tagging" });
} catch (e) {
Toast.error(e);
@@ -186,13 +215,14 @@ export const Performer: React.FC = () => {
ImageUtils.onImageChange(event, onImageLoad);
}
- function onDisplayFreeOnesDialog(scraper: GQL.ListPerformerScrapersListPerformerScrapers) {
+ function onDisplayFreeOnesDialog(
+ scraper: GQL.ListPerformerScrapersListPerformerScrapers
+ ) {
setIsDisplayingScraperDialog(scraper);
}
function getQueryScraperPerformerInput() {
- if (!scrapePerformerDetails)
- return {};
+ if (!scrapePerformerDetails) return {};
const { __typename, ...ret } = scrapePerformerDetails;
return ret;
@@ -202,11 +232,12 @@ export const Performer: React.FC = () => {
setIsDisplayingScraperDialog(undefined);
setIsLoading(true);
try {
- if (!scrapePerformerDetails || !isDisplayingScraperDialog)
- return;
- const result = await StashService.queryScrapePerformer(isDisplayingScraperDialog.id, getQueryScraperPerformerInput());
- if (!result?.data?.scrapePerformer)
- return;
+ if (!scrapePerformerDetails || !isDisplayingScraperDialog) return;
+ const result = await StashService.queryScrapePerformer(
+ isDisplayingScraperDialog.id,
+ getQueryScraperPerformerInput()
+ );
+ if (!result?.data?.scrapePerformer) return;
updatePerformerEditState(result.data.scrapePerformer);
} catch (e) {
Toast.error(e);
@@ -215,12 +246,13 @@ export const Performer: React.FC = () => {
}
async function onScrapePerformerURL() {
- if (!url)
- return;
+ if (!url) return;
setIsLoading(true);
try {
const result = await StashService.queryScrapePerformerURL(url);
- if (!result.data || !result.data.scrapePerformerURL) { return; }
+ if (!result.data || !result.data.scrapePerformerURL) {
+ return;
+ }
updatePerformerEditState(result.data.scrapePerformerURL);
} catch (e) {
Toast.error(e);
@@ -235,7 +267,7 @@ export const Performer: React.FC = () => {
value: ethnicity,
isEditing,
onChange: (value: string) => setEthnicity(value),
- selectOptions: ["white", "black", "asian", "hispanic"],
+ selectOptions: ["white", "black", "asian", "hispanic"]
});
}
@@ -250,8 +282,10 @@ export const Performer: React.FC = () => {
setScrapePerformerDetails(query)}
+ scraperId={
+ isDisplayingScraperDialog ? isDisplayingScraperDialog.id : ""
+ }
+ onSelectPerformer={query => setScrapePerformerDetails(query)}
/>
@@ -259,9 +293,12 @@ export const Performer: React.FC = () => {
}
function urlScrapable(scrapedUrl: string) {
- return !!scrapedUrl && (Scrapers?.data?.listPerformerScrapers ?? []).some(s => (
- (s?.performer?.urls ?? []).some(u => scrapedUrl.includes(u))
- ));
+ return (
+ !!scrapedUrl &&
+ (Scrapers?.data?.listPerformerScrapers ?? []).some(s =>
+ (s?.performer?.urls ?? []).some(u => scrapedUrl.includes(u))
+ )
+ );
}
function maybeRenderScrapeButton() {
@@ -269,12 +306,10 @@ export const Performer: React.FC = () => {
return undefined;
}
return (
- onScrapePerformerURL()}>
+ onScrapePerformerURL()}>
- )
+ );
}
function renderURLField() {
@@ -290,7 +325,9 @@ export const Performer: React.FC = () => {
readOnly={!isEditing}
plaintext={!isEditing}
placeholder="URL"
- onChange={(event: React.FormEvent) => setUrl(event.currentTarget.value) }
+ onChange={(event: React.FormEvent) =>
+ setUrl(event.currentTarget.value)
+ }
/>
@@ -309,7 +346,10 @@ export const Performer: React.FC = () => {
performer={performer}
isNew={isNew}
isEditing={isEditing}
- onToggleEdit={() => { setIsEditing(!isEditing); updatePerformerEditState(performer); }}
+ onToggleEdit={() => {
+ setIsEditing(!isEditing);
+ updatePerformerEditState(performer);
+ }}
onSave={onSave}
onDelete={onDelete}
onImageChange={onImageChangeHandler}
@@ -319,12 +359,12 @@ export const Performer: React.FC = () => {
/>
setName(event.target.value)}
- />
+ readOnly={!isEditing}
+ plaintext={!isEditing}
+ defaultValue={name}
+ placeholder="Name"
+ onChange={(event: any) => setName(event.target.value)}
+ />
@@ -334,12 +374,14 @@ export const Performer: React.FC = () => {
readOnly={!isEditing}
plaintext={!isEditing}
placeholder="Aliases"
- onChange={(event: React.FormEvent) => setAliases(event.currentTarget.value) }
+ onChange={(event: React.FormEvent) =>
+ setAliases(event.currentTarget.value)
+ }
/>
- Favorite:
+ Favorite:
{
-
);
};
-
diff --git a/ui/v2.5/src/components/scenes/SceneMarkerList.tsx b/ui/v2.5/src/components/scenes/SceneMarkerList.tsx
index 3ca359505..02dfa5b93 100644
--- a/ui/v2.5/src/components/scenes/SceneMarkerList.tsx
+++ b/ui/v2.5/src/components/scenes/SceneMarkerList.tsx
@@ -1,8 +1,11 @@
import _ from "lodash";
import React from "react";
import { QueryHookResult } from "react-apollo-hooks";
-import { useHistory } from 'react-router-dom';
-import { FindSceneMarkersQuery, FindSceneMarkersVariables } from "src/core/generated-graphql";
+import { useHistory } from "react-router-dom";
+import {
+ FindSceneMarkersQuery,
+ FindSceneMarkersVariables
+} from "src/core/generated-graphql";
import { StashService } from "src/core/StashService";
import { NavUtils } from "src/utils";
import { useSceneMarkersList } from "src/hooks";
@@ -12,29 +15,41 @@ import { WallPanel } from "../Wall/WallPanel";
export const SceneMarkerList: React.FC = () => {
const history = useHistory();
- const otherOperations = [{
- text: "Play Random",
- onClick: playRandom
- }];
+ const otherOperations = [
+ {
+ text: "Play Random",
+ onClick: playRandom
+ }
+ ];
const listData = useSceneMarkersList({
otherOperations,
- renderContent,
+ renderContent
});
- async function playRandom(result: QueryHookResult