This commit is contained in:
Infinite
2020-02-13 19:57:25 +01:00
parent 8fe6eb0d77
commit 750759e4bf
20 changed files with 120 additions and 100 deletions

View File

@@ -1,11 +1,11 @@
import React from "react"; import React from "react";
interface IErrorBoundaryProps { interface IErrorBoundaryProps {
children?: React.ReactNode, children?: React.ReactNode;
} }
type ErrorInfo = { type ErrorInfo = {
componentStack: string, componentStack: string;
}; };
interface IErrorBoundaryState { interface IErrorBoundaryState {
@@ -13,7 +13,10 @@ interface IErrorBoundaryState {
errorInfo?: ErrorInfo; errorInfo?: ErrorInfo;
} }
export class ErrorBoundary extends React.Component<IErrorBoundaryProps, IErrorBoundaryState> { export class ErrorBoundary extends React.Component<
IErrorBoundaryProps,
IErrorBoundaryState
> {
constructor(props: IErrorBoundaryProps) { constructor(props: IErrorBoundaryProps) {
super(props); super(props);
this.state = {}; this.state = {};

View File

@@ -26,9 +26,7 @@ export const AddFilter: React.FC<IAddFilterProps> = (
const defaultValue = useRef<string | number | undefined>(); const defaultValue = useRef<string | number | undefined>();
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [criterion, setCriterion] = useState<Criterion>( const [criterion, setCriterion] = useState<Criterion>(new NoneCriterion());
new NoneCriterion()
);
const valueStage = useRef<CriterionValue>(criterion.value); const valueStage = useRef<CriterionValue>(criterion.value);
@@ -143,7 +141,8 @@ export const AddFilter: React.FC<IAddFilterProps> = (
if ( if (
criterion.type !== "performers" && criterion.type !== "performers" &&
criterion.type !== "studios" && criterion.type !== "studios" &&
criterion.type !== "tags") criterion.type !== "tags"
)
return; return;
return ( return (

View File

@@ -73,7 +73,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
} }
function onChangeSortBy(event: React.MouseEvent<SafeAnchor>) { function onChangeSortBy(event: React.MouseEvent<SafeAnchor>) {
const target = event.currentTarget as unknown as HTMLAnchorElement; const target = (event.currentTarget as unknown) as HTMLAnchorElement;
props.onChangeSortBy(target.text); props.onChangeSortBy(target.text);
} }

View File

@@ -132,7 +132,9 @@ export const ParserInput: React.FC<IParserInputProps> = (
<InputGroup className="col-8"> <InputGroup className="col-8">
<Form.Control <Form.Control
id="filename-pattern" id="filename-pattern"
onChange={(e: React.FormEvent<HTMLInputElement>) => setPattern(e.currentTarget.value)} onChange={(e: React.FormEvent<HTMLInputElement>) =>
setPattern(e.currentTarget.value)
}
value={pattern} value={pattern}
/> />
<InputGroup.Append> <InputGroup.Append>
@@ -158,7 +160,9 @@ export const ParserInput: React.FC<IParserInputProps> = (
<Form.Label className="col-2">Ignored words</Form.Label> <Form.Label className="col-2">Ignored words</Form.Label>
<InputGroup className="col-8"> <InputGroup className="col-8">
<Form.Control <Form.Control
onChange={(e: React.FormEvent<HTMLInputElement>) => setIgnoreWords(e.currentTarget.value)} onChange={(e: React.FormEvent<HTMLInputElement>) =>
setIgnoreWords(e.currentTarget.value)
}
value={ignoreWords} value={ignoreWords}
/> />
</InputGroup> </InputGroup>

View File

@@ -10,7 +10,7 @@ import { useToast } from "src/hooks";
import { Pagination } from "src/components/List/Pagination"; import { Pagination } from "src/components/List/Pagination";
import { IParserInput, ParserInput } from "./ParserInput"; import { IParserInput, ParserInput } from "./ParserInput";
import { ParserField } from "./ParserField"; import { ParserField } from "./ParserField";
import { SceneParserResult, SceneParserRow } from './SceneParserRow'; import { SceneParserResult, SceneParserRow } from "./SceneParserRow";
const initialParserInput = { const initialParserInput = {
pattern: "{title}.{ext}", pattern: "{title}.{ext}",

View File

@@ -1,9 +1,9 @@
import React from "react"; import React from "react";
import _ from "lodash"; import _ from "lodash";
import { Form } from 'react-bootstrap'; import { Form } from "react-bootstrap";
import { import {
ParseSceneFilenamesQuery, ParseSceneFilenamesQuery,
SlimSceneDataFragment, SlimSceneDataFragment
} from "src/core/generated-graphql"; } from "src/core/generated-graphql";
import { import {
PerformerSelect, PerformerSelect,
@@ -77,15 +77,9 @@ export class SceneParserResult {
url: this.scene.url, url: this.scene.url,
rating: this.scene.rating, rating: this.scene.rating,
gallery_id: this.scene.gallery?.id, gallery_id: this.scene.gallery?.id,
title: this.title.isSet title: this.title.isSet ? this.title.value : this.scene.title,
? this.title.value date: this.date.isSet ? this.date.value : this.scene.date,
: this.scene.title, studio_id: this.studio.isSet ? this.studio.value : this.scene.studio?.id,
date: this.date.isSet
? this.date.value
: this.scene.date,
studio_id: this.studio.isSet
? this.studio.value
: this.scene.studio?.id,
performer_ids: this.performers.isSet performer_ids: this.performers.isSet
? this.performers.value ? this.performers.value
: this.scene.performers.map(performer => performer.id), : this.scene.performers.map(performer => performer.id),
@@ -135,7 +129,9 @@ function SceneParserStringField(props: ISceneParserFieldProps<string>) {
disabled={!props.parserResult.isSet} disabled={!props.parserResult.isSet}
className={props.className} className={props.className}
value={props.parserResult.value || ""} value={props.parserResult.value || ""}
onChange={(event: React.FormEvent<HTMLInputElement>) => maybeValueChanged(event.currentTarget.value)} onChange={(event: React.FormEvent<HTMLInputElement>) =>
maybeValueChanged(event.currentTarget.value)
}
/> />
</Form.Group> </Form.Group>
</td> </td>
@@ -150,7 +146,8 @@ function SceneParserPerformerField(props: ISceneParserFieldProps<string[]>) {
} }
} }
const originalPerformers = (props.originalParserResult?.originalValue ?? []) as string[]; const originalPerformers = (props.originalParserResult?.originalValue ??
[]) as string[];
const newPerformers = props.parserResult.value ?? []; const newPerformers = props.parserResult.value ?? [];
return ( return (
@@ -165,11 +162,7 @@ function SceneParserPerformerField(props: ISceneParserFieldProps<string[]>) {
</td> </td>
<td> <td>
<Form.Group className={props.className}> <Form.Group className={props.className}>
<PerformerSelect <PerformerSelect isDisabled isMulti ids={originalPerformers} />
isDisabled
isMulti
ids={originalPerformers}
/>
<PerformerSelect <PerformerSelect
isMulti isMulti
onSelect={items => { onSelect={items => {
@@ -205,11 +198,7 @@ function SceneParserTagField(props: ISceneParserFieldProps<string[]>) {
</td> </td>
<td> <td>
<Form.Group className={props.className}> <Form.Group className={props.className}>
<TagSelect <TagSelect isDisabled isMulti ids={originalTags} />
isDisabled
isMulti
ids={originalTags}
/>
<TagSelect <TagSelect
isMulti isMulti
onSelect={items => { onSelect={items => {
@@ -230,7 +219,9 @@ function SceneParserStudioField(props: ISceneParserFieldProps<string>) {
} }
} }
const originalStudio = props.originalParserResult?.originalValue ? [props.originalParserResult?.originalValue] : []; const originalStudio = props.originalParserResult?.originalValue
? [props.originalParserResult?.originalValue]
: [];
const newStudio = props.parserResult.value ? [props.parserResult.value] : []; const newStudio = props.parserResult.value ? [props.parserResult.value] : [];
return ( return (
@@ -245,10 +236,7 @@ function SceneParserStudioField(props: ISceneParserFieldProps<string>) {
</td> </td>
<td> <td>
<Form.Group className={props.className}> <Form.Group className={props.className}>
<StudioSelect <StudioSelect isDisabled ids={originalStudio} />
isDisabled
ids={originalStudio}
/>
<StudioSelect <StudioSelect
onSelect={items => { onSelect={items => {
maybeValueChanged(items[0].id); maybeValueChanged(items[0].id);
@@ -317,9 +305,11 @@ export const SceneParserRow = (props: ISceneParserRowProps) => {
className="parser-field-title" className="parser-field-title"
parserResult={props.scene.title} parserResult={props.scene.title}
onSetChanged={isSet => onSetChanged={isSet =>
onTitleChanged(isSet, props.scene.title.value ?? '') onTitleChanged(isSet, props.scene.title.value ?? "")
}
onValueChanged={value =>
onTitleChanged(props.scene.title.isSet, value)
} }
onValueChanged={value => onTitleChanged(props.scene.title.isSet, value)}
/> />
)} )}
{props.showFields.get("Date") && ( {props.showFields.get("Date") && (
@@ -329,7 +319,7 @@ export const SceneParserRow = (props: ISceneParserRowProps) => {
className="parser-field-date" className="parser-field-date"
parserResult={props.scene.date} parserResult={props.scene.date}
onSetChanged={isSet => onSetChanged={isSet =>
onDateChanged(isSet, props.scene.date.value ?? '') onDateChanged(isSet, props.scene.date.value ?? "")
} }
onValueChanged={value => onDateChanged(props.scene.date.isSet, value)} onValueChanged={value => onDateChanged(props.scene.date.isSet, value)}
/> />
@@ -342,10 +332,7 @@ export const SceneParserRow = (props: ISceneParserRowProps) => {
parserResult={props.scene.performers} parserResult={props.scene.performers}
originalParserResult={props.scene.performers} originalParserResult={props.scene.performers}
onSetChanged={set => onSetChanged={set =>
onPerformerIdsChanged( onPerformerIdsChanged(set, props.scene.performers.value ?? [])
set,
props.scene.performers.value ?? []
)
} }
onValueChanged={value => onValueChanged={value =>
onPerformerIdsChanged(props.scene.performers.isSet, value) onPerformerIdsChanged(props.scene.performers.isSet, value)
@@ -375,11 +362,13 @@ export const SceneParserRow = (props: ISceneParserRowProps) => {
parserResult={props.scene.studio} parserResult={props.scene.studio}
originalParserResult={props.scene.studio} originalParserResult={props.scene.studio}
onSetChanged={set => onSetChanged={set =>
onStudioIdChanged(set, props.scene.studio.value ?? '') onStudioIdChanged(set, props.scene.studio.value ?? "")
}
onValueChanged={value =>
onStudioIdChanged(props.scene.studio.isSet, value)
} }
onValueChanged={value => onStudioIdChanged(props.scene.studio.isSet, value)}
/> />
)} )}
</tr> </tr>
); );
} };

View File

@@ -69,9 +69,10 @@ export class ScenePlayerImpl extends React.Component<
public UNSAFE_componentWillReceiveProps(props: IScenePlayerProps) { public UNSAFE_componentWillReceiveProps(props: IScenePlayerProps) {
if (props.scene !== this.props.scene) { if (props.scene !== this.props.scene) {
this.setState( state => ( this.setState(state => ({
{ ...state, config: this.makeJWPlayerConfig(this.props.scene) } ...state,
)); config: this.makeJWPlayerConfig(this.props.scene)
}));
} }
} }
@@ -147,7 +148,9 @@ export class ScenePlayerImpl extends React.Component<
let seekHook: let seekHook:
| ((seekToPosition: number, _videoTag: HTMLVideoElement) => void) | ((seekToPosition: number, _videoTag: HTMLVideoElement) => void)
| undefined; | undefined;
let getCurrentTimeHook: ((_videoTag: HTMLVideoElement) => number) | undefined; let getCurrentTimeHook:
| ((_videoTag: HTMLVideoElement) => number)
| undefined;
if (!this.props.scene.is_streamable) { if (!this.props.scene.is_streamable) {
getDurationHook = () => { getDurationHook = () => {
@@ -163,7 +166,7 @@ export class ScenePlayerImpl extends React.Component<
}; };
getCurrentTimeHook = (_videoTag: HTMLVideoElement) => { getCurrentTimeHook = (_videoTag: HTMLVideoElement) => {
const start = Number.parseInt(_videoTag.dataset?.start ?? '0', 10); const start = Number.parseInt(_videoTag.dataset?.start ?? "0", 10);
return _videoTag.currentTime + start; return _videoTag.currentTime + start;
}; };
} }

View File

@@ -293,7 +293,8 @@ export const ScenePlayerScrubber: React.FC<IScenePlayerScrubberProps> = (
const percentage = marker.seconds / duration; const percentage = marker.seconds / duration;
const left = const left =
scrubberSliderEl.current.scrollWidth * percentage - tag!.clientWidth / 2; scrubberSliderEl.current.scrollWidth * percentage -
tag!.clientWidth / 2;
return { return {
left: `${left}px`, left: `${left}px`,
height: 20 height: 20

View File

@@ -305,7 +305,9 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
<td>URL</td> <td>URL</td>
<td> <td>
<Form.Control <Form.Control
onChange={(newValue: React.FormEvent<HTMLInputElement>) => setUrl(newValue.currentTarget.value)} onChange={(newValue: React.FormEvent<HTMLInputElement>) =>
setUrl(newValue.currentTarget.value)
}
value={url} value={url}
placeholder="URL" placeholder="URL"
/> />
@@ -376,7 +378,9 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
<Form.Control <Form.Control
as="textarea" as="textarea"
className="scene-description" className="scene-description"
onChange={(newValue: React.FormEvent<HTMLTextAreaElement>) => setDetails(newValue.currentTarget.value)} onChange={(newValue: React.FormEvent<HTMLTextAreaElement>) =>
setDetails(newValue.currentTarget.value)
}
value={details} value={details}
/> />
</Form.Group> </Form.Group>

View File

@@ -265,7 +265,9 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (
<Form.Control <Form.Control
as="select" as="select"
value={rating} value={rating}
onChange={(event: React.FormEvent<HTMLSelectElement>) => setRating(event.currentTarget.value)} onChange={(event: React.FormEvent<HTMLSelectElement>) =>
setRating(event.currentTarget.value)
}
> >
{["", "1", "2", "3", "4", "5"].map(opt => ( {["", "1", "2", "3", "4", "5"].map(opt => (
<option key={opt} value={opt}> <option key={opt} value={opt}>

View File

@@ -175,7 +175,9 @@ export const SettingsConfigurationPanel: React.FC = () => {
<Form.Control <Form.Control
className="col col-sm-6" className="col col-sm-6"
defaultValue={databasePath} defaultValue={databasePath}
onChange={(e: React.FormEvent<HTMLInputElement>) => setDatabasePath(e.currentTarget.value)} onChange={(e: React.FormEvent<HTMLInputElement>) =>
setDatabasePath(e.currentTarget.value)
}
/> />
<Form.Text className="text-muted"> <Form.Text className="text-muted">
File location for the SQLite database (requires restart) File location for the SQLite database (requires restart)
@@ -187,7 +189,9 @@ export const SettingsConfigurationPanel: React.FC = () => {
<Form.Control <Form.Control
className="col col-sm-6" className="col col-sm-6"
defaultValue={generatedPath} defaultValue={generatedPath}
onChange={(e: React.FormEvent<HTMLInputElement>) => setGeneratedPath(e.currentTarget.value)} onChange={(e: React.FormEvent<HTMLInputElement>) =>
setGeneratedPath(e.currentTarget.value)
}
/> />
<Form.Text className="text-muted"> <Form.Text className="text-muted">
Directory location for the generated files (scene markers, scene Directory location for the generated files (scene markers, scene

View File

@@ -141,7 +141,9 @@ export const SettingsInterfacePanel: React.FC = () => {
<Form.Control <Form.Control
as="textarea" as="textarea"
value={css} value={css}
onChange={(e: React.FormEvent<HTMLTextAreaElement>) => setCSS(e.currentTarget.value)} onChange={(e: React.FormEvent<HTMLTextAreaElement>) =>
setCSS(e.currentTarget.value)
}
rows={16} rows={16}
className="col col-sm-6" className="col col-sm-6"
></Form.Control> ></Form.Control>

View File

@@ -78,7 +78,9 @@ export const DurationInput: React.FC<IProps> = (props: IProps) => {
className="duration-control" className="duration-control"
disabled={props.disabled} disabled={props.disabled}
value={value} value={value}
onChange={(e: React.FormEvent<HTMLInputElement>) => setValue(e.currentTarget.value)} onChange={(e: React.FormEvent<HTMLInputElement>) =>
setValue(e.currentTarget.value)
}
onBlur={() => onBlur={() =>
props.onValueChange(DurationUtils.stringToSeconds(value)) props.onValueChange(DurationUtils.stringToSeconds(value))
} }

View File

@@ -51,7 +51,9 @@ export const FolderSelect: React.FC<IProps> = (props: IProps) => {
<InputGroup> <InputGroup>
<Form.Control <Form.Control
placeholder="File path" placeholder="File path"
onChange={(e: React.FormEvent<HTMLInputElement>) => setCurrentDirectory(e.currentTarget.value)} onChange={(e: React.FormEvent<HTMLInputElement>) =>
setCurrentDirectory(e.currentTarget.value)
}
defaultValue={currentDirectory} defaultValue={currentDirectory}
/> />
<InputGroup.Append> <InputGroup.Append>

View File

@@ -287,7 +287,9 @@ export const TagSelect: React.FC<IFilterProps> = props => {
const onChange = (selectedItems: ValueType<Option>) => { const onChange = (selectedItems: ValueType<Option>) => {
const selectedValues = getSelectedValues(selectedItems); const selectedValues = getSelectedValues(selectedItems);
setSelectedIds(selectedValues); setSelectedIds(selectedValues);
props.onSelect?.(tags.filter(item => selectedValues.indexOf(item.id) !== -1)); props.onSelect?.(
tags.filter(item => selectedValues.indexOf(item.id) !== -1)
);
}; };
return ( return (
@@ -361,7 +363,7 @@ const SelectComponent: React.FC<ISelectProps & ITypeProps> = ({
isClearable, isClearable,
defaultValue, defaultValue,
noOptionsMessage: () => (type !== "tags" ? "None" : null), noOptionsMessage: () => (type !== "tags" ? "None" : null),
placeholder: isDisabled ? '' : placeholder, placeholder: isDisabled ? "" : placeholder,
onInputChange, onInputChange,
isDisabled, isDisabled,
isLoading, isLoading,

View File

@@ -160,7 +160,9 @@ export const TagList: React.FC = () => {
<Form.Group controlId="tag-name"> <Form.Group controlId="tag-name">
<Form.Label>Name</Form.Label> <Form.Label>Name</Form.Label>
<Form.Control <Form.Control
onChange={(newValue:React.FormEvent<HTMLInputElement>) => setName(newValue.currentTarget.value)} onChange={(newValue: React.FormEvent<HTMLInputElement>) =>
setName(newValue.currentTarget.value)
}
defaultValue={(editingTag && editingTag.name) || ""} defaultValue={(editingTag && editingTag.name) || ""}
/> />
</Form.Group> </Form.Group>

View File

@@ -16,7 +16,10 @@ import {
FindStudiosQueryResult, FindStudiosQueryResult,
FindPerformersQueryResult FindPerformersQueryResult
} from "src/core/generated-graphql"; } from "src/core/generated-graphql";
import { useInterfaceLocalForage, IInterfaceConfig } from "src/hooks/LocalForage"; import {
useInterfaceLocalForage,
IInterfaceConfig
} from "src/hooks/LocalForage";
import { LoadingIndicator } from "src/components/Shared"; import { LoadingIndicator } from "src/components/Shared";
import { ListFilter } from "src/components/List/ListFilter"; import { ListFilter } from "src/components/List/ListFilter";
import { Pagination } from "src/components/List/Pagination"; import { Pagination } from "src/components/List/Pagination";
@@ -142,7 +145,7 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
itemsPerPage: newFilter.itemsPerPage, itemsPerPage: newFilter.itemsPerPage,
currentPage: newFilter.currentPage currentPage: newFilter.currentPage
} }
} };
return data; return data;
}); });
} }
@@ -214,9 +217,7 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
// Remove duplicate modifiers // Remove duplicate modifiers
newFilter.criteria = newFilter.criteria.filter((obj, pos, arr) => { newFilter.criteria = newFilter.criteria.filter((obj, pos, arr) => {
return ( return arr.map(mapObj => mapObj.getId()).indexOf(obj.getId()) === pos;
arr.map(mapObj => mapObj.getId()).indexOf(obj.getId()) === pos
);
}); });
newFilter.currentPage = 1; newFilter.currentPage = 1;

View File

@@ -26,7 +26,9 @@ interface ILocalForage<T> {
const Loading: Record<string, boolean> = {}; const Loading: Record<string, boolean> = {};
const Cache: Record<string, ValidTypes> = {}; const Cache: Record<string, ValidTypes> = {};
function useLocalForage(key: Key): [ILocalForage<ValidTypes>, Dispatch<SetStateAction<ValidTypes>>] { function useLocalForage(
key: Key
): [ILocalForage<ValidTypes>, Dispatch<SetStateAction<ValidTypes>>] {
const [error, setError] = React.useState(null); const [error, setError] = React.useState(null);
const [data, setData] = React.useState(Cache[key]); const [data, setData] = React.useState(Cache[key]);
const [loading, setLoading] = React.useState(Loading[key]); const [loading, setLoading] = React.useState(Loading[key]);
@@ -67,9 +69,9 @@ function useLocalForage(key: Key): [ILocalForage<ValidTypes>, Dispatch<SetStateA
return [{ data, error, loading: isLoading }, setData]; return [{ data, error, loading: isLoading }, setData];
} }
export function useInterfaceLocalForage(): export function useInterfaceLocalForage(): [
[ILocalForage<IInterfaceConfig>, ILocalForage<IInterfaceConfig>,
Dispatch<SetStateAction<IInterfaceConfig>>] Dispatch<SetStateAction<IInterfaceConfig>>
{ ] {
return useLocalForage("interface"); return useLocalForage("interface");
} }

View File

@@ -121,11 +121,9 @@ export abstract class Criterion {
public inputType: "number" | "text" | undefined; public inputType: "number" | "text" | undefined;
public getLabelValue(): string { public getLabelValue(): string {
if(typeof this.value === "string") if (typeof this.value === "string") return this.value;
return this.value; if (typeof this.value === "number") return this.value.toString();
if(typeof this.value === "number") return this.value.map(v => v.label).join(", ");
return this.value.toString();
return this.value.map(v => v.label).join(', ');
} }
public getLabel(): string { public getLabel(): string {

View File

@@ -271,7 +271,7 @@ export class ListFilterModel {
const encodedCriterion: Partial<Criterion> = { const encodedCriterion: Partial<Criterion> = {
type: criterion.type, type: criterion.type,
value: criterion.value, value: criterion.value,
modifier: criterion.modifier, modifier: criterion.modifier
}; };
const jsonCriterion = JSON.stringify(encodedCriterion); const jsonCriterion = JSON.stringify(encodedCriterion);
encodedCriteria.push(jsonCriterion); encodedCriteria.push(jsonCriterion);