mirror of https://github.com/stashapp/stash.git
Add batch delete for performers/tags/studios/movies (#1053)
* Add batch delete for performers/tags/studios/movies * Fix ListFilter styling bug
This commit is contained in:
parent
8a3d940aa7
commit
aad4ddc46d
|
@ -24,4 +24,8 @@ mutation MovieUpdate($input: MovieUpdateInput!) {
|
|||
|
||||
mutation MovieDestroy($id: ID!) {
|
||||
movieDestroy(input: { id: $id })
|
||||
}
|
||||
}
|
||||
|
||||
mutation MoviesDestroy($ids: [ID!]!) {
|
||||
moviesDestroy(ids: $ids)
|
||||
}
|
||||
|
|
|
@ -55,3 +55,7 @@ mutation PerformerUpdate(
|
|||
mutation PerformerDestroy($id: ID!) {
|
||||
performerDestroy(input: { id: $id })
|
||||
}
|
||||
|
||||
mutation PerformersDestroy($ids: [ID!]!) {
|
||||
performersDestroy(ids: $ids)
|
||||
}
|
||||
|
|
|
@ -21,3 +21,7 @@ mutation StudioUpdate(
|
|||
mutation StudioDestroy($id: ID!) {
|
||||
studioDestroy(input: { id: $id })
|
||||
}
|
||||
|
||||
mutation StudiosDestroy($ids: [ID!]!) {
|
||||
studiosDestroy(ids: $ids)
|
||||
}
|
||||
|
|
|
@ -8,8 +8,12 @@ mutation TagDestroy($id: ID!) {
|
|||
tagDestroy(input: { id: $id })
|
||||
}
|
||||
|
||||
mutation TagsDestroy($ids: [ID!]!) {
|
||||
tagsDestroy(ids: $ids)
|
||||
}
|
||||
|
||||
mutation TagUpdate($input: TagUpdateInput!) {
|
||||
tagUpdate(input: $input) {
|
||||
...TagData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -175,18 +175,22 @@ type Mutation {
|
|||
performerCreate(input: PerformerCreateInput!): Performer
|
||||
performerUpdate(input: PerformerUpdateInput!): Performer
|
||||
performerDestroy(input: PerformerDestroyInput!): Boolean!
|
||||
performersDestroy(ids: [ID!]!): Boolean!
|
||||
|
||||
studioCreate(input: StudioCreateInput!): Studio
|
||||
studioUpdate(input: StudioUpdateInput!): Studio
|
||||
studioDestroy(input: StudioDestroyInput!): Boolean!
|
||||
studiosDestroy(ids: [ID!]!): Boolean!
|
||||
|
||||
movieCreate(input: MovieCreateInput!): Movie
|
||||
movieUpdate(input: MovieUpdateInput!): Movie
|
||||
movieDestroy(input: MovieDestroyInput!): Boolean!
|
||||
moviesDestroy(ids: [ID!]!): Boolean!
|
||||
|
||||
tagCreate(input: TagCreateInput!): Tag
|
||||
tagUpdate(input: TagUpdateInput!): Tag
|
||||
tagDestroy(input: TagDestroyInput!): Boolean!
|
||||
tagsDestroy(ids: [ID!]!): Boolean!
|
||||
|
||||
"""Change general configuration options"""
|
||||
configureGeneral(input: ConfigGeneralInput!): ConfigGeneralResult!
|
||||
|
|
|
@ -222,3 +222,18 @@ func (r *mutationResolver) MovieDestroy(ctx context.Context, input models.MovieD
|
|||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) MoviesDestroy(ctx context.Context, ids []string) (bool, error) {
|
||||
qb := models.NewMovieQueryBuilder()
|
||||
tx := database.DB.MustBeginTx(ctx, nil)
|
||||
for _, id := range ids {
|
||||
if err := qb.Destroy(id, tx); err != nil {
|
||||
_ = tx.Rollback()
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
|
|
@ -242,3 +242,18 @@ func (r *mutationResolver) PerformerDestroy(ctx context.Context, input models.Pe
|
|||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) PerformersDestroy(ctx context.Context, ids []string) (bool, error) {
|
||||
qb := models.NewPerformerQueryBuilder()
|
||||
tx := database.DB.MustBeginTx(ctx, nil)
|
||||
for _, id := range ids {
|
||||
if err := qb.Destroy(id, tx); err != nil {
|
||||
_ = tx.Rollback()
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
|
|
@ -182,3 +182,18 @@ func (r *mutationResolver) StudioDestroy(ctx context.Context, input models.Studi
|
|||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) StudiosDestroy(ctx context.Context, ids []string) (bool, error) {
|
||||
qb := models.NewStudioQueryBuilder()
|
||||
tx := database.DB.MustBeginTx(ctx, nil)
|
||||
for _, id := range ids {
|
||||
if err := qb.Destroy(id, tx); err != nil {
|
||||
_ = tx.Rollback()
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
|
|
@ -152,3 +152,19 @@ func (r *mutationResolver) TagDestroy(ctx context.Context, input models.TagDestr
|
|||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) TagsDestroy(ctx context.Context, ids []string) (bool, error) {
|
||||
qb := models.NewTagQueryBuilder()
|
||||
tx := database.DB.MustBeginTx(ctx, nil)
|
||||
|
||||
for _, id := range ids {
|
||||
if err := qb.Destroy(id, tx); err != nil {
|
||||
_ = tx.Rollback()
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Allow configuration of visible navbar items.
|
||||
|
||||
### 🎨 Improvements
|
||||
* Add batch deleting of performers, tags, studios, and movies.
|
||||
* Reset cache after scan/clean to ensure scenes are updated.
|
||||
* Add more video/image resolution tags.
|
||||
* Add option to strip file extension from scene title when populating from scanning task.
|
||||
|
|
|
@ -76,11 +76,13 @@ export const DeleteImagesDialog: React.FC<IDeleteImageDialogProps> = (
|
|||
</p>
|
||||
<Form>
|
||||
<Form.Check
|
||||
id="delete-image"
|
||||
checked={deleteFile}
|
||||
label="Delete file"
|
||||
onChange={() => setDeleteFile(!deleteFile)}
|
||||
/>
|
||||
<Form.Check
|
||||
id="delete-image-generated"
|
||||
checked={deleteGenerated}
|
||||
label="Delete generated supporting files"
|
||||
onChange={() => setDeleteGenerated(!deleteGenerated)}
|
||||
|
|
|
@ -426,29 +426,25 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||
}
|
||||
|
||||
function maybeRenderSelectedButtons() {
|
||||
if (props.itemsSelected) {
|
||||
if (props.itemsSelected && (props.onEdit || props.onDelete)) {
|
||||
return (
|
||||
<>
|
||||
{props.onEdit ? (
|
||||
<ButtonGroup className="mr-1">
|
||||
<OverlayTrigger overlay={<Tooltip id="edit">Edit</Tooltip>}>
|
||||
<Button variant="secondary" onClick={onEdit}>
|
||||
<Icon icon="pencil-alt" />
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
</ButtonGroup>
|
||||
) : undefined}
|
||||
<ButtonGroup className="ml-2">
|
||||
{props.onEdit && (
|
||||
<OverlayTrigger overlay={<Tooltip id="edit">Edit</Tooltip>}>
|
||||
<Button variant="secondary" onClick={onEdit}>
|
||||
<Icon icon="pencil-alt" />
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
)}
|
||||
|
||||
{props.onDelete ? (
|
||||
<ButtonGroup className="mr-1">
|
||||
<OverlayTrigger overlay={<Tooltip id="delete">Delete</Tooltip>}>
|
||||
<Button variant="danger" onClick={onDelete}>
|
||||
<Icon icon="trash" />
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
</ButtonGroup>
|
||||
) : undefined}
|
||||
</>
|
||||
{props.onDelete && (
|
||||
<OverlayTrigger overlay={<Tooltip id="delete">Delete</Tooltip>}>
|
||||
<Button variant="danger" onClick={onDelete}>
|
||||
<Icon icon="trash" />
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
)}
|
||||
</ButtonGroup>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -456,8 +452,8 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||
function render() {
|
||||
return (
|
||||
<>
|
||||
<ButtonToolbar className="align-items-center justify-content-center">
|
||||
<div className="my-1 d-flex">
|
||||
<ButtonToolbar className="align-items-center justify-content-center mb-2">
|
||||
<div className="d-flex">
|
||||
<InputGroup className="mr-2 flex-grow-1">
|
||||
<FormControl
|
||||
ref={queryRef}
|
||||
|
@ -530,18 +526,15 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||
))}
|
||||
</Form.Control>
|
||||
|
||||
<ButtonGroup className="mx-3 my-1">
|
||||
{maybeRenderSelectedButtons()}
|
||||
{renderMore()}
|
||||
</ButtonGroup>
|
||||
{maybeRenderSelectedButtons()}
|
||||
|
||||
<ButtonGroup className="my-1">
|
||||
{renderDisplayModeOptions()}
|
||||
</ButtonGroup>
|
||||
<div className="mx-2">{renderMore()}</div>
|
||||
|
||||
<ButtonGroup>{renderDisplayModeOptions()}</ButtonGroup>
|
||||
{maybeRenderZoom()}
|
||||
</ButtonToolbar>
|
||||
|
||||
<div className="d-flex justify-content-center mt-1">
|
||||
<div className="d-flex justify-content-center">
|
||||
{renderFilterTags()}
|
||||
</div>
|
||||
</>
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
import React, { useState } from "react";
|
||||
import _ from "lodash";
|
||||
import Mousetrap from "mousetrap";
|
||||
import { FindMoviesQueryResult } from "src/core/generated-graphql";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import {
|
||||
FindMoviesQueryResult,
|
||||
SlimMovieDataFragment,
|
||||
} from "src/core/generated-graphql";
|
||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||
import { DisplayMode } from "src/models/list-filter/types";
|
||||
import { queryFindMovies } from "src/core/StashService";
|
||||
import { queryFindMovies, useMoviesDestroy } from "src/core/StashService";
|
||||
import { showWhenSelected, useMoviesList } from "src/hooks/ListHook";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { ExportDialog, DeleteEntityDialog } from "src/components/Shared";
|
||||
import { MovieCard } from "./MovieCard";
|
||||
import { ExportDialog } from "../Shared/ExportDialog";
|
||||
|
||||
export const MovieList: React.FC = () => {
|
||||
const history = useHistory();
|
||||
|
@ -44,12 +47,26 @@ export const MovieList: React.FC = () => {
|
|||
};
|
||||
};
|
||||
|
||||
const renderDeleteDialog = (
|
||||
selectedMovies: SlimMovieDataFragment[],
|
||||
onClose: (confirmed: boolean) => void
|
||||
) => (
|
||||
<DeleteEntityDialog
|
||||
selected={selectedMovies}
|
||||
onClose={onClose}
|
||||
singularEntity="movie"
|
||||
pluralEntity="movies"
|
||||
destroyMutation={useMoviesDestroy}
|
||||
/>
|
||||
);
|
||||
|
||||
const listData = useMoviesList({
|
||||
renderContent,
|
||||
addKeybinds,
|
||||
otherOperations,
|
||||
selectable: true,
|
||||
persistState: true,
|
||||
renderDeleteDialog,
|
||||
});
|
||||
|
||||
async function viewRandom(
|
||||
|
|
|
@ -2,13 +2,19 @@ import _ from "lodash";
|
|||
import React, { useState } from "react";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import Mousetrap from "mousetrap";
|
||||
import { FindPerformersQueryResult } from "src/core/generated-graphql";
|
||||
import { queryFindPerformers } from "src/core/StashService";
|
||||
import {
|
||||
FindPerformersQueryResult,
|
||||
SlimPerformerDataFragment,
|
||||
} from "src/core/generated-graphql";
|
||||
import {
|
||||
queryFindPerformers,
|
||||
usePerformersDestroy,
|
||||
} from "src/core/StashService";
|
||||
import { usePerformersList } from "src/hooks";
|
||||
import { showWhenSelected } from "src/hooks/ListHook";
|
||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||
import { DisplayMode } from "src/models/list-filter/types";
|
||||
import { ExportDialog } from "src/components/Shared/ExportDialog";
|
||||
import { ExportDialog, DeleteEntityDialog } from "src/components/Shared";
|
||||
import { PerformerCard } from "./PerformerCard";
|
||||
import { PerformerListTable } from "./PerformerListTable";
|
||||
|
||||
|
@ -76,12 +82,26 @@ export const PerformerList: React.FC = () => {
|
|||
}
|
||||
}
|
||||
|
||||
const renderDeleteDialog = (
|
||||
selectedPerformers: SlimPerformerDataFragment[],
|
||||
onClose: (confirmed: boolean) => void
|
||||
) => (
|
||||
<DeleteEntityDialog
|
||||
selected={selectedPerformers}
|
||||
onClose={onClose}
|
||||
singularEntity="performer"
|
||||
pluralEntity="performers"
|
||||
destroyMutation={usePerformersDestroy}
|
||||
/>
|
||||
);
|
||||
|
||||
const listData = usePerformersList({
|
||||
otherOperations,
|
||||
renderContent,
|
||||
addKeybinds,
|
||||
selectable: true,
|
||||
persistState: true,
|
||||
renderDeleteDialog,
|
||||
});
|
||||
|
||||
async function getRandom(
|
||||
|
|
|
@ -76,11 +76,13 @@ export const DeleteScenesDialog: React.FC<IDeleteSceneDialogProps> = (
|
|||
</p>
|
||||
<Form>
|
||||
<Form.Check
|
||||
id="delete-file"
|
||||
checked={deleteFile}
|
||||
label="Delete file"
|
||||
onChange={() => setDeleteFile(!deleteFile)}
|
||||
/>
|
||||
<Form.Check
|
||||
id="delete-generated"
|
||||
checked={deleteGenerated}
|
||||
label="Delete generated supporting files"
|
||||
onChange={() => setDeleteGenerated(!deleteGenerated)}
|
||||
|
|
|
@ -68,13 +68,18 @@ export const SceneList: React.FC<ISceneList> = ({
|
|||
};
|
||||
};
|
||||
|
||||
const renderDeleteDialog = (
|
||||
selectedScenes: SlimSceneDataFragment[],
|
||||
onClose: (confirmed: boolean) => void
|
||||
) => <DeleteScenesDialog selected={selectedScenes} onClose={onClose} />;
|
||||
|
||||
const listData = useScenesList({
|
||||
zoomable: true,
|
||||
selectable: true,
|
||||
otherOperations,
|
||||
renderContent,
|
||||
renderEditDialog: renderEditScenesDialog,
|
||||
renderDeleteDialog: renderDeleteScenesDialog,
|
||||
renderDeleteDialog,
|
||||
filterHook,
|
||||
addKeybinds,
|
||||
persistState,
|
||||
|
@ -166,17 +171,6 @@ export const SceneList: React.FC<ISceneList> = ({
|
|||
);
|
||||
}
|
||||
|
||||
function renderDeleteScenesDialog(
|
||||
selectedScenes: SlimSceneDataFragment[],
|
||||
onClose: (confirmed: boolean) => void
|
||||
) {
|
||||
return (
|
||||
<>
|
||||
<DeleteScenesDialog selected={selectedScenes} onClose={onClose} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function renderSceneCard(
|
||||
scene: SlimSceneDataFragment,
|
||||
selectedIds: Set<string>,
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
import React, { useState } from "react";
|
||||
import { defineMessages, FormattedMessage, useIntl } from "react-intl";
|
||||
import { FetchResult } from "@apollo/client";
|
||||
|
||||
import { Modal } from "src/components/Shared";
|
||||
import { useToast } from "src/hooks";
|
||||
|
||||
interface IDeletionEntity {
|
||||
id: string;
|
||||
name?: string | null;
|
||||
}
|
||||
|
||||
type DestroyMutation = (input: {
|
||||
ids: string[];
|
||||
}) => [() => Promise<FetchResult>, {}];
|
||||
|
||||
interface IDeleteEntityDialogProps {
|
||||
selected: IDeletionEntity[];
|
||||
onClose: (confirmed: boolean) => void;
|
||||
singularEntity: string;
|
||||
pluralEntity: string;
|
||||
destroyMutation: DestroyMutation;
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
deleteHeader: {
|
||||
id: "delete-header",
|
||||
defaultMessage:
|
||||
"Delete {count, plural, =1 {{singularEntity}} other {{pluralEntity}}}",
|
||||
},
|
||||
deleteToast: {
|
||||
id: "delete-toast",
|
||||
defaultMessage:
|
||||
"Deleted {count, plural, =1 {{singularEntity}} other {{pluralEntity}}}",
|
||||
},
|
||||
deleteMessage: {
|
||||
id: "delete-message",
|
||||
defaultMessage:
|
||||
"Are you sure you want to delete {count, plural, =1 {this {singularEntity}} other {these {pluralEntity}}}?",
|
||||
},
|
||||
overflowMessage: {
|
||||
id: "overflow-message",
|
||||
defaultMessage:
|
||||
"...and {count} other {count, plural, =1 {{ singularEntity}} other {{ pluralEntity }}}.",
|
||||
},
|
||||
});
|
||||
|
||||
const DeleteEntityDialog: React.FC<IDeleteEntityDialogProps> = ({
|
||||
selected,
|
||||
onClose,
|
||||
singularEntity,
|
||||
pluralEntity,
|
||||
destroyMutation,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const Toast = useToast();
|
||||
const [deleteEntities] = destroyMutation({ ids: selected.map((p) => p.id) });
|
||||
const count = selected.length;
|
||||
|
||||
// Network state
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
|
||||
async function onDelete() {
|
||||
setIsDeleting(true);
|
||||
try {
|
||||
await deleteEntities();
|
||||
Toast.success({
|
||||
content: intl.formatMessage(messages.deleteToast, {
|
||||
count,
|
||||
singularEntity,
|
||||
pluralEntity,
|
||||
}),
|
||||
});
|
||||
} catch (e) {
|
||||
Toast.error(e);
|
||||
}
|
||||
setIsDeleting(false);
|
||||
onClose(true);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
show
|
||||
icon="trash-alt"
|
||||
header={intl.formatMessage(messages.deleteHeader, {
|
||||
count,
|
||||
singularEntity,
|
||||
pluralEntity,
|
||||
})}
|
||||
accept={{ variant: "danger", onClick: onDelete, text: "Delete" }}
|
||||
cancel={{
|
||||
onClick: () => onClose(false),
|
||||
text: "Cancel",
|
||||
variant: "secondary",
|
||||
}}
|
||||
isRunning={isDeleting}
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
values={{ count, singularEntity, pluralEntity }}
|
||||
{...messages.deleteMessage}
|
||||
/>
|
||||
</p>
|
||||
<ul>
|
||||
{selected.slice(0, 10).map((s) => (
|
||||
<li>{s.name}</li>
|
||||
))}
|
||||
{selected.length > 10 && (
|
||||
<FormattedMessage
|
||||
values={{
|
||||
count: selected.length - 10,
|
||||
singularEntity,
|
||||
pluralEntity,
|
||||
}}
|
||||
{...messages.overflowMessage}
|
||||
/>
|
||||
)}
|
||||
</ul>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteEntityDialog;
|
|
@ -24,3 +24,5 @@ export { default as ErrorMessage } from "./ErrorMessage";
|
|||
export { default as TruncatedText } from "./TruncatedText";
|
||||
export { BasicCard } from "./BasicCard";
|
||||
export { RatingStars } from "./RatingStars";
|
||||
export { ExportDialog } from "./ExportDialog";
|
||||
export { default as DeleteEntityDialog } from "./DeleteEntityDialog";
|
||||
|
|
|
@ -2,13 +2,16 @@ import React, { useState } from "react";
|
|||
import _ from "lodash";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import Mousetrap from "mousetrap";
|
||||
import { FindStudiosQueryResult } from "src/core/generated-graphql";
|
||||
import {
|
||||
FindStudiosQueryResult,
|
||||
SlimStudioDataFragment,
|
||||
} from "src/core/generated-graphql";
|
||||
import { useStudiosList } from "src/hooks";
|
||||
import { showWhenSelected } from "src/hooks/ListHook";
|
||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||
import { DisplayMode } from "src/models/list-filter/types";
|
||||
import { queryFindStudios } from "src/core/StashService";
|
||||
import { ExportDialog } from "../Shared/ExportDialog";
|
||||
import { queryFindStudios, useStudiosDestroy } from "src/core/StashService";
|
||||
import { ExportDialog, DeleteEntityDialog } from "src/components/Shared";
|
||||
import { StudioCard } from "./StudioCard";
|
||||
|
||||
interface IStudioList {
|
||||
|
@ -109,6 +112,19 @@ export const StudioList: React.FC<IStudioList> = ({
|
|||
}
|
||||
}
|
||||
|
||||
const renderDeleteDialog = (
|
||||
selectedStudios: SlimStudioDataFragment[],
|
||||
onClose: (confirmed: boolean) => void
|
||||
) => (
|
||||
<DeleteEntityDialog
|
||||
selected={selectedStudios}
|
||||
onClose={onClose}
|
||||
singularEntity="studio"
|
||||
pluralEntity="studios"
|
||||
destroyMutation={useStudiosDestroy}
|
||||
/>
|
||||
);
|
||||
|
||||
const listData = useStudiosList({
|
||||
renderContent,
|
||||
filterHook,
|
||||
|
@ -116,6 +132,7 @@ export const StudioList: React.FC<IStudioList> = ({
|
|||
otherOperations,
|
||||
selectable: true,
|
||||
persistState: !fromParent,
|
||||
renderDeleteDialog,
|
||||
});
|
||||
|
||||
function renderStudios(
|
||||
|
|
|
@ -12,11 +12,12 @@ import {
|
|||
queryFindTags,
|
||||
mutateMetadataAutoTag,
|
||||
useTagDestroy,
|
||||
useTagsDestroy,
|
||||
} from "src/core/StashService";
|
||||
import { useToast } from "src/hooks";
|
||||
import { FormattedNumber } from "react-intl";
|
||||
import { NavUtils } from "src/utils";
|
||||
import { Icon, Modal } from "src/components/Shared";
|
||||
import { Icon, Modal, DeleteEntityDialog } from "src/components/Shared";
|
||||
import { TagCard } from "./TagCard";
|
||||
import { ExportDialog } from "../Shared/ExportDialog";
|
||||
|
||||
|
@ -121,6 +122,19 @@ export const TagList: React.FC<ITagList> = ({ filterHook }) => {
|
|||
}
|
||||
}
|
||||
|
||||
const renderDeleteDialog = (
|
||||
selectedTags: GQL.TagDataFragment[],
|
||||
onClose: (confirmed: boolean) => void
|
||||
) => (
|
||||
<DeleteEntityDialog
|
||||
selected={selectedTags}
|
||||
onClose={onClose}
|
||||
singularEntity="tag"
|
||||
pluralEntity="tags"
|
||||
destroyMutation={useTagsDestroy}
|
||||
/>
|
||||
);
|
||||
|
||||
const listData = useTagsList({
|
||||
renderContent,
|
||||
filterHook,
|
||||
|
@ -130,6 +144,7 @@ export const TagList: React.FC<ITagList> = ({ filterHook }) => {
|
|||
zoomable: true,
|
||||
defaultZoomIndex: 0,
|
||||
persistState: true,
|
||||
renderDeleteDialog,
|
||||
});
|
||||
|
||||
function getDeleteTagInput() {
|
||||
|
|
|
@ -309,6 +309,18 @@ export const usePerformerDestroy = () =>
|
|||
update: deleteCache(performerMutationImpactedQueries),
|
||||
});
|
||||
|
||||
export const usePerformersDestroy = (
|
||||
variables: GQL.PerformersDestroyMutationVariables
|
||||
) =>
|
||||
GQL.usePerformersDestroyMutation({
|
||||
variables,
|
||||
refetchQueries: getQueryNames([
|
||||
GQL.FindPerformersDocument,
|
||||
GQL.AllPerformersForFilterDocument,
|
||||
]),
|
||||
update: deleteCache(performerMutationImpactedQueries),
|
||||
});
|
||||
|
||||
const sceneMutationImpactedQueries = [
|
||||
GQL.FindPerformerDocument,
|
||||
GQL.FindPerformersDocument,
|
||||
|
@ -562,6 +574,12 @@ export const useStudioDestroy = (input: GQL.StudioDestroyInput) =>
|
|||
update: deleteCache(studioMutationImpactedQueries),
|
||||
});
|
||||
|
||||
export const useStudiosDestroy = (input: GQL.StudiosDestroyMutationVariables) =>
|
||||
GQL.useStudiosDestroyMutation({
|
||||
variables: input,
|
||||
update: deleteCache(studioMutationImpactedQueries),
|
||||
});
|
||||
|
||||
export const movieMutationImpactedQueries = [
|
||||
GQL.FindSceneDocument,
|
||||
GQL.FindScenesDocument,
|
||||
|
@ -589,6 +607,12 @@ export const useMovieDestroy = (input: GQL.MovieDestroyInput) =>
|
|||
update: deleteCache(movieMutationImpactedQueries),
|
||||
});
|
||||
|
||||
export const useMoviesDestroy = (input: GQL.MoviesDestroyMutationVariables) =>
|
||||
GQL.useMoviesDestroyMutation({
|
||||
variables: input,
|
||||
update: deleteCache(movieMutationImpactedQueries),
|
||||
});
|
||||
|
||||
export const tagMutationImpactedQueries = [
|
||||
GQL.FindSceneDocument,
|
||||
GQL.FindScenesDocument,
|
||||
|
@ -622,6 +646,12 @@ export const useTagDestroy = (input: GQL.TagDestroyInput) =>
|
|||
update: deleteCache(tagMutationImpactedQueries),
|
||||
});
|
||||
|
||||
export const useTagsDestroy = (input: GQL.TagsDestroyMutationVariables) =>
|
||||
GQL.useTagsDestroyMutation({
|
||||
variables: input,
|
||||
update: deleteCache(tagMutationImpactedQueries),
|
||||
});
|
||||
|
||||
export const useConfigureGeneral = (input: GQL.ConfigGeneralInput) =>
|
||||
GQL.useConfigureGeneralMutation({
|
||||
variables: { input },
|
||||
|
|
Loading…
Reference in New Issue