import React, { useEffect, useState } from "react"; import { Form, Col, Row } from "react-bootstrap"; import _ from "lodash"; import { useBulkSceneUpdate } from "src/core/StashService"; import * as GQL from "src/core/generated-graphql"; import { StudioSelect, Modal } from "src/components/Shared"; import { useToast } from "src/hooks"; import { FormUtils } from "src/utils"; import MultiSet from "../Shared/MultiSet"; import { RatingStars } from "./SceneDetails/RatingStars"; interface IListOperationProps { selected: GQL.SlimSceneDataFragment[]; onClose: (applied: boolean) => void; } export const EditScenesDialog: React.FC = ( props: IListOperationProps ) => { const Toast = useToast(); const [rating, setRating] = useState(); const [studioId, setStudioId] = useState(); const [performerMode, setPerformerMode] = React.useState< GQL.BulkUpdateIdMode >(GQL.BulkUpdateIdMode.Add); const [performerIds, setPerformerIds] = useState(); const [tagMode, setTagMode] = React.useState( GQL.BulkUpdateIdMode.Add ); const [tagIds, setTagIds] = useState(); const [updateScenes] = useBulkSceneUpdate(getSceneInput()); // Network state const [isUpdating, setIsUpdating] = useState(false); function makeBulkUpdateIds( ids: string[], mode: GQL.BulkUpdateIdMode ): GQL.BulkUpdateIds { return { mode, ids, }; } function getSceneInput(): GQL.BulkSceneUpdateInput { // need to determine what we are actually setting on each scene const aggregateRating = getRating(props.selected); const aggregateStudioId = getStudioId(props.selected); const aggregatePerformerIds = getPerformerIds(props.selected); const aggregateTagIds = getTagIds(props.selected); const sceneInput: GQL.BulkSceneUpdateInput = { ids: props.selected.map((scene) => { return scene.id; }), }; // if rating is undefined if (rating === undefined) { // and all scenes have the same rating, then we are unsetting the rating. if (aggregateRating) { // null rating unsets it sceneInput.rating = null; } // otherwise not setting the rating } else { // if rating is set, then we are setting the rating for all sceneInput.rating = rating; } // if studioId is undefined if (studioId === undefined) { // and all scenes have the same studioId, // then unset the studioId, otherwise ignoring studioId if (aggregateStudioId) { // null studio_id unsets it sceneInput.studio_id = null; } } else { // if studioId is set, then we are setting it sceneInput.studio_id = studioId; } // if performerIds are empty if ( performerMode === GQL.BulkUpdateIdMode.Set && (!performerIds || performerIds.length === 0) ) { // and all scenes have the same ids, if (aggregatePerformerIds.length > 0) { // then unset the performerIds, otherwise ignore sceneInput.performer_ids = makeBulkUpdateIds( performerIds || [], performerMode ); } } else { // if performerIds non-empty, then we are setting them sceneInput.performer_ids = makeBulkUpdateIds( performerIds || [], performerMode ); } // if tagIds non-empty, then we are setting them if ( tagMode === GQL.BulkUpdateIdMode.Set && (!tagIds || tagIds.length === 0) ) { // and all scenes have the same ids, if (aggregateTagIds.length > 0) { // then unset the tagIds, otherwise ignore sceneInput.tag_ids = makeBulkUpdateIds(tagIds || [], tagMode); } } else { // if tagIds non-empty, then we are setting them sceneInput.tag_ids = makeBulkUpdateIds(tagIds || [], tagMode); } return sceneInput; } async function onSave() { setIsUpdating(true); try { await updateScenes(); Toast.success({ content: "Updated scenes" }); props.onClose(true); } catch (e) { Toast.error(e); } setIsUpdating(false); } function getRating(state: GQL.SlimSceneDataFragment[]) { let ret: number | undefined; let first = true; state.forEach((scene: GQL.SlimSceneDataFragment) => { if (first) { ret = scene.rating ?? undefined; first = false; } else if (ret !== scene.rating) { ret = undefined; } }); return ret; } function getStudioId(state: GQL.SlimSceneDataFragment[]) { let ret: string | undefined; let first = true; state.forEach((scene: GQL.SlimSceneDataFragment) => { if (first) { ret = scene?.studio?.id; first = false; } else { const studio = scene?.studio?.id; if (ret !== studio) { ret = undefined; } } }); return ret; } function getPerformerIds(state: GQL.SlimSceneDataFragment[]) { let ret: string[] = []; let first = true; state.forEach((scene: GQL.SlimSceneDataFragment) => { if (first) { ret = scene.performers ? scene.performers.map((p) => p.id).sort() : []; first = false; } else { const perfIds = scene.performers ? scene.performers.map((p) => p.id).sort() : []; if (!_.isEqual(ret, perfIds)) { ret = []; } } }); return ret; } function getTagIds(state: GQL.SlimSceneDataFragment[]) { let ret: string[] = []; let first = true; state.forEach((scene: GQL.SlimSceneDataFragment) => { if (first) { ret = scene.tags ? scene.tags.map((t) => t.id).sort() : []; first = false; } else { const tIds = scene.tags ? scene.tags.map((t) => t.id).sort() : []; if (!_.isEqual(ret, tIds)) { ret = []; } } }); return ret; } useEffect(() => { const state = props.selected; let updateRating: number | undefined; let updateStudioID: string | undefined; let updatePerformerIds: string[] = []; let updateTagIds: string[] = []; let first = true; state.forEach((scene: GQL.SlimSceneDataFragment) => { const sceneRating = scene.rating; const sceneStudioID = scene?.studio?.id; const scenePerformerIDs = (scene.performers ?? []) .map((p) => p.id) .sort(); const sceneTagIDs = (scene.tags ?? []).map((p) => p.id).sort(); if (first) { updateRating = sceneRating ?? undefined; updateStudioID = sceneStudioID; updatePerformerIds = scenePerformerIDs; updateTagIds = sceneTagIDs; first = false; } else { if (sceneRating !== updateRating) { updateRating = undefined; } if (sceneStudioID !== updateStudioID) { updateStudioID = undefined; } if (!_.isEqual(scenePerformerIDs, updatePerformerIds)) { updatePerformerIds = []; } if (!_.isEqual(sceneTagIDs, updateTagIds)) { updateTagIds = []; } } }); setRating(updateRating); setStudioId(updateStudioID); if (performerMode === GQL.BulkUpdateIdMode.Set) { setPerformerIds(updatePerformerIds); } if (tagMode === GQL.BulkUpdateIdMode.Set) { setTagIds(updateTagIds); } }, [props.selected, performerMode, tagMode]); function renderMultiSelect( type: "performers" | "tags", ids: string[] | undefined ) { let mode = GQL.BulkUpdateIdMode.Add; switch (type) { case "performers": mode = performerMode; break; case "tags": mode = tagMode; break; } return ( { const itemIDs = items.map((i) => i.id); switch (type) { case "performers": setPerformerIds(itemIDs); break; case "tags": setTagIds(itemIDs); break; } }} onSetMode={(newMode) => { switch (type) { case "performers": setPerformerMode(newMode); break; case "tags": setTagMode(newMode); break; } }} ids={ids ?? []} mode={mode} /> ); } function render() { return ( props.onClose(false), text: "Cancel", variant: "secondary", }} isRunning={isUpdating} >
{FormUtils.renderLabel({ title: "Rating", })} setRating(value)} disabled={isUpdating} /> {FormUtils.renderLabel({ title: "Studio", })} setStudioId(items.length > 0 ? items[0]?.id : undefined) } ids={studioId ? [studioId] : []} isDisabled={isUpdating} /> Performers {renderMultiSelect("performers", performerIds)} Tags {renderMultiSelect("tags", tagIds)}
); } return render(); };