mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 08:26:00 +01:00
Add galleries to image edit panel (#4573)
* Add Galleries to ImageEditPanel * Exclude filesystem-based galleries from selection --------- Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
parent
7bb38ae6dc
commit
f5a42ede2d
2 changed files with 59 additions and 0 deletions
|
|
@ -28,6 +28,10 @@ import { Placement } from "react-bootstrap/esm/Overlay";
|
|||
import { sortByRelevance } from "src/utils/query";
|
||||
import { galleryTitle } from "src/core/galleries";
|
||||
import { PatchComponent } from "src/patch";
|
||||
import {
|
||||
Criterion,
|
||||
CriterionValue,
|
||||
} from "src/models/list-filter/criteria/criterion";
|
||||
|
||||
export type Gallery = Pick<GQL.Gallery, "id" | "title"> & {
|
||||
files: Pick<GQL.GalleryFile, "path">[];
|
||||
|
|
@ -40,6 +44,8 @@ const _GallerySelect: React.FC<
|
|||
IFilterValueProps<Gallery> & {
|
||||
hoverPlacement?: Placement;
|
||||
excludeIds?: string[];
|
||||
} & {
|
||||
extraCriteria?: Array<Criterion<CriterionValue>>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { configuration } = React.useContext(ConfigurationContext);
|
||||
|
|
@ -56,6 +62,11 @@ const _GallerySelect: React.FC<
|
|||
filter.itemsPerPage = maxOptionsShown;
|
||||
filter.sortBy = "title";
|
||||
filter.sortDirection = GQL.SortDirectionEnum.Asc;
|
||||
|
||||
if (props.extraCriteria) {
|
||||
filter.criteria = [...props.extraCriteria];
|
||||
}
|
||||
|
||||
const query = await queryFindGalleriesForSelect(filter);
|
||||
let ret = query.data.findGalleries.galleries.filter((gallery) => {
|
||||
// HACK - we should probably exclude these in the backend query, but
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ import {
|
|||
import { formikUtils } from "src/utils/form";
|
||||
import { Tag, TagSelect } from "src/components/Tags/TagSelect";
|
||||
import { Studio, StudioSelect } from "src/components/Studios/StudioSelect";
|
||||
import { galleryTitle } from "src/core/galleries";
|
||||
import { Gallery, GallerySelect } from "src/components/Galleries/GallerySelect";
|
||||
import { PathCriterion } from "src/models/list-filter/criteria/path";
|
||||
|
||||
interface IProps {
|
||||
image: GQL.ImageDataFragment;
|
||||
|
|
@ -31,6 +34,14 @@ interface IProps {
|
|||
onDelete: () => void;
|
||||
}
|
||||
|
||||
function getExcludeFilebaseGalleriesFilter() {
|
||||
const ret = new PathCriterion();
|
||||
ret.modifier = GQL.CriterionModifier.IsNull;
|
||||
return ret;
|
||||
}
|
||||
|
||||
const excludeFileBasedGalleries = [getExcludeFilebaseGalleriesFilter()];
|
||||
|
||||
export const ImageEditPanel: React.FC<IProps> = ({
|
||||
image,
|
||||
isVisible,
|
||||
|
|
@ -45,10 +56,22 @@ export const ImageEditPanel: React.FC<IProps> = ({
|
|||
|
||||
const { configuration } = React.useContext(ConfigurationContext);
|
||||
|
||||
const [galleries, setGalleries] = useState<Gallery[]>([]);
|
||||
const [performers, setPerformers] = useState<Performer[]>([]);
|
||||
const [tags, setTags] = useState<Tag[]>([]);
|
||||
const [studio, setStudio] = useState<Studio | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setGalleries(
|
||||
image.galleries?.map((g) => ({
|
||||
id: g.id,
|
||||
title: galleryTitle(g),
|
||||
files: g.files,
|
||||
folder: g.folder,
|
||||
})) ?? []
|
||||
);
|
||||
}, [image.galleries]);
|
||||
|
||||
const schema = yup.object({
|
||||
title: yup.string().ensure(),
|
||||
code: yup.string().ensure(),
|
||||
|
|
@ -57,6 +80,7 @@ export const ImageEditPanel: React.FC<IProps> = ({
|
|||
details: yup.string().ensure(),
|
||||
photographer: yup.string().ensure(),
|
||||
rating100: yup.number().integer().nullable().defined(),
|
||||
gallery_ids: yup.array(yup.string().required()).defined(),
|
||||
studio_id: yup.string().required().nullable(),
|
||||
performer_ids: yup.array(yup.string().required()).defined(),
|
||||
tag_ids: yup.array(yup.string().required()).defined(),
|
||||
|
|
@ -70,6 +94,7 @@ export const ImageEditPanel: React.FC<IProps> = ({
|
|||
details: image.details ?? "",
|
||||
photographer: image.photographer ?? "",
|
||||
rating100: image.rating100 ?? null,
|
||||
gallery_ids: (image.galleries ?? []).map((g) => g.id),
|
||||
studio_id: image.studio?.id ?? null,
|
||||
performer_ids: (image.performers ?? []).map((p) => p.id),
|
||||
tag_ids: (image.tags ?? []).map((t) => t.id),
|
||||
|
|
@ -88,6 +113,14 @@ export const ImageEditPanel: React.FC<IProps> = ({
|
|||
formik.setFieldValue("rating100", v);
|
||||
}
|
||||
|
||||
function onSetGalleries(items: Gallery[]) {
|
||||
setGalleries(items);
|
||||
formik.setFieldValue(
|
||||
"gallery_ids",
|
||||
items.map((i) => i.id)
|
||||
);
|
||||
}
|
||||
|
||||
function onSetPerformers(items: Performer[]) {
|
||||
setPerformers(items);
|
||||
formik.setFieldValue(
|
||||
|
|
@ -189,6 +222,20 @@ export const ImageEditPanel: React.FC<IProps> = ({
|
|||
renderURLListField,
|
||||
} = formikUtils(intl, formik, splitProps);
|
||||
|
||||
function renderGalleriesField() {
|
||||
const title = intl.formatMessage({ id: "galleries" });
|
||||
const control = (
|
||||
<GallerySelect
|
||||
values={galleries}
|
||||
onSelect={(items) => onSetGalleries(items)}
|
||||
isMulti
|
||||
extraCriteria={excludeFileBasedGalleries}
|
||||
/>
|
||||
);
|
||||
|
||||
return renderField("gallery_ids", title, control);
|
||||
}
|
||||
|
||||
function renderStudioField() {
|
||||
const title = intl.formatMessage({ id: "studio" });
|
||||
const control = (
|
||||
|
|
@ -278,6 +325,7 @@ export const ImageEditPanel: React.FC<IProps> = ({
|
|||
{renderInputField("photographer")}
|
||||
{renderRatingField("rating100", "rating")}
|
||||
|
||||
{renderGalleriesField()}
|
||||
{renderStudioField()}
|
||||
{renderPerformersField()}
|
||||
{renderTagsField()}
|
||||
|
|
|
|||
Loading…
Reference in a new issue