diff --git a/graphql/documents/data/tag.graphql b/graphql/documents/data/tag.graphql index d5095fb35..cfaa28137 100644 --- a/graphql/documents/data/tag.graphql +++ b/graphql/documents/data/tag.graphql @@ -24,3 +24,16 @@ fragment TagData on Tag { ...SlimTagData } } + +fragment SelectTagData on Tag { + id + name + description + aliases + image_path + + parents { + id + name + } +} diff --git a/graphql/documents/queries/misc.graphql b/graphql/documents/queries/misc.graphql index 61354be53..03777141d 100644 --- a/graphql/documents/queries/misc.graphql +++ b/graphql/documents/queries/misc.graphql @@ -21,14 +21,6 @@ query AllMoviesForFilter { } } -query AllTagsForFilter { - allTags { - id - name - aliases - } -} - query Stats { stats { scene_count diff --git a/graphql/documents/queries/tag.graphql b/graphql/documents/queries/tag.graphql index bb69d4515..aec1ef8bd 100644 --- a/graphql/documents/queries/tag.graphql +++ b/graphql/documents/queries/tag.graphql @@ -12,3 +12,16 @@ query FindTag($id: ID!) { ...TagData } } + +query FindTagsForSelect( + $filter: FindFilterType + $tag_filter: TagFilterType + $ids: [Int!] +) { + findTags(filter: $filter, tag_filter: $tag_filter, ids: $ids) { + count + tags { + ...SelectTagData + } + } +} diff --git a/graphql/schema/schema.graphql b/graphql/schema/schema.graphql index 9c35d103f..3e7a81a9e 100644 --- a/graphql/schema/schema.graphql +++ b/graphql/schema/schema.graphql @@ -89,6 +89,7 @@ type Query { findTags( tag_filter: TagFilterType filter: FindFilterType + ids: [Int!] ): FindTagsResultType! "Retrieve random scene markers for the wall" @@ -203,9 +204,9 @@ type Query { allGalleries: [Gallery!]! allStudios: [Studio!]! allMovies: [Movie!]! - allTags: [Tag!]! allPerformers: [Performer!]! @deprecated(reason: "Use findPerformers instead") + allTags: [Tag!]! @deprecated(reason: "Use findTags instead") # Get everything with minimal metadata diff --git a/internal/api/resolver_query_find_tag.go b/internal/api/resolver_query_find_tag.go index fd4b04ad2..6ec93d21a 100644 --- a/internal/api/resolver_query_find_tag.go +++ b/internal/api/resolver_query_find_tag.go @@ -23,9 +23,19 @@ func (r *queryResolver) FindTag(ctx context.Context, id string) (ret *models.Tag return ret, nil } -func (r *queryResolver) FindTags(ctx context.Context, tagFilter *models.TagFilterType, filter *models.FindFilterType) (ret *FindTagsResultType, err error) { +func (r *queryResolver) FindTags(ctx context.Context, tagFilter *models.TagFilterType, filter *models.FindFilterType, ids []int) (ret *FindTagsResultType, err error) { if err := r.withReadTxn(ctx, func(ctx context.Context) error { - tags, total, err := r.repository.Tag.Query(ctx, tagFilter, filter) + var tags []*models.Tag + var err error + var total int + + if len(ids) > 0 { + tags, err = r.repository.Tag.FindMany(ctx, ids) + total = len(tags) + } else { + tags, total, err = r.repository.Tag.Query(ctx, tagFilter, filter) + } + if err != nil { return err } diff --git a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx index 6f40970c1..16061c3cb 100644 --- a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx @@ -18,11 +18,7 @@ import { useListGalleryScrapers, mutateReloadScrapers, } from "src/core/StashService"; -import { - TagSelect, - SceneSelect, - StudioSelect, -} from "src/components/Shared/Select"; +import { SceneSelect, StudioSelect } from "src/components/Shared/Select"; import { Icon } from "src/components/Shared/Icon"; import { LoadingIndicator } from "src/components/Shared/LoadingIndicator"; import { useToast } from "src/hooks/Toast"; @@ -44,6 +40,7 @@ import { yupUniqueStringList, } from "src/utils/yup"; import { formikUtils } from "src/utils/form"; +import { Tag, TagSelect } from "src/components/Tags/TagSelect"; interface IProps { gallery: Partial; @@ -68,6 +65,7 @@ export const GalleryEditPanel: React.FC = ({ ); const [performers, setPerformers] = useState([]); + const [tags, setTags] = useState([]); const isNew = gallery.id === undefined; const { configuration: stashConfig } = React.useContext(ConfigurationContext); @@ -146,6 +144,14 @@ export const GalleryEditPanel: React.FC = ({ ); } + function onSetTags(items: Tag[]) { + setTags(items); + formik.setFieldValue( + "tag_ids", + items.map((item) => item.id) + ); + } + useRatingKeybinds( isVisible, stashConfig?.ui?.ratingSystemOptions?.type, @@ -156,6 +162,10 @@ export const GalleryEditPanel: React.FC = ({ setPerformers(gallery.performers ?? []); }, [gallery.performers]); + useEffect(() => { + setTags(gallery.tags ?? []); + }, [gallery.tags]); + useEffect(() => { if (isVisible) { Mousetrap.bind("s s", () => { @@ -340,8 +350,15 @@ export const GalleryEditPanel: React.FC = ({ }); if (idTags.length > 0) { - const newIds = idTags.map((t) => t.stored_id); - formik.setFieldValue("tag_ids", newIds as string[]); + onSetTags( + idTags.map((p) => { + return { + id: p.stored_id!, + name: p.name ?? "", + aliases: [], + }; + }) + ); } } } @@ -438,13 +455,8 @@ export const GalleryEditPanel: React.FC = ({ const control = ( - formik.setFieldValue( - "tag_ids", - items.map((item) => item.id) - ) - } - ids={formik.values.tag_ids} + onSelect={onSetTags} + values={tags} hoverPlacement="right" /> ); diff --git a/ui/v2.5/src/components/Images/ImageDetails/ImageEditPanel.tsx b/ui/v2.5/src/components/Images/ImageDetails/ImageEditPanel.tsx index 3a3daf708..96181f257 100644 --- a/ui/v2.5/src/components/Images/ImageDetails/ImageEditPanel.tsx +++ b/ui/v2.5/src/components/Images/ImageDetails/ImageEditPanel.tsx @@ -4,7 +4,7 @@ import { FormattedMessage, useIntl } from "react-intl"; import Mousetrap from "mousetrap"; import * as GQL from "src/core/generated-graphql"; import * as yup from "yup"; -import { TagSelect, StudioSelect } from "src/components/Shared/Select"; +import { StudioSelect } from "src/components/Shared/Select"; import { LoadingIndicator } from "src/components/Shared/LoadingIndicator"; import { useToast } from "src/hooks/Toast"; import { useFormik } from "formik"; @@ -22,6 +22,7 @@ import { PerformerSelect, } from "src/components/Performers/PerformerSelect"; import { formikUtils } from "src/utils/form"; +import { Tag, TagSelect } from "src/components/Tags/TagSelect"; interface IProps { image: GQL.ImageDataFragment; @@ -45,6 +46,7 @@ export const ImageEditPanel: React.FC = ({ const { configuration } = React.useContext(ConfigurationContext); const [performers, setPerformers] = useState([]); + const [tags, setTags] = useState([]); const schema = yup.object({ title: yup.string().ensure(), @@ -93,6 +95,14 @@ export const ImageEditPanel: React.FC = ({ ); } + function onSetTags(items: Tag[]) { + setTags(items); + formik.setFieldValue( + "tag_ids", + items.map((item) => item.id) + ); + } + useRatingKeybinds( true, configuration?.ui?.ratingSystemOptions?.type, @@ -103,6 +113,10 @@ export const ImageEditPanel: React.FC = ({ setPerformers(image.performers ?? []); }, [image.performers]); + useEffect(() => { + setTags(image.tags ?? []); + }, [image.tags]); + useEffect(() => { if (isVisible) { Mousetrap.bind("s s", () => { @@ -196,13 +210,8 @@ export const ImageEditPanel: React.FC = ({ const control = ( - formik.setFieldValue( - "tag_ids", - items.map((item) => item.id) - ) - } - ids={formik.values.tag_ids} + onSelect={onSetTags} + values={tags} hoverPlacement="right" /> ); diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx index 1466e82f8..2277d7b53 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx @@ -15,7 +15,6 @@ import { Icon } from "src/components/Shared/Icon"; import { ImageInput } from "src/components/Shared/ImageInput"; import { LoadingIndicator } from "src/components/Shared/LoadingIndicator"; import { CollapseButton } from "src/components/Shared/CollapseButton"; -import { TagSelect } from "src/components/Shared/Select"; import { CountrySelect } from "src/components/Shared/CountrySelect"; import { URLField } from "src/components/Shared/URLField"; import ImageUtils from "src/utils/image"; @@ -49,6 +48,7 @@ import { yupDateString, yupUniqueAliases, } from "src/utils/yup"; +import { Tag, TagSelect } from "src/components/Tags/TagSelect"; const isScraper = ( scraper: GQL.Scraper | GQL.StashBox @@ -83,6 +83,8 @@ export const PerformerEditPanel: React.FC = ({ // Network state const [isLoading, setIsLoading] = useState(false); + const [tags, setTags] = useState([]); + const Scrapers = useListPerformerScrapers(); const [queryableScrapers, setQueryableScrapers] = useState([]); @@ -161,6 +163,18 @@ export const PerformerEditPanel: React.FC = ({ onSubmit: (values) => onSave(schema.cast(values)), }); + function onSetTags(items: Tag[]) { + setTags(items); + formik.setFieldValue( + "tag_ids", + items.map((item) => item.id) + ); + } + + useEffect(() => { + setTags(performer.tags ?? []); + }, [performer.tags]); + function translateScrapedGender(scrapedGender?: string) { if (!scrapedGender) { return; @@ -300,8 +314,15 @@ export const PerformerEditPanel: React.FC = ({ } if (state.tags) { // map tags to their ids and filter out those not found - const newTagIds = state.tags.map((t) => t.stored_id).filter((t) => t); - formik.setFieldValue("tag_ids", newTagIds); + onSetTags( + state.tags.map((p) => { + return { + id: p.stored_id!, + name: p.name ?? "", + aliases: [], + }; + }) + ); setNewTags(state.tags.filter((t) => !t.stored_id)); } @@ -725,13 +746,8 @@ export const PerformerEditPanel: React.FC = ({ - formik.setFieldValue( - "tag_ids", - items.map((item) => item.id) - ) - } - ids={formik.values.tag_ids} + onSelect={onSetTags} + values={tags} /> {renderNewTags()} diff --git a/ui/v2.5/src/components/Performers/PerformerSelect.tsx b/ui/v2.5/src/components/Performers/PerformerSelect.tsx index fdebeae93..5a1632ef9 100644 --- a/ui/v2.5/src/components/Performers/PerformerSelect.tsx +++ b/ui/v2.5/src/components/Performers/PerformerSelect.tsx @@ -85,7 +85,7 @@ export const PerformerSelect: React.FC< thisOptionProps = { ...optionProps, children: ( - + import("./SceneScrapeDialog")); const SceneQueryModal = lazyComponent(() => import("./SceneQueryModal")); @@ -80,6 +80,7 @@ export const SceneEditPanel: React.FC = ({ [] ); const [performers, setPerformers] = useState([]); + const [tags, setTags] = useState([]); const Scrapers = useListSceneScrapers(); const [fragmentScrapers, setFragmentScrapers] = useState([]); @@ -104,6 +105,10 @@ export const SceneEditPanel: React.FC = ({ setPerformers(scene.performers ?? []); }, [scene.performers]); + useEffect(() => { + setTags(scene.tags ?? []); + }, [scene.tags]); + const { configuration: stashConfig } = React.useContext(ConfigurationContext); // Network state @@ -202,6 +207,14 @@ export const SceneEditPanel: React.FC = ({ ); } + function onSetTags(items: Tag[]) { + setTags(items); + formik.setFieldValue( + "tag_ids", + items.map((item) => item.id) + ); + } + useRatingKeybinds( isVisible, stashConfig?.ui?.ratingSystemOptions?.type, @@ -578,8 +591,15 @@ export const SceneEditPanel: React.FC = ({ }); if (idTags.length > 0) { - const newIds = idTags.map((p) => p.stored_id); - formik.setFieldValue("tag_ids", newIds as string[]); + onSetTags( + idTags.map((p) => { + return { + id: p.stored_id!, + name: p.name ?? "", + aliases: [], + }; + }) + ); } } @@ -748,13 +768,8 @@ export const SceneEditPanel: React.FC = ({ const control = ( - formik.setFieldValue( - "tag_ids", - items.map((item) => item.id) - ) - } - ids={formik.values.tag_ids} + onSelect={onSetTags} + values={tags} hoverPlacement="right" /> ); diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneMarkerForm.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneMarkerForm.tsx index 957ed81ff..7452bdd19 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneMarkerForm.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneMarkerForm.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import { Button, Form } from "react-bootstrap"; import { FormattedMessage, useIntl } from "react-intl"; import { useFormik } from "formik"; @@ -10,16 +10,13 @@ import { useSceneMarkerDestroy, } from "src/core/StashService"; import { DurationInput } from "src/components/Shared/DurationInput"; -import { - TagSelect, - MarkerTitleSuggest, - SelectObject, -} from "src/components/Shared/Select"; +import { MarkerTitleSuggest } from "src/components/Shared/Select"; import { getPlayerPosition } from "src/components/ScenePlayer/util"; import { useToast } from "src/hooks/Toast"; import isEqual from "lodash-es/isEqual"; import { formikUtils } from "src/utils/form"; import { yupFormikValidate } from "src/utils/yup"; +import { Tag, TagSelect } from "src/components/Tags/TagSelect"; interface ISceneMarkerForm { sceneID: string; @@ -39,6 +36,9 @@ export const SceneMarkerForm: React.FC = ({ const [sceneMarkerDestroy] = useSceneMarkerDestroy(); const Toast = useToast(); + const [primaryTag, setPrimaryTag] = useState(); + const [tags, setTags] = useState([]); + const isNew = marker === undefined; const schema = yup.object({ @@ -68,6 +68,34 @@ export const SceneMarkerForm: React.FC = ({ onSubmit: (values) => onSave(schema.cast(values)), }); + function onSetPrimaryTag(item: Tag) { + setPrimaryTag(item); + formik.setFieldValue("primary_tag_id", item.id); + } + + function onSetTags(items: Tag[]) { + setTags(items); + formik.setFieldValue( + "tag_ids", + items.map((item) => item.id) + ); + } + + useEffect(() => { + setPrimaryTag( + marker?.primary_tag ? { ...marker.primary_tag, aliases: [] } : undefined + ); + }, [marker?.primary_tag]); + + useEffect(() => { + setTags( + marker?.tags.map((t) => ({ + ...t, + aliases: [], + })) ?? [] + ); + }, [marker?.tags]); + async function onSave(input: InputValues) { try { if (isNew) { @@ -105,11 +133,6 @@ export const SceneMarkerForm: React.FC = ({ } } - async function onSetPrimaryTagID(tags: SelectObject[]) { - await formik.setFieldValue("primary_tag_id", tags[0]?.id); - await formik.setFieldTouched("primary_tag_id", true); - } - const splitProps = { labelProps: { column: true, @@ -145,14 +168,12 @@ export const SceneMarkerForm: React.FC = ({ } function renderPrimaryTagField() { - const primaryTagId = formik.values.primary_tag_id; - const title = intl.formatMessage({ id: "primary_tag" }); const control = ( <> onSetPrimaryTag(t[0])} + values={primaryTag ? [primaryTag] : []} hoverPlacement="right" /> {formik.touched.primary_tag_id && ( @@ -189,13 +210,8 @@ export const SceneMarkerForm: React.FC = ({ const control = ( - formik.setFieldValue( - "tag_ids", - items.map((item) => item.id) - ) - } - ids={formik.values.tag_ids} + onSelect={onSetTags} + values={tags} hoverPlacement="right" /> ); diff --git a/ui/v2.5/src/components/Shared/HoverPopover.tsx b/ui/v2.5/src/components/Shared/HoverPopover.tsx index 7d1c24641..87fcce045 100644 --- a/ui/v2.5/src/components/Shared/HoverPopover.tsx +++ b/ui/v2.5/src/components/Shared/HoverPopover.tsx @@ -9,6 +9,7 @@ interface IHoverPopover { placement?: OverlayProps["placement"]; onOpen?: () => void; onClose?: () => void; + target?: React.RefObject; } export const HoverPopover: React.FC = ({ @@ -20,6 +21,7 @@ export const HoverPopover: React.FC = ({ placement = "top", onOpen, onClose, + target, }) => { const [show, setShow] = useState(false); const triggerRef = useRef(null); @@ -61,7 +63,11 @@ export const HoverPopover: React.FC = ({ {children} {triggerRef.current && ( - + = (props) => { export const TagSelect: React.FC< IFilterProps & { excludeIds?: string[]; hoverPlacement?: Placement } > = (props) => { - const [tagAliases, setTagAliases] = useState>({}); - const [allAliases, setAllAliases] = useState([]); - const { data, loading } = useAllTagsForFilter(); - const [createTag] = useTagCreate(); - const intl = useIntl(); - const placeholder = - props.noSelectionString ?? - intl.formatMessage( - { id: "actions.select_entity" }, - { entityType: intl.formatMessage({ id: props.isMulti ? "tags" : "tag" }) } - ); - - const { configuration } = React.useContext(ConfigurationContext); - const defaultCreatable = - !configuration?.interface.disableDropdownCreate.tag ?? true; - - const exclude = useMemo(() => props.excludeIds ?? [], [props.excludeIds]); - const tags = useMemo( - () => (data?.allTags ?? []).filter((tag) => !exclude.includes(tag.id)), - [data?.allTags, exclude] - ); - - useEffect(() => { - // build the tag aliases map - const newAliases: Record = {}; - const newAll: string[] = []; - tags.forEach((t) => { - newAliases[t.id] = t.aliases; - newAll.push(...t.aliases); - }); - setTagAliases(newAliases); - setAllAliases(newAll); - }, [tags]); - - const TagOption: React.FC> = (optionProps) => { - const { inputValue } = optionProps.selectProps; - - let thisOptionProps = optionProps; - if ( - inputValue && - !optionProps.label.toLowerCase().includes(inputValue.toLowerCase()) - ) { - // must be alias - const newLabel = `${optionProps.data.label} (alias)`; - thisOptionProps = { - ...optionProps, - children: newLabel, - }; - } - - const id = optionProps.data.value; - const hide = (optionProps.data as Option & { __isNew__: boolean }) - .__isNew__; - - return ( - - - - ); - }; - - const filterOption = (option: Option, rawInput: string): boolean => { - if (!rawInput) { - return true; - } - - const input = rawInput.toLowerCase(); - const optionVal = option.label.toLowerCase(); - - if (optionVal.includes(input)) { - return true; - } - - // search for tag aliases - const aliases = tagAliases[option.value]; - // only match on alias if exact - if (aliases && aliases.some((a) => a.toLowerCase() === input)) { - return true; - } - - return false; - }; - - const onCreate = async (name: string) => { - const result = await createTag({ - variables: { - input: { - name, - }, - }, - }); - return { - item: result.data!.tagCreate!, - message: intl.formatMessage( - { id: "toast.created_entity" }, - { entity: intl.formatMessage({ id: "tag" }).toLocaleLowerCase() } - ), - }; - }; - - const isValidNewOption = ( - inputValue: string, - value: OnChangeValue, - options: OptionsOrGroups> - ) => { - if (!inputValue) { - return false; - } - - if ( - (options as Options + + + */} + {name} + {alias && {` (${alias})`}} + + + ), + }; + + return ; + }; + + const TagMultiValueLabel: React.FC< + MultiValueGenericProps + > = (optionProps) => { + let thisOptionProps = optionProps; + + const { object } = optionProps.data; + + thisOptionProps = { + ...optionProps, + children: object.name, + }; + + return ; + }; + + const TagValueLabel: React.FC> = ( + optionProps + ) => { + let thisOptionProps = optionProps; + + const { object } = optionProps.data; + + thisOptionProps = { + ...optionProps, + children: <>{object.name}, + }; + + return ; + }; + + const onCreate = async (name: string) => { + const result = await createTag({ + variables: { input: { name } }, + }); + return { + value: result.data!.tagCreate!.id, + item: result.data!.tagCreate!, + message: "Created tag", + }; + }; + + const getNamedObject = (id: string, name: string) => { + return { + id, + name, + aliases: [], + }; + }; + + const isValidNewOption = (inputValue: string, options: Tag[]) => { + if (!inputValue) { + return false; + } + + if ( + options.some((o) => { + return ( + o.name.toLowerCase() === inputValue.toLowerCase() || + o.aliases?.some((a) => a.toLowerCase() === inputValue.toLowerCase()) + ); + }) + ) { + return false; + } + + return true; + }; + + return ( + + {...props} + className={cx( + "tag-select", + { + "tag-select-active": props.active, + }, + props.className + )} + loadOptions={loadTags} + getNamedObject={getNamedObject} + isValidNewOption={isValidNewOption} + components={{ + Option: TagOption, + MultiValueLabel: TagMultiValueLabel, + SingleValue: TagValueLabel, + }} + isMulti={props.isMulti ?? false} + creatable={props.creatable ?? defaultCreatable} + onCreate={onCreate} + placeholder={ + props.noSelectionString ?? + intl.formatMessage( + { id: "actions.select_entity" }, + { + entityType: intl.formatMessage({ + id: props.isMulti ? "tags" : "tag", + }), + } + ) + } + closeMenuOnSelect={!props.isMulti} + /> + ); +}; + +export const TagIDSelect: React.FC> = ( + props +) => { + const { ids, onSelect: onSelectValues } = props; + + const [values, setValues] = useState([]); + const idsChanged = useCompare(ids); + + function onSelect(items: Tag[]) { + setValues(items); + onSelectValues?.(items); + } + + async function loadObjectsByID(idsToLoad: string[]): Promise { + const tagIDs = idsToLoad.map((id) => parseInt(id)); + const query = await queryFindTagsByIDForSelect(tagIDs); + const { tags: loadedTags } = query.data.findTags; + + return loadedTags; + } + + useEffect(() => { + if (!idsChanged) { + return; + } + + if (!ids || ids?.length === 0) { + setValues([]); + return; + } + + // load the values if we have ids and they haven't been loaded yet + const filteredValues = values.filter((v) => ids.includes(v.id.toString())); + if (filteredValues.length === ids.length) { + return; + } + + const load = async () => { + const items = await loadObjectsByID(ids); + setValues(items); + }; + + load(); + }, [ids, idsChanged, values]); + + return ; +}; diff --git a/ui/v2.5/src/components/Tags/styles.scss b/ui/v2.5/src/components/Tags/styles.scss index 8b84555b2..daee39858 100644 --- a/ui/v2.5/src/components/Tags/styles.scss +++ b/ui/v2.5/src/components/Tags/styles.scss @@ -90,3 +90,16 @@ transform: scale(0.7); } } + +.tag-select { + .alias { + font-weight: bold; + white-space: pre; + } +} + +.tag-select-image { + height: 25px; + margin-right: 0.5em; + width: 25px; +} diff --git a/ui/v2.5/src/core/StashService.ts b/ui/v2.5/src/core/StashService.ts index 15ee28682..5bf7e970b 100644 --- a/ui/v2.5/src/core/StashService.ts +++ b/ui/v2.5/src/core/StashService.ts @@ -344,7 +344,22 @@ export const queryFindTags = (filter: ListFilterModel) => }, }); -export const useAllTagsForFilter = () => GQL.useAllTagsForFilterQuery(); +export const queryFindTagsByIDForSelect = (tagIDs: number[]) => + client.query({ + query: GQL.FindTagsForSelectDocument, + variables: { + ids: tagIDs, + }, + }); + +export const queryFindTagsForSelect = (filter: ListFilterModel) => + client.query({ + query: GQL.FindTagsForSelectDocument, + variables: { + filter: filter.makeFindFilter(), + tag_filter: filter.makeFilter(), + }, + }); export const useFindSavedFilter = (id: string) => GQL.useFindSavedFilterQuery({ @@ -1598,8 +1613,6 @@ export const useTagCreate = () => const tag = result.data?.tagCreate; if (!tag) return; - appendObject(cache, tag, GQL.AllTagsForFilterDocument); - // update stats updateStats(cache, "tag_count", 1);