mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 12:54:38 +03:00
Add modes for performer/tag for bulk scene editing (#412)
This commit is contained in:
@@ -39,8 +39,8 @@ mutation BulkSceneUpdate(
|
|||||||
$rating: Int,
|
$rating: Int,
|
||||||
$studio_id: ID,
|
$studio_id: ID,
|
||||||
$gallery_id: ID,
|
$gallery_id: ID,
|
||||||
$performer_ids: [ID!],
|
$performer_ids: BulkUpdateIds,
|
||||||
$tag_ids: [ID!]) {
|
$tag_ids: BulkUpdateIds) {
|
||||||
|
|
||||||
bulkSceneUpdate(input: {
|
bulkSceneUpdate(input: {
|
||||||
ids: $ids,
|
ids: $ids,
|
||||||
|
|||||||
@@ -68,6 +68,17 @@ input SceneUpdateInput {
|
|||||||
cover_image: String
|
cover_image: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum BulkUpdateIdMode {
|
||||||
|
SET
|
||||||
|
ADD
|
||||||
|
REMOVE
|
||||||
|
}
|
||||||
|
|
||||||
|
input BulkUpdateIds {
|
||||||
|
ids: [ID!]
|
||||||
|
mode: BulkUpdateIdMode!
|
||||||
|
}
|
||||||
|
|
||||||
input BulkSceneUpdateInput {
|
input BulkSceneUpdateInput {
|
||||||
clientMutationId: String
|
clientMutationId: String
|
||||||
ids: [ID!]
|
ids: [ID!]
|
||||||
@@ -78,8 +89,8 @@ input BulkSceneUpdateInput {
|
|||||||
rating: Int
|
rating: Int
|
||||||
studio_id: ID
|
studio_id: ID
|
||||||
gallery_id: ID
|
gallery_id: ID
|
||||||
performer_ids: [ID!]
|
performer_ids: BulkUpdateIds
|
||||||
tag_ids: [ID!]
|
tag_ids: BulkUpdateIds
|
||||||
}
|
}
|
||||||
|
|
||||||
input SceneDestroyInput {
|
input SceneDestroyInput {
|
||||||
|
|||||||
@@ -269,9 +269,14 @@ func (r *mutationResolver) BulkSceneUpdate(ctx context.Context, input models.Bul
|
|||||||
|
|
||||||
// Save the performers
|
// Save the performers
|
||||||
if wasFieldIncluded(ctx, "performer_ids") {
|
if wasFieldIncluded(ctx, "performer_ids") {
|
||||||
|
performerIDs, err := adjustScenePerformerIDs(tx, sceneID, *input.PerformerIds)
|
||||||
|
if err != nil {
|
||||||
|
_ = tx.Rollback()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
var performerJoins []models.PerformersScenes
|
var performerJoins []models.PerformersScenes
|
||||||
for _, pid := range input.PerformerIds {
|
for _, performerID := range performerIDs {
|
||||||
performerID, _ := strconv.Atoi(pid)
|
|
||||||
performerJoin := models.PerformersScenes{
|
performerJoin := models.PerformersScenes{
|
||||||
PerformerID: performerID,
|
PerformerID: performerID,
|
||||||
SceneID: sceneID,
|
SceneID: sceneID,
|
||||||
@@ -286,9 +291,14 @@ func (r *mutationResolver) BulkSceneUpdate(ctx context.Context, input models.Bul
|
|||||||
|
|
||||||
// Save the tags
|
// Save the tags
|
||||||
if wasFieldIncluded(ctx, "tag_ids") {
|
if wasFieldIncluded(ctx, "tag_ids") {
|
||||||
|
tagIDs, err := adjustSceneTagIDs(tx, sceneID, *input.TagIds)
|
||||||
|
if err != nil {
|
||||||
|
_ = tx.Rollback()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
var tagJoins []models.ScenesTags
|
var tagJoins []models.ScenesTags
|
||||||
for _, tid := range input.TagIds {
|
for _, tagID := range tagIDs {
|
||||||
tagID, _ := strconv.Atoi(tid)
|
|
||||||
tagJoin := models.ScenesTags{
|
tagJoin := models.ScenesTags{
|
||||||
SceneID: sceneID,
|
SceneID: sceneID,
|
||||||
TagID: tagID,
|
TagID: tagID,
|
||||||
@@ -310,6 +320,72 @@ func (r *mutationResolver) BulkSceneUpdate(ctx context.Context, input models.Bul
|
|||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func adjustIDs(existingIDs []int, updateIDs models.BulkUpdateIds) []int {
|
||||||
|
for _, idStr := range updateIDs.Ids {
|
||||||
|
id, _ := strconv.Atoi(idStr)
|
||||||
|
|
||||||
|
// look for the id in the list
|
||||||
|
foundExisting := false
|
||||||
|
for idx, existingID := range existingIDs {
|
||||||
|
if existingID == id {
|
||||||
|
if updateIDs.Mode == models.BulkUpdateIDModeRemove {
|
||||||
|
// remove from the list
|
||||||
|
existingIDs = append(existingIDs[:idx], existingIDs[idx+1:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
foundExisting = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !foundExisting && updateIDs.Mode != models.BulkUpdateIDModeRemove {
|
||||||
|
existingIDs = append(existingIDs, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return existingIDs
|
||||||
|
}
|
||||||
|
|
||||||
|
func adjustScenePerformerIDs(tx *sqlx.Tx, sceneID int, ids models.BulkUpdateIds) ([]int, error) {
|
||||||
|
var ret []int
|
||||||
|
|
||||||
|
jqb := models.NewJoinsQueryBuilder()
|
||||||
|
if ids.Mode == models.BulkUpdateIDModeAdd || ids.Mode == models.BulkUpdateIDModeRemove {
|
||||||
|
// adding to the joins
|
||||||
|
performerJoins, err := jqb.GetScenePerformers(sceneID, tx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, join := range performerJoins {
|
||||||
|
ret = append(ret, join.PerformerID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return adjustIDs(ret, ids), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func adjustSceneTagIDs(tx *sqlx.Tx, sceneID int, ids models.BulkUpdateIds) ([]int, error) {
|
||||||
|
var ret []int
|
||||||
|
|
||||||
|
jqb := models.NewJoinsQueryBuilder()
|
||||||
|
if ids.Mode == models.BulkUpdateIDModeAdd || ids.Mode == models.BulkUpdateIDModeRemove {
|
||||||
|
// adding to the joins
|
||||||
|
tagJoins, err := jqb.GetSceneTags(sceneID, tx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, join := range tagJoins {
|
||||||
|
ret = append(ret, join.TagID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return adjustIDs(ret, ids), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *mutationResolver) SceneDestroy(ctx context.Context, input models.SceneDestroyInput) (bool, error) {
|
func (r *mutationResolver) SceneDestroy(ctx context.Context, input models.SceneDestroyInput) (bool, error) {
|
||||||
qb := models.NewSceneQueryBuilder()
|
qb := models.NewSceneQueryBuilder()
|
||||||
tx := database.DB.MustBeginTx(ctx, nil)
|
tx := database.DB.MustBeginTx(ctx, nil)
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import _ from "lodash";
|
|||||||
import { StashService } from "src/core/StashService";
|
import { StashService } from "src/core/StashService";
|
||||||
import * as GQL from "src/core/generated-graphql";
|
import * as GQL from "src/core/generated-graphql";
|
||||||
import {
|
import {
|
||||||
FilterSelect,
|
|
||||||
StudioSelect,
|
StudioSelect,
|
||||||
LoadingIndicator
|
LoadingIndicator
|
||||||
} from "src/components/Shared";
|
} from "src/components/Shared";
|
||||||
import { useToast } from "src/hooks";
|
import { useToast } from "src/hooks";
|
||||||
|
import MultiSet from "../Shared/MultiSet";
|
||||||
|
|
||||||
interface IListOperationProps {
|
interface IListOperationProps {
|
||||||
selected: GQL.SlimSceneDataFragment[];
|
selected: GQL.SlimSceneDataFragment[];
|
||||||
@@ -21,7 +21,9 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (
|
|||||||
const Toast = useToast();
|
const Toast = useToast();
|
||||||
const [rating, setRating] = useState<string>("");
|
const [rating, setRating] = useState<string>("");
|
||||||
const [studioId, setStudioId] = useState<string>();
|
const [studioId, setStudioId] = useState<string>();
|
||||||
|
const [performerMode, setPerformerMode] = React.useState<GQL.BulkUpdateIdMode>(GQL.BulkUpdateIdMode.Add);
|
||||||
const [performerIds, setPerformerIds] = useState<string[]>();
|
const [performerIds, setPerformerIds] = useState<string[]>();
|
||||||
|
const [tagMode, setTagMode] = React.useState<GQL.BulkUpdateIdMode>(GQL.BulkUpdateIdMode.Add);
|
||||||
const [tagIds, setTagIds] = useState<string[]>();
|
const [tagIds, setTagIds] = useState<string[]>();
|
||||||
|
|
||||||
const [updateScenes] = StashService.useBulkSceneUpdate(getSceneInput());
|
const [updateScenes] = StashService.useBulkSceneUpdate(getSceneInput());
|
||||||
@@ -29,6 +31,13 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (
|
|||||||
// Network state
|
// Network state
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
|
||||||
|
function makeBulkUpdateIds(ids: string[], mode: GQL.BulkUpdateIdMode) : GQL.BulkUpdateIds {
|
||||||
|
return {
|
||||||
|
mode,
|
||||||
|
ids
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function getSceneInput(): GQL.BulkSceneUpdateInput {
|
function getSceneInput(): GQL.BulkSceneUpdateInput {
|
||||||
// need to determine what we are actually setting on each scene
|
// need to determine what we are actually setting on each scene
|
||||||
const aggregateRating = getRating(props.selected);
|
const aggregateRating = getRating(props.selected);
|
||||||
@@ -69,27 +78,27 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if performerIds are empty
|
// if performerIds are empty
|
||||||
if (!performerIds || performerIds.length === 0) {
|
if (performerMode === GQL.BulkUpdateIdMode.Set && (!performerIds || performerIds.length === 0)) {
|
||||||
// and all scenes have the same ids,
|
// and all scenes have the same ids,
|
||||||
if (aggregatePerformerIds.length > 0) {
|
if (aggregatePerformerIds.length > 0) {
|
||||||
// then unset the performerIds, otherwise ignore
|
// then unset the performerIds, otherwise ignore
|
||||||
sceneInput.performer_ids = performerIds;
|
sceneInput.performer_ids = makeBulkUpdateIds(performerIds || [], performerMode);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if performerIds non-empty, then we are setting them
|
// if performerIds non-empty, then we are setting them
|
||||||
sceneInput.performer_ids = performerIds;
|
sceneInput.performer_ids = makeBulkUpdateIds(performerIds || [], performerMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if tagIds non-empty, then we are setting them
|
// if tagIds non-empty, then we are setting them
|
||||||
if (!tagIds || tagIds.length === 0) {
|
if (tagMode === GQL.BulkUpdateIdMode.Set && (!tagIds || tagIds.length === 0)) {
|
||||||
// and all scenes have the same ids,
|
// and all scenes have the same ids,
|
||||||
if (aggregateTagIds.length > 0) {
|
if (aggregateTagIds.length > 0) {
|
||||||
// then unset the tagIds, otherwise ignore
|
// then unset the tagIds, otherwise ignore
|
||||||
sceneInput.tag_ids = tagIds;
|
sceneInput.tag_ids = makeBulkUpdateIds(tagIds || [], tagMode);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if tagIds non-empty, then we are setting them
|
// if tagIds non-empty, then we are setting them
|
||||||
sceneInput.tag_ids = tagIds;
|
sceneInput.tag_ids = makeBulkUpdateIds(tagIds || [], tagMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sceneInput;
|
return sceneInput;
|
||||||
@@ -222,21 +231,31 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (
|
|||||||
|
|
||||||
setRating(updateRating);
|
setRating(updateRating);
|
||||||
setStudioId(updateStudioID);
|
setStudioId(updateStudioID);
|
||||||
|
if (performerMode === GQL.BulkUpdateIdMode.Set) {
|
||||||
setPerformerIds(updatePerformerIds);
|
setPerformerIds(updatePerformerIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagMode === GQL.BulkUpdateIdMode.Set) {
|
||||||
setTagIds(updateTagIds);
|
setTagIds(updateTagIds);
|
||||||
|
}
|
||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}, [props.selected]);
|
}, [props.selected, performerMode, tagMode]);
|
||||||
|
|
||||||
function renderMultiSelect(
|
function renderMultiSelect(
|
||||||
type: "performers" | "tags",
|
type: "performers" | "tags",
|
||||||
ids: string[] | undefined
|
ids: string[] | undefined
|
||||||
) {
|
) {
|
||||||
|
let mode = GQL.BulkUpdateIdMode.Add;
|
||||||
|
switch (type) {
|
||||||
|
case "performers": mode = performerMode; break;
|
||||||
|
case "tags": mode = tagMode; break;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FilterSelect
|
<MultiSet
|
||||||
type={type}
|
type={type}
|
||||||
isMulti
|
onUpdate={items => {
|
||||||
isClearable={false}
|
|
||||||
onSelect={items => {
|
|
||||||
const itemIDs = items.map(i => i.id);
|
const itemIDs = items.map(i => i.id);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "performers":
|
case "performers":
|
||||||
@@ -247,7 +266,14 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onSetMode={(mode) => {
|
||||||
|
switch (type) {
|
||||||
|
case "performers": setPerformerMode(mode); break;
|
||||||
|
case "tags": setTagMode(mode); break;
|
||||||
|
}
|
||||||
|
}}
|
||||||
ids={ids ?? []}
|
ids={ids ?? []}
|
||||||
|
mode={mode}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -264,6 +290,7 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (
|
|||||||
<Form.Label>Rating</Form.Label>
|
<Form.Label>Rating</Form.Label>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
as="select"
|
as="select"
|
||||||
|
className="btn-secondary"
|
||||||
value={rating}
|
value={rating}
|
||||||
onChange={(event: React.FormEvent<HTMLSelectElement>) =>
|
onChange={(event: React.FormEvent<HTMLSelectElement>) =>
|
||||||
setRating(event.currentTarget.value)
|
setRating(event.currentTarget.value)
|
||||||
|
|||||||
83
ui/v2.5/src/components/Shared/MultiSet.tsx
Normal file
83
ui/v2.5/src/components/Shared/MultiSet.tsx
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import * as GQL from "src/core/generated-graphql";
|
||||||
|
import { FilterSelect } from "./Select";
|
||||||
|
import { Button, InputGroup } from "react-bootstrap";
|
||||||
|
import { Icon } from "src/components/Shared";
|
||||||
|
|
||||||
|
type ValidTypes =
|
||||||
|
| GQL.SlimPerformerDataFragment
|
||||||
|
| GQL.Tag
|
||||||
|
| GQL.SlimStudioDataFragment;
|
||||||
|
|
||||||
|
interface IMultiSetProps {
|
||||||
|
type: "performers" | "studios" | "tags";
|
||||||
|
ids?: string[];
|
||||||
|
mode: GQL.BulkUpdateIdMode;
|
||||||
|
onUpdate: (items: ValidTypes[]) => void;
|
||||||
|
onSetMode: (mode: GQL.BulkUpdateIdMode) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MultiSet: React.FunctionComponent<IMultiSetProps> = (props: IMultiSetProps) => {
|
||||||
|
function onUpdate(items: ValidTypes[]) {
|
||||||
|
props.onUpdate(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getModeIcon() {
|
||||||
|
switch(props.mode) {
|
||||||
|
case GQL.BulkUpdateIdMode.Set:
|
||||||
|
return "pencil-alt";
|
||||||
|
case GQL.BulkUpdateIdMode.Add:
|
||||||
|
return "plus";
|
||||||
|
case GQL.BulkUpdateIdMode.Remove:
|
||||||
|
return "times";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getModeText() {
|
||||||
|
switch(props.mode) {
|
||||||
|
case GQL.BulkUpdateIdMode.Set:
|
||||||
|
return "Set";
|
||||||
|
case GQL.BulkUpdateIdMode.Add:
|
||||||
|
return "Add";
|
||||||
|
case GQL.BulkUpdateIdMode.Remove:
|
||||||
|
return "Remove";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextMode() {
|
||||||
|
switch(props.mode) {
|
||||||
|
case GQL.BulkUpdateIdMode.Set:
|
||||||
|
return GQL.BulkUpdateIdMode.Add;
|
||||||
|
case GQL.BulkUpdateIdMode.Add:
|
||||||
|
return GQL.BulkUpdateIdMode.Remove;
|
||||||
|
case GQL.BulkUpdateIdMode.Remove:
|
||||||
|
return GQL.BulkUpdateIdMode.Set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InputGroup className="multi-set">
|
||||||
|
<InputGroup.Prepend>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="secondary"
|
||||||
|
onClick={() => props.onSetMode(nextMode())}
|
||||||
|
title={getModeText()}
|
||||||
|
>
|
||||||
|
<Icon icon={getModeIcon()} className="fa-fw" />
|
||||||
|
</Button>
|
||||||
|
</InputGroup.Prepend>
|
||||||
|
|
||||||
|
<FilterSelect
|
||||||
|
type={props.type}
|
||||||
|
isMulti
|
||||||
|
isClearable={false}
|
||||||
|
onSelect={onUpdate}
|
||||||
|
ids={props.ids ?? []}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MultiSet;
|
||||||
@@ -68,3 +68,10 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.multi-set > div.input-group-prepend + div {
|
||||||
|
position: relative;
|
||||||
|
flex: 1 1;
|
||||||
|
min-width: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
@@ -8,11 +8,11 @@ import {
|
|||||||
} from "@blueprintjs/core";
|
} from "@blueprintjs/core";
|
||||||
import React, { FunctionComponent, useEffect, useState } from "react";
|
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||||
import { FilterSelect } from "../select/FilterSelect";
|
import { FilterSelect } from "../select/FilterSelect";
|
||||||
import { FilterMultiSelect } from "../select/FilterMultiSelect";
|
|
||||||
import { StashService } from "../../core/StashService";
|
import { StashService } from "../../core/StashService";
|
||||||
import * as GQL from "../../core/generated-graphql";
|
import * as GQL from "../../core/generated-graphql";
|
||||||
import { ErrorUtils } from "../../utils/errors";
|
import { ErrorUtils } from "../../utils/errors";
|
||||||
import { ToastUtils } from "../../utils/toasts";
|
import { ToastUtils } from "../../utils/toasts";
|
||||||
|
import { FilterMultiSet } from "../select/FilterMultiSet";
|
||||||
|
|
||||||
interface IListOperationProps {
|
interface IListOperationProps {
|
||||||
selected: GQL.SlimSceneDataFragment[],
|
selected: GQL.SlimSceneDataFragment[],
|
||||||
@@ -22,7 +22,9 @@ interface IListOperationProps {
|
|||||||
export const SceneSelectedOptions: FunctionComponent<IListOperationProps> = (props: IListOperationProps) => {
|
export const SceneSelectedOptions: FunctionComponent<IListOperationProps> = (props: IListOperationProps) => {
|
||||||
const [rating, setRating] = useState<string>("");
|
const [rating, setRating] = useState<string>("");
|
||||||
const [studioId, setStudioId] = useState<string | undefined>(undefined);
|
const [studioId, setStudioId] = useState<string | undefined>(undefined);
|
||||||
|
const [performerMode, setPerformerMode] = React.useState<GQL.BulkUpdateIdMode>(GQL.BulkUpdateIdMode.Add);
|
||||||
const [performerIds, setPerformerIds] = useState<string[] | undefined>(undefined);
|
const [performerIds, setPerformerIds] = useState<string[] | undefined>(undefined);
|
||||||
|
const [tagMode, setTagMode] = React.useState<GQL.BulkUpdateIdMode>(GQL.BulkUpdateIdMode.Add);
|
||||||
const [tagIds, setTagIds] = useState<string[] | undefined>(undefined);
|
const [tagIds, setTagIds] = useState<string[] | undefined>(undefined);
|
||||||
|
|
||||||
const updateScenes = StashService.useBulkSceneUpdate(getSceneInput());
|
const updateScenes = StashService.useBulkSceneUpdate(getSceneInput());
|
||||||
@@ -30,6 +32,13 @@ export const SceneSelectedOptions: FunctionComponent<IListOperationProps> = (pro
|
|||||||
// Network state
|
// Network state
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
function makeBulkUpdateIds(ids: string[], mode: GQL.BulkUpdateIdMode) : GQL.BulkUpdateIds {
|
||||||
|
return {
|
||||||
|
mode,
|
||||||
|
ids
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function getSceneInput() : GQL.BulkSceneUpdateInput {
|
function getSceneInput() : GQL.BulkSceneUpdateInput {
|
||||||
// need to determine what we are actually setting on each scene
|
// need to determine what we are actually setting on each scene
|
||||||
var aggregateRating = getRating(props.selected);
|
var aggregateRating = getRating(props.selected);
|
||||||
@@ -70,27 +79,27 @@ export const SceneSelectedOptions: FunctionComponent<IListOperationProps> = (pro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if performerIds are empty
|
// if performerIds are empty
|
||||||
if (!performerIds || performerIds.length === 0) {
|
if (performerMode == GQL.BulkUpdateIdMode.Set && (!performerIds || performerIds.length === 0)) {
|
||||||
// and all scenes have the same ids,
|
// and all scenes have the same ids,
|
||||||
if (aggregatePerformerIds.length > 0) {
|
if (aggregatePerformerIds.length > 0) {
|
||||||
// then unset the performerIds, otherwise ignore
|
// then unset the performerIds, otherwise ignore
|
||||||
sceneInput.performer_ids = performerIds;
|
sceneInput.performer_ids = makeBulkUpdateIds(performerIds || [], performerMode);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if performerIds non-empty, then we are setting them
|
// if performerIds non-empty, then we are setting them
|
||||||
sceneInput.performer_ids = performerIds;
|
sceneInput.performer_ids = makeBulkUpdateIds(performerIds || [], performerMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if tagIds non-empty, then we are setting them
|
// if tagIds non-empty, then we are setting them
|
||||||
if (!tagIds || tagIds.length === 0) {
|
if (tagMode == GQL.BulkUpdateIdMode.Set && (!tagIds || tagIds.length === 0)) {
|
||||||
// and all scenes have the same ids,
|
// and all scenes have the same ids,
|
||||||
if (aggregateTagIds.length > 0) {
|
if (aggregateTagIds.length > 0) {
|
||||||
// then unset the tagIds, otherwise ignore
|
// then unset the tagIds, otherwise ignore
|
||||||
sceneInput.tag_ids = tagIds;
|
sceneInput.tag_ids = makeBulkUpdateIds(tagIds || [], tagMode);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if tagIds non-empty, then we are setting them
|
// if tagIds non-empty, then we are setting them
|
||||||
sceneInput.tag_ids = tagIds;
|
sceneInput.tag_ids = makeBulkUpdateIds(tagIds || [], tagMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sceneInput;
|
return sceneInput;
|
||||||
@@ -232,17 +241,28 @@ export const SceneSelectedOptions: FunctionComponent<IListOperationProps> = (pro
|
|||||||
|
|
||||||
setRating(rating);
|
setRating(rating);
|
||||||
setStudioId(studioId);
|
setStudioId(studioId);
|
||||||
|
if (performerMode == GQL.BulkUpdateIdMode.Set) {
|
||||||
setPerformerIds(performerIds);
|
setPerformerIds(performerIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagMode == GQL.BulkUpdateIdMode.Set) {
|
||||||
setTagIds(tagIds);
|
setTagIds(tagIds);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
updateScenesEditState(props.selected);
|
updateScenesEditState(props.selected);
|
||||||
}, [props.selected]);
|
}, [props.selected, performerMode, tagMode]);
|
||||||
|
|
||||||
function renderMultiSelect(type: "performers" | "tags", initialIds: string[] | undefined) {
|
function renderMultiSelect(type: "performers" | "tags", initialIds: string[] | undefined) {
|
||||||
|
let mode = GQL.BulkUpdateIdMode.Add;
|
||||||
|
switch (type) {
|
||||||
|
case "performers": mode = performerMode; break;
|
||||||
|
case "tags": mode = tagMode; break;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FilterMultiSelect
|
<FilterMultiSet
|
||||||
type={type}
|
type={type}
|
||||||
onUpdate={(items) => {
|
onUpdate={(items) => {
|
||||||
const ids = items.map((i) => i.id);
|
const ids = items.map((i) => i.id);
|
||||||
@@ -251,7 +271,14 @@ export const SceneSelectedOptions: FunctionComponent<IListOperationProps> = (pro
|
|||||||
case "tags": setTagIds(ids); break;
|
case "tags": setTagIds(ids); break;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onSetMode={(mode) => {
|
||||||
|
switch (type) {
|
||||||
|
case "performers": setPerformerMode(mode); break;
|
||||||
|
case "tags": setTagMode(mode); break;
|
||||||
|
}
|
||||||
|
}}
|
||||||
initialIds={initialIds}
|
initialIds={initialIds}
|
||||||
|
mode={mode}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
74
ui/v2/src/components/select/FilterMultiSet.tsx
Normal file
74
ui/v2/src/components/select/FilterMultiSet.tsx
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { ControlGroup, Button } from "@blueprintjs/core";
|
||||||
|
import * as GQL from "../../core/generated-graphql";
|
||||||
|
import { FilterMultiSelect } from "./FilterMultiSelect";
|
||||||
|
|
||||||
|
type ValidTypes =
|
||||||
|
GQL.AllPerformersForFilterAllPerformers |
|
||||||
|
GQL.AllTagsForFilterAllTags |
|
||||||
|
GQL.AllMoviesForFilterAllMovies |
|
||||||
|
GQL.AllStudiosForFilterAllStudios;
|
||||||
|
|
||||||
|
interface IFilterMultiSetProps {
|
||||||
|
type: "performers" | "studios" | "movies" | "tags";
|
||||||
|
initialIds?: string[];
|
||||||
|
mode: GQL.BulkUpdateIdMode;
|
||||||
|
onUpdate: (items: ValidTypes[]) => void;
|
||||||
|
onSetMode: (mode: GQL.BulkUpdateIdMode) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FilterMultiSet: React.FunctionComponent<IFilterMultiSetProps> = (props: IFilterMultiSetProps) => {
|
||||||
|
function onUpdate(items: ValidTypes[]) {
|
||||||
|
props.onUpdate(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getModeIcon() {
|
||||||
|
switch(props.mode) {
|
||||||
|
case GQL.BulkUpdateIdMode.Set:
|
||||||
|
return "edit";
|
||||||
|
case GQL.BulkUpdateIdMode.Add:
|
||||||
|
return "plus";
|
||||||
|
case GQL.BulkUpdateIdMode.Remove:
|
||||||
|
return "cross";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getModeText() {
|
||||||
|
switch(props.mode) {
|
||||||
|
case GQL.BulkUpdateIdMode.Set:
|
||||||
|
return "Set";
|
||||||
|
case GQL.BulkUpdateIdMode.Add:
|
||||||
|
return "Add";
|
||||||
|
case GQL.BulkUpdateIdMode.Remove:
|
||||||
|
return "Remove";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextMode() {
|
||||||
|
switch(props.mode) {
|
||||||
|
case GQL.BulkUpdateIdMode.Set:
|
||||||
|
return GQL.BulkUpdateIdMode.Add;
|
||||||
|
case GQL.BulkUpdateIdMode.Add:
|
||||||
|
return GQL.BulkUpdateIdMode.Remove;
|
||||||
|
case GQL.BulkUpdateIdMode.Remove:
|
||||||
|
return GQL.BulkUpdateIdMode.Set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ControlGroup>
|
||||||
|
<Button
|
||||||
|
icon={getModeIcon()}
|
||||||
|
minimal={true}
|
||||||
|
onClick={() => props.onSetMode(nextMode())}
|
||||||
|
title={getModeText()}
|
||||||
|
/>
|
||||||
|
<FilterMultiSelect
|
||||||
|
type={props.type}
|
||||||
|
initialIds={props.initialIds}
|
||||||
|
onUpdate={onUpdate}
|
||||||
|
/>
|
||||||
|
</ControlGroup>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user