mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 21:04:37 +03:00
Fix scene edit state resetting when scene is updated (#1112)
* Fix organized click resetting scene form state * Fix state resetting for images/galleries * Fix favoriting a performer resetting edit state
This commit is contained in:
@@ -29,10 +29,11 @@
|
|||||||
* Support configurable number of threads for scanning and generation.
|
* Support configurable number of threads for scanning and generation.
|
||||||
|
|
||||||
### 🐛 Bug fixes
|
### 🐛 Bug fixes
|
||||||
|
* Fix edit data being lost when clicking the O-Counter, Organized or Favorite buttons.
|
||||||
* Exclude media in `generated` directory from the library.
|
* Exclude media in `generated` directory from the library.
|
||||||
* Prevent cover image from being incorrectly regenerated when a scene file's hash changes.
|
* Prevent cover image from being incorrectly regenerated when a scene file's hash changes.
|
||||||
* Fix version check sometimes giving incorrect results.
|
* Fix version check sometimes giving incorrect results.
|
||||||
* Fixed stash potentially deleting `downloads` directory when first run.
|
* Fix stash potentially deleting `downloads` directory when first run.
|
||||||
* Fix sprite generation when generated path has special characters.
|
* Fix sprite generation when generated path has special characters.
|
||||||
* Prevent studio from being set as its own parent
|
* Prevent studio from being set as its own parent
|
||||||
* Fixed performer scraper select overlapping search results
|
* Fixed performer scraper select overlapping search results
|
||||||
|
|||||||
@@ -39,18 +39,29 @@ interface IExistingProps {
|
|||||||
|
|
||||||
export const GalleryEditPanel: React.FC<
|
export const GalleryEditPanel: React.FC<
|
||||||
IProps & (INewProps | IExistingProps)
|
IProps & (INewProps | IExistingProps)
|
||||||
> = (props) => {
|
> = ({ gallery, isNew, isVisible, onDelete }) => {
|
||||||
const Toast = useToast();
|
const Toast = useToast();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [title, setTitle] = useState<string>();
|
const [title, setTitle] = useState<string>(gallery?.title ?? "");
|
||||||
const [details, setDetails] = useState<string>();
|
const [details, setDetails] = useState<string>(gallery?.details ?? "");
|
||||||
const [url, setUrl] = useState<string>();
|
const [url, setUrl] = useState<string>(gallery?.url ?? "");
|
||||||
const [date, setDate] = useState<string>();
|
const [date, setDate] = useState<string>(gallery?.date ?? "");
|
||||||
const [rating, setRating] = useState<number>();
|
const [rating, setRating] = useState<number>(gallery?.rating ?? NaN);
|
||||||
const [studioId, setStudioId] = useState<string>();
|
const [studioId, setStudioId] = useState<string | undefined>(
|
||||||
const [performerIds, setPerformerIds] = useState<string[]>();
|
gallery?.studio?.id ?? undefined
|
||||||
const [tagIds, setTagIds] = useState<string[]>();
|
);
|
||||||
const [scenes, setScenes] = useState<{ id: string; title: string }[]>([]);
|
const [performerIds, setPerformerIds] = useState<string[]>(
|
||||||
|
gallery?.performers.map((p) => p.id) ?? []
|
||||||
|
);
|
||||||
|
const [tagIds, setTagIds] = useState<string[]>(
|
||||||
|
gallery?.tags.map((t) => t.id) ?? []
|
||||||
|
);
|
||||||
|
const [scenes, setScenes] = useState<{ id: string; title: string }[]>(
|
||||||
|
gallery?.scenes.map((s) => ({
|
||||||
|
id: s.id,
|
||||||
|
title: s.title ?? TextUtils.fileNameFromPath(s.path ?? ""),
|
||||||
|
})) ?? []
|
||||||
|
);
|
||||||
|
|
||||||
const Scrapers = useListGalleryScrapers();
|
const Scrapers = useListGalleryScrapers();
|
||||||
|
|
||||||
@@ -60,18 +71,18 @@ export const GalleryEditPanel: React.FC<
|
|||||||
] = useState<GQL.ScrapedGallery | null>();
|
] = useState<GQL.ScrapedGallery | null>();
|
||||||
|
|
||||||
// Network state
|
// Network state
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const [createGallery] = useGalleryCreate();
|
const [createGallery] = useGalleryCreate();
|
||||||
const [updateGallery] = useGalleryUpdate();
|
const [updateGallery] = useGalleryUpdate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.isVisible) {
|
if (isVisible) {
|
||||||
Mousetrap.bind("s s", () => {
|
Mousetrap.bind("s s", () => {
|
||||||
onSave();
|
onSave();
|
||||||
});
|
});
|
||||||
Mousetrap.bind("d d", () => {
|
Mousetrap.bind("d d", () => {
|
||||||
props.onDelete();
|
onDelete();
|
||||||
});
|
});
|
||||||
|
|
||||||
// numeric keypresses get caught by jwplayer, so blur the element
|
// numeric keypresses get caught by jwplayer, so blur the element
|
||||||
@@ -107,34 +118,9 @@ export const GalleryEditPanel: React.FC<
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function updateGalleryEditState(state?: GQL.GalleryDataFragment) {
|
|
||||||
const perfIds = state?.performers?.map((performer) => performer.id);
|
|
||||||
const tIds = state?.tags ? state?.tags.map((tag) => tag.id) : undefined;
|
|
||||||
|
|
||||||
setTitle(state?.title ?? undefined);
|
|
||||||
setDetails(state?.details ?? undefined);
|
|
||||||
setUrl(state?.url ?? undefined);
|
|
||||||
setDate(state?.date ?? undefined);
|
|
||||||
setRating(state?.rating === null ? NaN : state?.rating);
|
|
||||||
setStudioId(state?.studio?.id ?? undefined);
|
|
||||||
setPerformerIds(perfIds);
|
|
||||||
setTagIds(tIds);
|
|
||||||
setScenes(
|
|
||||||
(state?.scenes ?? []).map((s) => ({
|
|
||||||
id: s.id,
|
|
||||||
title: s.title ?? TextUtils.fileNameFromPath(s.path ?? ""),
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateGalleryEditState(props.gallery);
|
|
||||||
setIsLoading(false);
|
|
||||||
}, [props.gallery]);
|
|
||||||
|
|
||||||
function getGalleryInput() {
|
function getGalleryInput() {
|
||||||
return {
|
return {
|
||||||
id: props.isNew ? undefined : props.gallery.id,
|
id: isNew ? undefined : gallery?.id ?? "",
|
||||||
title: title ?? "",
|
title: title ?? "",
|
||||||
details,
|
details,
|
||||||
url,
|
url,
|
||||||
@@ -150,7 +136,7 @@ export const GalleryEditPanel: React.FC<
|
|||||||
async function onSave() {
|
async function onSave() {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
if (props.isNew) {
|
if (isNew) {
|
||||||
const result = await createGallery({
|
const result = await createGallery({
|
||||||
variables: {
|
variables: {
|
||||||
input: getGalleryInput(),
|
input: getGalleryInput(),
|
||||||
@@ -176,9 +162,9 @@ export const GalleryEditPanel: React.FC<
|
|||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onScrapeDialogClosed(gallery?: GQL.ScrapedGalleryDataFragment) {
|
function onScrapeDialogClosed(data?: GQL.ScrapedGalleryDataFragment) {
|
||||||
if (gallery) {
|
if (data) {
|
||||||
updateGalleryFromScrapedGallery(gallery);
|
updateGalleryFromScrapedGallery(data);
|
||||||
}
|
}
|
||||||
setScrapedGallery(undefined);
|
setScrapedGallery(undefined);
|
||||||
}
|
}
|
||||||
@@ -194,8 +180,8 @@ export const GalleryEditPanel: React.FC<
|
|||||||
<GalleryScrapeDialog
|
<GalleryScrapeDialog
|
||||||
gallery={currentGallery}
|
gallery={currentGallery}
|
||||||
scraped={scrapedGallery}
|
scraped={scrapedGallery}
|
||||||
onClose={(gallery) => {
|
onClose={(data) => {
|
||||||
onScrapeDialogClosed(gallery);
|
onScrapeDialogClosed(data);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -208,30 +194,30 @@ export const GalleryEditPanel: React.FC<
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateGalleryFromScrapedGallery(
|
function updateGalleryFromScrapedGallery(
|
||||||
gallery: GQL.ScrapedGalleryDataFragment
|
galleryData: GQL.ScrapedGalleryDataFragment
|
||||||
) {
|
) {
|
||||||
if (gallery.title) {
|
if (galleryData.title) {
|
||||||
setTitle(gallery.title);
|
setTitle(galleryData.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gallery.details) {
|
if (galleryData.details) {
|
||||||
setDetails(gallery.details);
|
setDetails(galleryData.details);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gallery.date) {
|
if (galleryData.date) {
|
||||||
setDate(gallery.date);
|
setDate(galleryData.date);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gallery.url) {
|
if (galleryData.url) {
|
||||||
setUrl(gallery.url);
|
setUrl(galleryData.url);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gallery.studio && gallery.studio.stored_id) {
|
if (galleryData.studio?.stored_id) {
|
||||||
setStudioId(gallery.studio.stored_id);
|
setStudioId(galleryData.studio.stored_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gallery.performers && gallery.performers.length > 0) {
|
if (galleryData.performers?.length) {
|
||||||
const idPerfs = gallery.performers.filter((p) => {
|
const idPerfs = galleryData.performers.filter((p) => {
|
||||||
return p.stored_id !== undefined && p.stored_id !== null;
|
return p.stored_id !== undefined && p.stored_id !== null;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -241,13 +227,13 @@ export const GalleryEditPanel: React.FC<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gallery?.tags?.length) {
|
if (galleryData?.tags?.length) {
|
||||||
const idTags = gallery.tags.filter((p) => {
|
const idTags = galleryData.tags.filter((t) => {
|
||||||
return p.stored_id !== undefined && p.stored_id !== null;
|
return t.stored_id !== undefined && t.stored_id !== null;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (idTags.length > 0) {
|
if (idTags.length > 0) {
|
||||||
const newIds = idTags.map((p) => p.stored_id);
|
const newIds = idTags.map((t) => t.stored_id);
|
||||||
setTagIds(newIds as string[]);
|
setTagIds(newIds as string[]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -299,7 +285,7 @@ export const GalleryEditPanel: React.FC<
|
|||||||
<Button
|
<Button
|
||||||
className="edit-button"
|
className="edit-button"
|
||||||
variant="danger"
|
variant="danger"
|
||||||
onClick={() => props.onDelete()}
|
onClick={() => onDelete()}
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
@@ -343,7 +329,7 @@ export const GalleryEditPanel: React.FC<
|
|||||||
<Col xs={9}>
|
<Col xs={9}>
|
||||||
<RatingStars
|
<RatingStars
|
||||||
value={rating}
|
value={rating}
|
||||||
onSetRating={(value) => setRating(value)}
|
onSetRating={(value) => setRating(value ?? NaN)}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
|
|||||||
@@ -19,26 +19,32 @@ interface IProps {
|
|||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ImageEditPanel: React.FC<IProps> = (props: IProps) => {
|
export const ImageEditPanel: React.FC<IProps> = ({
|
||||||
|
image,
|
||||||
|
isVisible,
|
||||||
|
onDelete,
|
||||||
|
}) => {
|
||||||
const Toast = useToast();
|
const Toast = useToast();
|
||||||
const [title, setTitle] = useState<string>();
|
const [title, setTitle] = useState<string>(image?.title ?? "");
|
||||||
const [rating, setRating] = useState<number>();
|
const [rating, setRating] = useState<number>(image.rating ?? NaN);
|
||||||
const [studioId, setStudioId] = useState<string>();
|
const [studioId, setStudioId] = useState<string>(image.studio?.id ?? "");
|
||||||
const [performerIds, setPerformerIds] = useState<string[]>();
|
const [performerIds, setPerformerIds] = useState<string[]>(
|
||||||
const [tagIds, setTagIds] = useState<string[]>();
|
image.performers.map((p) => p.id)
|
||||||
|
);
|
||||||
|
const [tagIds, setTagIds] = useState<string[]>(image.tags.map((t) => t.id));
|
||||||
|
|
||||||
// Network state
|
// Network state
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const [updateImage] = useImageUpdate();
|
const [updateImage] = useImageUpdate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.isVisible) {
|
if (isVisible) {
|
||||||
Mousetrap.bind("s s", () => {
|
Mousetrap.bind("s s", () => {
|
||||||
onSave();
|
onSave();
|
||||||
});
|
});
|
||||||
Mousetrap.bind("d d", () => {
|
Mousetrap.bind("d d", () => {
|
||||||
props.onDelete();
|
onDelete();
|
||||||
});
|
});
|
||||||
|
|
||||||
// numeric keypresses get caught by jwplayer, so blur the element
|
// numeric keypresses get caught by jwplayer, so blur the element
|
||||||
@@ -74,26 +80,9 @@ export const ImageEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function updateImageEditState(state: Partial<GQL.ImageDataFragment>) {
|
|
||||||
const perfIds = state.performers?.map((performer) => performer.id);
|
|
||||||
const tIds = state.tags ? state.tags.map((tag) => tag.id) : undefined;
|
|
||||||
|
|
||||||
setTitle(state.title ?? undefined);
|
|
||||||
setRating(state.rating === null ? NaN : state.rating);
|
|
||||||
// setGalleryId(state?.gallery?.id ?? undefined);
|
|
||||||
setStudioId(state?.studio?.id ?? undefined);
|
|
||||||
setPerformerIds(perfIds);
|
|
||||||
setTagIds(tIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateImageEditState(props.image);
|
|
||||||
setIsLoading(false);
|
|
||||||
}, [props.image]);
|
|
||||||
|
|
||||||
function getImageInput(): GQL.ImageUpdateInput {
|
function getImageInput(): GQL.ImageUpdateInput {
|
||||||
return {
|
return {
|
||||||
id: props.image.id,
|
id: image.id,
|
||||||
title,
|
title,
|
||||||
rating: rating ?? null,
|
rating: rating ?? null,
|
||||||
studio_id: studioId ?? null,
|
studio_id: studioId ?? null,
|
||||||
@@ -131,7 +120,7 @@ export const ImageEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
<Button
|
<Button
|
||||||
className="edit-button"
|
className="edit-button"
|
||||||
variant="danger"
|
variant="danger"
|
||||||
onClick={() => props.onDelete()}
|
onClick={() => onDelete()}
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
@@ -152,7 +141,7 @@ export const ImageEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
<Col xs={9}>
|
<Col xs={9}>
|
||||||
<RatingStars
|
<RatingStars
|
||||||
value={rating}
|
value={rating}
|
||||||
onSetRating={(value) => setRating(value)}
|
onSetRating={(value) => setRating(value ?? NaN)}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
@@ -164,7 +153,7 @@ export const ImageEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
<Col xs={9}>
|
<Col xs={9}>
|
||||||
<StudioSelect
|
<StudioSelect
|
||||||
onSelect={(items) =>
|
onSelect={(items) =>
|
||||||
setStudioId(items.length > 0 ? items[0]?.id : undefined)
|
setStudioId(items.length > 0 ? items[0]?.id : "")
|
||||||
}
|
}
|
||||||
ids={studioId ? [studioId] : []}
|
ids={studioId ? [studioId] : []}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -223,10 +223,16 @@ export const Performer: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setFavorite(v: boolean) {
|
function setFavorite(v: boolean) {
|
||||||
onSave({
|
if (performer.id) {
|
||||||
id: performer.id,
|
updatePerformer({
|
||||||
favorite: v,
|
variables: {
|
||||||
});
|
input: {
|
||||||
|
id: performer.id,
|
||||||
|
favorite: v,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderIcons = () => (
|
const renderIcons = () => (
|
||||||
|
|||||||
@@ -68,24 +68,32 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
|||||||
|
|
||||||
// Editing performer state
|
// Editing performer state
|
||||||
const [image, setImage] = useState<string | null>();
|
const [image, setImage] = useState<string | null>();
|
||||||
const [name, setName] = useState<string>();
|
const [name, setName] = useState<string>(performer?.name ?? "");
|
||||||
const [aliases, setAliases] = useState<string>();
|
const [aliases, setAliases] = useState<string>(performer.aliases ?? "");
|
||||||
const [favorite, setFavorite] = useState<boolean>();
|
const [birthdate, setBirthdate] = useState<string>(performer.birthdate ?? "");
|
||||||
const [birthdate, setBirthdate] = useState<string>();
|
const [ethnicity, setEthnicity] = useState<string>(performer.ethnicity ?? "");
|
||||||
const [ethnicity, setEthnicity] = useState<string>();
|
const [country, setCountry] = useState<string>(performer.country ?? "");
|
||||||
const [country, setCountry] = useState<string>();
|
const [eyeColor, setEyeColor] = useState<string>(performer.eye_color ?? "");
|
||||||
const [eyeColor, setEyeColor] = useState<string>();
|
const [height, setHeight] = useState<string>(performer.height ?? "");
|
||||||
const [height, setHeight] = useState<string>();
|
const [measurements, setMeasurements] = useState<string>(
|
||||||
const [measurements, setMeasurements] = useState<string>();
|
performer.measurements ?? ""
|
||||||
const [fakeTits, setFakeTits] = useState<string>();
|
);
|
||||||
const [careerLength, setCareerLength] = useState<string>();
|
const [fakeTits, setFakeTits] = useState<string>(performer.fake_tits ?? "");
|
||||||
const [tattoos, setTattoos] = useState<string>();
|
const [careerLength, setCareerLength] = useState<string>(
|
||||||
const [piercings, setPiercings] = useState<string>();
|
performer.career_length ?? ""
|
||||||
const [url, setUrl] = useState<string>();
|
);
|
||||||
const [twitter, setTwitter] = useState<string>();
|
const [tattoos, setTattoos] = useState<string>(performer.tattoos ?? "");
|
||||||
const [instagram, setInstagram] = useState<string>();
|
const [piercings, setPiercings] = useState<string>(performer.piercings ?? "");
|
||||||
const [gender, setGender] = useState<string | undefined>(undefined);
|
const [url, setUrl] = useState<string>(performer.url ?? "");
|
||||||
const [stashIDs, setStashIDs] = useState<GQL.StashIdInput[]>([]);
|
const [twitter, setTwitter] = useState<string>(performer.twitter ?? "");
|
||||||
|
const [instagram, setInstagram] = useState<string>(performer.instagram ?? "");
|
||||||
|
const [gender, setGender] = useState<string | undefined>(
|
||||||
|
performer.gender ?? undefined
|
||||||
|
);
|
||||||
|
const [stashIDs, setStashIDs] = useState<GQL.StashIdInput[]>(
|
||||||
|
performer.stash_ids ?? []
|
||||||
|
);
|
||||||
|
const favorite = performer.favorite ?? false;
|
||||||
|
|
||||||
// Network state
|
// Network state
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
@@ -101,35 +109,6 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
|||||||
|
|
||||||
const imageEncoding = ImageUtils.usePasteImage(onImageLoad, isEditing);
|
const imageEncoding = ImageUtils.usePasteImage(onImageLoad, isEditing);
|
||||||
|
|
||||||
function updatePerformerEditState(
|
|
||||||
state: Partial<GQL.PerformerDataFragment | GQL.ScrapedPerformerDataFragment>
|
|
||||||
) {
|
|
||||||
if ((state as GQL.PerformerDataFragment).favorite !== undefined) {
|
|
||||||
setFavorite((state as GQL.PerformerDataFragment).favorite);
|
|
||||||
}
|
|
||||||
setName(state.name ?? undefined);
|
|
||||||
setAliases(state.aliases ?? undefined);
|
|
||||||
setBirthdate(state.birthdate ?? undefined);
|
|
||||||
setEthnicity(state.ethnicity ?? undefined);
|
|
||||||
setCountry(state.country ?? undefined);
|
|
||||||
setEyeColor(state.eye_color ?? undefined);
|
|
||||||
setHeight(state.height ?? undefined);
|
|
||||||
setMeasurements(state.measurements ?? undefined);
|
|
||||||
setFakeTits(state.fake_tits ?? undefined);
|
|
||||||
setCareerLength(state.career_length ?? undefined);
|
|
||||||
setTattoos(state.tattoos ?? undefined);
|
|
||||||
setPiercings(state.piercings ?? undefined);
|
|
||||||
setUrl(state.url ?? undefined);
|
|
||||||
setTwitter(state.twitter ?? undefined);
|
|
||||||
setInstagram(state.instagram ?? undefined);
|
|
||||||
setGender(
|
|
||||||
genderToString((state as GQL.PerformerDataFragment).gender ?? undefined)
|
|
||||||
);
|
|
||||||
if ((state as GQL.PerformerDataFragment).stash_ids !== undefined) {
|
|
||||||
setStashIDs((state as GQL.PerformerDataFragment).stash_ids);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function translateScrapedGender(scrapedGender?: string) {
|
function translateScrapedGender(scrapedGender?: string) {
|
||||||
if (!scrapedGender) {
|
if (!scrapedGender) {
|
||||||
return;
|
return;
|
||||||
@@ -245,10 +224,6 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isNew) updatePerformerEditState(performer);
|
|
||||||
}, [isNew, performer]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (onImageChange) {
|
if (onImageChange) {
|
||||||
onImageChange(image);
|
onImageChange(image);
|
||||||
|
|||||||
@@ -40,26 +40,43 @@ interface IProps {
|
|||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
export const SceneEditPanel: React.FC<IProps> = ({
|
||||||
|
scene,
|
||||||
|
isVisible,
|
||||||
|
onDelete,
|
||||||
|
}) => {
|
||||||
const Toast = useToast();
|
const Toast = useToast();
|
||||||
const [title, setTitle] = useState<string>();
|
const [title, setTitle] = useState<string>(scene.title ?? "");
|
||||||
const [details, setDetails] = useState<string>();
|
const [details, setDetails] = useState<string>(scene.details ?? "");
|
||||||
const [url, setUrl] = useState<string>();
|
const [url, setUrl] = useState<string>(scene.url ?? "");
|
||||||
const [date, setDate] = useState<string>();
|
const [date, setDate] = useState<string>(scene.date ?? "");
|
||||||
const [rating, setRating] = useState<number>();
|
const [rating, setRating] = useState<number | undefined>(
|
||||||
const [galleries, setGalleries] = useState<{ id: string; title: string }[]>(
|
scene.rating ?? undefined
|
||||||
[]
|
);
|
||||||
|
const [galleries, setGalleries] = useState<{ id: string; title: string }[]>(
|
||||||
|
scene.galleries.map((g) => ({
|
||||||
|
id: g.id,
|
||||||
|
title: g.title ?? TextUtils.fileNameFromPath(g.path ?? ""),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
const [studioId, setStudioId] = useState<string | undefined>(
|
||||||
|
scene.studio?.id
|
||||||
|
);
|
||||||
|
const [performerIds, setPerformerIds] = useState<string[]>(
|
||||||
|
scene.performers.map((p) => p.id)
|
||||||
|
);
|
||||||
|
const [movieIds, setMovieIds] = useState<string[]>(
|
||||||
|
scene.movies.map((m) => m.movie.id)
|
||||||
);
|
);
|
||||||
const [studioId, setStudioId] = useState<string>();
|
|
||||||
const [performerIds, setPerformerIds] = useState<string[]>();
|
|
||||||
const [movieIds, setMovieIds] = useState<string[] | undefined>(undefined);
|
|
||||||
const [
|
const [
|
||||||
movieSceneIndexes,
|
movieSceneIndexes,
|
||||||
setMovieSceneIndexes,
|
setMovieSceneIndexes,
|
||||||
] = useState<MovieSceneIndexMap>(new Map());
|
] = useState<MovieSceneIndexMap>(
|
||||||
const [tagIds, setTagIds] = useState<string[]>();
|
new Map(scene.movies.map((m) => [m.movie.id, m.scene_index ?? undefined]))
|
||||||
|
);
|
||||||
|
const [tagIds, setTagIds] = useState<string[]>(scene.tags.map((t) => t.id));
|
||||||
const [coverImage, setCoverImage] = useState<string>();
|
const [coverImage, setCoverImage] = useState<string>();
|
||||||
const [stashIDs, setStashIDs] = useState<GQL.StashIdInput[]>([]);
|
const [stashIDs, setStashIDs] = useState<GQL.StashIdInput[]>(scene.stash_ids);
|
||||||
|
|
||||||
const Scrapers = useListSceneScrapers();
|
const Scrapers = useListSceneScrapers();
|
||||||
const [queryableScrapers, setQueryableScrapers] = useState<GQL.Scraper[]>([]);
|
const [queryableScrapers, setQueryableScrapers] = useState<GQL.Scraper[]>([]);
|
||||||
@@ -71,17 +88,17 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
const stashConfig = useConfiguration();
|
const stashConfig = useConfiguration();
|
||||||
|
|
||||||
// Network state
|
// Network state
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const [updateScene] = useSceneUpdate();
|
const [updateScene] = useSceneUpdate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.isVisible) {
|
if (isVisible) {
|
||||||
Mousetrap.bind("s s", () => {
|
Mousetrap.bind("s s", () => {
|
||||||
onSave();
|
onSave();
|
||||||
});
|
});
|
||||||
Mousetrap.bind("d d", () => {
|
Mousetrap.bind("d d", () => {
|
||||||
props.onDelete();
|
onDelete();
|
||||||
});
|
});
|
||||||
|
|
||||||
// numeric keypresses get caught by jwplayer, so blur the element
|
// numeric keypresses get caught by jwplayer, so blur the element
|
||||||
@@ -141,7 +158,7 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!changed) {
|
if (!changed) {
|
||||||
movieSceneIndexes.forEach((v, id) => {
|
movieSceneIndexes.forEach((_v, id) => {
|
||||||
if (!newMap.has(id)) {
|
if (!newMap.has(id)) {
|
||||||
// id was removed
|
// id was removed
|
||||||
changed = true;
|
changed = true;
|
||||||
@@ -155,49 +172,11 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
}
|
}
|
||||||
}, [movieIds, movieSceneIndexes]);
|
}, [movieIds, movieSceneIndexes]);
|
||||||
|
|
||||||
function updateSceneEditState(state: Partial<GQL.SceneDataFragment>) {
|
|
||||||
const perfIds = state.performers?.map((performer) => performer.id);
|
|
||||||
const tIds = state.tags ? state.tags.map((tag) => tag.id) : undefined;
|
|
||||||
const moviIds = state.movies
|
|
||||||
? state.movies.map((sceneMovie) => sceneMovie.movie.id)
|
|
||||||
: undefined;
|
|
||||||
const movieSceneIdx: MovieSceneIndexMap = new Map();
|
|
||||||
if (state.movies) {
|
|
||||||
state.movies.forEach((m) => {
|
|
||||||
movieSceneIdx.set(m.movie.id, m.scene_index ?? undefined);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setTitle(state.title ?? undefined);
|
|
||||||
setDetails(state.details ?? undefined);
|
|
||||||
setUrl(state.url ?? undefined);
|
|
||||||
setDate(state.date ?? undefined);
|
|
||||||
setRating(state.rating === null ? NaN : state.rating);
|
|
||||||
setGalleries(
|
|
||||||
(state?.galleries ?? []).map((g) => ({
|
|
||||||
id: g.id,
|
|
||||||
title: g.title ?? TextUtils.fileNameFromPath(g.path ?? ""),
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
setStudioId(state?.studio?.id ?? undefined);
|
|
||||||
setMovieIds(moviIds);
|
|
||||||
setMovieSceneIndexes(movieSceneIdx);
|
|
||||||
setPerformerIds(perfIds);
|
|
||||||
setTagIds(tIds);
|
|
||||||
setStashIDs(state?.stash_ids ?? []);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateSceneEditState(props.scene);
|
|
||||||
setCoverImagePreview(props.scene?.paths?.screenshot ?? undefined);
|
|
||||||
setIsLoading(false);
|
|
||||||
}, [props.scene]);
|
|
||||||
|
|
||||||
const imageEncoding = ImageUtils.usePasteImage(onImageLoad, true);
|
const imageEncoding = ImageUtils.usePasteImage(onImageLoad, true);
|
||||||
|
|
||||||
function getSceneInput(): GQL.SceneUpdateInput {
|
function getSceneInput(): GQL.SceneUpdateInput {
|
||||||
return {
|
return {
|
||||||
id: props.scene.id,
|
id: scene.id,
|
||||||
title,
|
title,
|
||||||
details,
|
details,
|
||||||
url,
|
url,
|
||||||
@@ -284,7 +263,7 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
async function onScrapeStashBoxClicked(stashBoxIndex: number) {
|
async function onScrapeStashBoxClicked(stashBoxIndex: number) {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
const result = await queryStashBoxScene(stashBoxIndex, props.scene.id);
|
const result = await queryStashBoxScene(stashBoxIndex, scene.id);
|
||||||
if (!result.data || !result.data.queryStashBoxScene) {
|
if (!result.data || !result.data.queryStashBoxScene) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -339,9 +318,9 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onScrapeDialogClosed(scene?: GQL.ScrapedSceneDataFragment) {
|
function onScrapeDialogClosed(sceneData?: GQL.ScrapedSceneDataFragment) {
|
||||||
if (scene) {
|
if (sceneData) {
|
||||||
updateSceneFromScrapedScene(scene);
|
updateSceneFromScrapedScene(sceneData);
|
||||||
}
|
}
|
||||||
setScrapedScene(undefined);
|
setScrapedScene(undefined);
|
||||||
}
|
}
|
||||||
@@ -353,16 +332,14 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
|
|
||||||
const currentScene = getSceneInput();
|
const currentScene = getSceneInput();
|
||||||
if (!currentScene.cover_image) {
|
if (!currentScene.cover_image) {
|
||||||
currentScene.cover_image = props.scene.paths.screenshot;
|
currentScene.cover_image = scene.paths.screenshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SceneScrapeDialog
|
<SceneScrapeDialog
|
||||||
scene={currentScene}
|
scene={currentScene}
|
||||||
scraped={scrapedScene}
|
scraped={scrapedScene}
|
||||||
onClose={(scene) => {
|
onClose={(s) => onScrapeDialogClosed(s)}
|
||||||
onScrapeDialogClosed(scene);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -444,29 +421,31 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSceneFromScrapedScene(scene: GQL.ScrapedSceneDataFragment) {
|
function updateSceneFromScrapedScene(
|
||||||
if (scene.title) {
|
updatedScene: GQL.ScrapedSceneDataFragment
|
||||||
setTitle(scene.title);
|
) {
|
||||||
|
if (updatedScene.title) {
|
||||||
|
setTitle(updatedScene.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scene.details) {
|
if (updatedScene.details) {
|
||||||
setDetails(scene.details);
|
setDetails(updatedScene.details);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scene.date) {
|
if (updatedScene.date) {
|
||||||
setDate(scene.date);
|
setDate(updatedScene.date);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scene.url) {
|
if (updatedScene.url) {
|
||||||
setUrl(scene.url);
|
setUrl(updatedScene.url);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scene.studio && scene.studio.stored_id) {
|
if (updatedScene.studio && updatedScene.studio.stored_id) {
|
||||||
setStudioId(scene.studio.stored_id);
|
setStudioId(updatedScene.studio.stored_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scene.performers && scene.performers.length > 0) {
|
if (updatedScene.performers && updatedScene.performers.length > 0) {
|
||||||
const idPerfs = scene.performers.filter((p) => {
|
const idPerfs = updatedScene.performers.filter((p) => {
|
||||||
return p.stored_id !== undefined && p.stored_id !== null;
|
return p.stored_id !== undefined && p.stored_id !== null;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -476,8 +455,8 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scene.movies && scene.movies.length > 0) {
|
if (updatedScene.movies && updatedScene.movies.length > 0) {
|
||||||
const idMovis = scene.movies.filter((p) => {
|
const idMovis = updatedScene.movies.filter((p) => {
|
||||||
return p.stored_id !== undefined && p.stored_id !== null;
|
return p.stored_id !== undefined && p.stored_id !== null;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -487,8 +466,8 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scene?.tags?.length) {
|
if (updatedScene?.tags?.length) {
|
||||||
const idTags = scene.tags.filter((p) => {
|
const idTags = updatedScene.tags.filter((p) => {
|
||||||
return p.stored_id !== undefined && p.stored_id !== null;
|
return p.stored_id !== undefined && p.stored_id !== null;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -498,10 +477,10 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scene.image) {
|
if (updatedScene.image) {
|
||||||
// image is a base64 string
|
// image is a base64 string
|
||||||
setCoverImage(scene.image);
|
setCoverImage(updatedScene.image);
|
||||||
setCoverImagePreview(scene.image);
|
setCoverImagePreview(updatedScene.image);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -551,7 +530,7 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
<Button
|
<Button
|
||||||
className="edit-button"
|
className="edit-button"
|
||||||
variant="danger"
|
variant="danger"
|
||||||
onClick={() => props.onDelete()}
|
onClick={() => onDelete()}
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user