Add sidebar to scene markers list (#6603)

* Add tag markers filter
* Add marker count and markers filter to performer filter
* Add sidebar to marker list
This commit is contained in:
WithoutPants 2026-02-26 07:54:40 +11:00 committed by GitHub
parent c9f0dba62f
commit 5734ee43ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 504 additions and 123 deletions

View file

@ -177,6 +177,8 @@ input PerformerFilterType {
tag_count: IntCriterionInput
"Filter by scene count"
scene_count: IntCriterionInput
"Filter by marker count (via scene)"
marker_count: IntCriterionInput
"Filter by image count"
image_count: IntCriterionInput
"Filter by gallery count"
@ -220,6 +222,8 @@ input PerformerFilterType {
galleries_filter: GalleryFilterType
"Filter by related tags that meet this criteria"
tags_filter: TagFilterType
"Filter by related scene markers (via scene) that meet this criteria"
markers_filter: SceneMarkerFilterType
"Filter by creation time"
created_at: TimestampCriterionInput
"Filter by last update time"
@ -684,6 +688,8 @@ input TagFilterType {
performers_filter: PerformerFilterType
"Filter by related studios that meet this criteria"
studios_filter: StudioFilterType
"Filter by related scene markers that meet this criteria"
markers_filter: SceneMarkerFilterType
"Filter by creation time"
created_at: TimestampCriterionInput

View file

@ -158,6 +158,8 @@ type PerformerFilterType struct {
TagCount *IntCriterionInput `json:"tag_count"`
// Filter by scene count
SceneCount *IntCriterionInput `json:"scene_count"`
// Filter by scene marker count (via scene)
MarkerCount *IntCriterionInput `json:"marker_count"`
// Filter by image count
ImageCount *IntCriterionInput `json:"image_count"`
// Filter by gallery count
@ -202,6 +204,8 @@ type PerformerFilterType struct {
GalleriesFilter *GalleryFilterType `json:"galleries_filter"`
// Filter by related tags that meet this criteria
TagsFilter *TagFilterType `json:"tags_filter"`
// Filter by related scene markers (via scene) that meet this criteria
MarkersFilter *SceneMarkerFilterType `json:"markers_filter"`
// Filter by created at
CreatedAt *TimestampCriterionInput `json:"created_at"`
// Filter by updated at

View file

@ -56,6 +56,8 @@ type TagFilterType struct {
PerformersFilter *PerformerFilterType `json:"performers_filter"`
// Filter by related studios that meet this criteria
StudiosFilter *StudioFilterType `json:"studios_filter"`
// Filter by related scene markers that meet this criteria
MarkersFilter *SceneMarkerFilterType `json:"markers_filter"`
// Filter by created at
CreatedAt *TimestampCriterionInput `json:"created_at"`
// Filter by updated at

View file

@ -1089,11 +1089,16 @@ func (h *stashIDsCriterionHandler) handle(ctx context.Context, f *filterBuilder)
}
type relatedFilterHandler struct {
relatedIDCol string
relatedRepo repository
// column on the primary table that relates to the related table (eg scene_id)
relatedIDCol string
// repository for the related table (eg sceneRepository)
relatedRepo repository
// handler for the filter on the related table
relatedHandler criterionHandler
joinFn func(f *filterBuilder)
directJoin bool
// optional function to perform the necessary join(s) to the related table
joinFn func(f *filterBuilder)
// if true, related filter handler will be run using the existing filterBuilder instead of a subquery.
directJoin bool
}
func (h *relatedFilterHandler) handle(ctx context.Context, f *filterBuilder) {

View file

@ -195,6 +195,7 @@ func (qb *performerFilterHandler) criterionHandler() criterionHandler {
qb.tagCountCriterionHandler(filter.TagCount),
qb.sceneCountCriterionHandler(filter.SceneCount),
qb.markerCountCriterionHandler(filter.MarkerCount),
qb.imageCountCriterionHandler(filter.ImageCount),
qb.galleryCountCriterionHandler(filter.GalleryCount),
qb.playCounterCriterionHandler(filter.PlayCount),
@ -204,6 +205,16 @@ func (qb *performerFilterHandler) criterionHandler() criterionHandler {
&timestampCriterionHandler{filter.CreatedAt, tableName + ".created_at", nil},
&timestampCriterionHandler{filter.UpdatedAt, tableName + ".updated_at", nil},
&relatedFilterHandler{
relatedIDCol: "scene_markers.id",
relatedRepo: sceneMarkerRepository.repository,
relatedHandler: &sceneMarkerFilterHandler{filter.MarkersFilter},
joinFn: func(f *filterBuilder) {
performerRepository.scenes.innerJoin(f, "", "performers.id")
f.addInnerJoin(sceneMarkerTable, "", "scene_markers.scene_id = performers_scenes.scene_id")
},
},
&relatedFilterHandler{
relatedIDCol: "performers_scenes.scene_id",
relatedRepo: sceneRepository.repository,
@ -387,6 +398,22 @@ func (qb *performerFilterHandler) sceneCountCriterionHandler(count *models.IntCr
return h.handler(count)
}
func (qb *performerFilterHandler) markerCountCriterionHandler(count *models.IntCriterionInput) criterionHandlerFunc {
return func(ctx context.Context, f *filterBuilder) {
if count != nil {
performerRepository.scenes.innerJoin(f, "", "performers.id")
const query = `(SELECT COUNT(*) FROM scene_markers
INNER JOIN scenes ON scene_markers.scene_id = scenes.id
INNER JOIN performers_scenes ON performers_scenes.scene_id = scenes.id
WHERE performers_scenes.performer_id = performers.id)`
clause, args := getIntCriterionWhereClause(query, *count)
f.addWhere(clause, args...)
}
}
}
func (qb *performerFilterHandler) imageCountCriterionHandler(count *models.IntCriterionInput) criterionHandlerFunc {
h := countCriterionHandlerBuilder{
primaryTable: performerTable,

View file

@ -161,6 +161,20 @@ func (qb *tagFilterHandler) criterionHandler() criterionHandler {
tagRepository.studios.innerJoin(f, "", "tags.id")
},
},
&relatedFilterHandler{
relatedIDCol: "markers_tags.marker_id",
relatedRepo: sceneMarkerRepository.repository,
relatedHandler: &sceneMarkerFilterHandler{tagFilter.MarkersFilter},
joinFn: func(f *filterBuilder) {
f.addWith(`markers_tags AS (
SELECT mt.scene_marker_id AS marker_id, mt.tag_id AS tag_id FROM scene_markers_tags mt
UNION
SELECT m.id, m.primary_tag_id FROM scene_markers m
)`)
f.addInnerJoin("markers_tags", "", "markers_tags.tag_id = tags.id")
},
},
}
}

View file

@ -24,6 +24,7 @@ import {
IntCriterionInput,
PerformerFilterType,
SceneFilterType,
SceneMarkerFilterType,
StudioFilterType,
} from "src/core/generated-graphql";
import { useIntl } from "react-intl";
@ -527,6 +528,8 @@ interface IFilterType {
group_count?: InputMaybe<IntCriterionInput>;
studios_filter?: InputMaybe<StudioFilterType>;
studio_count?: InputMaybe<IntCriterionInput>;
marker_count?: InputMaybe<IntCriterionInput>;
markers_filter?: InputMaybe<SceneMarkerFilterType>;
}
export function setObjectFilter(
@ -549,6 +552,7 @@ export function setObjectFilter(
modifier: CriterionModifier.GreaterThan,
value: 0,
};
break;
}
out.scenes_filter = relatedFilterOutput as SceneFilterType;
break;
@ -559,6 +563,7 @@ export function setObjectFilter(
modifier: CriterionModifier.GreaterThan,
value: 0,
};
break;
}
out.performers_filter = relatedFilterOutput as PerformerFilterType;
break;
@ -569,6 +574,7 @@ export function setObjectFilter(
modifier: CriterionModifier.GreaterThan,
value: 0,
};
break;
}
out.galleries_filter = relatedFilterOutput as GalleryFilterType;
break;
@ -579,6 +585,7 @@ export function setObjectFilter(
modifier: CriterionModifier.GreaterThan,
value: 0,
};
break;
}
out.groups_filter = relatedFilterOutput as GroupFilterType;
break;
@ -589,9 +596,21 @@ export function setObjectFilter(
modifier: CriterionModifier.GreaterThan,
value: 0,
};
break;
}
out.studios_filter = relatedFilterOutput as StudioFilterType;
break;
case FilterMode.SceneMarkers:
// if empty, only get objects with scene markers
if (empty) {
out.marker_count = {
modifier: CriterionModifier.GreaterThan,
value: 0,
};
break;
}
out.markers_filter = relatedFilterOutput as SceneMarkerFilterType;
break;
default:
throw new Error("Invalid filter mode");
}

View file

@ -1,7 +1,7 @@
import cloneDeep from "lodash-es/cloneDeep";
import React from "react";
import React, { useCallback, useEffect } from "react";
import { useHistory } from "react-router-dom";
import { useIntl } from "react-intl";
import { FormattedMessage, useIntl } from "react-intl";
import Mousetrap from "mousetrap";
import * as GQL from "src/core/generated-graphql";
import {
@ -9,7 +9,7 @@ import {
useFindSceneMarkers,
} from "src/core/StashService";
import NavUtils from "src/utils/navigation";
import { ItemList, ItemListContext } from "../List/ItemList";
import { useFilteredItemList } from "../List/ItemList";
import { ListFilterModel } from "src/models/list-filter/filter";
import { DisplayMode } from "src/models/list-filter/types";
import { MarkerWallPanel } from "./SceneMarkerWallPanel";
@ -17,17 +17,179 @@ import { View } from "../List/views";
import { SceneMarkerCardGrid } from "./SceneMarkerCardGrid";
import { DeleteSceneMarkersDialog } from "./DeleteSceneMarkersDialog";
import { EditSceneMarkersDialog } from "./EditSceneMarkersDialog";
import { PatchComponent } from "src/patch";
import { IItemListOperation } from "../List/FilteredListToolbar";
import { PatchComponent, PatchContainerComponent } from "src/patch";
import {
FilteredListToolbar,
IItemListOperation,
} from "../List/FilteredListToolbar";
import {
Sidebar,
SidebarPane,
SidebarPaneContent,
SidebarStateContext,
useSidebarState,
} from "../Shared/Sidebar";
import { useCloseEditDelete, useFilterOperations } from "../List/util";
import {
FilteredSidebarHeader,
useFilteredSidebarKeybinds,
} from "../List/Filters/FilterSidebar";
import { useZoomKeybinds } from "../List/ZoomSlider";
import {
IListFilterOperation,
ListOperations,
} from "../List/ListOperationButtons";
import cx from "classnames";
import { FilterTags } from "../List/FilterTags";
import { Pagination, PaginationIndex } from "../List/Pagination";
import { LoadedContent } from "../List/PagedList";
import useFocus from "src/utils/focus";
import { SidebarPerformersFilter } from "../List/Filters/PerformersFilter";
import { SidebarTagsFilter } from "../List/Filters/TagsFilter";
import { Button } from "react-bootstrap";
function getItems(result: GQL.FindSceneMarkersQueryResult) {
return result?.data?.findSceneMarkers?.scene_markers ?? [];
const SceneMarkerList: React.FC<{
markers: GQL.SceneMarkerDataFragment[];
filter: ListFilterModel;
selectedIds: Set<string>;
onSelectChange: (id: string, selected: boolean, shiftKey: boolean) => void;
}> = PatchComponent(
"SceneList",
({ markers, filter, selectedIds, onSelectChange }) => {
if (markers.length === 0) {
return null;
}
if (filter.displayMode === DisplayMode.Wall) {
return (
<MarkerWallPanel
markers={markers}
zoomIndex={filter.zoomIndex}
selectedIds={selectedIds}
onSelectChange={onSelectChange}
/>
);
}
if (filter.displayMode === DisplayMode.Grid) {
return (
<SceneMarkerCardGrid
markers={markers}
zoomIndex={filter.zoomIndex}
selectedIds={selectedIds}
onSelectChange={onSelectChange}
/>
);
}
return null;
}
);
function usePlayRandom(filter: ListFilterModel, count: number) {
const history = useHistory();
const playRandom = useCallback(async () => {
// query for a random scene
if (count === 0) {
return;
}
const pages = Math.ceil(count / filter.itemsPerPage);
const page = Math.floor(Math.random() * pages) + 1;
const indexMax = Math.min(filter.itemsPerPage, count);
const index = Math.floor(Math.random() * indexMax);
const filterCopy = cloneDeep(filter);
filterCopy.currentPage = page;
filterCopy.sortBy = "random";
const queryResults = await queryFindSceneMarkers(filterCopy);
const marker = queryResults.data.findSceneMarkers.scene_markers[index];
if (marker) {
// navigate to the scene player page
const url = NavUtils.makeSceneMarkerUrl(marker);
history.push(url);
}
}, [filter, count, history]);
return playRandom;
}
function getCount(result: GQL.FindSceneMarkersQueryResult) {
return result?.data?.findSceneMarkers?.count ?? 0;
function useAddKeybinds(filter: ListFilterModel, count: number) {
const playRandom = usePlayRandom(filter, count);
useEffect(() => {
Mousetrap.bind("p r", () => {
playRandom();
});
return () => {
Mousetrap.unbind("p r");
};
}, [playRandom]);
}
const ScenesFilterSidebarSections = PatchContainerComponent(
"FilteredSceneMarkerList.SidebarSections"
);
const SidebarContent: React.FC<{
filter: ListFilterModel;
setFilter: (filter: ListFilterModel) => void;
filterHook?: (filter: ListFilterModel) => ListFilterModel;
view?: View;
sidebarOpen: boolean;
onClose?: () => void;
showEditFilter: (editingCriterion?: string) => void;
count?: number;
focus?: ReturnType<typeof useFocus>;
}> = ({
filter,
setFilter,
filterHook,
view,
showEditFilter,
sidebarOpen,
onClose,
count,
focus,
}) => {
const showResultsId =
count !== undefined ? "actions.show_count_results" : "actions.show_results";
return (
<>
<FilteredSidebarHeader
sidebarOpen={sidebarOpen}
showEditFilter={showEditFilter}
filter={filter}
setFilter={setFilter}
view={view}
focus={focus}
/>
<ScenesFilterSidebarSections>
<SidebarPerformersFilter
filter={filter}
setFilter={setFilter}
filterHook={filterHook}
/>
<SidebarTagsFilter
filter={filter}
setFilter={setFilter}
filterHook={filterHook}
/>
</ScenesFilterSidebarSections>
<div className="sidebar-footer">
<Button className="sidebar-close-button" onClick={onClose}>
<FormattedMessage id={showResultsId} values={{ count }} />
</Button>
</div>
</>
);
};
interface ISceneMarkerList {
filterHook?: (filter: ListFilterModel) => ListFilterModel;
view?: View;
@ -36,132 +198,274 @@ interface ISceneMarkerList {
extraOperations?: IItemListOperation<GQL.FindSceneMarkersQueryResult>[];
}
export const SceneMarkerList: React.FC<ISceneMarkerList> = PatchComponent(
"SceneMarkerList",
({ filterHook, view, alterQuery, extraOperations = [] }) => {
export const FilteredSceneMarkerList = PatchComponent(
"FilteredSceneMarkerList",
(props: ISceneMarkerList) => {
const intl = useIntl();
const history = useHistory();
const filterMode = GQL.FilterMode.SceneMarkers;
const searchFocus = useFocus();
const otherOperations = [
...extraOperations,
{
text: intl.formatMessage({ id: "actions.play_random" }),
onClick: playRandom,
},
];
const {
filterHook,
defaultSort,
view,
alterQuery,
extraOperations = [],
} = props;
function addKeybinds(
result: GQL.FindSceneMarkersQueryResult,
filter: ListFilterModel
) {
Mousetrap.bind("p r", () => {
playRandom(result, filter);
// States
const {
showSidebar,
setShowSidebar,
loading: sidebarStateLoading,
sectionOpen,
setSectionOpen,
} = useSidebarState(view);
const { filterState, queryResult, modalState, listSelect, showEditFilter } =
useFilteredItemList({
filterStateProps: {
filterMode: GQL.FilterMode.SceneMarkers,
defaultSort,
view,
useURL: alterQuery,
},
queryResultProps: {
useResult: useFindSceneMarkers,
getCount: (r) => r.data?.findSceneMarkers.count ?? 0,
getItems: (r) => r.data?.findSceneMarkers.scene_markers ?? [],
filterHook,
},
});
const { filter, setFilter } = filterState;
const { effectiveFilter, result, cachedResult, items, totalCount } =
queryResult;
const {
selectedIds,
selectedItems,
onSelectChange,
onSelectAll,
onSelectNone,
onInvertSelection,
hasSelection,
} = listSelect;
const { modal, showModal, closeModal } = modalState;
// Utility hooks
const { setPage, removeCriterion, clearAllCriteria } = useFilterOperations({
filter,
setFilter,
});
useAddKeybinds(filter, totalCount);
useFilteredSidebarKeybinds({
showSidebar,
setShowSidebar,
});
const onCloseEditDelete = useCloseEditDelete({
closeModal,
onSelectNone,
result,
});
const onEdit = useCallback(() => {
showModal(
<EditSceneMarkersDialog
selected={selectedItems}
onClose={onCloseEditDelete}
/>
);
}, [showModal, selectedItems, onCloseEditDelete]);
const onDelete = useCallback(() => {
showModal(
<DeleteSceneMarkersDialog
selected={selectedItems}
onClose={onCloseEditDelete}
/>
);
}, [showModal, selectedItems, onCloseEditDelete]);
useEffect(() => {
Mousetrap.bind("e", () => {
if (hasSelection) {
onEdit?.();
}
});
Mousetrap.bind("d d", () => {
if (hasSelection) {
onDelete?.();
}
});
return () => {
Mousetrap.unbind("p r");
Mousetrap.unbind("e");
Mousetrap.unbind("d d");
};
}
}, [onSelectAll, onSelectNone, hasSelection, onEdit, onDelete]);
async function playRandom(
result: GQL.FindSceneMarkersQueryResult,
filter: ListFilterModel
) {
// query for a random scene
if (result.data?.findSceneMarkers) {
const { count } = result.data.findSceneMarkers;
useZoomKeybinds({
zoomIndex: filter.zoomIndex,
onChangeZoom: (zoom) => setFilter(filter.setZoom(zoom)),
});
const index = Math.floor(Math.random() * count);
const filterCopy = cloneDeep(filter);
filterCopy.itemsPerPage = 1;
filterCopy.currentPage = index + 1;
const singleResult = await queryFindSceneMarkers(filterCopy);
if (singleResult.data.findSceneMarkers.scene_markers.length === 1) {
// navigate to the scene player page
const url = NavUtils.makeSceneMarkerUrl(
singleResult.data.findSceneMarkers.scene_markers[0]
);
history.push(url);
}
}
}
const playRandom = usePlayRandom(effectiveFilter, totalCount);
function renderContent(
result: GQL.FindSceneMarkersQueryResult,
filter: ListFilterModel,
selectedIds: Set<string>,
onSelectChange: (id: string, selected: boolean, shiftKey: boolean) => void
) {
if (!result.data?.findSceneMarkers) return;
const convertedExtraOperations: IListFilterOperation[] =
extraOperations.map((o) => ({
...o,
isDisplayed: o.isDisplayed
? () => o.isDisplayed!(result, filter, selectedIds)
: undefined,
onClick: () => {
o.onClick(result, filter, selectedIds);
},
}));
if (filter.displayMode === DisplayMode.Wall) {
return (
<MarkerWallPanel
markers={result.data.findSceneMarkers.scene_markers}
zoomIndex={filter.zoomIndex}
selectedIds={selectedIds}
onSelectChange={onSelectChange}
/>
);
}
const otherOperations = [
...convertedExtraOperations,
{
text: intl.formatMessage({ id: "actions.select_all" }),
onClick: () => onSelectAll(),
isDisplayed: () => totalCount > 0,
},
{
text: intl.formatMessage({ id: "actions.select_none" }),
onClick: () => onSelectNone(),
isDisplayed: () => hasSelection,
},
{
text: intl.formatMessage({ id: "actions.invert_selection" }),
onClick: () => onInvertSelection(),
isDisplayed: () => totalCount > 0,
},
{
text: intl.formatMessage({ id: "actions.play_random" }),
onClick: playRandom,
isDisplayed: () => totalCount > 1,
},
// {
// text: `${intl.formatMessage({ id: "actions.generate" })}…`,
// onClick: () =>
// showModal(
// <GenerateDialog
// type="scene"
// selectedIds={Array.from(selectedIds.values())}
// onClose={() => closeModal()}
// />
// ),
// isDisplayed: () => hasSelection,
// },
];
if (filter.displayMode === DisplayMode.Grid) {
return (
<SceneMarkerCardGrid
markers={result.data.findSceneMarkers.scene_markers}
zoomIndex={filter.zoomIndex}
selectedIds={selectedIds}
onSelectChange={onSelectChange}
/>
);
}
}
// render
if (sidebarStateLoading) return null;
function renderEditDialog(
selectedMarkers: GQL.SceneMarkerDataFragment[],
onClose: (applied: boolean) => void
) {
return (
<EditSceneMarkersDialog selected={selectedMarkers} onClose={onClose} />
);
}
function renderDeleteDialog(
selectedSceneMarkers: GQL.SceneMarkerDataFragment[],
onClose: (confirmed: boolean) => void
) {
return (
<DeleteSceneMarkersDialog
selected={selectedSceneMarkers}
onClose={onClose}
/>
);
}
const operations = (
<ListOperations
items={items.length}
hasSelection={hasSelection}
operations={otherOperations}
onEdit={onEdit}
onDelete={onDelete}
operationsMenuClassName="scene-marker-list-operations-dropdown"
/>
);
return (
<ItemListContext
filterMode={filterMode}
useResult={useFindSceneMarkers}
getItems={getItems}
getCount={getCount}
alterQuery={alterQuery}
filterHook={filterHook}
view={view}
selectable
<div
className={cx("item-list-container scene-list", {
"hide-sidebar": !showSidebar,
})}
>
<ItemList
view={view}
otherOperations={otherOperations}
addKeybinds={addKeybinds}
renderContent={renderContent}
renderEditDialog={renderEditDialog}
renderDeleteDialog={renderDeleteDialog}
/>
</ItemListContext>
{modal}
<SidebarStateContext.Provider value={{ sectionOpen, setSectionOpen }}>
<SidebarPane hideSidebar={!showSidebar}>
<Sidebar hide={!showSidebar} onHide={() => setShowSidebar(false)}>
<SidebarContent
filter={filter}
setFilter={setFilter}
filterHook={filterHook}
showEditFilter={showEditFilter}
view={view}
sidebarOpen={showSidebar}
onClose={() => setShowSidebar(false)}
count={cachedResult.loading ? undefined : totalCount}
focus={searchFocus}
/>
</Sidebar>
<SidebarPaneContent
onSidebarToggle={() => setShowSidebar(!showSidebar)}
>
<FilteredListToolbar
filter={filter}
listSelect={listSelect}
setFilter={setFilter}
showEditFilter={showEditFilter}
onDelete={onDelete}
onEdit={onEdit}
operationComponent={operations}
view={view}
zoomable
/>
<FilterTags
criteria={filter.criteria}
onEditCriterion={(c) => showEditFilter(c.criterionOption.type)}
onRemoveCriterion={removeCriterion}
onRemoveAll={clearAllCriteria}
/>
<div className="pagination-index-container">
<Pagination
currentPage={filter.currentPage}
itemsPerPage={filter.itemsPerPage}
totalItems={totalCount}
onChangePage={(page) => setFilter(filter.changePage(page))}
/>
<PaginationIndex
loading={cachedResult.loading}
itemsPerPage={filter.itemsPerPage}
currentPage={filter.currentPage}
totalItems={totalCount}
/>
</div>
<LoadedContent loading={result.loading} error={result.error}>
<SceneMarkerList
filter={effectiveFilter}
markers={items}
selectedIds={selectedIds}
onSelectChange={onSelectChange}
/>
</LoadedContent>
{totalCount > filter.itemsPerPage && (
<div className="pagination-footer-container">
<div className="pagination-footer">
<Pagination
itemsPerPage={filter.itemsPerPage}
currentPage={filter.currentPage}
totalItems={totalCount}
onChangePage={setPage}
pagePopupPlacement="top"
/>
</div>
</div>
)}
</SidebarPaneContent>
</SidebarPane>
</SidebarStateContext.Provider>
</div>
);
}
);
export default SceneMarkerList;
export default FilteredSceneMarkerList;

View file

@ -5,7 +5,7 @@ import {
TagsCriterion,
TagsCriterionOption,
} from "src/models/list-filter/criteria/tags";
import { SceneMarkerList } from "src/components/Scenes/SceneMarkerList";
import { FilteredSceneMarkerList } from "src/components/Scenes/SceneMarkerList";
import { View } from "src/components/List/views";
function useFilterHook(tag: GQL.TagDataFragment, showSubTagContent?: boolean) {
@ -60,7 +60,7 @@ export const TagMarkersPanel: React.FC<ITagMarkersPanel> = ({
const filterHook = useFilterHook(tag, showSubTagContent);
return (
<SceneMarkerList
<FilteredSceneMarkerList
filterHook={filterHook}
alterQuery={active}
view={View.TagMarkers}