mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
Changes
This commit is contained in:
@@ -18,7 +18,7 @@
|
|||||||
"lodash": "4.17.13",
|
"lodash": "4.17.13",
|
||||||
"node-sass": "4.12.0",
|
"node-sass": "4.12.0",
|
||||||
"query-string": "6.5.0",
|
"query-string": "6.5.0",
|
||||||
"react": "16.12.0",
|
"react": "~16.12.0",
|
||||||
"react-apollo": "2.5.6",
|
"react-apollo": "2.5.6",
|
||||||
"react-apollo-hooks": "0.4.5",
|
"react-apollo-hooks": "0.4.5",
|
||||||
"react-bootstrap": "^1.0.0-beta.16",
|
"react-bootstrap": "^1.0.0-beta.16",
|
||||||
@@ -68,6 +68,6 @@
|
|||||||
"graphql-codegen-typescript-client": "0.18.2",
|
"graphql-codegen-typescript-client": "0.18.2",
|
||||||
"graphql-codegen-typescript-common": "0.18.2",
|
"graphql-codegen-typescript-common": "0.18.2",
|
||||||
"graphql-codegen-typescript-react-apollo": "0.18.2",
|
"graphql-codegen-typescript-react-apollo": "0.18.2",
|
||||||
"typescript": "3.4.5"
|
"typescript": "~3.7.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import _ from "lodash";
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Spinner } from 'react-bootstrap';
|
import { Spinner } from 'react-bootstrap';
|
||||||
import * as GQL from "../../core/generated-graphql";
|
import * as GQL from "../../core/generated-graphql";
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import _ from "lodash";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Table } from 'react-bootstrap';
|
import { Table } from 'react-bootstrap';
|
||||||
import { QueryHookResult } from "react-apollo-hooks";
|
import { QueryHookResult } from "react-apollo-hooks";
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import _ from "lodash";
|
|
||||||
import React, { FunctionComponent, useState } from "react";
|
import React, { FunctionComponent, useState } from "react";
|
||||||
import Lightbox from "react-images";
|
import Lightbox from "react-images";
|
||||||
import Gallery from "react-photo-gallery";
|
import Gallery from "react-photo-gallery";
|
||||||
|
|||||||
@@ -1,19 +1,8 @@
|
|||||||
import {
|
import React from "react";
|
||||||
H1,
|
import { Table, Spinner } from 'react-bootstrap';
|
||||||
H4,
|
|
||||||
H6,
|
|
||||||
HTMLTable,
|
|
||||||
Spinner,
|
|
||||||
Tag,
|
|
||||||
} from "@blueprintjs/core";
|
|
||||||
import React, { FunctionComponent } from "react";
|
|
||||||
import * as GQL from "../../core/generated-graphql";
|
|
||||||
import { TextUtils } from "../../utils/text";
|
|
||||||
import { StashService } from "../../core/StashService";
|
import { StashService } from "../../core/StashService";
|
||||||
|
|
||||||
interface IProps {}
|
export const SettingsAboutPanel: React.FC = () => {
|
||||||
|
|
||||||
export const SettingsAboutPanel: FunctionComponent<IProps> = (props: IProps) => {
|
|
||||||
const { data, error, loading } = StashService.useVersion();
|
const { data, error, loading } = StashService.useVersion();
|
||||||
|
|
||||||
function maybeRenderTag() {
|
function maybeRenderTag() {
|
||||||
@@ -30,7 +19,7 @@ export const SettingsAboutPanel: FunctionComponent<IProps> = (props: IProps) =>
|
|||||||
if (!data || !data.version) { return; }
|
if (!data || !data.version) { return; }
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<HTMLTable>
|
<Table>
|
||||||
<tbody>
|
<tbody>
|
||||||
{maybeRenderTag()}
|
{maybeRenderTag()}
|
||||||
<tr>
|
<tr>
|
||||||
@@ -42,14 +31,14 @@ export const SettingsAboutPanel: FunctionComponent<IProps> = (props: IProps) =>
|
|||||||
<td>{data.version.build_time}</td>
|
<td>{data.version.build_time}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</HTMLTable>
|
</Table>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<H4>About</H4>
|
<h4>About</h4>
|
||||||
{!data || loading ? <Spinner size={Spinner.SIZE_LARGE} /> : undefined}
|
{!data || loading ? <Spinner animation="border" variant="light" /> : undefined}
|
||||||
{!!error ? <span>error.message</span> : undefined}
|
{!!error ? <span>error.message</span> : undefined}
|
||||||
{renderVersion()}
|
{renderVersion()}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -3,25 +3,19 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
Divider,
|
Divider,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
H1,
|
|
||||||
H4,
|
|
||||||
H6,
|
|
||||||
InputGroup,
|
InputGroup,
|
||||||
Spinner,
|
Spinner,
|
||||||
Tag,
|
|
||||||
Checkbox,
|
Checkbox,
|
||||||
HTMLSelect,
|
HTMLSelect,
|
||||||
} from "@blueprintjs/core";
|
} from "@blueprintjs/core";
|
||||||
import React, { FunctionComponent, useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import * as GQL from "../../core/generated-graphql";
|
import * as GQL from "../../core/generated-graphql";
|
||||||
import { StashService } from "../../core/StashService";
|
import { StashService } from "../../core/StashService";
|
||||||
import { ErrorUtils } from "../../utils/errors";
|
import { ErrorUtils } from "../../utils/errors";
|
||||||
import { ToastUtils } from "../../utils/toasts";
|
import { ToastUtils } from "../../utils/toasts";
|
||||||
import { FolderSelect } from "../Shared/FolderSelect/FolderSelect";
|
import { FolderSelect } from "../Shared/FolderSelect/FolderSelect";
|
||||||
|
|
||||||
interface IProps {}
|
export const SettingsConfigurationPanel: React.FC = () => {
|
||||||
|
|
||||||
export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IProps) => {
|
|
||||||
// Editing config state
|
// Editing config state
|
||||||
const [stashes, setStashes] = useState<string[]>([]);
|
const [stashes, setStashes] = useState<string[]>([]);
|
||||||
const [databasePath, setDatabasePath] = useState<string | undefined>(undefined);
|
const [databasePath, setDatabasePath] = useState<string | undefined>(undefined);
|
||||||
@@ -51,7 +45,6 @@ export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IPr
|
|||||||
logLevel,
|
logLevel,
|
||||||
logAccess,
|
logAccess,
|
||||||
excludes,
|
excludes,
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -86,7 +79,7 @@ export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
function excludeRemoveRegex(idx: number) {
|
function excludeRemoveRegex(idx: number) {
|
||||||
const newExcludes = excludes.filter((regex, i) => i!== idx );
|
const newExcludes = excludes.filter((_regex, i) => i !== idx );
|
||||||
|
|
||||||
setExcludes(newExcludes);
|
setExcludes(newExcludes);
|
||||||
}
|
}
|
||||||
@@ -148,7 +141,7 @@ export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IPr
|
|||||||
<>
|
<>
|
||||||
{!!error ? <h1>{error.message}</h1> : undefined}
|
{!!error ? <h1>{error.message}</h1> : undefined}
|
||||||
{(!data || !data.configuration || loading) ? <Spinner size={Spinner.SIZE_LARGE} /> : undefined}
|
{(!data || !data.configuration || loading) ? <Spinner size={Spinner.SIZE_LARGE} /> : undefined}
|
||||||
<H4>Library</H4>
|
<h4>Library</h4>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
@@ -208,7 +201,7 @@ export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IPr
|
|||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<H4>Video</H4>
|
<h4>Video</h4>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label="Maximum transcode size"
|
label="Maximum transcode size"
|
||||||
helperText="Maximum size for generated transcodes"
|
helperText="Maximum size for generated transcodes"
|
||||||
@@ -233,7 +226,7 @@ export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IPr
|
|||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<H4>Authentication</H4>
|
<h4>Authentication</h4>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label="Username"
|
label="Username"
|
||||||
helperText="Username to access Stash. Leave blank to disable user authentication"
|
helperText="Username to access Stash. Leave blank to disable user authentication"
|
||||||
@@ -249,7 +242,7 @@ export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IPr
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
<H4>Logging</H4>
|
<h4>Logging</h4>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label="Log file"
|
label="Log file"
|
||||||
helperText="Path to the file to output logging to. Blank to disable file logging. Requires restart."
|
helperText="Path to the file to output logging to. Blank to disable file logging. Requires restart."
|
||||||
|
|||||||
@@ -3,12 +3,10 @@ import {
|
|||||||
Checkbox,
|
Checkbox,
|
||||||
Divider,
|
Divider,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
H4,
|
|
||||||
Spinner,
|
Spinner,
|
||||||
TextArea,
|
TextArea,
|
||||||
NumericInput
|
NumericInput
|
||||||
} from "@blueprintjs/core";
|
} from "@blueprintjs/core";
|
||||||
import _ from "lodash";
|
|
||||||
import React, { FunctionComponent, useEffect, useState } from "react";
|
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||||
import { StashService } from "../../core/StashService";
|
import { StashService } from "../../core/StashService";
|
||||||
import { ErrorUtils } from "../../utils/errors";
|
import { ErrorUtils } from "../../utils/errors";
|
||||||
@@ -64,7 +62,7 @@ export const SettingsInterfacePanel: FunctionComponent<IProps> = () => {
|
|||||||
<>
|
<>
|
||||||
{!!config.error ? <h1>{config.error.message}</h1> : undefined}
|
{!!config.error ? <h1>{config.error.message}</h1> : undefined}
|
||||||
{(!config.data || !config.data.configuration || config.loading) ? <Spinner size={Spinner.SIZE_LARGE} /> : undefined}
|
{(!config.data || !config.data.configuration || config.loading) ? <Spinner size={Spinner.SIZE_LARGE} /> : undefined}
|
||||||
<H4>User Interface</H4>
|
<h4>User Interface</h4>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label="Scene / Marker Wall"
|
label="Scene / Marker Wall"
|
||||||
helperText="Configuration for wall items"
|
helperText="Configuration for wall items"
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ export const SettingsLogsPanel: FunctionComponent<IProps> = (props: IProps) => {
|
|||||||
const logLevels = ["Debug", "Info", "Warning", "Error"];
|
const logLevels = ["Debug", "Info", "Warning", "Error"];
|
||||||
|
|
||||||
function filterByLogLevel(logEntry : LogEntry) {
|
function filterByLogLevel(logEntry : LogEntry) {
|
||||||
if (logLevel == "Debug") {
|
if (logLevel === "Debug") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,21 +4,16 @@ import {
|
|||||||
Checkbox,
|
Checkbox,
|
||||||
Divider,
|
Divider,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
H4,
|
|
||||||
AnchorButton,
|
|
||||||
ProgressBar,
|
ProgressBar,
|
||||||
H5,
|
|
||||||
} from "@blueprintjs/core";
|
} from "@blueprintjs/core";
|
||||||
import React, { FunctionComponent, useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { StashService } from "../../../core/StashService";
|
import { StashService } from "../../../core/StashService";
|
||||||
import { ErrorUtils } from "../../../utils/errors";
|
import { ErrorUtils } from "../../../utils/errors";
|
||||||
import { ToastUtils } from "../../../utils/toasts";
|
import { ToastUtils } from "../../../utils/toasts";
|
||||||
import { GenerateButton } from "./GenerateButton";
|
import { GenerateButton } from "./GenerateButton";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
interface IProps {}
|
export const SettingsTasksPanel: React.FC = () => {
|
||||||
|
|
||||||
export const SettingsTasksPanel: FunctionComponent<IProps> = (props: IProps) => {
|
|
||||||
const [isImportAlertOpen, setIsImportAlertOpen] = useState<boolean>(false);
|
const [isImportAlertOpen, setIsImportAlertOpen] = useState<boolean>(false);
|
||||||
const [isCleanAlertOpen, setIsCleanAlertOpen] = useState<boolean>(false);
|
const [isCleanAlertOpen, setIsCleanAlertOpen] = useState<boolean>(false);
|
||||||
const [useFileMetadata, setUseFileMetadata] = useState<boolean>(false);
|
const [useFileMetadata, setUseFileMetadata] = useState<boolean>(false);
|
||||||
@@ -173,7 +168,7 @@ export const SettingsTasksPanel: FunctionComponent<IProps> = (props: IProps) =>
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<H5>Status: {status}</H5>
|
<h5>Status: {status}</h5>
|
||||||
{!!status && status !== "Idle" ? <ProgressBar value={progress}/> : undefined}
|
{!!status && status !== "Idle" ? <ProgressBar value={progress}/> : undefined}
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
{maybeRenderStop()}
|
{maybeRenderStop()}
|
||||||
@@ -186,13 +181,13 @@ export const SettingsTasksPanel: FunctionComponent<IProps> = (props: IProps) =>
|
|||||||
{renderImportAlert()}
|
{renderImportAlert()}
|
||||||
{renderCleanAlert()}
|
{renderCleanAlert()}
|
||||||
|
|
||||||
<H4>Running Jobs</H4>
|
<h4>Running Jobs</h4>
|
||||||
|
|
||||||
{renderJobStatus()}
|
{renderJobStatus()}
|
||||||
|
|
||||||
<Divider/>
|
<Divider/>
|
||||||
|
|
||||||
<H4>Library</H4>
|
<h4>Library</h4>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
helperText="Scan for new content and add it to the database."
|
helperText="Scan for new content and add it to the database."
|
||||||
labelFor="scan"
|
labelFor="scan"
|
||||||
@@ -208,7 +203,7 @@ export const SettingsTasksPanel: FunctionComponent<IProps> = (props: IProps) =>
|
|||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<H4>Auto Tagging</H4>
|
<h4>Auto Tagging</h4>
|
||||||
|
|
||||||
<FormGroup
|
<FormGroup
|
||||||
helperText="Auto-tag content based on filenames."
|
helperText="Auto-tag content based on filenames."
|
||||||
@@ -240,7 +235,7 @@ export const SettingsTasksPanel: FunctionComponent<IProps> = (props: IProps) =>
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<H4>Generated Content</H4>
|
<h4>Generated Content</h4>
|
||||||
<GenerateButton />
|
<GenerateButton />
|
||||||
<FormGroup
|
<FormGroup
|
||||||
helperText="Check for missing files and remove them from the database. This is a destructive action."
|
helperText="Check for missing files and remove them from the database. This is a destructive action."
|
||||||
@@ -251,7 +246,7 @@ export const SettingsTasksPanel: FunctionComponent<IProps> = (props: IProps) =>
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<H4>Metadata</H4>
|
<h4>Metadata</h4>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
helperText="Export the database content into JSON format"
|
helperText="Export the database content into JSON format"
|
||||||
labelFor="export"
|
labelFor="export"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import _ from "lodash";
|
|
||||||
import React, { FunctionComponent } from "react";
|
import React, { FunctionComponent } from "react";
|
||||||
import { QueryHookResult } from "react-apollo-hooks";
|
import { QueryHookResult } from "react-apollo-hooks";
|
||||||
import { FindStudiosQuery, FindStudiosVariables } from "../../core/generated-graphql";
|
import { FindStudiosQuery, FindStudiosVariables } from "../../core/generated-graphql";
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ export const WallItem: FunctionComponent<IWallItemProps> = (props: IWallItemProp
|
|||||||
loop={true}
|
loop={true}
|
||||||
ref={videoHoverHook.videoEl}
|
ref={videoHoverHook.videoEl}
|
||||||
/>
|
/>
|
||||||
<img src={previewPath || screenshotPath} onError={() => previewNotFound()} />
|
<img alt="Preview" src={previewPath || screenshotPath} onError={() => previewNotFound()} />
|
||||||
{showTextContainer ?
|
{showTextContainer ?
|
||||||
<div className="scene-wall-item-text-container">
|
<div className="scene-wall-item-text-container">
|
||||||
<div style={{lineHeight: 1}}>
|
<div style={{lineHeight: 1}}>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import _ from "lodash";
|
|
||||||
import React, { FunctionComponent, useState } from "react";
|
import React, { FunctionComponent, useState } from "react";
|
||||||
import * as GQL from "../../core/generated-graphql";
|
import * as GQL from "../../core/generated-graphql";
|
||||||
import "./Wall.scss";
|
import "./Wall.scss";
|
||||||
|
|||||||
@@ -1,15 +1,7 @@
|
|||||||
import {
|
|
||||||
Button,
|
|
||||||
Classes,
|
|
||||||
Dialog,
|
|
||||||
FormGroup,
|
|
||||||
HTMLSelect,
|
|
||||||
InputGroup,
|
|
||||||
Tooltip,
|
|
||||||
} from "@blueprintjs/core";
|
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import React, { FunctionComponent, useEffect, useRef, useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import { isArray } from "util";
|
import { Button, Form, Modal, OverlayTrigger, Tooltip } from 'react-bootstrap'
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { CriterionModifier } from "../../core/generated-graphql";
|
import { CriterionModifier } from "../../core/generated-graphql";
|
||||||
import { Criterion, CriterionType } from "../../models/list-filter/criteria/criterion";
|
import { Criterion, CriterionType } from "../../models/list-filter/criteria/criterion";
|
||||||
import { NoneCriterion } from "../../models/list-filter/criteria/none";
|
import { NoneCriterion } from "../../models/list-filter/criteria/none";
|
||||||
@@ -27,8 +19,8 @@ interface IAddFilterProps {
|
|||||||
editingCriterion?: Criterion;
|
editingCriterion?: Criterion;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterProps) => {
|
export const AddFilter: React.FC<IAddFilterProps> = (props: IAddFilterProps) => {
|
||||||
const singleValueSelect = useRef<HTMLSelect>(null);
|
const defaultValue= useRef<string|number|undefined>();
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [criterion, setCriterion] = useState<Criterion<any, any>>(new NoneCriterion());
|
const [criterion, setCriterion] = useState<Criterion<any, any>>(new NoneCriterion());
|
||||||
@@ -71,8 +63,8 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onAddFilter() {
|
function onAddFilter() {
|
||||||
if (!isArray(criterion.value) && !!singleValueSelect.current) {
|
if (!Array.isArray(criterion.value) && defaultValue.current) {
|
||||||
const value = singleValueSelect.current.props.defaultValue;
|
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];
|
criterion.value = criterion.options[0];
|
||||||
} else if (typeof value === "number" && value === undefined) {
|
} else if (typeof value === "number" && value === undefined) {
|
||||||
@@ -101,11 +93,15 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
|
|||||||
if (criterion.modifierOptions.length === 0) { return; }
|
if (criterion.modifierOptions.length === 0) { return; }
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<HTMLSelect
|
<Form.Control
|
||||||
options={criterion.modifierOptions}
|
as="select"
|
||||||
onChange={onChangedModifierSelect}
|
onChange={onChangedModifierSelect}
|
||||||
defaultValue={criterion.modifier}
|
value={criterion.modifier}
|
||||||
/>
|
>
|
||||||
|
{ criterion.modifierOptions.map(c => (
|
||||||
|
<option value={c.value}>{c.label}</option>
|
||||||
|
))}
|
||||||
|
</Form.Control>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -116,7 +112,7 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isArray(criterion.value)) {
|
if (Array.isArray(criterion.value)) {
|
||||||
let type: "performers" | "studios" | "tags" | "" = "";
|
let type: "performers" | "studios" | "tags" | "" = "";
|
||||||
if (criterion instanceof PerformersCriterion) {
|
if (criterion instanceof PerformersCriterion) {
|
||||||
type = "performers";
|
type = "performers";
|
||||||
@@ -140,21 +136,25 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (criterion.options) {
|
if (criterion.options) {
|
||||||
|
defaultValue.current = criterion.value;
|
||||||
return (
|
return (
|
||||||
<HTMLSelect
|
<Form.Control
|
||||||
ref={singleValueSelect}
|
as="select"
|
||||||
options={criterion.options}
|
|
||||||
onChange={onChangedSingleSelect}
|
onChange={onChangedSingleSelect}
|
||||||
defaultValue={criterion.value}
|
value={criterion.value}
|
||||||
/>
|
>
|
||||||
|
{ criterion.options.map(c => (
|
||||||
|
<option value={c}>{c}</option>
|
||||||
|
))}
|
||||||
|
</Form.Control>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<InputGroup
|
<Form.Control
|
||||||
type={criterion.inputType}
|
type={criterion.inputType}
|
||||||
onChange={onChangedInput}
|
onChange={onChangedInput}
|
||||||
onBlur={onBlurInput}
|
onBlur={onBlurInput}
|
||||||
defaultValue={criterion.value ? criterion.value : ""}
|
value={criterion.value || ""}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -162,57 +162,62 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FormGroup>
|
<Form.Group>
|
||||||
{renderModifier()}
|
{renderModifier()}
|
||||||
</FormGroup>
|
</Form.Group>
|
||||||
<FormGroup>
|
<Form.Group>
|
||||||
{renderSelect()}
|
{renderSelect()}
|
||||||
</FormGroup>
|
</Form.Group>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function maybeRenderFilterSelect() {
|
function maybeRenderFilterSelect() {
|
||||||
if (!!props.editingCriterion) { return; }
|
if (props.editingCriterion) { return; }
|
||||||
return (
|
return (
|
||||||
<FormGroup label="Filter">
|
<Form.Group controlId="filter">
|
||||||
<HTMLSelect
|
<Form.Label>Filter</Form.Label>
|
||||||
style={{flexBasis: "min-content"}}
|
<Form.Control
|
||||||
options={props.filter.criterionOptions}
|
as="select"
|
||||||
onChange={onChangedCriteriaType}
|
onChange={onChangedCriteriaType}
|
||||||
defaultValue={criterion.type}
|
value={criterion.type}>
|
||||||
/>
|
{ props.filter.criterionOptions.map(c => (
|
||||||
</FormGroup>
|
<option value={c.value}>{c.label}</option>
|
||||||
|
))}
|
||||||
|
</Form.Control>
|
||||||
|
</Form.Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = !props.editingCriterion ? "Add Filter" : "Update Filter";
|
const title = !props.editingCriterion ? "Add Filter" : "Update Filter";
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Tooltip
|
<OverlayTrigger
|
||||||
hoverOpenDelay={200}
|
placement="top"
|
||||||
content="Filter"
|
overlay={<Tooltip id="filter-tooltip">Filter</Tooltip>}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
icon="filter"
|
|
||||||
onClick={() => onToggle()}
|
onClick={() => onToggle()}
|
||||||
active={isOpen}
|
active={isOpen}
|
||||||
large={true}
|
|
||||||
>
|
>
|
||||||
|
<FontAwesomeIcon icon="filter" />
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</OverlayTrigger>
|
||||||
|
|
||||||
<Dialog isOpen={isOpen} onClose={() => onToggle()} title={title}>
|
<Modal
|
||||||
|
show={isOpen}
|
||||||
|
onHide={() => onToggle()}>
|
||||||
|
<Modal.Header>{title}</Modal.Header>
|
||||||
|
<Modal.Body>
|
||||||
<div className="dialog-content">
|
<div className="dialog-content">
|
||||||
{maybeRenderFilterSelect()}
|
{maybeRenderFilterSelect()}
|
||||||
{maybeRenderFilterPopoverContents()}
|
{maybeRenderFilterPopoverContents()}
|
||||||
</div>
|
</div>
|
||||||
<div className={Classes.DIALOG_FOOTER}>
|
</Modal.Body>
|
||||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
<Modal.Footer>
|
||||||
<Button onClick={onAddFilter} disabled={criterion.type === "none"}>{title}</Button>
|
<Button onClick={onAddFilter} disabled={criterion.type === "none"}>{title}</Button>
|
||||||
</div>
|
</Modal.Footer>
|
||||||
</div>
|
</Modal>
|
||||||
</Dialog>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,17 +1,8 @@
|
|||||||
import {
|
|
||||||
Button,
|
|
||||||
ButtonGroup,
|
|
||||||
HTMLSelect,
|
|
||||||
InputGroup,
|
|
||||||
Menu,
|
|
||||||
MenuItem,
|
|
||||||
Popover,
|
|
||||||
Tag,
|
|
||||||
Tooltip,
|
|
||||||
Slider,
|
|
||||||
} from "@blueprintjs/core";
|
|
||||||
import { debounce } from "lodash";
|
import { debounce } from "lodash";
|
||||||
import React, { FunctionComponent, SyntheticEvent, useEffect, useState } from "react";
|
import React, { SyntheticEvent, useCallback, useState } from "react";
|
||||||
|
import { Badge, Button, ButtonGroup, Dropdown, Form, OverlayTrigger, Tooltip } from 'react-bootstrap';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
|
||||||
import { Criterion } from "../../models/list-filter/criteria/criterion";
|
import { Criterion } from "../../models/list-filter/criteria/criterion";
|
||||||
import { ListFilterModel } from "../../models/list-filter/filter";
|
import { ListFilterModel } from "../../models/list-filter/filter";
|
||||||
import { DisplayMode } from "../../models/list-filter/types";
|
import { DisplayMode } from "../../models/list-filter/types";
|
||||||
@@ -40,17 +31,15 @@ interface IListFilterProps {
|
|||||||
|
|
||||||
const PAGE_SIZE_OPTIONS = ["20", "40", "60", "120"];
|
const PAGE_SIZE_OPTIONS = ["20", "40", "60", "120"];
|
||||||
|
|
||||||
export const ListFilter: FunctionComponent<IListFilterProps> = (props: IListFilterProps) => {
|
export const ListFilter: React.FC<IListFilterProps> = (props: IListFilterProps) => {
|
||||||
let searchCallback: any;
|
const searchCallback = useCallback(
|
||||||
|
debounce((event: any) => {
|
||||||
|
props.onChangeQuery(event.target.value);
|
||||||
|
}, 500), [props.onChangeQuery]
|
||||||
|
);
|
||||||
|
|
||||||
const [editingCriterion, setEditingCriterion] = useState<Criterion | undefined>(undefined);
|
const [editingCriterion, setEditingCriterion] = useState<Criterion | undefined>(undefined);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
searchCallback = debounce((event: any) => {
|
|
||||||
props.onChangeQuery(event.target.value);
|
|
||||||
}, 500);
|
|
||||||
});
|
|
||||||
|
|
||||||
function onChangePageSize(event: SyntheticEvent<HTMLSelectElement>) {
|
function onChangePageSize(event: SyntheticEvent<HTMLSelectElement>) {
|
||||||
const val = event!.currentTarget!.value;
|
const val = event!.currentTarget!.value;
|
||||||
props.onChangePageSize(parseInt(val, 10));
|
props.onChangePageSize(parseInt(val, 10));
|
||||||
@@ -99,16 +88,16 @@ export const ListFilter: FunctionComponent<IListFilterProps> = (props: IListFilt
|
|||||||
|
|
||||||
function renderSortByOptions() {
|
function renderSortByOptions() {
|
||||||
return props.filter.sortByOptions.map((option) => (
|
return props.filter.sortByOptions.map((option) => (
|
||||||
<MenuItem onClick={onChangeSortBy} text={option} key={option} />
|
<Dropdown.Item onClick={onChangeSortBy} key={option}>{option}</Dropdown.Item>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDisplayModeOptions() {
|
function renderDisplayModeOptions() {
|
||||||
function getIcon(option: DisplayMode) {
|
function getIcon(option: DisplayMode) {
|
||||||
switch (option) {
|
switch (option) {
|
||||||
case DisplayMode.Grid: return "grid-view";
|
case DisplayMode.Grid: return "th-large";
|
||||||
case DisplayMode.List: return "list";
|
case DisplayMode.List: return "list";
|
||||||
case DisplayMode.Wall: return "symbol-square";
|
case DisplayMode.Wall: return "square";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function getLabel(option: DisplayMode) {
|
function getLabel(option: DisplayMode) {
|
||||||
@@ -119,29 +108,30 @@ export const ListFilter: FunctionComponent<IListFilterProps> = (props: IListFilt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return props.filter.displayModeOptions.map((option) => (
|
return props.filter.displayModeOptions.map((option) => (
|
||||||
<Tooltip content={getLabel(option)} hoverOpenDelay={200}>
|
<OverlayTrigger overlay={<Tooltip id="display-mode-tooltip">{getLabel(option)}</Tooltip>}>
|
||||||
<Button
|
<Button
|
||||||
key={option}
|
key={option}
|
||||||
active={props.filter.displayMode === option}
|
active={props.filter.displayMode === option}
|
||||||
onClick={() => onChangeDisplayMode(option)}
|
onClick={() => onChangeDisplayMode(option)}
|
||||||
icon={getIcon(option)}
|
>
|
||||||
/>
|
<FontAwesomeIcon icon={getIcon(option)} />
|
||||||
</Tooltip>
|
</Button>
|
||||||
|
</OverlayTrigger>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderFilterTags() {
|
function renderFilterTags() {
|
||||||
return props.filter.criteria.map((criterion) => (
|
return props.filter.criteria.map((criterion) => (
|
||||||
<Tag
|
<Badge
|
||||||
key={criterion.getId()}
|
|
||||||
className="tag-item"
|
className="tag-item"
|
||||||
itemID={criterion.getId()}
|
variant="secondary"
|
||||||
interactive={true}
|
|
||||||
onRemove={() => onRemoveCriterionTag(criterion)}
|
|
||||||
onClick={() => onClickCriterionTag(criterion)}
|
onClick={() => onClickCriterionTag(criterion)}
|
||||||
>
|
>
|
||||||
{criterion.getLabel()}
|
{criterion.getLabel()}
|
||||||
</Tag>
|
<Button onClick={() => onRemoveCriterionTag(criterion)}>
|
||||||
|
<FontAwesomeIcon icon="times" />
|
||||||
|
</Button>
|
||||||
|
</Badge>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,45 +149,40 @@ export const ListFilter: FunctionComponent<IListFilterProps> = (props: IListFilt
|
|||||||
|
|
||||||
function renderSelectAll() {
|
function renderSelectAll() {
|
||||||
if (props.onSelectAll) {
|
if (props.onSelectAll) {
|
||||||
return <MenuItem onClick={() => onSelectAll()} text="Select All" />;
|
return <Dropdown.Item onClick={() => onSelectAll()}>Select All</Dropdown.Item>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderSelectNone() {
|
function renderSelectNone() {
|
||||||
if (props.onSelectNone) {
|
if (props.onSelectNone) {
|
||||||
return <MenuItem onClick={() => onSelectNone()} text="Select None" />;
|
return <Dropdown.Item onClick={() => onSelectNone()}>Select None</Dropdown.Item>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderMore() {
|
function renderMore() {
|
||||||
let options = [];
|
let options = [
|
||||||
options.push(renderSelectAll());
|
renderSelectAll(),
|
||||||
options.push(renderSelectNone());
|
renderSelectNone()
|
||||||
|
];
|
||||||
|
|
||||||
if (props.otherOperations) {
|
if (props.otherOperations) {
|
||||||
props.otherOperations.forEach((o) => {
|
props.otherOperations.forEach((o) => {
|
||||||
options.push(<MenuItem onClick={o.onClick} text={o.text} />);
|
options.push(<Dropdown.Item onClick={o.onClick}>{o.text}</Dropdown.Item>);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
options = options.filter((o) => !!o);
|
if (options.length > 0) {
|
||||||
|
|
||||||
let menuItems = options as JSX.Element[];
|
|
||||||
|
|
||||||
function renderMoreOptions() {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Dropdown>
|
||||||
{menuItems}
|
<Dropdown.Toggle variant="secondary" id="more-menu">
|
||||||
</>
|
<Button>
|
||||||
)
|
<FontAwesomeIcon icon="ellipsis-h" />
|
||||||
}
|
</Button>
|
||||||
|
</Dropdown.Toggle>
|
||||||
if (menuItems.length > 0) {
|
<Dropdown.Menu>
|
||||||
return (
|
{options}
|
||||||
<Popover position="bottom">
|
</Dropdown.Menu>
|
||||||
<Button icon="more"/>
|
</Dropdown>
|
||||||
<Menu>{renderMoreOptions()}</Menu>
|
|
||||||
</Popover>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -212,13 +197,11 @@ export const ListFilter: FunctionComponent<IListFilterProps> = (props: IListFilt
|
|||||||
if (props.onChangeZoom) {
|
if (props.onChangeZoom) {
|
||||||
return (
|
return (
|
||||||
<span className="zoom-slider">
|
<span className="zoom-slider">
|
||||||
<Slider
|
<Form.Control
|
||||||
|
type="range"
|
||||||
min={0}
|
min={0}
|
||||||
value={props.zoomIndex}
|
|
||||||
initialValue={props.zoomIndex}
|
|
||||||
max={3}
|
max={3}
|
||||||
labelRenderer={false}
|
onChange={(event: any) => onChangeZoom(Number.parseInt(event.target.value))}
|
||||||
onChange={(v) => onChangeZoom(v)}
|
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@@ -229,37 +212,37 @@ export const ListFilter: FunctionComponent<IListFilterProps> = (props: IListFilt
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="filter-container">
|
<div className="filter-container">
|
||||||
<InputGroup
|
<Form.Control
|
||||||
large={true}
|
|
||||||
placeholder="Search..."
|
placeholder="Search..."
|
||||||
defaultValue={props.filter.searchTerm}
|
value={props.filter.searchTerm}
|
||||||
onChange={onChangeQuery}
|
onChange={onChangeQuery}
|
||||||
className="filter-item"
|
className="filter-item"
|
||||||
/>
|
/>
|
||||||
<HTMLSelect
|
<Form.Control
|
||||||
large={true}
|
as="select"
|
||||||
style={{flexBasis: "min-content"}}
|
|
||||||
options={PAGE_SIZE_OPTIONS}
|
|
||||||
onChange={onChangePageSize}
|
onChange={onChangePageSize}
|
||||||
value={props.filter.itemsPerPage}
|
value={props.filter.itemsPerPage.toString()}
|
||||||
className="filter-item"
|
className="filter-item"
|
||||||
/>
|
|
||||||
<ButtonGroup className="filter-item">
|
|
||||||
<Popover position="bottom">
|
|
||||||
<Button large={true}>{props.filter.sortBy}</Button>
|
|
||||||
<Menu>{renderSortByOptions()}</Menu>
|
|
||||||
</Popover>
|
|
||||||
|
|
||||||
<Tooltip
|
|
||||||
content={props.filter.sortDirection === "asc" ? "Ascending" : "Descending"}
|
|
||||||
hoverOpenDelay={200}
|
|
||||||
>
|
>
|
||||||
<Button
|
{ PAGE_SIZE_OPTIONS.map(s => <option value={s}>{s}</option>) }
|
||||||
rightIcon={props.filter.sortDirection === "asc" ? "caret-up" : "caret-down"}
|
</Form.Control>
|
||||||
onClick={onChangeSortDirection}
|
<ButtonGroup className="filter-item">
|
||||||
/>
|
<Dropdown>
|
||||||
</Tooltip>
|
<Dropdown.Toggle variant="secondary" id="more-menu">
|
||||||
|
<Button>{props.filter.sortBy}</Button>
|
||||||
|
</Dropdown.Toggle>
|
||||||
|
<Dropdown.Menu>
|
||||||
|
{renderSortByOptions()}
|
||||||
|
</Dropdown.Menu>
|
||||||
|
</Dropdown>
|
||||||
|
|
||||||
|
<OverlayTrigger overlay={
|
||||||
|
<Tooltip id="sort-direction-tooltip">{props.filter.sortDirection === "asc" ? "Ascending" : "Descending"}</Tooltip>
|
||||||
|
}>
|
||||||
|
<Button onClick={onChangeSortDirection}>
|
||||||
|
<FontAwesomeIcon icon={props.filter.sortDirection === "asc" ? "caret-up" : "caret-down"} />
|
||||||
|
</Button>
|
||||||
|
</OverlayTrigger>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
|
||||||
<AddFilter
|
<AddFilter
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Button, ButtonGroup } from "@blueprintjs/core";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { Button, ButtonGroup } from 'react-bootstrap';
|
||||||
|
|
||||||
interface IPaginationProps {
|
interface IPaginationProps {
|
||||||
itemsPerPage: number;
|
itemsPerPage: number;
|
||||||
@@ -36,28 +36,24 @@ export class Pagination extends React.Component<IPaginationProps, IPaginationSta
|
|||||||
if (!this.state || !this.state.pages || this.state.pages.length <= 1) { return null; }
|
if (!this.state || !this.state.pages || this.state.pages.length <= 1) { return null; }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ButtonGroup large={true} className="filter-container">
|
<ButtonGroup className="filter-container">
|
||||||
<Button
|
<Button
|
||||||
text="First"
|
|
||||||
disabled={this.props.currentPage === 1}
|
disabled={this.props.currentPage === 1}
|
||||||
onClick={() => this.setPage(1)}
|
onClick={() => this.setPage(1)}
|
||||||
/>
|
>First</Button>
|
||||||
<Button
|
<Button
|
||||||
text="Previous"
|
|
||||||
disabled={this.props.currentPage === 1}
|
disabled={this.props.currentPage === 1}
|
||||||
onClick={() => this.setPage(this.props.currentPage - 1)}
|
onClick={() => this.setPage(this.props.currentPage - 1)}
|
||||||
/>
|
>Previous</Button>
|
||||||
{this.renderPageButtons()}
|
{this.renderPageButtons()}
|
||||||
<Button
|
<Button
|
||||||
text="Next"
|
|
||||||
disabled={this.props.currentPage === this.state.totalPages}
|
disabled={this.props.currentPage === this.state.totalPages}
|
||||||
onClick={() => this.setPage(this.props.currentPage + 1)}
|
onClick={() => this.setPage(this.props.currentPage + 1)}
|
||||||
/>
|
>Next</Button>
|
||||||
<Button
|
<Button
|
||||||
text="Last"
|
|
||||||
disabled={this.props.currentPage === this.state.totalPages}
|
disabled={this.props.currentPage === this.state.totalPages}
|
||||||
onClick={() => this.setPage(this.state.totalPages)}
|
onClick={() => this.setPage(this.state.totalPages)}
|
||||||
/>
|
>Last</Button>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -66,10 +62,9 @@ export class Pagination extends React.Component<IPaginationProps, IPaginationSta
|
|||||||
return this.state.pages.map((page: number, index: number) => (
|
return this.state.pages.map((page: number, index: number) => (
|
||||||
<Button
|
<Button
|
||||||
key={index}
|
key={index}
|
||||||
text={page}
|
|
||||||
active={this.props.currentPage === page}
|
active={this.props.currentPage === page}
|
||||||
onClick={() => this.setPage(page)}
|
onClick={() => this.setPage(page)}
|
||||||
/>
|
>{page}</Button>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -431,19 +431,19 @@ export const SceneFilenameParser: React.FC = () => {
|
|||||||
return !r.studioId.set;
|
return !r.studioId.set;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (newAllTitleSet != allTitleSet) {
|
if (newAllTitleSet !== allTitleSet) {
|
||||||
setAllTitleSet(newAllTitleSet);
|
setAllTitleSet(newAllTitleSet);
|
||||||
}
|
}
|
||||||
if (newAllDateSet != allDateSet) {
|
if (newAllDateSet !== allDateSet) {
|
||||||
setAllDateSet(newAllDateSet);
|
setAllDateSet(newAllDateSet);
|
||||||
}
|
}
|
||||||
if (newAllPerformerSet != allPerformerSet) {
|
if (newAllPerformerSet !== allPerformerSet) {
|
||||||
setAllTagSet(newAllPerformerSet);
|
setAllTagSet(newAllPerformerSet);
|
||||||
}
|
}
|
||||||
if (newAllTagSet != allTagSet) {
|
if (newAllTagSet !== allTagSet) {
|
||||||
setAllTagSet(newAllTagSet);
|
setAllTagSet(newAllTagSet);
|
||||||
}
|
}
|
||||||
if (newAllStudioSet != allStudioSet) {
|
if (newAllStudioSet !== allStudioSet) {
|
||||||
setAllStudioSet(newAllStudioSet);
|
setAllStudioSet(newAllStudioSet);
|
||||||
}
|
}
|
||||||
}, [parserResult]);
|
}, [parserResult]);
|
||||||
@@ -927,7 +927,7 @@ export const SceneFilenameParser: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderTable() {
|
function renderTable() {
|
||||||
if (parserResult.length == 0) { return undefined; }
|
if (parserResult.length === 0) { return undefined; }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import React, { CSSProperties, FunctionComponent, RefObject, useEffect, useRef, useState } from "react";
|
import React, { CSSProperties, useEffect, useRef, useState } from "react";
|
||||||
import * as GQL from "../../../core/generated-graphql";
|
import * as GQL from "../../../core/generated-graphql";
|
||||||
import { TextUtils } from "../../../utils/text";
|
import { TextUtils } from "../../../utils/text";
|
||||||
import "./ScenePlayerScrubber.scss";
|
import "./ScenePlayerScrubber.scss";
|
||||||
@@ -20,7 +20,7 @@ interface ISceneSpriteItem {
|
|||||||
h: number;
|
h: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ScenePlayerScrubber: FunctionComponent<IScenePlayerScrubberProps> = (props: IScenePlayerScrubberProps) => {
|
export const ScenePlayerScrubber: React.FC<IScenePlayerScrubberProps> = (props: IScenePlayerScrubberProps) => {
|
||||||
const contentEl = useRef<HTMLDivElement>(null);
|
const contentEl = useRef<HTMLDivElement>(null);
|
||||||
const positionIndicatorEl = useRef<HTMLDivElement>(null);
|
const positionIndicatorEl = useRef<HTMLDivElement>(null);
|
||||||
const scrubberSliderEl = useRef<HTMLDivElement>(null);
|
const scrubberSliderEl = useRef<HTMLDivElement>(null);
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (props: IList
|
|||||||
async function onSave() {
|
async function onSave() {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
const result = await updateScenes();
|
await updateScenes();
|
||||||
ToastUtils.success("Updated scenes");
|
ToastUtils.success("Updated scenes");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ErrorUtils.handle(e);
|
ErrorUtils.handle(e);
|
||||||
@@ -130,7 +130,7 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (props: IList
|
|||||||
first = false;
|
first = false;
|
||||||
} else {
|
} else {
|
||||||
var studioId = scene.studio ? scene.studio.id : undefined;
|
var studioId = scene.studio ? scene.studio.id : undefined;
|
||||||
if (ret != studioId) {
|
if (ret !== studioId) {
|
||||||
ret = undefined;
|
ret = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,7 +208,7 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (props: IList
|
|||||||
if (rating !== thisRating) {
|
if (rating !== thisRating) {
|
||||||
rating = "";
|
rating = "";
|
||||||
}
|
}
|
||||||
if (studioId != thisStudio) {
|
if (studioId !== thisStudio) {
|
||||||
studioId = undefined;
|
studioId = undefined;
|
||||||
}
|
}
|
||||||
const perfIds = !!scene.performers ? scene.performers.map(toId).sort() : [];
|
const perfIds = !!scene.performers ? scene.performers.map(toId).sort() : [];
|
||||||
@@ -261,7 +261,7 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (props: IList
|
|||||||
as="select"
|
as="select"
|
||||||
onChange={(event: any) => setRating(event.target.value)}>
|
onChange={(event: any) => setRating(event.target.value)}>
|
||||||
{ ["", 1, 2, 3, 4, 5].map(opt => (
|
{ ["", 1, 2, 3, 4, 5].map(opt => (
|
||||||
<option selected={opt == rating} value={opt}>{opt}</option>
|
<option selected={opt === rating} value={opt}>{opt}</option>
|
||||||
)) }
|
)) }
|
||||||
</Form.Control>
|
</Form.Control>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { Button } from 'react-bootstrap';
|
||||||
|
|
||||||
import { Button, MenuItem } from "@blueprintjs/core";
|
|
||||||
import { ISelectProps, ItemPredicate, ItemRenderer, Select } from "@blueprintjs/select";
|
import { ISelectProps, ItemPredicate, ItemRenderer, Select } from "@blueprintjs/select";
|
||||||
import * as GQL from "../../core/generated-graphql";
|
import * as GQL from "../../core/generated-graphql";
|
||||||
import { StashService } from "../../core/StashService";
|
import { StashService } from "../../core/StashService";
|
||||||
@@ -91,7 +91,7 @@ export const FilterSelect: React.FunctionComponent<IProps> = (props: IProps) =>
|
|||||||
};
|
};
|
||||||
|
|
||||||
function onItemSelect(item: ValidTypes | undefined) {
|
function onItemSelect(item: ValidTypes | undefined) {
|
||||||
if (item && item.id == "0") {
|
if (item && item.id === "0") {
|
||||||
item = undefined;
|
item = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ export const FilterSelect: React.FunctionComponent<IProps> = (props: IProps) =>
|
|||||||
popoverProps={{position: "bottom"}}
|
popoverProps={{position: "bottom"}}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<Button fill={true} text={buttonText} />
|
<Button>{buttonText}</Button>
|
||||||
</InternalSelect>
|
</InternalSelect>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { MenuItem } from "@blueprintjs/core";
|
import { MenuItem } from "@blueprintjs/core";
|
||||||
import { ItemPredicate, ItemRenderer, Suggest } from "@blueprintjs/select";
|
import { ItemRenderer, Suggest } from "@blueprintjs/select";
|
||||||
import * as GQL from "../../core/generated-graphql";
|
import * as GQL from "../../core/generated-graphql";
|
||||||
import { StashService } from "../../core/StashService";
|
import { StashService } from "../../core/StashService";
|
||||||
import { HTMLInputProps } from "../../models";
|
import { HTMLInputProps } from "../../models";
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export const ValidGalleriesSelect: React.FunctionComponent<IProps> = (props: IPr
|
|||||||
};
|
};
|
||||||
|
|
||||||
function onItemSelect(item: GQL.ValidGalleriesForSceneValidGalleriesForScene | undefined) {
|
function onItemSelect(item: GQL.ValidGalleriesForSceneValidGalleriesForScene | undefined) {
|
||||||
if (item && item.id == "0") {
|
if (item && item.id === "0") {
|
||||||
item = undefined;
|
item = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -525,7 +525,7 @@ export class StashService {
|
|||||||
if (_.isPlainObject(value)) {
|
if (_.isPlainObject(value)) {
|
||||||
return _.mapValues(value, StashService.nullToUndefined);
|
return _.mapValues(value, StashService.nullToUndefined);
|
||||||
}
|
}
|
||||||
if (_.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
return value.map(StashService.nullToUndefined);
|
return value.map(StashService.nullToUndefined);
|
||||||
}
|
}
|
||||||
if (value === null) {
|
if (value === null) {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { isArray } from "util";
|
|
||||||
import { CriterionModifier } from "../../../core/generated-graphql";
|
import { CriterionModifier } from "../../../core/generated-graphql";
|
||||||
import { ILabeledId, ILabeledValue } from "../types";
|
import { ILabeledId, ILabeledValue } from "../types";
|
||||||
|
|
||||||
@@ -94,7 +93,7 @@ export abstract class Criterion<Option = any, Value = any> {
|
|||||||
let valueString: string;
|
let valueString: string;
|
||||||
if (this.modifier === CriterionModifier.IsNull || this.modifier === CriterionModifier.NotNull) {
|
if (this.modifier === CriterionModifier.IsNull || this.modifier === CriterionModifier.NotNull) {
|
||||||
valueString = "";
|
valueString = "";
|
||||||
} else if (isArray(this.value) && this.value.length > 0) {
|
} else if (Array.isArray(this.value) && this.value.length > 0) {
|
||||||
let items = this.value;
|
let items = this.value;
|
||||||
if ((this.value as ILabeledId[])[0].label) {
|
if ((this.value as ILabeledId[])[0].label) {
|
||||||
items = this.value.map((item) => item.label) as any;
|
items = this.value.map((item) => item.label) as any;
|
||||||
@@ -103,7 +102,7 @@ export abstract class Criterion<Option = any, Value = any> {
|
|||||||
} else if (typeof this.value === "string") {
|
} else if (typeof this.value === "string") {
|
||||||
valueString = this.value;
|
valueString = this.value;
|
||||||
} else {
|
} else {
|
||||||
valueString = this.value.toString();
|
valueString = (this.value as any).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${Criterion.getLabel(this.type)} ${modifierString} ${valueString}`;
|
return `${Criterion.getLabel(this.type)} ${modifierString} ${valueString}`;
|
||||||
@@ -115,7 +114,7 @@ export abstract class Criterion<Option = any, Value = any> {
|
|||||||
|
|
||||||
public set(modifier: CriterionModifier, value: Value) {
|
public set(modifier: CriterionModifier, value: Value) {
|
||||||
this.modifier = modifier;
|
this.modifier = modifier;
|
||||||
if (isArray(this.value)) {
|
if (Array.isArray(this.value)) {
|
||||||
this.value.push(value);
|
this.value.push(value);
|
||||||
} else {
|
} else {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export class ImageUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static pasteImage(e : any, onLoadEnd: (this: FileReader) => any) {
|
public static pasteImage(e : any, onLoadEnd: (this: FileReader) => any) {
|
||||||
if (e.clipboardData.files.length == 0) {
|
if (e.clipboardData.files.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10128,7 +10128,7 @@ react-transition-group@^4.0.0:
|
|||||||
loose-envify "^1.4.0"
|
loose-envify "^1.4.0"
|
||||||
prop-types "^15.6.2"
|
prop-types "^15.6.2"
|
||||||
|
|
||||||
react@16.12.0:
|
react@~16.12.0:
|
||||||
version "16.12.0"
|
version "16.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-16.12.0.tgz#0c0a9c6a142429e3614834d5a778e18aa78a0b83"
|
resolved "https://registry.yarnpkg.com/react/-/react-16.12.0.tgz#0c0a9c6a142429e3614834d5a778e18aa78a0b83"
|
||||||
integrity sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA==
|
integrity sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA==
|
||||||
@@ -11781,12 +11781,7 @@ typedarray@^0.0.6:
|
|||||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||||
|
|
||||||
typescript@3.4.5:
|
typescript@^3.2.2, typescript@~3.7.4:
|
||||||
version "3.4.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.5.tgz#2d2618d10bb566572b8d7aad5180d84257d70a99"
|
|
||||||
integrity sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==
|
|
||||||
|
|
||||||
typescript@^3.2.2:
|
|
||||||
version "3.7.4"
|
version "3.7.4"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.4.tgz#1743a5ec5fef6a1fa9f3e4708e33c81c73876c19"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.4.tgz#1743a5ec5fef6a1fa9f3e4708e33c81c73876c19"
|
||||||
integrity sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw==
|
integrity sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw==
|
||||||
|
|||||||
Reference in New Issue
Block a user