mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 04:44:37 +03:00
Add bulk movie update (#2283)
* Add bulk movie edit dialog * Implement common bulk edit functions Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
@@ -22,6 +22,12 @@ mutation MovieUpdate($input: MovieUpdateInput!) {
|
||||
}
|
||||
}
|
||||
|
||||
mutation BulkMovieUpdate($input: BulkMovieUpdateInput!) {
|
||||
bulkMovieUpdate(input: $input) {
|
||||
...MovieData
|
||||
}
|
||||
}
|
||||
|
||||
mutation MovieDestroy($id: ID!) {
|
||||
movieDestroy(input: { id: $id })
|
||||
}
|
||||
|
||||
@@ -224,6 +224,7 @@ type Mutation {
|
||||
movieUpdate(input: MovieUpdateInput!): Movie
|
||||
movieDestroy(input: MovieDestroyInput!): Boolean!
|
||||
moviesDestroy(ids: [ID!]!): Boolean!
|
||||
bulkMovieUpdate(input: BulkMovieUpdateInput!): [Movie!]
|
||||
|
||||
tagCreate(input: TagCreateInput!): Tag
|
||||
tagUpdate(input: TagUpdateInput!): Tag
|
||||
|
||||
@@ -54,6 +54,14 @@ input MovieUpdateInput {
|
||||
back_image: String
|
||||
}
|
||||
|
||||
input BulkMovieUpdateInput {
|
||||
clientMutationId: String
|
||||
ids: [ID!]
|
||||
rating: Int
|
||||
studio_id: ID
|
||||
director: String
|
||||
}
|
||||
|
||||
input MovieDestroyInput {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package api
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -220,6 +221,71 @@ func (r *mutationResolver) MovieUpdate(ctx context.Context, input models.MovieUp
|
||||
return r.getMovie(ctx, movie.ID)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) BulkMovieUpdate(ctx context.Context, input models.BulkMovieUpdateInput) ([]*models.Movie, error) {
|
||||
movieIDs, err := utils.StringSliceToIntSlice(input.Ids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updatedTime := time.Now()
|
||||
|
||||
translator := changesetTranslator{
|
||||
inputMap: getUpdateInputMap(ctx),
|
||||
}
|
||||
|
||||
updatedMovie := models.MoviePartial{
|
||||
UpdatedAt: &models.SQLiteTimestamp{Timestamp: updatedTime},
|
||||
}
|
||||
|
||||
updatedMovie.Rating = translator.nullInt64(input.Rating, "rating")
|
||||
updatedMovie.StudioID = translator.nullInt64FromString(input.StudioID, "studio_id")
|
||||
updatedMovie.Director = translator.nullString(input.Director, "director")
|
||||
|
||||
ret := []*models.Movie{}
|
||||
|
||||
if err := r.withTxn(ctx, func(repo models.Repository) error {
|
||||
qb := repo.Movie()
|
||||
|
||||
for _, movieID := range movieIDs {
|
||||
updatedMovie.ID = movieID
|
||||
|
||||
existing, err := qb.Find(movieID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if existing == nil {
|
||||
return fmt.Errorf("movie with id %d not found", movieID)
|
||||
}
|
||||
|
||||
movie, err := qb.Update(updatedMovie)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ret = append(ret, movie)
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var newRet []*models.Movie
|
||||
for _, movie := range ret {
|
||||
r.hookExecutor.ExecutePostHooks(ctx, movie.ID, plugin.MovieUpdatePost, input, translator.getFields())
|
||||
|
||||
movie, err = r.getMovie(ctx, movie.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newRet = append(newRet, movie)
|
||||
}
|
||||
|
||||
return newRet, nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) MovieDestroy(ctx context.Context, input models.MovieDestroyInput) (bool, error) {
|
||||
id, err := strconv.Atoi(input.ID)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
### ✨ New Features
|
||||
* Added support for bulk-editing movies. ([#2283](https://github.com/stashapp/stash/pull/2283))
|
||||
* Added support for filtering scenes, images and galleries featuring favourite performers and performer age at time of production. ([#2257](https://github.com/stashapp/stash/pull/2257))
|
||||
* Added support for filtering scenes with (or without) phash duplicates. ([#2257](https://github.com/stashapp/stash/pull/2257))
|
||||
* Added support for sorting scenes by phash. ([#2257](https://github.com/stashapp/stash/pull/2257))
|
||||
|
||||
@@ -9,6 +9,14 @@ import { useToast } from "src/hooks";
|
||||
import { FormUtils } from "src/utils";
|
||||
import MultiSet from "../Shared/MultiSet";
|
||||
import { RatingStars } from "../Scenes/SceneDetails/RatingStars";
|
||||
import {
|
||||
getAggregateInputIDs,
|
||||
getAggregateInputValue,
|
||||
getAggregatePerformerIds,
|
||||
getAggregateRating,
|
||||
getAggregateStudioId,
|
||||
getAggregateTagIds,
|
||||
} from "src/utils/bulkUpdate";
|
||||
|
||||
interface IListOperationProps {
|
||||
selected: GQL.SlimGalleryDataFragment[];
|
||||
@@ -42,22 +50,12 @@ export const EditGalleriesDialog: React.FC<IListOperationProps> = (
|
||||
|
||||
const checkboxRef = React.createRef<HTMLInputElement>();
|
||||
|
||||
function makeBulkUpdateIds(
|
||||
ids: string[],
|
||||
mode: GQL.BulkUpdateIdMode
|
||||
): GQL.BulkUpdateIds {
|
||||
return {
|
||||
mode,
|
||||
ids,
|
||||
};
|
||||
}
|
||||
|
||||
function getGalleryInput(): GQL.BulkGalleryUpdateInput {
|
||||
// need to determine what we are actually setting on each gallery
|
||||
const aggregateRating = getRating(props.selected);
|
||||
const aggregateStudioId = getStudioId(props.selected);
|
||||
const aggregatePerformerIds = getPerformerIds(props.selected);
|
||||
const aggregateTagIds = getTagIds(props.selected);
|
||||
const aggregateRating = getAggregateRating(props.selected);
|
||||
const aggregateStudioId = getAggregateStudioId(props.selected);
|
||||
const aggregatePerformerIds = getAggregatePerformerIds(props.selected);
|
||||
const aggregateTagIds = getAggregateTagIds(props.selected);
|
||||
|
||||
const galleryInput: GQL.BulkGalleryUpdateInput = {
|
||||
ids: props.selected.map((gallery) => {
|
||||
@@ -65,67 +63,22 @@ export const EditGalleriesDialog: React.FC<IListOperationProps> = (
|
||||
}),
|
||||
};
|
||||
|
||||
// if rating is undefined
|
||||
if (rating === undefined) {
|
||||
// and all galleries have the same rating, then we are unsetting the rating.
|
||||
if (aggregateRating) {
|
||||
// null to unset rating
|
||||
galleryInput.rating = null;
|
||||
}
|
||||
// otherwise not setting the rating
|
||||
} else {
|
||||
// if rating is set, then we are setting the rating for all
|
||||
galleryInput.rating = rating;
|
||||
}
|
||||
galleryInput.rating = getAggregateInputValue(rating, aggregateRating);
|
||||
galleryInput.studio_id = getAggregateInputValue(
|
||||
studioId,
|
||||
aggregateStudioId
|
||||
);
|
||||
|
||||
// if studioId is undefined
|
||||
if (studioId === undefined) {
|
||||
// and all galleries have the same studioId,
|
||||
// then unset the studioId, otherwise ignoring studioId
|
||||
if (aggregateStudioId) {
|
||||
// null to unset studio_id
|
||||
galleryInput.studio_id = null;
|
||||
}
|
||||
} else {
|
||||
// if studioId is set, then we are setting it
|
||||
galleryInput.studio_id = studioId;
|
||||
}
|
||||
|
||||
// if performerIds are empty
|
||||
if (
|
||||
performerMode === GQL.BulkUpdateIdMode.Set &&
|
||||
(!performerIds || performerIds.length === 0)
|
||||
) {
|
||||
// and all galleries have the same ids,
|
||||
if (aggregatePerformerIds.length > 0) {
|
||||
// then unset the performerIds, otherwise ignore
|
||||
galleryInput.performer_ids = makeBulkUpdateIds(
|
||||
performerIds || [],
|
||||
performerMode
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// if performerIds non-empty, then we are setting them
|
||||
galleryInput.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 galleries have the same ids,
|
||||
if (aggregateTagIds.length > 0) {
|
||||
// then unset the tagIds, otherwise ignore
|
||||
galleryInput.tag_ids = makeBulkUpdateIds(tagIds || [], tagMode);
|
||||
}
|
||||
} else {
|
||||
// if tagIds non-empty, then we are setting them
|
||||
galleryInput.tag_ids = makeBulkUpdateIds(tagIds || [], tagMode);
|
||||
}
|
||||
galleryInput.performer_ids = getAggregateInputIDs(
|
||||
performerMode,
|
||||
performerIds,
|
||||
aggregatePerformerIds
|
||||
);
|
||||
galleryInput.tag_ids = getAggregateInputIDs(
|
||||
tagMode,
|
||||
tagIds,
|
||||
aggregateTagIds
|
||||
);
|
||||
|
||||
if (organized !== undefined) {
|
||||
galleryInput.organized = organized;
|
||||
@@ -157,85 +110,6 @@ export const EditGalleriesDialog: React.FC<IListOperationProps> = (
|
||||
setIsUpdating(false);
|
||||
}
|
||||
|
||||
function getRating(state: GQL.SlimGalleryDataFragment[]) {
|
||||
let ret: number | undefined;
|
||||
let first = true;
|
||||
|
||||
state.forEach((gallery) => {
|
||||
if (first) {
|
||||
ret = gallery.rating ?? undefined;
|
||||
first = false;
|
||||
} else if (ret !== gallery.rating) {
|
||||
ret = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function getStudioId(state: GQL.SlimGalleryDataFragment[]) {
|
||||
let ret: string | undefined;
|
||||
let first = true;
|
||||
|
||||
state.forEach((gallery) => {
|
||||
if (first) {
|
||||
ret = gallery?.studio?.id;
|
||||
first = false;
|
||||
} else {
|
||||
const studio = gallery?.studio?.id;
|
||||
if (ret !== studio) {
|
||||
ret = undefined;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function getPerformerIds(state: GQL.SlimGalleryDataFragment[]) {
|
||||
let ret: string[] = [];
|
||||
let first = true;
|
||||
|
||||
state.forEach((gallery) => {
|
||||
if (first) {
|
||||
ret = gallery.performers
|
||||
? gallery.performers.map((p) => p.id).sort()
|
||||
: [];
|
||||
first = false;
|
||||
} else {
|
||||
const perfIds = gallery.performers
|
||||
? gallery.performers.map((p) => p.id).sort()
|
||||
: [];
|
||||
|
||||
if (!_.isEqual(ret, perfIds)) {
|
||||
ret = [];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function getTagIds(state: GQL.SlimGalleryDataFragment[]) {
|
||||
let ret: string[] = [];
|
||||
let first = true;
|
||||
|
||||
state.forEach((gallery) => {
|
||||
if (first) {
|
||||
ret = gallery.tags ? gallery.tags.map((t) => t.id).sort() : [];
|
||||
first = false;
|
||||
} else {
|
||||
const tIds = gallery.tags ? gallery.tags.map((t) => t.id).sort() : [];
|
||||
|
||||
if (!_.isEqual(ret, tIds)) {
|
||||
ret = [];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const state = props.selected;
|
||||
let updateRating: number | undefined;
|
||||
|
||||
@@ -9,6 +9,14 @@ import { useToast } from "src/hooks";
|
||||
import { FormUtils } from "src/utils";
|
||||
import MultiSet from "../Shared/MultiSet";
|
||||
import { RatingStars } from "../Scenes/SceneDetails/RatingStars";
|
||||
import {
|
||||
getAggregateInputIDs,
|
||||
getAggregateInputValue,
|
||||
getAggregatePerformerIds,
|
||||
getAggregateRating,
|
||||
getAggregateStudioId,
|
||||
getAggregateTagIds,
|
||||
} from "src/utils/bulkUpdate";
|
||||
|
||||
interface IListOperationProps {
|
||||
selected: GQL.SlimImageDataFragment[];
|
||||
@@ -42,22 +50,12 @@ export const EditImagesDialog: React.FC<IListOperationProps> = (
|
||||
|
||||
const checkboxRef = React.createRef<HTMLInputElement>();
|
||||
|
||||
function makeBulkUpdateIds(
|
||||
ids: string[],
|
||||
mode: GQL.BulkUpdateIdMode
|
||||
): GQL.BulkUpdateIds {
|
||||
return {
|
||||
mode,
|
||||
ids,
|
||||
};
|
||||
}
|
||||
|
||||
function getImageInput(): GQL.BulkImageUpdateInput {
|
||||
// need to determine what we are actually setting on each image
|
||||
const aggregateRating = getRating(props.selected);
|
||||
const aggregateStudioId = getStudioId(props.selected);
|
||||
const aggregatePerformerIds = getPerformerIds(props.selected);
|
||||
const aggregateTagIds = getTagIds(props.selected);
|
||||
const aggregateRating = getAggregateRating(props.selected);
|
||||
const aggregateStudioId = getAggregateStudioId(props.selected);
|
||||
const aggregatePerformerIds = getAggregatePerformerIds(props.selected);
|
||||
const aggregateTagIds = getAggregateTagIds(props.selected);
|
||||
|
||||
const imageInput: GQL.BulkImageUpdateInput = {
|
||||
ids: props.selected.map((image) => {
|
||||
@@ -65,67 +63,15 @@ export const EditImagesDialog: React.FC<IListOperationProps> = (
|
||||
}),
|
||||
};
|
||||
|
||||
// if rating is undefined
|
||||
if (rating === undefined) {
|
||||
// and all images have the same rating, then we are unsetting the rating.
|
||||
if (aggregateRating) {
|
||||
// null rating to unset it
|
||||
imageInput.rating = null;
|
||||
}
|
||||
// otherwise not setting the rating
|
||||
} else {
|
||||
// if rating is set, then we are setting the rating for all
|
||||
imageInput.rating = rating;
|
||||
}
|
||||
imageInput.rating = getAggregateInputValue(rating, aggregateRating);
|
||||
imageInput.studio_id = getAggregateInputValue(studioId, aggregateStudioId);
|
||||
|
||||
// if studioId is undefined
|
||||
if (studioId === undefined) {
|
||||
// and all images have the same studioId,
|
||||
// then unset the studioId, otherwise ignoring studioId
|
||||
if (aggregateStudioId) {
|
||||
// null studio_id to unset it
|
||||
imageInput.studio_id = null;
|
||||
}
|
||||
} else {
|
||||
// if studioId is set, then we are setting it
|
||||
imageInput.studio_id = studioId;
|
||||
}
|
||||
|
||||
// if performerIds are empty
|
||||
if (
|
||||
performerMode === GQL.BulkUpdateIdMode.Set &&
|
||||
(!performerIds || performerIds.length === 0)
|
||||
) {
|
||||
// and all images have the same ids,
|
||||
if (aggregatePerformerIds.length > 0) {
|
||||
// then unset the performerIds, otherwise ignore
|
||||
imageInput.performer_ids = makeBulkUpdateIds(
|
||||
performerIds || [],
|
||||
performerMode
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// if performerIds non-empty, then we are setting them
|
||||
imageInput.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 images have the same ids,
|
||||
if (aggregateTagIds.length > 0) {
|
||||
// then unset the tagIds, otherwise ignore
|
||||
imageInput.tag_ids = makeBulkUpdateIds(tagIds || [], tagMode);
|
||||
}
|
||||
} else {
|
||||
// if tagIds non-empty, then we are setting them
|
||||
imageInput.tag_ids = makeBulkUpdateIds(tagIds || [], tagMode);
|
||||
}
|
||||
imageInput.performer_ids = getAggregateInputIDs(
|
||||
performerMode,
|
||||
performerIds,
|
||||
aggregatePerformerIds
|
||||
);
|
||||
imageInput.tag_ids = getAggregateInputIDs(tagMode, tagIds, aggregateTagIds);
|
||||
|
||||
if (organized !== undefined) {
|
||||
imageInput.organized = organized;
|
||||
@@ -155,83 +101,6 @@ export const EditImagesDialog: React.FC<IListOperationProps> = (
|
||||
setIsUpdating(false);
|
||||
}
|
||||
|
||||
function getRating(state: GQL.SlimImageDataFragment[]) {
|
||||
let ret: number | undefined;
|
||||
let first = true;
|
||||
|
||||
state.forEach((image: GQL.SlimImageDataFragment) => {
|
||||
if (first) {
|
||||
ret = image.rating ?? undefined;
|
||||
first = false;
|
||||
} else if (ret !== image.rating) {
|
||||
ret = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function getStudioId(state: GQL.SlimImageDataFragment[]) {
|
||||
let ret: string | undefined;
|
||||
let first = true;
|
||||
|
||||
state.forEach((image: GQL.SlimImageDataFragment) => {
|
||||
if (first) {
|
||||
ret = image?.studio?.id;
|
||||
first = false;
|
||||
} else {
|
||||
const studio = image?.studio?.id;
|
||||
if (ret !== studio) {
|
||||
ret = undefined;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function getPerformerIds(state: GQL.SlimImageDataFragment[]) {
|
||||
let ret: string[] = [];
|
||||
let first = true;
|
||||
|
||||
state.forEach((image: GQL.SlimImageDataFragment) => {
|
||||
if (first) {
|
||||
ret = image.performers ? image.performers.map((p) => p.id).sort() : [];
|
||||
first = false;
|
||||
} else {
|
||||
const perfIds = image.performers
|
||||
? image.performers.map((p) => p.id).sort()
|
||||
: [];
|
||||
|
||||
if (!_.isEqual(ret, perfIds)) {
|
||||
ret = [];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function getTagIds(state: GQL.SlimImageDataFragment[]) {
|
||||
let ret: string[] = [];
|
||||
let first = true;
|
||||
|
||||
state.forEach((image: GQL.SlimImageDataFragment) => {
|
||||
if (first) {
|
||||
ret = image.tags ? image.tags.map((t) => t.id).sort() : [];
|
||||
first = false;
|
||||
} else {
|
||||
const tIds = image.tags ? image.tags.map((t) => t.id).sort() : [];
|
||||
|
||||
if (!_.isEqual(ret, tIds)) {
|
||||
ret = [];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const state = props.selected;
|
||||
let updateRating: number | undefined;
|
||||
|
||||
162
ui/v2.5/src/components/Movies/EditMoviesDialog.tsx
Normal file
162
ui/v2.5/src/components/Movies/EditMoviesDialog.tsx
Normal file
@@ -0,0 +1,162 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Form, Col, Row } from "react-bootstrap";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import { useBulkMovieUpdate } from "src/core/StashService";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { Modal, StudioSelect } from "src/components/Shared";
|
||||
import { useToast } from "src/hooks";
|
||||
import { FormUtils } from "src/utils";
|
||||
import { RatingStars } from "../Scenes/SceneDetails/RatingStars";
|
||||
import {
|
||||
getAggregateInputValue,
|
||||
getAggregateRating,
|
||||
getAggregateStudioId,
|
||||
} from "src/utils/bulkUpdate";
|
||||
|
||||
interface IListOperationProps {
|
||||
selected: GQL.MovieDataFragment[];
|
||||
onClose: (applied: boolean) => void;
|
||||
}
|
||||
|
||||
export const EditMoviesDialog: React.FC<IListOperationProps> = (
|
||||
props: IListOperationProps
|
||||
) => {
|
||||
const intl = useIntl();
|
||||
const Toast = useToast();
|
||||
const [rating, setRating] = useState<number | undefined>();
|
||||
const [studioId, setStudioId] = useState<string | undefined>();
|
||||
const [director, setDirector] = useState<string | undefined>();
|
||||
|
||||
const [updateMovies] = useBulkMovieUpdate(getMovieInput());
|
||||
|
||||
const [isUpdating, setIsUpdating] = useState(false);
|
||||
|
||||
function getMovieInput(): GQL.BulkMovieUpdateInput {
|
||||
const aggregateRating = getAggregateRating(props.selected);
|
||||
const aggregateStudioId = getAggregateStudioId(props.selected);
|
||||
|
||||
const movieInput: GQL.BulkMovieUpdateInput = {
|
||||
ids: props.selected.map((movie) => movie.id),
|
||||
director,
|
||||
};
|
||||
|
||||
// if rating is undefined
|
||||
movieInput.rating = getAggregateInputValue(rating, aggregateRating);
|
||||
movieInput.studio_id = getAggregateInputValue(studioId, aggregateStudioId);
|
||||
|
||||
return movieInput;
|
||||
}
|
||||
|
||||
async function onSave() {
|
||||
setIsUpdating(true);
|
||||
try {
|
||||
await updateMovies();
|
||||
Toast.success({
|
||||
content: intl.formatMessage(
|
||||
{ id: "toast.updated_entity" },
|
||||
{
|
||||
entity: intl.formatMessage({ id: "movies" }).toLocaleLowerCase(),
|
||||
}
|
||||
),
|
||||
});
|
||||
props.onClose(true);
|
||||
} catch (e) {
|
||||
Toast.error(e);
|
||||
}
|
||||
setIsUpdating(false);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const state = props.selected;
|
||||
let updateRating: number | undefined;
|
||||
let updateStudioId: string | undefined;
|
||||
let updateDirector: string | undefined;
|
||||
let first = true;
|
||||
|
||||
state.forEach((movie: GQL.MovieDataFragment) => {
|
||||
if (first) {
|
||||
first = false;
|
||||
updateRating = movie.rating ?? undefined;
|
||||
updateStudioId = movie.studio?.id ?? undefined;
|
||||
updateDirector = movie.director ?? undefined;
|
||||
} else {
|
||||
if (movie.rating !== updateRating) {
|
||||
updateRating = undefined;
|
||||
}
|
||||
if (movie.studio?.id !== updateStudioId) {
|
||||
updateStudioId = undefined;
|
||||
}
|
||||
if (movie.director !== updateDirector) {
|
||||
updateDirector = undefined;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setRating(updateRating);
|
||||
setStudioId(updateStudioId);
|
||||
setDirector(updateDirector);
|
||||
}, [props.selected]);
|
||||
|
||||
function render() {
|
||||
return (
|
||||
<Modal
|
||||
show
|
||||
icon="pencil-alt"
|
||||
header="Edit Movies"
|
||||
accept={{
|
||||
onClick: onSave,
|
||||
text: intl.formatMessage({ id: "actions.apply" }),
|
||||
}}
|
||||
cancel={{
|
||||
onClick: () => props.onClose(false),
|
||||
text: intl.formatMessage({ id: "actions.cancel" }),
|
||||
variant: "secondary",
|
||||
}}
|
||||
isRunning={isUpdating}
|
||||
>
|
||||
<Form>
|
||||
<Form.Group controlId="rating" as={Row}>
|
||||
{FormUtils.renderLabel({
|
||||
title: intl.formatMessage({ id: "rating" }),
|
||||
})}
|
||||
<Col xs={9}>
|
||||
<RatingStars
|
||||
value={rating}
|
||||
onSetRating={(value) => setRating(value)}
|
||||
disabled={isUpdating}
|
||||
/>
|
||||
</Col>
|
||||
</Form.Group>
|
||||
<Form.Group controlId="studio" as={Row}>
|
||||
{FormUtils.renderLabel({
|
||||
title: intl.formatMessage({ id: "studio" }),
|
||||
})}
|
||||
<Col xs={9}>
|
||||
<StudioSelect
|
||||
onSelect={(items) =>
|
||||
setStudioId(items.length > 0 ? items[0]?.id : undefined)
|
||||
}
|
||||
ids={studioId ? [studioId] : []}
|
||||
isDisabled={isUpdating}
|
||||
/>
|
||||
</Col>
|
||||
</Form.Group>
|
||||
<Form.Group controlId="director">
|
||||
<Form.Label>
|
||||
<FormattedMessage id="director" />
|
||||
</Form.Label>
|
||||
<Form.Control
|
||||
className="input-control"
|
||||
type="text"
|
||||
value={director}
|
||||
onChange={(event) => setDirector(event.currentTarget.value)}
|
||||
placeholder={intl.formatMessage({ id: "director" })}
|
||||
/>
|
||||
</Form.Group>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
return render();
|
||||
};
|
||||
@@ -6,6 +6,7 @@ import { useHistory } from "react-router-dom";
|
||||
import {
|
||||
FindMoviesQueryResult,
|
||||
SlimMovieDataFragment,
|
||||
MovieDataFragment,
|
||||
} from "src/core/generated-graphql";
|
||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||
import { DisplayMode } from "src/models/list-filter/types";
|
||||
@@ -17,6 +18,7 @@ import {
|
||||
} from "src/hooks/ListHook";
|
||||
import { ExportDialog, DeleteEntityDialog } from "src/components/Shared";
|
||||
import { MovieCard } from "./MovieCard";
|
||||
import { EditMoviesDialog } from "./EditMoviesDialog";
|
||||
|
||||
interface IMovieList {
|
||||
filterHook?: (filter: ListFilterModel) => ListFilterModel;
|
||||
@@ -57,6 +59,17 @@ export const MovieList: React.FC<IMovieList> = ({ filterHook }) => {
|
||||
};
|
||||
};
|
||||
|
||||
function renderEditDialog(
|
||||
selectedMovies: MovieDataFragment[],
|
||||
onClose: (applied: boolean) => void
|
||||
) {
|
||||
return (
|
||||
<>
|
||||
<EditMoviesDialog selected={selectedMovies} onClose={onClose} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const renderDeleteDialog = (
|
||||
selectedMovies: SlimMovieDataFragment[],
|
||||
onClose: (confirmed: boolean) => void
|
||||
@@ -76,6 +89,7 @@ export const MovieList: React.FC<IMovieList> = ({ filterHook }) => {
|
||||
otherOperations,
|
||||
selectable: true,
|
||||
persistState: PersistanceLevel.ALL,
|
||||
renderEditDialog,
|
||||
renderDeleteDialog,
|
||||
filterHook,
|
||||
});
|
||||
|
||||
@@ -9,6 +9,12 @@ import { useToast } from "src/hooks";
|
||||
import { FormUtils } from "src/utils";
|
||||
import MultiSet from "../Shared/MultiSet";
|
||||
import { RatingStars } from "../Scenes/SceneDetails/RatingStars";
|
||||
import {
|
||||
getAggregateInputIDs,
|
||||
getAggregateInputValue,
|
||||
getAggregateRating,
|
||||
getAggregateTagIds,
|
||||
} from "src/utils/bulkUpdate";
|
||||
import { genderStrings, stringToGender } from "src/utils/gender";
|
||||
|
||||
interface IListOperationProps {
|
||||
@@ -46,20 +52,10 @@ export const EditPerformersDialog: React.FC<IListOperationProps> = (
|
||||
|
||||
const checkboxRef = React.createRef<HTMLInputElement>();
|
||||
|
||||
function makeBulkUpdateIds(
|
||||
ids: string[],
|
||||
mode: GQL.BulkUpdateIdMode
|
||||
): GQL.BulkUpdateIds {
|
||||
return {
|
||||
mode,
|
||||
ids,
|
||||
};
|
||||
}
|
||||
|
||||
function getPerformerInput(): GQL.BulkPerformerUpdateInput {
|
||||
// need to determine what we are actually setting on each performer
|
||||
const aggregateTagIds = getTagIds(props.selected);
|
||||
const aggregateRating = getRating(props.selected);
|
||||
const aggregateTagIds = getAggregateTagIds(props.selected);
|
||||
const aggregateRating = getAggregateRating(props.selected);
|
||||
|
||||
const performerInput: GQL.BulkPerformerUpdateInput = {
|
||||
ids: props.selected.map((performer) => {
|
||||
@@ -67,33 +63,13 @@ export const EditPerformersDialog: React.FC<IListOperationProps> = (
|
||||
}),
|
||||
};
|
||||
|
||||
// if rating is undefined
|
||||
if (rating === undefined) {
|
||||
// and all galleries have the same rating, then we are unsetting the rating.
|
||||
if (aggregateRating) {
|
||||
// null to unset rating
|
||||
performerInput.rating = null;
|
||||
}
|
||||
// otherwise not setting the rating
|
||||
} else {
|
||||
// if rating is set, then we are setting the rating for all
|
||||
performerInput.rating = rating;
|
||||
}
|
||||
performerInput.rating = getAggregateInputValue(rating, aggregateRating);
|
||||
|
||||
// if tagIds non-empty, then we are setting them
|
||||
if (
|
||||
tagMode === GQL.BulkUpdateIdMode.Set &&
|
||||
(!tagIds || tagIds.length === 0)
|
||||
) {
|
||||
// and all performers have the same ids,
|
||||
if (aggregateTagIds.length > 0) {
|
||||
// then unset the tagIds, otherwise ignore
|
||||
performerInput.tag_ids = makeBulkUpdateIds(tagIds || [], tagMode);
|
||||
}
|
||||
} else {
|
||||
// if tagIds non-empty, then we are setting them
|
||||
performerInput.tag_ids = makeBulkUpdateIds(tagIds || [], tagMode);
|
||||
}
|
||||
performerInput.tag_ids = getAggregateInputIDs(
|
||||
tagMode,
|
||||
tagIds,
|
||||
aggregateTagIds
|
||||
);
|
||||
|
||||
performerInput.favorite = favorite;
|
||||
performerInput.ethnicity = ethnicity;
|
||||
@@ -130,44 +106,6 @@ export const EditPerformersDialog: React.FC<IListOperationProps> = (
|
||||
setIsUpdating(false);
|
||||
}
|
||||
|
||||
function getTagIds(state: GQL.SlimPerformerDataFragment[]) {
|
||||
let ret: string[] = [];
|
||||
let first = true;
|
||||
|
||||
state.forEach((performer: GQL.SlimPerformerDataFragment) => {
|
||||
if (first) {
|
||||
ret = performer.tags ? performer.tags.map((t) => t.id).sort() : [];
|
||||
first = false;
|
||||
} else {
|
||||
const tIds = performer.tags
|
||||
? performer.tags.map((t) => t.id).sort()
|
||||
: [];
|
||||
|
||||
if (!_.isEqual(ret, tIds)) {
|
||||
ret = [];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function getRating(state: GQL.SlimPerformerDataFragment[]) {
|
||||
let ret: number | undefined;
|
||||
let first = true;
|
||||
|
||||
state.forEach((performer) => {
|
||||
if (first) {
|
||||
ret = performer.rating ?? undefined;
|
||||
first = false;
|
||||
} else if (ret !== performer.rating) {
|
||||
ret = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const state = props.selected;
|
||||
let updateTagIds: string[] = [];
|
||||
|
||||
@@ -9,6 +9,15 @@ import { useToast } from "src/hooks";
|
||||
import { FormUtils } from "src/utils";
|
||||
import MultiSet from "../Shared/MultiSet";
|
||||
import { RatingStars } from "./SceneDetails/RatingStars";
|
||||
import {
|
||||
getAggregateInputIDs,
|
||||
getAggregateInputValue,
|
||||
getAggregateMovieIds,
|
||||
getAggregatePerformerIds,
|
||||
getAggregateRating,
|
||||
getAggregateStudioId,
|
||||
getAggregateTagIds,
|
||||
} from "src/utils/bulkUpdate";
|
||||
|
||||
interface IListOperationProps {
|
||||
selected: GQL.SlimSceneDataFragment[];
|
||||
@@ -47,23 +56,13 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
|
||||
|
||||
const checkboxRef = React.createRef<HTMLInputElement>();
|
||||
|
||||
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 aggregateMovieIds = getMovieIds(props.selected);
|
||||
const aggregateRating = getAggregateRating(props.selected);
|
||||
const aggregateStudioId = getAggregateStudioId(props.selected);
|
||||
const aggregatePerformerIds = getAggregatePerformerIds(props.selected);
|
||||
const aggregateTagIds = getAggregateTagIds(props.selected);
|
||||
const aggregateMovieIds = getAggregateMovieIds(props.selected);
|
||||
|
||||
const sceneInput: GQL.BulkSceneUpdateInput = {
|
||||
ids: props.selected.map((scene) => {
|
||||
@@ -71,82 +70,20 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
|
||||
}),
|
||||
};
|
||||
|
||||
// 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;
|
||||
}
|
||||
sceneInput.rating = getAggregateInputValue(rating, aggregateRating);
|
||||
sceneInput.studio_id = getAggregateInputValue(studioId, aggregateStudioId);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// if movieIds non-empty, then we are setting them
|
||||
if (
|
||||
movieMode === GQL.BulkUpdateIdMode.Set &&
|
||||
(!movieIds || movieIds.length === 0)
|
||||
) {
|
||||
// and all scenes have the same ids,
|
||||
if (aggregateMovieIds.length > 0) {
|
||||
// then unset the movieIds, otherwise ignore
|
||||
sceneInput.movie_ids = makeBulkUpdateIds(movieIds || [], movieMode);
|
||||
}
|
||||
} else {
|
||||
// if movieIds non-empty, then we are setting them
|
||||
sceneInput.movie_ids = makeBulkUpdateIds(movieIds || [], movieMode);
|
||||
}
|
||||
sceneInput.performer_ids = getAggregateInputIDs(
|
||||
performerMode,
|
||||
performerIds,
|
||||
aggregatePerformerIds
|
||||
);
|
||||
sceneInput.tag_ids = getAggregateInputIDs(tagMode, tagIds, aggregateTagIds);
|
||||
sceneInput.movie_ids = getAggregateInputIDs(
|
||||
movieMode,
|
||||
movieIds,
|
||||
aggregateMovieIds
|
||||
);
|
||||
|
||||
if (organized !== undefined) {
|
||||
sceneInput.organized = organized;
|
||||
@@ -172,105 +109,6 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
|
||||
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;
|
||||
}
|
||||
|
||||
function getMovieIds(state: GQL.SlimSceneDataFragment[]) {
|
||||
let ret: string[] = [];
|
||||
let first = true;
|
||||
|
||||
state.forEach((scene: GQL.SlimSceneDataFragment) => {
|
||||
if (first) {
|
||||
ret = scene.movies ? scene.movies.map((m) => m.movie.id).sort() : [];
|
||||
first = false;
|
||||
} else {
|
||||
const mIds = scene.movies
|
||||
? scene.movies.map((m) => m.movie.id).sort()
|
||||
: [];
|
||||
|
||||
if (!_.isEqual(ret, mIds)) {
|
||||
ret = [];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const state = props.selected;
|
||||
let updateRating: number | undefined;
|
||||
|
||||
@@ -659,6 +659,14 @@ export const useMovieUpdate = () =>
|
||||
update: deleteCache(movieMutationImpactedQueries),
|
||||
});
|
||||
|
||||
export const useBulkMovieUpdate = (input: GQL.BulkMovieUpdateInput) =>
|
||||
GQL.useBulkMovieUpdateMutation({
|
||||
variables: {
|
||||
input,
|
||||
},
|
||||
update: deleteCache(movieMutationImpactedQueries),
|
||||
});
|
||||
|
||||
export const useMovieDestroy = (input: GQL.MovieDestroyInput) =>
|
||||
GQL.useMovieDestroyMutation({
|
||||
variables: input,
|
||||
|
||||
175
ui/v2.5/src/utils/bulkUpdate.ts
Normal file
175
ui/v2.5/src/utils/bulkUpdate.ts
Normal file
@@ -0,0 +1,175 @@
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import _ from "lodash";
|
||||
|
||||
interface IHasRating {
|
||||
rating?: GQL.Maybe<number> | undefined;
|
||||
}
|
||||
|
||||
export function getAggregateRating(state: IHasRating[]) {
|
||||
let ret: number | undefined;
|
||||
let first = true;
|
||||
|
||||
state.forEach((o) => {
|
||||
if (first) {
|
||||
ret = o.rating ?? undefined;
|
||||
first = false;
|
||||
} else if (ret !== o.rating) {
|
||||
ret = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
interface IHasID {
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface IHasStudio {
|
||||
studio?: GQL.Maybe<IHasID> | undefined;
|
||||
}
|
||||
|
||||
export function getAggregateStudioId(state: IHasStudio[]) {
|
||||
let ret: string | undefined;
|
||||
let first = true;
|
||||
|
||||
state.forEach((o) => {
|
||||
if (first) {
|
||||
ret = o?.studio?.id;
|
||||
first = false;
|
||||
} else {
|
||||
const studio = o?.studio?.id;
|
||||
if (ret !== studio) {
|
||||
ret = undefined;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
interface IHasPerformers {
|
||||
performers: IHasID[];
|
||||
}
|
||||
|
||||
export function getAggregatePerformerIds(state: IHasPerformers[]) {
|
||||
let ret: string[] = [];
|
||||
let first = true;
|
||||
|
||||
state.forEach((o) => {
|
||||
if (first) {
|
||||
ret = o.performers ? o.performers.map((p) => p.id).sort() : [];
|
||||
first = false;
|
||||
} else {
|
||||
const perfIds = o.performers ? o.performers.map((p) => p.id).sort() : [];
|
||||
|
||||
if (!_.isEqual(ret, perfIds)) {
|
||||
ret = [];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
interface IHasTags {
|
||||
tags: IHasID[];
|
||||
}
|
||||
|
||||
export function getAggregateTagIds(state: IHasTags[]) {
|
||||
let ret: string[] = [];
|
||||
let first = true;
|
||||
|
||||
state.forEach((o) => {
|
||||
if (first) {
|
||||
ret = o.tags ? o.tags.map((t) => t.id).sort() : [];
|
||||
first = false;
|
||||
} else {
|
||||
const tIds = o.tags ? o.tags.map((t) => t.id).sort() : [];
|
||||
|
||||
if (!_.isEqual(ret, tIds)) {
|
||||
ret = [];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
interface IMovie {
|
||||
movie: IHasID;
|
||||
}
|
||||
|
||||
interface IHasMovies {
|
||||
movies: IMovie[];
|
||||
}
|
||||
|
||||
export function getAggregateMovieIds(state: IHasMovies[]) {
|
||||
let ret: string[] = [];
|
||||
let first = true;
|
||||
|
||||
state.forEach((o) => {
|
||||
if (first) {
|
||||
ret = o.movies ? o.movies.map((m) => m.movie.id).sort() : [];
|
||||
first = false;
|
||||
} else {
|
||||
const mIds = o.movies ? o.movies.map((m) => m.movie.id).sort() : [];
|
||||
|
||||
if (!_.isEqual(ret, mIds)) {
|
||||
ret = [];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function makeBulkUpdateIds(
|
||||
ids: string[],
|
||||
mode: GQL.BulkUpdateIdMode
|
||||
): GQL.BulkUpdateIds {
|
||||
return {
|
||||
mode,
|
||||
ids,
|
||||
};
|
||||
}
|
||||
|
||||
export function getAggregateInputValue<V>(
|
||||
inputValue: V | null | undefined,
|
||||
aggregateValue: V | null | undefined
|
||||
) {
|
||||
if (inputValue === undefined) {
|
||||
// and all objects have the same value, then we are unsetting the value.
|
||||
if (aggregateValue !== undefined) {
|
||||
// null to unset rating
|
||||
return null;
|
||||
}
|
||||
// otherwise not setting the rating
|
||||
return undefined;
|
||||
} else {
|
||||
// if value is set, then we are setting the value for all
|
||||
return inputValue;
|
||||
}
|
||||
}
|
||||
|
||||
export function getAggregateInputIDs(
|
||||
mode: GQL.BulkUpdateIdMode,
|
||||
inputIds: string[] | undefined,
|
||||
aggregateIds: string[]
|
||||
) {
|
||||
if (
|
||||
mode === GQL.BulkUpdateIdMode.Set &&
|
||||
(!inputIds || inputIds.length === 0)
|
||||
) {
|
||||
// and all scenes have the same ids,
|
||||
if (aggregateIds.length > 0) {
|
||||
// then unset the performerIds, otherwise ignore
|
||||
return makeBulkUpdateIds(inputIds || [], mode);
|
||||
}
|
||||
} else {
|
||||
// if performerIds non-empty, then we are setting them
|
||||
return makeBulkUpdateIds(inputIds || [], mode);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
Reference in New Issue
Block a user