From e3687803c5b671f1120682c90f2ddeb5909e375b Mon Sep 17 00:00:00 2001 From: WithoutPants <53250216+WithoutPants@users.noreply.github.com> Date: Tue, 13 Aug 2019 09:01:54 +1000 Subject: [PATCH 1/3] Add delete tag button --- ui/v2/src/components/Tags/TagList.tsx | 48 ++++++++++++++++++++++++++- ui/v2/src/core/StashService.ts | 3 ++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/ui/v2/src/components/Tags/TagList.tsx b/ui/v2/src/components/Tags/TagList.tsx index c61f9be2e..aae49b025 100644 --- a/ui/v2/src/components/Tags/TagList.tsx +++ b/ui/v2/src/components/Tags/TagList.tsx @@ -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 React, { FunctionComponent, useEffect, useState } from "react"; import { QueryHookResult } from "react-apollo-hooks"; @@ -22,11 +22,15 @@ export const TagList: FunctionComponent = (props: IProps) => { // Editing / New state const [editingTag, setEditingTag] = useState | undefined>(undefined); + const [deletingTag, setDeletingTag] = useState | undefined>(undefined); const [name, setName] = useState(""); const { data, error, loading } = StashService.useAllTags(); const updateTag = StashService.useTagUpdate(getTagInput() as GQL.TagUpdateInput); const createTag = StashService.useTagCreate(getTagInput() as GQL.TagCreateInput); + const deleteTag = StashService.useTagDestroy(getDeleteTagInput() as GQL.TagDestroyInput); + + const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState(false); useEffect(() => { setIsLoading(loading); @@ -42,12 +46,22 @@ export const TagList: FunctionComponent = (props: IProps) => { } }, [editingTag]); + useEffect(() => { + setIsDeleteAlertOpen(!!deletingTag); + }, [deletingTag]); + function getTagInput() { const tagInput: Partial = { name }; if (!!editingTag) { (tagInput as Partial).id = editingTag.id; } return tagInput; } + function getDeleteTagInput() { + const tagInput: Partial = {}; + if (!!deletingTag) { tagInput.id = deletingTag.id; } + return tagInput; + } + async function onEdit() { try { if (!!editingTag && !!editingTag.id) { @@ -63,11 +77,41 @@ export const TagList: FunctionComponent = (props: IProps) => { } } + async function onDelete() { + try { + await deleteTag(); + ToastUtils.success("Deleted tag"); + setDeletingTag(undefined); + } catch (e) { + ErrorUtils.handle(e); + } + } + + function renderDeleteAlert() { + return ( + setDeletingTag(undefined)} + onConfirm={() => onDelete()} + > +

+ Are you sure you want to delete {deletingTag && deletingTag.name}? +

+
+ ); + } + if (!data || !data.allTags || isLoading) { return ; } if (!!error) { return <>{error.message}; } const tagElements = tags.map((tag) => { return ( + <> + {renderDeleteAlert()}
setEditingTag(tag)}>{tag.name}
@@ -76,8 +120,10 @@ export const TagList: FunctionComponent = (props: IProps) => { Markers: {tag.scene_marker_count} Total: {(tag.scene_count || 0) + (tag.scene_marker_count || 0)} +
+ ); }); return ( diff --git a/ui/v2/src/core/StashService.ts b/ui/v2/src/core/StashService.ts index 02eb5078c..0cf3e0a97 100644 --- a/ui/v2/src/core/StashService.ts +++ b/ui/v2/src/core/StashService.ts @@ -151,6 +151,9 @@ export class StashService { public static useTagUpdate(input: GQL.TagUpdateInput) { 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) { return GQL.useConfigureGeneral({ variables: { input }, refetchQueries: ["Configuration"] }); From 45f72a549f2e5beedad6c36ff08c55ec1aa0b904 Mon Sep 17 00:00:00 2001 From: WithoutPants <53250216+WithoutPants@users.noreply.github.com> Date: Tue, 13 Aug 2019 09:03:16 +1000 Subject: [PATCH 2/3] Delete scene/marker tag refs when deleting tags --- pkg/models/querybuilder_tag.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pkg/models/querybuilder_tag.go b/pkg/models/querybuilder_tag.go index e6036b751..e5ed270c6 100644 --- a/pkg/models/querybuilder_tag.go +++ b/pkg/models/querybuilder_tag.go @@ -52,6 +52,19 @@ func (qb *TagQueryBuilder) Update(updatedTag Tag, tx *sqlx.Tx) (*Tag, 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 + } + + // does not unset primary_tag_id in scene_markers because it is not nullable + return executeDeleteQuery("tags", id, tx) } From 9d41963d0598e739c91199af5dd3fec16f82a811 Mon Sep 17 00:00:00 2001 From: WithoutPants <53250216+WithoutPants@users.noreply.github.com> Date: Thu, 17 Oct 2019 11:56:08 +1100 Subject: [PATCH 3/3] Display error when deleting tag used as primary --- pkg/models/querybuilder_tag.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pkg/models/querybuilder_tag.go b/pkg/models/querybuilder_tag.go index e5ed270c6..16a8bcb82 100644 --- a/pkg/models/querybuilder_tag.go +++ b/pkg/models/querybuilder_tag.go @@ -1,6 +1,7 @@ package models import ( + "errors" "database/sql" "github.com/jmoiron/sqlx" "github.com/stashapp/stash/pkg/database" @@ -63,7 +64,17 @@ func (qb *TagQueryBuilder) Destroy(id string, tx *sqlx.Tx) error { return err } - // does not unset primary_tag_id in scene_markers because it is not nullable + // 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) }