Files
stash/ui/v2.5/src/components/SceneFilenameParser/SceneParserRow.tsx
InfiniteTF 5cd7dcaeb2 Library updates (#792)
* Upgrade Typescript to 4.0
* Update i18n-iso-countries to 6.0
* Update react-intl to 5.8.0
* Update jimp to 0.16.1
* Update apollo and graphql libraries
* Update various libraries and fix linting/type errors
* Refactor cache invalidation
* Codegen refetch queries
2020-09-11 13:01:00 +10:00

455 lines
13 KiB
TypeScript

import React from "react";
import _ from "lodash";
import { Form } from "react-bootstrap";
import {
ParseSceneFilenamesQuery,
SlimSceneDataFragment,
} from "src/core/generated-graphql";
import {
PerformerSelect,
TagSelect,
StudioSelect,
} from "src/components/Shared";
import { TextUtils } from "src/utils";
class ParserResult<T> {
public value?: T;
public originalValue?: T;
public isSet: boolean = false;
public setOriginalValue(value?: T) {
this.originalValue = value;
this.value = value;
}
public setValue(value?: T) {
if (value) {
this.value = value;
this.isSet = !_.isEqual(this.value, this.originalValue);
}
}
}
export class SceneParserResult {
public id: string;
public filename: string;
public title: ParserResult<string> = new ParserResult<string>();
public date: ParserResult<string> = new ParserResult<string>();
public rating: ParserResult<number> = new ParserResult<number>();
public studio: ParserResult<string> = new ParserResult<string>();
public tags: ParserResult<string[]> = new ParserResult<string[]>();
public performers: ParserResult<string[]> = new ParserResult<string[]>();
public scene: SlimSceneDataFragment;
constructor(
result: ParseSceneFilenamesQuery["parseSceneFilenames"]["results"][0]
) {
this.scene = result.scene;
this.id = this.scene.id;
this.filename = TextUtils.fileNameFromPath(this.scene.path);
this.title.setOriginalValue(this.scene.title ?? undefined);
this.date.setOriginalValue(this.scene.date ?? undefined);
this.rating.setOriginalValue(this.scene.rating ?? undefined);
this.performers.setOriginalValue(this.scene.performers.map((p) => p.id));
this.tags.setOriginalValue(this.scene.tags.map((t) => t.id));
this.studio.setOriginalValue(this.scene.studio?.id);
this.title.setValue(result.title ?? undefined);
this.date.setValue(result.date ?? undefined);
this.rating.setValue(result.rating ?? undefined);
this.performers.setValue(result.performer_ids ?? undefined);
this.tags.setValue(result.tag_ids ?? undefined);
this.studio.setValue(result.studio_id ?? undefined);
}
// returns true if any of its fields have set == true
public isChanged() {
return (
this.title.isSet ||
this.date.isSet ||
this.rating.isSet ||
this.performers.isSet ||
this.studio.isSet ||
this.tags.isSet
);
}
public toSceneUpdateInput() {
return {
id: this.id,
details: this.scene.details,
url: this.scene.url,
rating: this.rating.isSet ? this.rating.value : this.scene.rating,
gallery_id: this.scene.gallery?.id,
title: this.title.isSet ? this.title.value : this.scene.title,
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
? this.performers.value
: this.scene.performers.map((performer) => performer.id),
tag_ids: this.tags.isSet
? this.tags.value
: this.scene.tags.map((tag) => tag.id),
};
}
}
interface ISceneParserFieldProps<T> {
parserResult: ParserResult<T>;
className?: string;
onSetChanged: (isSet: boolean) => void;
onValueChanged: (value: T) => void;
originalParserResult?: ParserResult<T>;
}
function SceneParserStringField(props: ISceneParserFieldProps<string>) {
function maybeValueChanged(value: string) {
if (value !== props.parserResult.value) {
props.onValueChanged(value);
}
}
const result = props.originalParserResult || props.parserResult;
return (
<>
<td>
<Form.Check
checked={props.parserResult.isSet}
onChange={() => {
props.onSetChanged(!props.parserResult.isSet);
}}
/>
</td>
<td>
<Form.Group>
<Form.Control
readOnly
className={props.className}
defaultValue={result.originalValue || ""}
/>
<Form.Control
readOnly={!props.parserResult.isSet}
className={props.className}
value={props.parserResult.value || ""}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
maybeValueChanged(event.currentTarget.value)
}
/>
</Form.Group>
</td>
</>
);
}
function SceneParserRatingField(
props: ISceneParserFieldProps<number | undefined>
) {
function maybeValueChanged(value?: number) {
if (value !== props.parserResult.value) {
props.onValueChanged(value);
}
}
const result = props.originalParserResult || props.parserResult;
const options = ["", 1, 2, 3, 4, 5];
return (
<>
<td>
<Form.Check
checked={props.parserResult.isSet}
onChange={() => {
props.onSetChanged(!props.parserResult.isSet);
}}
/>
</td>
<td>
<Form.Group>
<Form.Control
readOnly
className={props.className}
defaultValue={result.originalValue || ""}
/>
<Form.Control
as="select"
className={props.className}
disabled={!props.parserResult.isSet}
value={props.parserResult.value?.toString()}
onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
maybeValueChanged(
event.currentTarget.value === ""
? undefined
: Number.parseInt(event.currentTarget.value, 10)
)
}
>
{options.map((opt) => (
<option value={opt} key={opt}>
{opt}
</option>
))}
</Form.Control>
</Form.Group>
</td>
</>
);
}
function SceneParserPerformerField(props: ISceneParserFieldProps<string[]>) {
function maybeValueChanged(value: string[]) {
if (value !== props.parserResult.value) {
props.onValueChanged(value);
}
}
const originalPerformers = (props.originalParserResult?.originalValue ??
[]) as string[];
const newPerformers = props.parserResult.value ?? [];
return (
<>
<td>
<Form.Check
checked={props.parserResult.isSet}
onChange={() => {
props.onSetChanged(!props.parserResult.isSet);
}}
/>
</td>
<td>
<Form.Group className={props.className}>
<PerformerSelect isDisabled isMulti ids={originalPerformers} />
<PerformerSelect
isMulti
isDisabled={!props.parserResult.isSet}
onSelect={(items) => {
maybeValueChanged(items.map((i) => i.id));
}}
ids={newPerformers}
/>
</Form.Group>
</td>
</>
);
}
function SceneParserTagField(props: ISceneParserFieldProps<string[]>) {
function maybeValueChanged(value: string[]) {
if (value !== props.parserResult.value) {
props.onValueChanged(value);
}
}
const originalTags = props.originalParserResult?.originalValue ?? [];
const newTags = props.parserResult.value ?? [];
return (
<>
<td>
<Form.Check
checked={props.parserResult.isSet}
onChange={() => {
props.onSetChanged(!props.parserResult.isSet);
}}
/>
</td>
<td>
<Form.Group className={props.className}>
<TagSelect isDisabled isMulti ids={originalTags} />
<TagSelect
isMulti
isDisabled={!props.parserResult.isSet}
onSelect={(items) => {
maybeValueChanged(items.map((i) => i.id));
}}
ids={newTags}
/>
</Form.Group>
</td>
</>
);
}
function SceneParserStudioField(props: ISceneParserFieldProps<string>) {
function maybeValueChanged(value: string) {
if (value !== props.parserResult.value) {
props.onValueChanged(value);
}
}
const originalStudio = props.originalParserResult?.originalValue
? [props.originalParserResult?.originalValue]
: [];
const newStudio = props.parserResult.value ? [props.parserResult.value] : [];
return (
<>
<td>
<Form.Check
checked={props.parserResult.isSet}
onChange={() => {
props.onSetChanged(!props.parserResult.isSet);
}}
/>
</td>
<td>
<Form.Group className={props.className}>
<StudioSelect isDisabled ids={originalStudio} />
<StudioSelect
isDisabled={!props.parserResult.isSet}
onSelect={(items) => {
maybeValueChanged(items[0].id);
}}
ids={newStudio}
/>
</Form.Group>
</td>
</>
);
}
interface ISceneParserRowProps {
scene: SceneParserResult;
onChange: (changedScene: SceneParserResult) => void;
showFields: Map<string, boolean>;
}
export const SceneParserRow = (props: ISceneParserRowProps) => {
function changeParser<T>(result: ParserResult<T>, isSet: boolean, value?: T) {
const newParser = _.clone(result);
newParser.isSet = isSet;
newParser.value = value;
return newParser;
}
function onTitleChanged(set: boolean, value: string) {
const newResult = _.clone(props.scene);
newResult.title = changeParser(newResult.title, set, value);
props.onChange(newResult);
}
function onDateChanged(set: boolean, value: string) {
const newResult = _.clone(props.scene);
newResult.date = changeParser(newResult.date, set, value);
props.onChange(newResult);
}
function onRatingChanged(set: boolean, value?: number) {
const newResult = _.clone(props.scene);
newResult.rating = changeParser(newResult.rating, set, value);
props.onChange(newResult);
}
function onPerformerIdsChanged(set: boolean, value: string[]) {
const newResult = _.clone(props.scene);
newResult.performers = changeParser(newResult.performers, set, value);
props.onChange(newResult);
}
function onTagIdsChanged(set: boolean, value: string[]) {
const newResult = _.clone(props.scene);
newResult.tags = changeParser(newResult.tags, set, value);
props.onChange(newResult);
}
function onStudioIdChanged(set: boolean, value: string) {
const newResult = _.clone(props.scene);
newResult.studio = changeParser(newResult.studio, set, value);
props.onChange(newResult);
}
return (
<tr className="scene-parser-row">
<td className="text-left parser-field-filename">
{props.scene.filename}
</td>
{props.showFields.get("Title") && (
<SceneParserStringField
key="title"
className="parser-field-title input-control text-input"
parserResult={props.scene.title}
onSetChanged={(isSet) =>
onTitleChanged(isSet, props.scene.title.value ?? "")
}
onValueChanged={(value) =>
onTitleChanged(props.scene.title.isSet, value)
}
/>
)}
{props.showFields.get("Date") && (
<SceneParserStringField
key="date"
className="parser-field-date input-control text-input"
parserResult={props.scene.date}
onSetChanged={(isSet) =>
onDateChanged(isSet, props.scene.date.value ?? "")
}
onValueChanged={(value) =>
onDateChanged(props.scene.date.isSet, value)
}
/>
)}
{props.showFields.get("Rating") && (
<SceneParserRatingField
key="rating"
className="parser-field-rating input-control text-input"
parserResult={props.scene.rating}
onSetChanged={(isSet) =>
onRatingChanged(isSet, props.scene.rating.value ?? undefined)
}
onValueChanged={(value) =>
onRatingChanged(props.scene.rating.isSet, value)
}
/>
)}
{props.showFields.get("Performers") && (
<SceneParserPerformerField
key="performers"
className="parser-field-performers input-control text-input"
parserResult={props.scene.performers}
originalParserResult={props.scene.performers}
onSetChanged={(set) =>
onPerformerIdsChanged(set, props.scene.performers.value ?? [])
}
onValueChanged={(value) =>
onPerformerIdsChanged(props.scene.performers.isSet, value)
}
/>
)}
{props.showFields.get("Tags") && (
<SceneParserTagField
key="tags"
className="parser-field-tags input-control text-input"
parserResult={props.scene.tags}
originalParserResult={props.scene.tags}
onSetChanged={(isSet) =>
onTagIdsChanged(isSet, props.scene.tags.value ?? [])
}
onValueChanged={(value) =>
onTagIdsChanged(props.scene.tags.isSet, value)
}
/>
)}
{props.showFields.get("Studio") && (
<SceneParserStudioField
key="studio"
className="parser-field-studio input-control text-input"
parserResult={props.scene.studio}
originalParserResult={props.scene.studio}
onSetChanged={(set) =>
onStudioIdChanged(set, props.scene.studio.value ?? "")
}
onValueChanged={(value) =>
onStudioIdChanged(props.scene.studio.isSet, value)
}
/>
)}
</tr>
);
};