mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 08:26:00 +01:00
213 lines
5.8 KiB
TypeScript
213 lines
5.8 KiB
TypeScript
import { faEllipsisV } from "@fortawesome/free-solid-svg-icons";
|
|
import React, { useState, PropsWithChildren } from "react";
|
|
import { Button, Form, Row, Col, Dropdown } from "react-bootstrap";
|
|
import { FormattedMessage } from "react-intl";
|
|
import { Icon } from "src/components/Shared/Icon";
|
|
import * as GQL from "src/core/generated-graphql";
|
|
import { FolderSelectDialog } from "../Shared/FolderSelect/FolderSelectDialog";
|
|
import { BooleanSetting } from "./Inputs";
|
|
import { SettingSection } from "./SettingSection";
|
|
|
|
interface IStashProps {
|
|
index: number;
|
|
stash: GQL.StashConfig;
|
|
onSave: (instance: GQL.StashConfig) => void;
|
|
onEdit: () => void;
|
|
onDelete: () => void;
|
|
}
|
|
|
|
const Stash: React.FC<IStashProps> = ({
|
|
index,
|
|
stash,
|
|
onSave,
|
|
onEdit,
|
|
onDelete,
|
|
}) => {
|
|
// eslint-disable-next-line
|
|
const handleInput = (key: string, value: any) => {
|
|
const newObj = {
|
|
...stash,
|
|
[key]: value,
|
|
};
|
|
onSave(newObj);
|
|
};
|
|
|
|
const classAdd = index % 2 === 1 ? "bg-dark" : "";
|
|
|
|
return (
|
|
<Row className={`stash-row align-items-center ${classAdd}`}>
|
|
<Form.Label column md={7}>
|
|
{stash.path}
|
|
</Form.Label>
|
|
<Col md={2} xs={4} className="col form-label">
|
|
{/* NOTE - language is opposite to meaning:
|
|
internally exclude flags, displayed as include */}
|
|
<div>
|
|
<h6 className="d-md-none">
|
|
<FormattedMessage id="videos" />
|
|
</h6>
|
|
<BooleanSetting
|
|
id={`stash-exclude-video-${index}`}
|
|
checked={!stash.excludeVideo}
|
|
onChange={(v) => handleInput("excludeVideo", !v)}
|
|
/>
|
|
</div>
|
|
</Col>
|
|
|
|
<Col md={2} xs={4} className="col-form-label">
|
|
<div>
|
|
<h6 className="d-md-none">
|
|
<FormattedMessage id="images" />
|
|
</h6>
|
|
<BooleanSetting
|
|
id={`stash-exclude-image-${index}`}
|
|
checked={!stash.excludeImage}
|
|
onChange={(v) => handleInput("excludeImage", !v)}
|
|
/>
|
|
</div>
|
|
</Col>
|
|
<Col className="justify-content-end" xs={4} md={1}>
|
|
<Dropdown className="text-right">
|
|
<Dropdown.Toggle
|
|
variant="minimal"
|
|
id={`stash-menu-${index}`}
|
|
className="minimal"
|
|
>
|
|
<Icon icon={faEllipsisV} />
|
|
</Dropdown.Toggle>
|
|
<Dropdown.Menu className="bg-secondary text-white">
|
|
<Dropdown.Item onClick={() => onEdit()}>
|
|
<FormattedMessage id="actions.edit" />
|
|
</Dropdown.Item>
|
|
<Dropdown.Item onClick={() => onDelete()}>
|
|
<FormattedMessage id="actions.delete" />
|
|
</Dropdown.Item>
|
|
</Dropdown.Menu>
|
|
</Dropdown>
|
|
</Col>
|
|
</Row>
|
|
);
|
|
};
|
|
|
|
interface IStashConfigurationProps {
|
|
stashes: GQL.StashConfig[];
|
|
setStashes: (v: GQL.StashConfig[]) => void;
|
|
}
|
|
|
|
const StashConfiguration: React.FC<IStashConfigurationProps> = ({
|
|
stashes,
|
|
setStashes,
|
|
}) => {
|
|
const [isCreating, setIsCreating] = useState(false);
|
|
const [editingIndex, setEditingIndex] = useState<number | undefined>();
|
|
|
|
function onEdit(index: number) {
|
|
setEditingIndex(index);
|
|
}
|
|
|
|
function onDelete(index: number) {
|
|
setStashes(stashes.filter((v, i) => i !== index));
|
|
}
|
|
|
|
function onNew() {
|
|
setIsCreating(true);
|
|
}
|
|
|
|
const handleSave = (index: number, stash: GQL.StashConfig) =>
|
|
setStashes(stashes.map((s, i) => (i === index ? stash : s)));
|
|
|
|
return (
|
|
<>
|
|
{isCreating ? (
|
|
<FolderSelectDialog
|
|
onClose={(v) => {
|
|
if (v)
|
|
setStashes([
|
|
...stashes,
|
|
{
|
|
path: v,
|
|
excludeVideo: false,
|
|
excludeImage: false,
|
|
},
|
|
]);
|
|
setIsCreating(false);
|
|
}}
|
|
/>
|
|
) : undefined}
|
|
|
|
{editingIndex !== undefined ? (
|
|
<FolderSelectDialog
|
|
defaultValue={stashes[editingIndex].path}
|
|
onClose={(v) => {
|
|
if (v)
|
|
setStashes(
|
|
stashes.map((vv, index) => {
|
|
if (index === editingIndex) {
|
|
return {
|
|
...vv,
|
|
path: v,
|
|
};
|
|
}
|
|
return vv;
|
|
})
|
|
);
|
|
setEditingIndex(undefined);
|
|
}}
|
|
/>
|
|
) : undefined}
|
|
|
|
<div className="content" id="stash-table">
|
|
{stashes.length > 0 && (
|
|
<Row className="d-none d-md-flex">
|
|
<h6 className="col-md-7">
|
|
<FormattedMessage id="path" />
|
|
</h6>
|
|
<h6 className="col-md-2 col-4">
|
|
<FormattedMessage id="videos" />
|
|
</h6>
|
|
<h6 className="col-md-2 col-4">
|
|
<FormattedMessage id="images" />
|
|
</h6>
|
|
</Row>
|
|
)}
|
|
{stashes.map((stash, index) => (
|
|
<Stash
|
|
index={index}
|
|
stash={stash}
|
|
onSave={(s) => handleSave(index, s)}
|
|
onEdit={() => onEdit(index)}
|
|
onDelete={() => onDelete(index)}
|
|
key={stash.path}
|
|
/>
|
|
))}
|
|
<Button className="mt-2" variant="secondary" onClick={() => onNew()}>
|
|
<FormattedMessage id="actions.add_directory" />
|
|
</Button>
|
|
</div>
|
|
</>
|
|
);
|
|
};
|
|
|
|
interface IStashSetting {
|
|
value: GQL.StashConfigInput[];
|
|
onChange: (v: GQL.StashConfigInput[]) => void;
|
|
}
|
|
|
|
export const StashSetting: React.FC<PropsWithChildren<IStashSetting>> = ({
|
|
value,
|
|
onChange,
|
|
children,
|
|
}) => {
|
|
return (
|
|
<SettingSection
|
|
id="stashes"
|
|
headingID="library"
|
|
subHeadingID="config.general.directory_locations_to_your_content"
|
|
>
|
|
<StashConfiguration stashes={value} setStashes={(v) => onChange(v)} />
|
|
{children}
|
|
</SettingSection>
|
|
);
|
|
};
|
|
|
|
export default StashConfiguration;
|