mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 16:34:02 +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 { sortByRelevance } from "src/utils/query";
|
||||||
import { galleryTitle } from "src/core/galleries";
|
import { galleryTitle } from "src/core/galleries";
|
||||||
import { PatchComponent } from "src/patch";
|
import { PatchComponent } from "src/patch";
|
||||||
|
import {
|
||||||
|
Criterion,
|
||||||
|
CriterionValue,
|
||||||
|
} from "src/models/list-filter/criteria/criterion";
|
||||||
|
|
||||||
export type Gallery = Pick<GQL.Gallery, "id" | "title"> & {
|
export type Gallery = Pick<GQL.Gallery, "id" | "title"> & {
|
||||||
files: Pick<GQL.GalleryFile, "path">[];
|
files: Pick<GQL.GalleryFile, "path">[];
|
||||||
|
|
@ -40,6 +44,8 @@ const _GallerySelect: React.FC<
|
||||||
IFilterValueProps<Gallery> & {
|
IFilterValueProps<Gallery> & {
|
||||||
hoverPlacement?: Placement;
|
hoverPlacement?: Placement;
|
||||||
excludeIds?: string[];
|
excludeIds?: string[];
|
||||||
|
} & {
|
||||||
|
extraCriteria?: Array<Criterion<CriterionValue>>;
|
||||||
}
|
}
|
||||||
> = (props) => {
|
> = (props) => {
|
||||||
const { configuration } = React.useContext(ConfigurationContext);
|
const { configuration } = React.useContext(ConfigurationContext);
|
||||||
|
|
@ -56,6 +62,11 @@ const _GallerySelect: React.FC<
|
||||||
filter.itemsPerPage = maxOptionsShown;
|
filter.itemsPerPage = maxOptionsShown;
|
||||||
filter.sortBy = "title";
|
filter.sortBy = "title";
|
||||||
filter.sortDirection = GQL.SortDirectionEnum.Asc;
|
filter.sortDirection = GQL.SortDirectionEnum.Asc;
|
||||||
|
|
||||||
|
if (props.extraCriteria) {
|
||||||
|
filter.criteria = [...props.extraCriteria];
|
||||||
|
}
|
||||||
|
|
||||||
const query = await queryFindGalleriesForSelect(filter);
|
const query = await queryFindGalleriesForSelect(filter);
|
||||||
let ret = query.data.findGalleries.galleries.filter((gallery) => {
|
let ret = query.data.findGalleries.galleries.filter((gallery) => {
|
||||||
// HACK - we should probably exclude these in the backend query, but
|
// HACK - we should probably exclude these in the backend query, but
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,9 @@ import {
|
||||||
import { formikUtils } from "src/utils/form";
|
import { formikUtils } from "src/utils/form";
|
||||||
import { Tag, TagSelect } from "src/components/Tags/TagSelect";
|
import { Tag, TagSelect } from "src/components/Tags/TagSelect";
|
||||||
import { Studio, StudioSelect } from "src/components/Studios/StudioSelect";
|
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 {
|
interface IProps {
|
||||||
image: GQL.ImageDataFragment;
|
image: GQL.ImageDataFragment;
|
||||||
|
|
@ -31,6 +34,14 @@ interface IProps {
|
||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getExcludeFilebaseGalleriesFilter() {
|
||||||
|
const ret = new PathCriterion();
|
||||||
|
ret.modifier = GQL.CriterionModifier.IsNull;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const excludeFileBasedGalleries = [getExcludeFilebaseGalleriesFilter()];
|
||||||
|
|
||||||
export const ImageEditPanel: React.FC<IProps> = ({
|
export const ImageEditPanel: React.FC<IProps> = ({
|
||||||
image,
|
image,
|
||||||
isVisible,
|
isVisible,
|
||||||
|
|
@ -45,10 +56,22 @@ export const ImageEditPanel: React.FC<IProps> = ({
|
||||||
|
|
||||||
const { configuration } = React.useContext(ConfigurationContext);
|
const { configuration } = React.useContext(ConfigurationContext);
|
||||||
|
|
||||||
|
const [galleries, setGalleries] = useState<Gallery[]>([]);
|
||||||
const [performers, setPerformers] = useState<Performer[]>([]);
|
const [performers, setPerformers] = useState<Performer[]>([]);
|
||||||
const [tags, setTags] = useState<Tag[]>([]);
|
const [tags, setTags] = useState<Tag[]>([]);
|
||||||
const [studio, setStudio] = useState<Studio | null>(null);
|
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({
|
const schema = yup.object({
|
||||||
title: yup.string().ensure(),
|
title: yup.string().ensure(),
|
||||||
code: yup.string().ensure(),
|
code: yup.string().ensure(),
|
||||||
|
|
@ -57,6 +80,7 @@ export const ImageEditPanel: React.FC<IProps> = ({
|
||||||
details: yup.string().ensure(),
|
details: yup.string().ensure(),
|
||||||
photographer: yup.string().ensure(),
|
photographer: yup.string().ensure(),
|
||||||
rating100: yup.number().integer().nullable().defined(),
|
rating100: yup.number().integer().nullable().defined(),
|
||||||
|
gallery_ids: yup.array(yup.string().required()).defined(),
|
||||||
studio_id: yup.string().required().nullable(),
|
studio_id: yup.string().required().nullable(),
|
||||||
performer_ids: yup.array(yup.string().required()).defined(),
|
performer_ids: yup.array(yup.string().required()).defined(),
|
||||||
tag_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 ?? "",
|
details: image.details ?? "",
|
||||||
photographer: image.photographer ?? "",
|
photographer: image.photographer ?? "",
|
||||||
rating100: image.rating100 ?? null,
|
rating100: image.rating100 ?? null,
|
||||||
|
gallery_ids: (image.galleries ?? []).map((g) => g.id),
|
||||||
studio_id: image.studio?.id ?? null,
|
studio_id: image.studio?.id ?? null,
|
||||||
performer_ids: (image.performers ?? []).map((p) => p.id),
|
performer_ids: (image.performers ?? []).map((p) => p.id),
|
||||||
tag_ids: (image.tags ?? []).map((t) => t.id),
|
tag_ids: (image.tags ?? []).map((t) => t.id),
|
||||||
|
|
@ -88,6 +113,14 @@ export const ImageEditPanel: React.FC<IProps> = ({
|
||||||
formik.setFieldValue("rating100", v);
|
formik.setFieldValue("rating100", v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onSetGalleries(items: Gallery[]) {
|
||||||
|
setGalleries(items);
|
||||||
|
formik.setFieldValue(
|
||||||
|
"gallery_ids",
|
||||||
|
items.map((i) => i.id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function onSetPerformers(items: Performer[]) {
|
function onSetPerformers(items: Performer[]) {
|
||||||
setPerformers(items);
|
setPerformers(items);
|
||||||
formik.setFieldValue(
|
formik.setFieldValue(
|
||||||
|
|
@ -189,6 +222,20 @@ export const ImageEditPanel: React.FC<IProps> = ({
|
||||||
renderURLListField,
|
renderURLListField,
|
||||||
} = formikUtils(intl, formik, splitProps);
|
} = 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() {
|
function renderStudioField() {
|
||||||
const title = intl.formatMessage({ id: "studio" });
|
const title = intl.formatMessage({ id: "studio" });
|
||||||
const control = (
|
const control = (
|
||||||
|
|
@ -278,6 +325,7 @@ export const ImageEditPanel: React.FC<IProps> = ({
|
||||||
{renderInputField("photographer")}
|
{renderInputField("photographer")}
|
||||||
{renderRatingField("rating100", "rating")}
|
{renderRatingField("rating100", "rating")}
|
||||||
|
|
||||||
|
{renderGalleriesField()}
|
||||||
{renderStudioField()}
|
{renderStudioField()}
|
||||||
{renderPerformersField()}
|
{renderPerformersField()}
|
||||||
{renderTagsField()}
|
{renderTagsField()}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue