mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 12:24:38 +03:00
Merge pull request #97 from WithoutPants/delete_tag
Add delete tag button #96
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"github.com/stashapp/stash/pkg/database"
|
"github.com/stashapp/stash/pkg/database"
|
||||||
@@ -52,6 +53,29 @@ func (qb *TagQueryBuilder) Update(updatedTag Tag, tx *sqlx.Tx) (*Tag, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (qb *TagQueryBuilder) Destroy(id string, tx *sqlx.Tx) error {
|
func (qb *TagQueryBuilder) Destroy(id string, tx *sqlx.Tx) error {
|
||||||
|
// delete tag from scenes and markers first
|
||||||
|
_, err := tx.Exec("DELETE FROM scenes_tags WHERE tag_id = ?", id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Exec("DELETE FROM scene_markers_tags WHERE tag_id = ?", id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// cannot unset primary_tag_id in scene_markers because it is not nullable
|
||||||
|
countQuery := "SELECT COUNT(*) as count FROM scene_markers where primary_tag_id = ?"
|
||||||
|
args := []interface{}{id}
|
||||||
|
primaryMarkers, err := runCountQuery(countQuery, args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if primaryMarkers > 0 {
|
||||||
|
return errors.New("Cannot delete tag used as a primary tag in scene markers")
|
||||||
|
}
|
||||||
|
|
||||||
return executeDeleteQuery("tags", id, tx)
|
return executeDeleteQuery("tags", id, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Button, Classes, Dialog, EditableText, FormGroup, HTMLTable, InputGroup, Spinner, Tag } from "@blueprintjs/core";
|
import { Alert, Button, Classes, Dialog, EditableText, FormGroup, HTMLTable, InputGroup, Spinner, Tag } from "@blueprintjs/core";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import React, { FunctionComponent, useEffect, useState } from "react";
|
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||||
import { QueryHookResult } from "react-apollo-hooks";
|
import { QueryHookResult } from "react-apollo-hooks";
|
||||||
@@ -22,11 +22,15 @@ export const TagList: FunctionComponent<IProps> = (props: IProps) => {
|
|||||||
|
|
||||||
// Editing / New state
|
// Editing / New state
|
||||||
const [editingTag, setEditingTag] = useState<Partial<GQL.TagDataFragment> | undefined>(undefined);
|
const [editingTag, setEditingTag] = useState<Partial<GQL.TagDataFragment> | undefined>(undefined);
|
||||||
|
const [deletingTag, setDeletingTag] = useState<Partial<GQL.TagDataFragment> | undefined>(undefined);
|
||||||
const [name, setName] = useState<string>("");
|
const [name, setName] = useState<string>("");
|
||||||
|
|
||||||
const { data, error, loading } = StashService.useAllTags();
|
const { data, error, loading } = StashService.useAllTags();
|
||||||
const updateTag = StashService.useTagUpdate(getTagInput() as GQL.TagUpdateInput);
|
const updateTag = StashService.useTagUpdate(getTagInput() as GQL.TagUpdateInput);
|
||||||
const createTag = StashService.useTagCreate(getTagInput() as GQL.TagCreateInput);
|
const createTag = StashService.useTagCreate(getTagInput() as GQL.TagCreateInput);
|
||||||
|
const deleteTag = StashService.useTagDestroy(getDeleteTagInput() as GQL.TagDestroyInput);
|
||||||
|
|
||||||
|
const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsLoading(loading);
|
setIsLoading(loading);
|
||||||
@@ -42,12 +46,22 @@ export const TagList: FunctionComponent<IProps> = (props: IProps) => {
|
|||||||
}
|
}
|
||||||
}, [editingTag]);
|
}, [editingTag]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsDeleteAlertOpen(!!deletingTag);
|
||||||
|
}, [deletingTag]);
|
||||||
|
|
||||||
function getTagInput() {
|
function getTagInput() {
|
||||||
const tagInput: Partial<GQL.TagCreateInput | GQL.TagUpdateInput> = { name };
|
const tagInput: Partial<GQL.TagCreateInput | GQL.TagUpdateInput> = { name };
|
||||||
if (!!editingTag) { (tagInput as Partial<GQL.TagUpdateInput>).id = editingTag.id; }
|
if (!!editingTag) { (tagInput as Partial<GQL.TagUpdateInput>).id = editingTag.id; }
|
||||||
return tagInput;
|
return tagInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDeleteTagInput() {
|
||||||
|
const tagInput: Partial<GQL.TagDestroyInput> = {};
|
||||||
|
if (!!deletingTag) { tagInput.id = deletingTag.id; }
|
||||||
|
return tagInput;
|
||||||
|
}
|
||||||
|
|
||||||
async function onEdit() {
|
async function onEdit() {
|
||||||
try {
|
try {
|
||||||
if (!!editingTag && !!editingTag.id) {
|
if (!!editingTag && !!editingTag.id) {
|
||||||
@@ -63,11 +77,41 @@ export const TagList: FunctionComponent<IProps> = (props: IProps) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function onDelete() {
|
||||||
|
try {
|
||||||
|
await deleteTag();
|
||||||
|
ToastUtils.success("Deleted tag");
|
||||||
|
setDeletingTag(undefined);
|
||||||
|
} catch (e) {
|
||||||
|
ErrorUtils.handle(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderDeleteAlert() {
|
||||||
|
return (
|
||||||
|
<Alert
|
||||||
|
cancelButtonText="Cancel"
|
||||||
|
confirmButtonText="Delete"
|
||||||
|
icon="trash"
|
||||||
|
intent="danger"
|
||||||
|
isOpen={isDeleteAlertOpen}
|
||||||
|
onCancel={() => setDeletingTag(undefined)}
|
||||||
|
onConfirm={() => onDelete()}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
Are you sure you want to delete {deletingTag && deletingTag.name}?
|
||||||
|
</p>
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!data || !data.allTags || isLoading) { return <Spinner size={Spinner.SIZE_LARGE} />; }
|
if (!data || !data.allTags || isLoading) { return <Spinner size={Spinner.SIZE_LARGE} />; }
|
||||||
if (!!error) { return <>{error.message}</>; }
|
if (!!error) { return <>{error.message}</>; }
|
||||||
|
|
||||||
const tagElements = tags.map((tag) => {
|
const tagElements = tags.map((tag) => {
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
{renderDeleteAlert()}
|
||||||
<div key={tag.id} className="tag-list-row">
|
<div key={tag.id} className="tag-list-row">
|
||||||
<span onClick={() => setEditingTag(tag)}>{tag.name}</span>
|
<span onClick={() => setEditingTag(tag)}>{tag.name}</span>
|
||||||
<div style={{float: "right"}}>
|
<div style={{float: "right"}}>
|
||||||
@@ -76,8 +120,10 @@ export const TagList: FunctionComponent<IProps> = (props: IProps) => {
|
|||||||
Markers: {tag.scene_marker_count}
|
Markers: {tag.scene_marker_count}
|
||||||
</Link>
|
</Link>
|
||||||
<span>Total: {(tag.scene_count || 0) + (tag.scene_marker_count || 0)}</span>
|
<span>Total: {(tag.scene_count || 0) + (tag.scene_marker_count || 0)}</span>
|
||||||
|
<Button intent="danger" icon="trash" onClick={() => setDeletingTag(tag)}></Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -157,6 +157,9 @@ export class StashService {
|
|||||||
public static useTagUpdate(input: GQL.TagUpdateInput) {
|
public static useTagUpdate(input: GQL.TagUpdateInput) {
|
||||||
return GQL.useTagUpdate({ variables: input, refetchQueries: ["AllTags"] });
|
return GQL.useTagUpdate({ variables: input, refetchQueries: ["AllTags"] });
|
||||||
}
|
}
|
||||||
|
public static useTagDestroy(input: GQL.TagDestroyInput) {
|
||||||
|
return GQL.useTagDestroy({ variables: input, refetchQueries: ["AllTags"] });
|
||||||
|
}
|
||||||
|
|
||||||
public static useConfigureGeneral(input: GQL.ConfigGeneralInput) {
|
public static useConfigureGeneral(input: GQL.ConfigGeneralInput) {
|
||||||
return GQL.useConfigureGeneral({ variables: { input }, refetchQueries: ["Configuration"] });
|
return GQL.useConfigureGeneral({ variables: { input }, refetchQueries: ["Configuration"] });
|
||||||
|
|||||||
Reference in New Issue
Block a user