mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 12:24:38 +03:00
Remove hotkeys and fix tag selection (#505)
* Remove broken scene player hotkeys * Disable closing tag select menu after a select
This commit is contained in:
@@ -46,7 +46,6 @@
|
|||||||
"react-apollo": "^3.1.3",
|
"react-apollo": "^3.1.3",
|
||||||
"react-bootstrap": "^1.0.0-beta.16",
|
"react-bootstrap": "^1.0.0-beta.16",
|
||||||
"react-dom": "16.12.0",
|
"react-dom": "16.12.0",
|
||||||
"react-hotkeys": "^2.0.0",
|
|
||||||
"react-images": "0.5.19",
|
"react-images": "0.5.19",
|
||||||
"react-intl": "^3.12.0",
|
"react-intl": "^3.12.0",
|
||||||
"react-jw-player": "1.19.0",
|
"react-jw-player": "1.19.0",
|
||||||
|
|||||||
@@ -45,7 +45,10 @@ export const GalleryList: React.FC = () => {
|
|||||||
</Link>
|
</Link>
|
||||||
</td>
|
</td>
|
||||||
<td className="d-none d-sm-block">
|
<td className="d-none d-sm-block">
|
||||||
<Link to={`/galleries/${gallery.id}`}>{gallery.path} ({gallery.files.length} {gallery.files.length === 1 ? 'image' : 'images'})</Link>
|
<Link to={`/galleries/${gallery.id}`}>
|
||||||
|
{gallery.path} ({gallery.files.length}{" "}
|
||||||
|
{gallery.files.length === 1 ? "image" : "images"})
|
||||||
|
</Link>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ export const Movie: React.FC = () => {
|
|||||||
})}
|
})}
|
||||||
{TableUtils.renderHtmlSelect({
|
{TableUtils.renderHtmlSelect({
|
||||||
title: "Rating",
|
title: "Rating",
|
||||||
value: rating ? rating : "",
|
value: rating ?? "",
|
||||||
isEditing,
|
isEditing,
|
||||||
onChange: (value: string) =>
|
onChange: (value: string) =>
|
||||||
setRating(Number.parseInt(value, 10)),
|
setRating(Number.parseInt(value, 10)),
|
||||||
|
|||||||
@@ -189,7 +189,10 @@ export const Performer: React.FC = () => {
|
|||||||
{performer.twitter && (
|
{performer.twitter && (
|
||||||
<Button className="minimal">
|
<Button className="minimal">
|
||||||
<a
|
<a
|
||||||
href={TextUtils.sanitiseURL(performer.twitter, TextUtils.twitterURL)}
|
href={TextUtils.sanitiseURL(
|
||||||
|
performer.twitter,
|
||||||
|
TextUtils.twitterURL
|
||||||
|
)}
|
||||||
className="twitter"
|
className="twitter"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
@@ -201,7 +204,10 @@ export const Performer: React.FC = () => {
|
|||||||
{performer.instagram && (
|
{performer.instagram && (
|
||||||
<Button className="minimal">
|
<Button className="minimal">
|
||||||
<a
|
<a
|
||||||
href={TextUtils.sanitiseURL(performer.instagram, TextUtils.instagramURL)}
|
href={TextUtils.sanitiseURL(
|
||||||
|
performer.instagram,
|
||||||
|
TextUtils.instagramURL
|
||||||
|
)}
|
||||||
className="instagram"
|
className="instagram"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
@@ -215,18 +221,14 @@ export const Performer: React.FC = () => {
|
|||||||
|
|
||||||
function renderPerformerImage() {
|
function renderPerformerImage() {
|
||||||
if (imagePreview) {
|
if (imagePreview) {
|
||||||
return (
|
return <img className="photo" src={imagePreview} alt="Performer" />;
|
||||||
<img className="photo" src={imagePreview} alt="Performer" />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderNewView() {
|
function renderNewView() {
|
||||||
return (
|
return (
|
||||||
<div className="row new-view">
|
<div className="row new-view">
|
||||||
<div className="col-4">
|
<div className="col-4">{renderPerformerImage()}</div>
|
||||||
{renderPerformerImage()}
|
|
||||||
</div>
|
|
||||||
<div className="col-6">
|
<div className="col-6">
|
||||||
<h2>Create Performer</h2>
|
<h2>Create Performer</h2>
|
||||||
{renderTabs()}
|
{renderTabs()}
|
||||||
|
|||||||
@@ -11,7 +11,12 @@ import {
|
|||||||
ScrapePerformerSuggest,
|
ScrapePerformerSuggest,
|
||||||
LoadingIndicator,
|
LoadingIndicator,
|
||||||
} from "src/components/Shared";
|
} from "src/components/Shared";
|
||||||
import { ImageUtils, TableUtils, TextUtils, EditableTextUtils } from "src/utils";
|
import {
|
||||||
|
ImageUtils,
|
||||||
|
TableUtils,
|
||||||
|
TextUtils,
|
||||||
|
EditableTextUtils,
|
||||||
|
} from "src/utils";
|
||||||
import { useToast } from "src/hooks";
|
import { useToast } from "src/hooks";
|
||||||
|
|
||||||
interface IPerformerDetails {
|
interface IPerformerDetails {
|
||||||
@@ -100,22 +105,22 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function translateScrapedGender(gender?: string) {
|
function translateScrapedGender(scrapedGender?: string) {
|
||||||
if (!gender) {
|
if (!scrapedGender) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let retEnum: GQL.GenderEnum | undefined;
|
let retEnum: GQL.GenderEnum | undefined;
|
||||||
|
|
||||||
// try to translate from enum values first
|
// try to translate from enum values first
|
||||||
const upperGender = gender?.toUpperCase();
|
const upperGender = scrapedGender?.toUpperCase();
|
||||||
const asEnum = StashService.genderToString(upperGender as GQL.GenderEnum);
|
const asEnum = StashService.genderToString(upperGender as GQL.GenderEnum);
|
||||||
if (asEnum) {
|
if (asEnum) {
|
||||||
retEnum = StashService.stringToGender(asEnum);
|
retEnum = StashService.stringToGender(asEnum);
|
||||||
} else {
|
} else {
|
||||||
// try to match against gender strings
|
// try to match against gender strings
|
||||||
const caseInsensitive = true;
|
const caseInsensitive = true;
|
||||||
retEnum = StashService.stringToGender(gender, caseInsensitive);
|
retEnum = StashService.stringToGender(scrapedGender, caseInsensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
return StashService.genderToString(retEnum);
|
return StashService.genderToString(retEnum);
|
||||||
@@ -131,7 +136,10 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
|||||||
|
|
||||||
// image is a base64 string
|
// image is a base64 string
|
||||||
// #404: don't overwrite image if it has been modified by the user
|
// #404: don't overwrite image if it has been modified by the user
|
||||||
if (image === undefined && (state as GQL.ScrapedPerformerDataFragment).image !== undefined) {
|
if (
|
||||||
|
image === undefined &&
|
||||||
|
(state as GQL.ScrapedPerformerDataFragment).image !== undefined
|
||||||
|
) {
|
||||||
const imageStr = (state as GQL.ScrapedPerformerDataFragment).image;
|
const imageStr = (state as GQL.ScrapedPerformerDataFragment).image;
|
||||||
setImage(imageStr ?? undefined);
|
setImage(imageStr ?? undefined);
|
||||||
if (onImageChange) {
|
if (onImageChange) {
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ export const ParserInput: React.FC<IParserInputProps> = (
|
|||||||
key={item.field}
|
key={item.field}
|
||||||
onSelect={() => addParserField(item)}
|
onSelect={() => addParserField(item)}
|
||||||
>
|
>
|
||||||
<span>{item.field || "{}"}</span>
|
<span className="mr-2">{item.field || "{}"}</span>
|
||||||
<span className="ml-auto text-muted">{item.helperText}</span>
|
<span className="ml-auto text-muted">{item.helperText}</span>
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactJWPlayer from "react-jw-player";
|
import ReactJWPlayer from "react-jw-player";
|
||||||
import { HotKeys } from "react-hotkeys";
|
|
||||||
import * as GQL from "src/core/generated-graphql";
|
import * as GQL from "src/core/generated-graphql";
|
||||||
import { StashService } from "src/core/StashService";
|
import { StashService } from "src/core/StashService";
|
||||||
import { JWUtils } from "src/utils";
|
import { JWUtils } from "src/utils";
|
||||||
@@ -21,13 +20,6 @@ interface IScenePlayerState {
|
|||||||
config: Record<string, any>;
|
config: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const KeyMap = {
|
|
||||||
NUM0: "0",
|
|
||||||
NUM1: "1",
|
|
||||||
NUM2: "2",
|
|
||||||
SPACE: " ",
|
|
||||||
};
|
|
||||||
|
|
||||||
export class ScenePlayerImpl extends React.Component<
|
export class ScenePlayerImpl extends React.Component<
|
||||||
IScenePlayerProps,
|
IScenePlayerProps,
|
||||||
IScenePlayerState
|
IScenePlayerState
|
||||||
@@ -37,21 +29,6 @@ export class ScenePlayerImpl extends React.Component<
|
|||||||
private player: any;
|
private player: any;
|
||||||
private lastTime = 0;
|
private lastTime = 0;
|
||||||
|
|
||||||
private KeyHandlers = {
|
|
||||||
NUM0: () => {
|
|
||||||
this.onReset();
|
|
||||||
},
|
|
||||||
NUM1: () => {
|
|
||||||
this.onDecrease();
|
|
||||||
},
|
|
||||||
NUM2: () => {
|
|
||||||
this.onIncrease();
|
|
||||||
},
|
|
||||||
SPACE: () => {
|
|
||||||
this.onPause();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props: IScenePlayerProps) {
|
constructor(props: IScenePlayerProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.onReady = this.onReady.bind(this);
|
this.onReady = this.onReady.bind(this);
|
||||||
@@ -207,11 +184,6 @@ export class ScenePlayerImpl extends React.Component<
|
|||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
return (
|
return (
|
||||||
<HotKeys
|
|
||||||
keyMap={KeyMap}
|
|
||||||
handlers={this.KeyHandlers}
|
|
||||||
className="row scene-player"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
id="jwplayer-container"
|
id="jwplayer-container"
|
||||||
className="w-100 col-sm-9 m-sm-auto no-gutter"
|
className="w-100 col-sm-9 m-sm-auto no-gutter"
|
||||||
@@ -231,7 +203,6 @@ export class ScenePlayerImpl extends React.Component<
|
|||||||
onScrolled={this.onScrubberScrolled}
|
onScrolled={this.onScrubberScrolled}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</HotKeys>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -182,7 +182,9 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button className="minimal">
|
<Button className="minimal">
|
||||||
<span className="fa-icon"><SweatDrops /></span>
|
<span className="fa-icon">
|
||||||
|
<SweatDrops />
|
||||||
|
</span>
|
||||||
<span>{props.scene.o_counter}</span>
|
<span>{props.scene.o_counter}</span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -151,9 +151,7 @@ export const SceneFileInfoPanel: React.FC<ISceneFileInfoPanelProps> = (
|
|||||||
<div className="row">
|
<div className="row">
|
||||||
<span className="col-4">Downloaded From</span>
|
<span className="col-4">Downloaded From</span>
|
||||||
<span className="col-8 text-truncate">
|
<span className="col-8 text-truncate">
|
||||||
<a href={TextUtils.sanitiseURL(props.scene.url)}>
|
<a href={TextUtils.sanitiseURL(props.scene.url)}>{props.scene.url}</a>
|
||||||
{props.scene.url}
|
|
||||||
</a>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
numericValue={Number.parseInt(fieldProps.field.value ?? "0", 10)}
|
numericValue={Number.parseInt(fieldProps.field.value ?? "0", 10)}
|
||||||
mandatory={true}
|
mandatory
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -307,7 +307,8 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
onChange={() => setForceMkv(!forceMkv)}
|
onChange={() => setForceMkv(!forceMkv)}
|
||||||
/>
|
/>
|
||||||
<Form.Text className="text-muted">
|
<Form.Text className="text-muted">
|
||||||
Treat Matroska (MKV) as a supported container. Recommended for Chromium based browsers
|
Treat Matroska (MKV) as a supported container. Recommended for
|
||||||
|
Chromium based browsers
|
||||||
</Form.Text>
|
</Form.Text>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Form.Group id="force-options-hevc">
|
<Form.Group id="force-options-hevc">
|
||||||
@@ -318,7 +319,8 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
onChange={() => setForceHevc(!forceHevc)}
|
onChange={() => setForceHevc(!forceHevc)}
|
||||||
/>
|
/>
|
||||||
<Form.Text className="text-muted">
|
<Form.Text className="text-muted">
|
||||||
Treat HEVC as a supported codec. Recommended for Safari or some Android based browsers
|
Treat HEVC as a supported codec. Recommended for Safari or some
|
||||||
|
Android based browsers
|
||||||
</Form.Text>
|
</Form.Text>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
|
|||||||
@@ -68,9 +68,10 @@ class LogEntry {
|
|||||||
const MAX_LOG_ENTRIES = 200;
|
const MAX_LOG_ENTRIES = 200;
|
||||||
const logLevels = ["Debug", "Info", "Warning", "Error"];
|
const logLevels = ["Debug", "Info", "Warning", "Error"];
|
||||||
|
|
||||||
const logReducer = (existingEntries:LogEntry[], newEntries:LogEntry[]) => (
|
const logReducer = (existingEntries: LogEntry[], newEntries: LogEntry[]) => [
|
||||||
[...newEntries.reverse(), ...existingEntries]
|
...newEntries.reverse(),
|
||||||
);
|
...existingEntries,
|
||||||
|
];
|
||||||
|
|
||||||
export const SettingsLogsPanel: React.FC = () => {
|
export const SettingsLogsPanel: React.FC = () => {
|
||||||
const { data, error } = StashService.useLoggingSubscribe();
|
const { data, error } = StashService.useLoggingSubscribe();
|
||||||
@@ -78,10 +79,9 @@ export const SettingsLogsPanel: React.FC = () => {
|
|||||||
const [currentData, dispatchLogUpdate] = useReducer(logReducer, []);
|
const [currentData, dispatchLogUpdate] = useReducer(logReducer, []);
|
||||||
const [logLevel, setLogLevel] = useState<string>("Info");
|
const [logLevel, setLogLevel] = useState<string>("Info");
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const newData = (data?.loggingSubscribe ?? []).map((e) => new LogEntry(e));
|
const newData = (data?.loggingSubscribe ?? []).map((e) => new LogEntry(e));
|
||||||
dispatchLogUpdate(newData)
|
dispatchLogUpdate(newData);
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
const oldData = (existingData?.logs ?? []).map((e) => new LogEntry(e));
|
const oldData = (existingData?.logs ?? []).map((e) => new LogEntry(e));
|
||||||
|
|||||||
@@ -105,9 +105,9 @@ export const SettingsTasksPanel: React.FC = () => {
|
|||||||
cancel={{ onClick: () => setIsCleanAlertOpen(false) }}
|
cancel={{ onClick: () => setIsCleanAlertOpen(false) }}
|
||||||
>
|
>
|
||||||
<p>
|
<p>
|
||||||
Are you sure you want to Clean? This will delete database information and
|
Are you sure you want to Clean? This will delete database information
|
||||||
generated content for all scenes and galleries that are no longer found in the
|
and generated content for all scenes and galleries that are no longer
|
||||||
filesystem.
|
found in the filesystem.
|
||||||
</p>
|
</p>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,14 +7,19 @@ interface IProps {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
numericValue: number | undefined;
|
numericValue: number | undefined;
|
||||||
mandatory?: boolean;
|
mandatory?: boolean;
|
||||||
onValueChange(valueAsNumber: number | undefined, valueAsString?: string): void;
|
onValueChange(
|
||||||
|
valueAsNumber: number | undefined,
|
||||||
|
valueAsString?: string
|
||||||
|
): void;
|
||||||
onReset?(): void;
|
onReset?(): void;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DurationInput: React.FC<IProps> = (props: IProps) => {
|
export const DurationInput: React.FC<IProps> = (props: IProps) => {
|
||||||
const [value, setValue] = useState<string | undefined>(
|
const [value, setValue] = useState<string | undefined>(
|
||||||
props.numericValue !== undefined ? DurationUtils.secondsToString(props.numericValue) : undefined
|
props.numericValue !== undefined
|
||||||
|
? DurationUtils.secondsToString(props.numericValue)
|
||||||
|
: undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export const ImageInput: React.FC<IImageInput> = ({
|
|||||||
<Form.Control
|
<Form.Control
|
||||||
type="file"
|
type="file"
|
||||||
onChange={onImageChange}
|
onChange={onImageChange}
|
||||||
accept={`.jpg,.jpeg,.png${acceptSVG ? ',.svg' : ''}`}
|
accept={`.jpg,.jpeg,.png${acceptSVG ? ",.svg" : ""}`}
|
||||||
/>
|
/>
|
||||||
</Form.Label>
|
</Form.Label>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ interface ISelectProps {
|
|||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
showDropdown?: boolean;
|
showDropdown?: boolean;
|
||||||
groupHeader?: string;
|
groupHeader?: string;
|
||||||
|
closeMenuOnSelect?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ISceneGallerySelect {
|
interface ISceneGallerySelect {
|
||||||
@@ -355,6 +356,7 @@ export const TagSelect: React.FC<IFilterProps> = (props) => {
|
|||||||
items={items}
|
items={items}
|
||||||
onCreateOption={onCreate}
|
onCreateOption={onCreate}
|
||||||
selectedOptions={selected}
|
selectedOptions={selected}
|
||||||
|
closeMenuOnSelect={false}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -376,6 +378,7 @@ const SelectComponent: React.FC<ISelectProps & ITypeProps> = ({
|
|||||||
placeholder,
|
placeholder,
|
||||||
showDropdown = true,
|
showDropdown = true,
|
||||||
groupHeader,
|
groupHeader,
|
||||||
|
closeMenuOnSelect = true,
|
||||||
}) => {
|
}) => {
|
||||||
const defaultValue =
|
const defaultValue =
|
||||||
items.filter((item) => initialIds?.indexOf(item.value) !== -1) ?? null;
|
items.filter((item) => initialIds?.indexOf(item.value) !== -1) ?? null;
|
||||||
@@ -421,6 +424,7 @@ const SelectComponent: React.FC<ISelectProps & ITypeProps> = ({
|
|||||||
isDisabled,
|
isDisabled,
|
||||||
isLoading,
|
isLoading,
|
||||||
styles,
|
styles,
|
||||||
|
closeMenuOnSelect,
|
||||||
components: {
|
components: {
|
||||||
IndicatorSeparator: () => null,
|
IndicatorSeparator: () => null,
|
||||||
...((!showDropdown || isDisabled) && { DropdownIndicator: () => null }),
|
...((!showDropdown || isDisabled) && { DropdownIndicator: () => null }),
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const Stats: React.FC = () => {
|
|||||||
|
|
||||||
if (error) return <span>error.message</span>;
|
if (error) return <span>error.message</span>;
|
||||||
|
|
||||||
var size = data.stats.scene_size_count.split(" ")
|
const size = data.stats.scene_size_count.split(" ");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
@@ -18,7 +18,7 @@ export const Stats: React.FC = () => {
|
|||||||
<div className="stats-element">
|
<div className="stats-element">
|
||||||
<p className="title">
|
<p className="title">
|
||||||
<FormattedNumber value={parseFloat(size[0])} />
|
<FormattedNumber value={parseFloat(size[0])} />
|
||||||
{" " + size[1]}
|
{` ${size[1]}`}
|
||||||
</p>
|
</p>
|
||||||
<p className="heading">
|
<p className="heading">
|
||||||
<FormattedMessage id="library-size" defaultMessage="Library size" />
|
<FormattedMessage id="library-size" defaultMessage="Library size" />
|
||||||
|
|||||||
@@ -196,14 +196,6 @@ div.dropdown-menu {
|
|||||||
|
|
||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
& > * {
|
|
||||||
margin-right: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > :last-child {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,8 +388,8 @@ div.dropdown-menu {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
input[type=file], /* FF, IE7+, chrome (except button) */
|
input[type="file"], /* FF, IE7+, chrome (except button) */
|
||||||
input[type=file]::-webkit-file-upload-button {
|
input[type="file"]::-webkit-file-upload-button {
|
||||||
/* chromes and blink button */
|
/* chromes and blink button */
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export class PerformerIsMissingCriterion extends IsMissingCriterion {
|
|||||||
"piercings",
|
"piercings",
|
||||||
"aliases",
|
"aliases",
|
||||||
"gender",
|
"gender",
|
||||||
"scenes"
|
"scenes",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ import {
|
|||||||
} from "./criterion";
|
} from "./criterion";
|
||||||
import { FavoriteCriterion } from "./favorite";
|
import { FavoriteCriterion } from "./favorite";
|
||||||
import { HasMarkersCriterion } from "./has-markers";
|
import { HasMarkersCriterion } from "./has-markers";
|
||||||
import {PerformerIsMissingCriterion, SceneIsMissingCriterion} from "./is-missing";
|
import {
|
||||||
|
PerformerIsMissingCriterion,
|
||||||
|
SceneIsMissingCriterion,
|
||||||
|
} from "./is-missing";
|
||||||
import { NoneCriterion } from "./none";
|
import { NoneCriterion } from "./none";
|
||||||
import { PerformersCriterion } from "./performers";
|
import { PerformersCriterion } from "./performers";
|
||||||
import { RatingCriterion } from "./rating";
|
import { RatingCriterion } from "./rating";
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
IsMissingCriterion,
|
IsMissingCriterion,
|
||||||
PerformerIsMissingCriterionOption,
|
PerformerIsMissingCriterionOption,
|
||||||
SceneIsMissingCriterionOption
|
SceneIsMissingCriterionOption,
|
||||||
} from "./criteria/is-missing";
|
} from "./criteria/is-missing";
|
||||||
import { NoneCriterionOption } from "./criteria/none";
|
import { NoneCriterionOption } from "./criteria/none";
|
||||||
import {
|
import {
|
||||||
@@ -148,7 +148,9 @@ export class ListFilterModel {
|
|||||||
new FavoriteCriterionOption(),
|
new FavoriteCriterionOption(),
|
||||||
new GenderCriterionOption(),
|
new GenderCriterionOption(),
|
||||||
new PerformerIsMissingCriterionOption(),
|
new PerformerIsMissingCriterionOption(),
|
||||||
...numberCriteria.concat(stringCriteria).map(c => ListFilterModel.createCriterionOption(c))
|
...numberCriteria
|
||||||
|
.concat(stringCriteria)
|
||||||
|
.map((c) => ListFilterModel.createCriterionOption(c)),
|
||||||
];
|
];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const renderTextArea = (options: {
|
|||||||
value={options.value}
|
value={options.value}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const renderEditableText = (options: {
|
const renderEditableText = (options: {
|
||||||
title?: string;
|
title?: string;
|
||||||
@@ -42,8 +42,8 @@ const renderEditableText = (options: {
|
|||||||
}
|
}
|
||||||
placeholder={options.title}
|
placeholder={options.title}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const renderInputGroup = (options: {
|
const renderInputGroup = (options: {
|
||||||
title?: string;
|
title?: string;
|
||||||
@@ -55,11 +55,7 @@ const renderInputGroup = (options: {
|
|||||||
}) => {
|
}) => {
|
||||||
if (options.url && !options.isEditing) {
|
if (options.url && !options.isEditing) {
|
||||||
return (
|
return (
|
||||||
<a
|
<a href={options.url} target="_blank" rel="noopener noreferrer">
|
||||||
href={options.url}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
{options.value}
|
{options.value}
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
@@ -77,13 +73,13 @@ const renderInputGroup = (options: {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const renderDurationInput = (options: {
|
const renderDurationInput = (options: {
|
||||||
value: string | undefined;
|
value: string | undefined;
|
||||||
isEditing: boolean;
|
isEditing: boolean;
|
||||||
url?: string;
|
url?: string;
|
||||||
asString?: boolean
|
asString?: boolean;
|
||||||
onChange: (value: string | undefined) => void;
|
onChange: (value: string | undefined) => void;
|
||||||
}) => {
|
}) => {
|
||||||
let numericValue: number | undefined;
|
let numericValue: number | undefined;
|
||||||
@@ -121,14 +117,15 @@ const renderDurationInput = (options: {
|
|||||||
onValueChange={(valueAsNumber: number, valueAsString?: string) => {
|
onValueChange={(valueAsNumber: number, valueAsString?: string) => {
|
||||||
let value = valueAsString;
|
let value = valueAsString;
|
||||||
if (!options.asString) {
|
if (!options.asString) {
|
||||||
value = valueAsNumber !== undefined ? valueAsNumber.toString() : undefined;
|
value =
|
||||||
|
valueAsNumber !== undefined ? valueAsNumber.toString() : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
options.onChange(value);
|
options.onChange(value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const renderHtmlSelect = (options: {
|
const renderHtmlSelect = (options: {
|
||||||
value?: string | number;
|
value?: string | number;
|
||||||
@@ -164,7 +161,7 @@ const renderHtmlSelect = (options: {
|
|||||||
))}
|
))}
|
||||||
</Form.Control>
|
</Form.Control>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
// TODO: isediting
|
// TODO: isediting
|
||||||
const renderFilterSelect = (options: {
|
const renderFilterSelect = (options: {
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ const renderEditableTextTableRow = (options: {
|
|||||||
}) => (
|
}) => (
|
||||||
<tr>
|
<tr>
|
||||||
<td>{options.title}</td>
|
<td>{options.title}</td>
|
||||||
<td>
|
<td>{EditableTextUtils.renderEditableText(options)}</td>
|
||||||
{EditableTextUtils.renderEditableText(options)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -23,9 +21,7 @@ const renderTextArea = (options: {
|
|||||||
}) => (
|
}) => (
|
||||||
<tr>
|
<tr>
|
||||||
<td>{options.title}</td>
|
<td>{options.title}</td>
|
||||||
<td>
|
<td>{EditableTextUtils.renderTextArea(options)}</td>
|
||||||
{EditableTextUtils.renderTextArea(options)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -39,9 +35,7 @@ const renderInputGroup = (options: {
|
|||||||
}) => (
|
}) => (
|
||||||
<tr>
|
<tr>
|
||||||
<td>{options.title}</td>
|
<td>{options.title}</td>
|
||||||
<td>
|
<td>{EditableTextUtils.renderInputGroup(options)}</td>
|
||||||
{EditableTextUtils.renderInputGroup(options)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -56,9 +50,7 @@ const renderDurationInput = (options: {
|
|||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
<td>{options.title}</td>
|
<td>{options.title}</td>
|
||||||
<td>
|
<td>{EditableTextUtils.renderDurationInput(options)}</td>
|
||||||
{EditableTextUtils.renderDurationInput(options)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -72,9 +64,7 @@ const renderHtmlSelect = (options: {
|
|||||||
}) => (
|
}) => (
|
||||||
<tr>
|
<tr>
|
||||||
<td>{options.title}</td>
|
<td>{options.title}</td>
|
||||||
<td>
|
<td>{EditableTextUtils.renderHtmlSelect(options)}</td>
|
||||||
{EditableTextUtils.renderHtmlSelect(options)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -87,9 +77,7 @@ const renderFilterSelect = (options: {
|
|||||||
}) => (
|
}) => (
|
||||||
<tr>
|
<tr>
|
||||||
<td>{options.title}</td>
|
<td>{options.title}</td>
|
||||||
<td>
|
<td>{EditableTextUtils.renderFilterSelect(options)}</td>
|
||||||
{EditableTextUtils.renderFilterSelect(options)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -102,9 +90,7 @@ const renderMultiSelect = (options: {
|
|||||||
}) => (
|
}) => (
|
||||||
<tr>
|
<tr>
|
||||||
<td>{options.title}</td>
|
<td>{options.title}</td>
|
||||||
<td>
|
<td>{EditableTextUtils.renderMultiSelect(options)}</td>
|
||||||
{EditableTextUtils.renderMultiSelect(options)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -103,12 +103,12 @@ const sanitiseURL = (url?: string, siteURL?: URL) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// otherwise, construct the url from the protocol, host and passed url
|
// otherwise, construct the url from the protocol, host and passed url
|
||||||
return siteURL.protocol + siteURL.host + "/" + url;
|
return `${siteURL.protocol}${siteURL.host}/${url}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// just prepend the protocol - assume https
|
// just prepend the protocol - assume https
|
||||||
return "https://" + url;
|
return `https://${url}`;
|
||||||
}
|
};
|
||||||
|
|
||||||
const TextUtils = {
|
const TextUtils = {
|
||||||
truncate,
|
truncate,
|
||||||
|
|||||||
Reference in New Issue
Block a user