This commit is contained in:
Infinite
2020-01-04 22:46:08 +01:00
parent 4d44deff64
commit f50cb45ca5
25 changed files with 188 additions and 241 deletions

View File

@@ -18,7 +18,7 @@
"lodash": "4.17.13",
"node-sass": "4.12.0",
"query-string": "6.5.0",
"react": "16.12.0",
"react": "~16.12.0",
"react-apollo": "2.5.6",
"react-apollo-hooks": "0.4.5",
"react-bootstrap": "^1.0.0-beta.16",
@@ -68,6 +68,6 @@
"graphql-codegen-typescript-client": "0.18.2",
"graphql-codegen-typescript-common": "0.18.2",
"graphql-codegen-typescript-react-apollo": "0.18.2",
"typescript": "3.4.5"
"typescript": "~3.7.4"
}
}

View File

@@ -1,4 +1,3 @@
import _ from "lodash";
import React, { useEffect, useState } from "react";
import { Spinner } from 'react-bootstrap';
import * as GQL from "../../core/generated-graphql";

View File

@@ -1,4 +1,3 @@
import _ from "lodash";
import React from "react";
import { Table } from 'react-bootstrap';
import { QueryHookResult } from "react-apollo-hooks";

View File

@@ -1,4 +1,3 @@
import _ from "lodash";
import React, { FunctionComponent, useState } from "react";
import Lightbox from "react-images";
import Gallery from "react-photo-gallery";

View File

@@ -1,19 +1,8 @@
import {
H1,
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 React from "react";
import { Table, Spinner } from 'react-bootstrap';
import { StashService } from "../../core/StashService";
interface IProps {}
export const SettingsAboutPanel: FunctionComponent<IProps> = (props: IProps) => {
export const SettingsAboutPanel: React.FC = () => {
const { data, error, loading } = StashService.useVersion();
function maybeRenderTag() {
@@ -30,7 +19,7 @@ export const SettingsAboutPanel: FunctionComponent<IProps> = (props: IProps) =>
if (!data || !data.version) { return; }
return (
<>
<HTMLTable>
<Table>
<tbody>
{maybeRenderTag()}
<tr>
@@ -42,14 +31,14 @@ export const SettingsAboutPanel: FunctionComponent<IProps> = (props: IProps) =>
<td>{data.version.build_time}</td>
</tr>
</tbody>
</HTMLTable>
</Table>
</>
);
}
return (
<>
<H4>About</H4>
{!data || loading ? <Spinner size={Spinner.SIZE_LARGE} /> : undefined}
<h4>About</h4>
{!data || loading ? <Spinner animation="border" variant="light" /> : undefined}
{!!error ? <span>error.message</span> : undefined}
{renderVersion()}
</>

View File

@@ -3,25 +3,19 @@ import {
Button,
Divider,
FormGroup,
H1,
H4,
H6,
InputGroup,
Spinner,
Tag,
Checkbox,
HTMLSelect,
} from "@blueprintjs/core";
import React, { FunctionComponent, useEffect, useState } from "react";
import React, { useEffect, useState } from "react";
import * as GQL from "../../core/generated-graphql";
import { StashService } from "../../core/StashService";
import { ErrorUtils } from "../../utils/errors";
import { ToastUtils } from "../../utils/toasts";
import { FolderSelect } from "../Shared/FolderSelect/FolderSelect";
interface IProps {}
export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IProps) => {
export const SettingsConfigurationPanel: React.FC = () => {
// Editing config state
const [stashes, setStashes] = useState<string[]>([]);
const [databasePath, setDatabasePath] = useState<string | undefined>(undefined);
@@ -51,7 +45,6 @@ export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IPr
logLevel,
logAccess,
excludes,
});
useEffect(() => {
@@ -86,7 +79,7 @@ export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IPr
}
function excludeRemoveRegex(idx: number) {
const newExcludes = excludes.filter((regex, i) => i!== idx );
const newExcludes = excludes.filter((_regex, i) => i !== idx );
setExcludes(newExcludes);
}
@@ -148,7 +141,7 @@ export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IPr
<>
{!!error ? <h1>{error.message}</h1> : undefined}
{(!data || !data.configuration || loading) ? <Spinner size={Spinner.SIZE_LARGE} /> : undefined}
<H4>Library</H4>
<h4>Library</h4>
<FormGroup>
<FormGroup>
<FormGroup
@@ -208,7 +201,7 @@ export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IPr
<Divider />
<FormGroup>
<H4>Video</H4>
<h4>Video</h4>
<FormGroup
label="Maximum transcode size"
helperText="Maximum size for generated transcodes"
@@ -233,7 +226,7 @@ export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IPr
<Divider />
<FormGroup>
<H4>Authentication</H4>
<h4>Authentication</h4>
<FormGroup
label="Username"
helperText="Username to access Stash. Leave blank to disable user authentication"
@@ -249,7 +242,7 @@ export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IPr
</FormGroup>
<Divider />
<H4>Logging</H4>
<h4>Logging</h4>
<FormGroup
label="Log file"
helperText="Path to the file to output logging to. Blank to disable file logging. Requires restart."

View File

@@ -3,12 +3,10 @@ import {
Checkbox,
Divider,
FormGroup,
H4,
Spinner,
TextArea,
NumericInput
} from "@blueprintjs/core";
import _ from "lodash";
import React, { FunctionComponent, useEffect, useState } from "react";
import { StashService } from "../../core/StashService";
import { ErrorUtils } from "../../utils/errors";
@@ -64,7 +62,7 @@ export const SettingsInterfacePanel: FunctionComponent<IProps> = () => {
<>
{!!config.error ? <h1>{config.error.message}</h1> : undefined}
{(!config.data || !config.data.configuration || config.loading) ? <Spinner size={Spinner.SIZE_LARGE} /> : undefined}
<H4>User Interface</H4>
<h4>User Interface</h4>
<FormGroup
label="Scene / Marker Wall"
helperText="Configuration for wall items"

View File

@@ -155,7 +155,7 @@ export const SettingsLogsPanel: FunctionComponent<IProps> = (props: IProps) => {
const logLevels = ["Debug", "Info", "Warning", "Error"];
function filterByLogLevel(logEntry : LogEntry) {
if (logLevel == "Debug") {
if (logLevel === "Debug") {
return true;
}

View File

@@ -4,21 +4,16 @@ import {
Checkbox,
Divider,
FormGroup,
H4,
AnchorButton,
ProgressBar,
H5,
} from "@blueprintjs/core";
import React, { FunctionComponent, useState, useEffect } from "react";
import React, { useState, useEffect } from "react";
import { StashService } from "../../../core/StashService";
import { ErrorUtils } from "../../../utils/errors";
import { ToastUtils } from "../../../utils/toasts";
import { GenerateButton } from "./GenerateButton";
import { Link } from "react-router-dom";
interface IProps {}
export const SettingsTasksPanel: FunctionComponent<IProps> = (props: IProps) => {
export const SettingsTasksPanel: React.FC = () => {
const [isImportAlertOpen, setIsImportAlertOpen] = useState<boolean>(false);
const [isCleanAlertOpen, setIsCleanAlertOpen] = useState<boolean>(false);
const [useFileMetadata, setUseFileMetadata] = useState<boolean>(false);
@@ -173,7 +168,7 @@ export const SettingsTasksPanel: FunctionComponent<IProps> = (props: IProps) =>
return (
<>
<FormGroup>
<H5>Status: {status}</H5>
<h5>Status: {status}</h5>
{!!status && status !== "Idle" ? <ProgressBar value={progress}/> : undefined}
</FormGroup>
{maybeRenderStop()}
@@ -186,13 +181,13 @@ export const SettingsTasksPanel: FunctionComponent<IProps> = (props: IProps) =>
{renderImportAlert()}
{renderCleanAlert()}
<H4>Running Jobs</H4>
<h4>Running Jobs</h4>
{renderJobStatus()}
<Divider/>
<H4>Library</H4>
<h4>Library</h4>
<FormGroup
helperText="Scan for new content and add it to the database."
labelFor="scan"
@@ -208,7 +203,7 @@ export const SettingsTasksPanel: FunctionComponent<IProps> = (props: IProps) =>
<Divider />
<H4>Auto Tagging</H4>
<h4>Auto Tagging</h4>
<FormGroup
helperText="Auto-tag content based on filenames."
@@ -240,7 +235,7 @@ export const SettingsTasksPanel: FunctionComponent<IProps> = (props: IProps) =>
</FormGroup>
<Divider />
<H4>Generated Content</H4>
<h4>Generated Content</h4>
<GenerateButton />
<FormGroup
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>
<Divider />
<H4>Metadata</H4>
<h4>Metadata</h4>
<FormGroup
helperText="Export the database content into JSON format"
labelFor="export"

View File

@@ -1,4 +1,3 @@
import _ from "lodash";
import React, { FunctionComponent } from "react";
import { QueryHookResult } from "react-apollo-hooks";
import { FindStudiosQuery, FindStudiosVariables } from "../../core/generated-graphql";

View File

@@ -116,7 +116,7 @@ export const WallItem: FunctionComponent<IWallItemProps> = (props: IWallItemProp
loop={true}
ref={videoHoverHook.videoEl}
/>
<img src={previewPath || screenshotPath} onError={() => previewNotFound()} />
<img alt="Preview" src={previewPath || screenshotPath} onError={() => previewNotFound()} />
{showTextContainer ?
<div className="scene-wall-item-text-container">
<div style={{lineHeight: 1}}>

View File

@@ -1,4 +1,3 @@
import _ from "lodash";
import React, { FunctionComponent, useState } from "react";
import * as GQL from "../../core/generated-graphql";
import "./Wall.scss";

View File

@@ -1,15 +1,7 @@
import {
Button,
Classes,
Dialog,
FormGroup,
HTMLSelect,
InputGroup,
Tooltip,
} from "@blueprintjs/core";
import _ from "lodash";
import React, { FunctionComponent, useEffect, useRef, useState } from "react";
import { isArray } from "util";
import React, { useEffect, useRef, useState } from "react";
import { Button, Form, Modal, OverlayTrigger, Tooltip } from 'react-bootstrap'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { CriterionModifier } from "../../core/generated-graphql";
import { Criterion, CriterionType } from "../../models/list-filter/criteria/criterion";
import { NoneCriterion } from "../../models/list-filter/criteria/none";
@@ -27,8 +19,8 @@ interface IAddFilterProps {
editingCriterion?: Criterion;
}
export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterProps) => {
const singleValueSelect = useRef<HTMLSelect>(null);
export const AddFilter: React.FC<IAddFilterProps> = (props: IAddFilterProps) => {
const defaultValue= useRef<string|number|undefined>();
const [isOpen, setIsOpen] = useState(false);
const [criterion, setCriterion] = useState<Criterion<any, any>>(new NoneCriterion());
@@ -71,8 +63,8 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
}
function onAddFilter() {
if (!isArray(criterion.value) && !!singleValueSelect.current) {
const value = singleValueSelect.current.props.defaultValue;
if (!Array.isArray(criterion.value) && defaultValue.current) {
const value = defaultValue.current;
if (criterion.options && (value === undefined || value === "" || typeof value === "number")) {
criterion.value = criterion.options[0];
} else if (typeof value === "number" && value === undefined) {
@@ -101,11 +93,15 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
if (criterion.modifierOptions.length === 0) { return; }
return (
<div>
<HTMLSelect
options={criterion.modifierOptions}
<Form.Control
as="select"
onChange={onChangedModifierSelect}
defaultValue={criterion.modifier}
/>
value={criterion.modifier}
>
{ criterion.modifierOptions.map(c => (
<option value={c.value}>{c.label}</option>
))}
</Form.Control>
</div>
);
}
@@ -116,7 +112,7 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
return;
}
if (isArray(criterion.value)) {
if (Array.isArray(criterion.value)) {
let type: "performers" | "studios" | "tags" | "" = "";
if (criterion instanceof PerformersCriterion) {
type = "performers";
@@ -140,21 +136,25 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
}
} else {
if (criterion.options) {
defaultValue.current = criterion.value;
return (
<HTMLSelect
ref={singleValueSelect}
options={criterion.options}
<Form.Control
as="select"
onChange={onChangedSingleSelect}
defaultValue={criterion.value}
/>
value={criterion.value}
>
{ criterion.options.map(c => (
<option value={c}>{c}</option>
))}
</Form.Control>
);
} else {
return (
<InputGroup
<Form.Control
type={criterion.inputType}
onChange={onChangedInput}
onBlur={onBlurInput}
defaultValue={criterion.value ? criterion.value : ""}
value={criterion.value || ""}
/>
)
}
@@ -162,57 +162,62 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
}
return (
<>
<FormGroup>
<Form.Group>
{renderModifier()}
</FormGroup>
<FormGroup>
</Form.Group>
<Form.Group>
{renderSelect()}
</FormGroup>
</Form.Group>
</>
);
};
function maybeRenderFilterSelect() {
if (!!props.editingCriterion) { return; }
if (props.editingCriterion) { return; }
return (
<FormGroup label="Filter">
<HTMLSelect
style={{flexBasis: "min-content"}}
options={props.filter.criterionOptions}
<Form.Group controlId="filter">
<Form.Label>Filter</Form.Label>
<Form.Control
as="select"
onChange={onChangedCriteriaType}
defaultValue={criterion.type}
/>
</FormGroup>
value={criterion.type}>
{ props.filter.criterionOptions.map(c => (
<option value={c.value}>{c.label}</option>
))}
</Form.Control>
</Form.Group>
);
}
const title = !props.editingCriterion ? "Add Filter" : "Update Filter";
return (
<>
<Tooltip
hoverOpenDelay={200}
content="Filter"
<OverlayTrigger
placement="top"
overlay={<Tooltip id="filter-tooltip">Filter</Tooltip>}
>
<Button
icon="filter"
onClick={() => onToggle()}
active={isOpen}
large={true}
>
<FontAwesomeIcon icon="filter" />
</Button>
</Tooltip>
</OverlayTrigger>
<Dialog isOpen={isOpen} onClose={() => onToggle()} title={title}>
<div className="dialog-content">
{maybeRenderFilterSelect()}
{maybeRenderFilterPopoverContents()}
</div>
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button onClick={onAddFilter} disabled={criterion.type === "none"}>{title}</Button>
<Modal
show={isOpen}
onHide={() => onToggle()}>
<Modal.Header>{title}</Modal.Header>
<Modal.Body>
<div className="dialog-content">
{maybeRenderFilterSelect()}
{maybeRenderFilterPopoverContents()}
</div>
</div>
</Dialog>
</Modal.Body>
<Modal.Footer>
<Button onClick={onAddFilter} disabled={criterion.type === "none"}>{title}</Button>
</Modal.Footer>
</Modal>
</>
);
};

View File

@@ -1,17 +1,8 @@
import {
Button,
ButtonGroup,
HTMLSelect,
InputGroup,
Menu,
MenuItem,
Popover,
Tag,
Tooltip,
Slider,
} from "@blueprintjs/core";
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 { ListFilterModel } from "../../models/list-filter/filter";
import { DisplayMode } from "../../models/list-filter/types";
@@ -40,17 +31,15 @@ interface IListFilterProps {
const PAGE_SIZE_OPTIONS = ["20", "40", "60", "120"];
export const ListFilter: FunctionComponent<IListFilterProps> = (props: IListFilterProps) => {
let searchCallback: any;
export const ListFilter: React.FC<IListFilterProps> = (props: IListFilterProps) => {
const searchCallback = useCallback(
debounce((event: any) => {
props.onChangeQuery(event.target.value);
}, 500), [props.onChangeQuery]
);
const [editingCriterion, setEditingCriterion] = useState<Criterion | undefined>(undefined);
useEffect(() => {
searchCallback = debounce((event: any) => {
props.onChangeQuery(event.target.value);
}, 500);
});
function onChangePageSize(event: SyntheticEvent<HTMLSelectElement>) {
const val = event!.currentTarget!.value;
props.onChangePageSize(parseInt(val, 10));
@@ -99,16 +88,16 @@ export const ListFilter: FunctionComponent<IListFilterProps> = (props: IListFilt
function renderSortByOptions() {
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 getIcon(option: DisplayMode) {
switch (option) {
case DisplayMode.Grid: return "grid-view";
case DisplayMode.Grid: return "th-large";
case DisplayMode.List: return "list";
case DisplayMode.Wall: return "symbol-square";
case DisplayMode.Wall: return "square";
}
}
function getLabel(option: DisplayMode) {
@@ -119,29 +108,30 @@ export const ListFilter: FunctionComponent<IListFilterProps> = (props: IListFilt
}
}
return props.filter.displayModeOptions.map((option) => (
<Tooltip content={getLabel(option)} hoverOpenDelay={200}>
<OverlayTrigger overlay={<Tooltip id="display-mode-tooltip">{getLabel(option)}</Tooltip>}>
<Button
key={option}
active={props.filter.displayMode === option}
onClick={() => onChangeDisplayMode(option)}
icon={getIcon(option)}
/>
</Tooltip>
>
<FontAwesomeIcon icon={getIcon(option)} />
</Button>
</OverlayTrigger>
));
}
function renderFilterTags() {
return props.filter.criteria.map((criterion) => (
<Tag
key={criterion.getId()}
<Badge
className="tag-item"
itemID={criterion.getId()}
interactive={true}
onRemove={() => onRemoveCriterionTag(criterion)}
variant="secondary"
onClick={() => onClickCriterionTag(criterion)}
>
{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() {
if (props.onSelectAll) {
return <MenuItem onClick={() => onSelectAll()} text="Select All" />;
return <Dropdown.Item onClick={() => onSelectAll()}>Select All</Dropdown.Item>;
}
}
function renderSelectNone() {
if (props.onSelectNone) {
return <MenuItem onClick={() => onSelectNone()} text="Select None" />;
return <Dropdown.Item onClick={() => onSelectNone()}>Select None</Dropdown.Item>;
}
}
function renderMore() {
let options = [];
options.push(renderSelectAll());
options.push(renderSelectNone());
let options = [
renderSelectAll(),
renderSelectNone()
];
if (props.otherOperations) {
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);
let menuItems = options as JSX.Element[];
function renderMoreOptions() {
if (options.length > 0) {
return (
<>
{menuItems}
</>
)
}
if (menuItems.length > 0) {
return (
<Popover position="bottom">
<Button icon="more"/>
<Menu>{renderMoreOptions()}</Menu>
</Popover>
<Dropdown>
<Dropdown.Toggle variant="secondary" id="more-menu">
<Button>
<FontAwesomeIcon icon="ellipsis-h" />
</Button>
</Dropdown.Toggle>
<Dropdown.Menu>
{options}
</Dropdown.Menu>
</Dropdown>
);
}
}
@@ -212,13 +197,11 @@ export const ListFilter: FunctionComponent<IListFilterProps> = (props: IListFilt
if (props.onChangeZoom) {
return (
<span className="zoom-slider">
<Slider
<Form.Control
type="range"
min={0}
value={props.zoomIndex}
initialValue={props.zoomIndex}
max={3}
labelRenderer={false}
onChange={(v) => onChangeZoom(v)}
onChange={(event: any) => onChangeZoom(Number.parseInt(event.target.value))}
/>
</span>
);
@@ -229,37 +212,37 @@ export const ListFilter: FunctionComponent<IListFilterProps> = (props: IListFilt
return (
<>
<div className="filter-container">
<InputGroup
large={true}
<Form.Control
placeholder="Search..."
defaultValue={props.filter.searchTerm}
value={props.filter.searchTerm}
onChange={onChangeQuery}
className="filter-item"
/>
<HTMLSelect
large={true}
style={{flexBasis: "min-content"}}
options={PAGE_SIZE_OPTIONS}
<Form.Control
as="select"
onChange={onChangePageSize}
value={props.filter.itemsPerPage}
value={props.filter.itemsPerPage.toString()}
className="filter-item"
/>
>
{ PAGE_SIZE_OPTIONS.map(s => <option value={s}>{s}</option>) }
</Form.Control>
<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
rightIcon={props.filter.sortDirection === "asc" ? "caret-up" : "caret-down"}
onClick={onChangeSortDirection}
/>
</Tooltip>
<Dropdown>
<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>
<AddFilter

View File

@@ -1,5 +1,5 @@
import { Button, ButtonGroup } from "@blueprintjs/core";
import React from "react";
import { Button, ButtonGroup } from 'react-bootstrap';
interface IPaginationProps {
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; }
return (
<ButtonGroup large={true} className="filter-container">
<ButtonGroup className="filter-container">
<Button
text="First"
disabled={this.props.currentPage === 1}
onClick={() => this.setPage(1)}
/>
>First</Button>
<Button
text="Previous"
disabled={this.props.currentPage === 1}
onClick={() => this.setPage(this.props.currentPage - 1)}
/>
>Previous</Button>
{this.renderPageButtons()}
<Button
text="Next"
disabled={this.props.currentPage === this.state.totalPages}
onClick={() => this.setPage(this.props.currentPage + 1)}
/>
>Next</Button>
<Button
text="Last"
disabled={this.props.currentPage === this.state.totalPages}
onClick={() => this.setPage(this.state.totalPages)}
/>
>Last</Button>
</ButtonGroup>
);
}
@@ -66,10 +62,9 @@ export class Pagination extends React.Component<IPaginationProps, IPaginationSta
return this.state.pages.map((page: number, index: number) => (
<Button
key={index}
text={page}
active={this.props.currentPage === page}
onClick={() => this.setPage(page)}
/>
>{page}</Button>
));
}

View File

@@ -431,19 +431,19 @@ export const SceneFilenameParser: React.FC = () => {
return !r.studioId.set;
});
if (newAllTitleSet != allTitleSet) {
if (newAllTitleSet !== allTitleSet) {
setAllTitleSet(newAllTitleSet);
}
if (newAllDateSet != allDateSet) {
if (newAllDateSet !== allDateSet) {
setAllDateSet(newAllDateSet);
}
if (newAllPerformerSet != allPerformerSet) {
if (newAllPerformerSet !== allPerformerSet) {
setAllTagSet(newAllPerformerSet);
}
if (newAllTagSet != allTagSet) {
if (newAllTagSet !== allTagSet) {
setAllTagSet(newAllTagSet);
}
if (newAllStudioSet != allStudioSet) {
if (newAllStudioSet !== allStudioSet) {
setAllStudioSet(newAllStudioSet);
}
}, [parserResult]);
@@ -927,7 +927,7 @@ export const SceneFilenameParser: React.FC = () => {
}
function renderTable() {
if (parserResult.length == 0) { return undefined; }
if (parserResult.length === 0) { return undefined; }
return (
<>

View File

@@ -1,5 +1,5 @@
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 { TextUtils } from "../../../utils/text";
import "./ScenePlayerScrubber.scss";
@@ -20,7 +20,7 @@ interface ISceneSpriteItem {
h: number;
}
export const ScenePlayerScrubber: FunctionComponent<IScenePlayerScrubberProps> = (props: IScenePlayerScrubberProps) => {
export const ScenePlayerScrubber: React.FC<IScenePlayerScrubberProps> = (props: IScenePlayerScrubberProps) => {
const contentEl = useRef<HTMLDivElement>(null);
const positionIndicatorEl = useRef<HTMLDivElement>(null);
const scrubberSliderEl = useRef<HTMLDivElement>(null);

View File

@@ -93,7 +93,7 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (props: IList
async function onSave() {
setIsLoading(true);
try {
const result = await updateScenes();
await updateScenes();
ToastUtils.success("Updated scenes");
} catch (e) {
ErrorUtils.handle(e);
@@ -130,7 +130,7 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (props: IList
first = false;
} else {
var studioId = scene.studio ? scene.studio.id : undefined;
if (ret != studioId) {
if (ret !== studioId) {
ret = undefined;
}
}
@@ -208,7 +208,7 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (props: IList
if (rating !== thisRating) {
rating = "";
}
if (studioId != thisStudio) {
if (studioId !== thisStudio) {
studioId = undefined;
}
const perfIds = !!scene.performers ? scene.performers.map(toId).sort() : [];
@@ -261,7 +261,7 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (props: IList
as="select"
onChange={(event: any) => setRating(event.target.value)}>
{ ["", 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.Group>

View File

@@ -1,6 +1,6 @@
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 * as GQL from "../../core/generated-graphql";
import { StashService } from "../../core/StashService";
@@ -91,7 +91,7 @@ export const FilterSelect: React.FunctionComponent<IProps> = (props: IProps) =>
};
function onItemSelect(item: ValidTypes | undefined) {
if (item && item.id == "0") {
if (item && item.id === "0") {
item = undefined;
}
@@ -111,7 +111,7 @@ export const FilterSelect: React.FunctionComponent<IProps> = (props: IProps) =>
popoverProps={{position: "bottom"}}
{...props}
>
<Button fill={true} text={buttonText} />
<Button>{buttonText}</Button>
</InternalSelect>
);
};

View File

@@ -1,7 +1,7 @@
import * as React from "react";
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 { StashService } from "../../core/StashService";
import { HTMLInputProps } from "../../models";

View File

@@ -50,7 +50,7 @@ export const ValidGalleriesSelect: React.FunctionComponent<IProps> = (props: IPr
};
function onItemSelect(item: GQL.ValidGalleriesForSceneValidGalleriesForScene | undefined) {
if (item && item.id == "0") {
if (item && item.id === "0") {
item = undefined;
}

View File

@@ -525,7 +525,7 @@ export class StashService {
if (_.isPlainObject(value)) {
return _.mapValues(value, StashService.nullToUndefined);
}
if (_.isArray(value)) {
if (Array.isArray(value)) {
return value.map(StashService.nullToUndefined);
}
if (value === null) {

View File

@@ -1,4 +1,3 @@
import { isArray } from "util";
import { CriterionModifier } from "../../../core/generated-graphql";
import { ILabeledId, ILabeledValue } from "../types";
@@ -94,7 +93,7 @@ export abstract class Criterion<Option = any, Value = any> {
let valueString: string;
if (this.modifier === CriterionModifier.IsNull || this.modifier === CriterionModifier.NotNull) {
valueString = "";
} else if (isArray(this.value) && this.value.length > 0) {
} else if (Array.isArray(this.value) && this.value.length > 0) {
let items = this.value;
if ((this.value as ILabeledId[])[0].label) {
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") {
valueString = this.value;
} else {
valueString = this.value.toString();
valueString = (this.value as any).toString();
}
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) {
this.modifier = modifier;
if (isArray(this.value)) {
if (Array.isArray(this.value)) {
this.value.push(value);
} else {
this.value = value;

View File

@@ -15,7 +15,7 @@ export class ImageUtils {
}
public static pasteImage(e : any, onLoadEnd: (this: FileReader) => any) {
if (e.clipboardData.files.length == 0) {
if (e.clipboardData.files.length === 0) {
return;
}
@@ -31,4 +31,4 @@ export class ImageUtils {
return () => window.removeEventListener("paste", pasteImage);
});
}
}
}

View File

@@ -10128,7 +10128,7 @@ react-transition-group@^4.0.0:
loose-envify "^1.4.0"
prop-types "^15.6.2"
react@16.12.0:
react@~16.12.0:
version "16.12.0"
resolved "https://registry.yarnpkg.com/react/-/react-16.12.0.tgz#0c0a9c6a142429e3614834d5a778e18aa78a0b83"
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"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript@3.4.5:
version "3.4.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.5.tgz#2d2618d10bb566572b8d7aad5180d84257d70a99"
integrity sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==
typescript@^3.2.2:
typescript@^3.2.2, typescript@~3.7.4:
version "3.7.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.4.tgz#1743a5ec5fef6a1fa9f3e4708e33c81c73876c19"
integrity sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw==