diff --git a/ui/v2.5/graphql/queries/image.graphql b/ui/v2.5/graphql/queries/image.graphql
index ee96d00d2..d2c6cdac8 100644
--- a/ui/v2.5/graphql/queries/image.graphql
+++ b/ui/v2.5/graphql/queries/image.graphql
@@ -9,14 +9,27 @@ query FindImages(
image_ids: $image_ids
) {
count
- megapixels
- filesize
images {
...SlimImageData
}
}
}
+query FindImagesMetadata(
+ $filter: FindFilterType
+ $image_filter: ImageFilterType
+ $image_ids: [Int!]
+) {
+ findImages(
+ filter: $filter
+ image_filter: $image_filter
+ image_ids: $image_ids
+ ) {
+ megapixels
+ filesize
+ }
+}
+
query FindImage($id: ID!, $checksum: String) {
findImage(id: $id, checksum: $checksum) {
...ImageData
diff --git a/ui/v2.5/src/components/Images/ImageList.tsx b/ui/v2.5/src/components/Images/ImageList.tsx
index 0e3753480..23e93a94c 100644
--- a/ui/v2.5/src/components/Images/ImageList.tsx
+++ b/ui/v2.5/src/components/Images/ImageList.tsx
@@ -4,7 +4,11 @@ import cloneDeep from "lodash-es/cloneDeep";
import { useHistory } from "react-router-dom";
import Mousetrap from "mousetrap";
import * as GQL from "src/core/generated-graphql";
-import { queryFindImages, useFindImages } from "src/core/StashService";
+import {
+ queryFindImages,
+ useFindImages,
+ useFindImagesMetadata,
+} from "src/core/StashService";
import { ItemList, ItemListContext, showWhenSelected } from "../List/ItemList";
import { useLightbox } from "src/hooks/Lightbox/hooks";
import { ListFilterModel } from "src/models/list-filter/filter";
@@ -269,9 +273,17 @@ function getCount(result: GQL.FindImagesQueryResult) {
return result?.data?.findImages?.count ?? 0;
}
-function renderMetadataByline(result: GQL.FindImagesQueryResult) {
- const megapixels = result?.data?.findImages?.megapixels;
- const size = result?.data?.findImages?.filesize;
+function renderMetadataByline(
+ result: GQL.FindImagesQueryResult,
+ metadataInfo?: GQL.FindImagesMetadataQueryResult
+) {
+ const megapixels = metadataInfo?.data?.findImages?.megapixels;
+ const size = metadataInfo?.data?.findImages?.filesize;
+
+ if (metadataInfo?.loading) {
+ // return ellipsis
+ return (...);
+ }
if (!megapixels && !size) {
return;
@@ -450,6 +462,7 @@ export const ImageList: React.FC = ({
{
+interface IItemListProps {
view?: View;
otherOperations?: IItemListOperation[];
renderContent: (
@@ -123,7 +123,7 @@ interface IItemListProps {
onChangePage: (page: number) => void,
pageCount: number
) => React.ReactNode;
- renderMetadataByline?: (data: T) => React.ReactNode;
+ renderMetadataByline?: (data: T, metadataInfo?: M) => React.ReactNode;
renderEditDialog?: (
selected: E[],
onClose: (applied: boolean) => void
@@ -140,8 +140,8 @@ interface IItemListProps {
renderToolbar?: (props: IFilteredListToolbar) => React.ReactNode;
}
-export const ItemList = (
- props: IItemListProps
+export const ItemList = (
+ props: IItemListProps
) => {
const {
view,
@@ -155,8 +155,8 @@ export const ItemList = (
} = props;
const { filter, setFilter: updateFilter } = useFilter();
- const { effectiveFilter, result, cachedResult, totalCount } =
- useQueryResultContext();
+ const { effectiveFilter, result, metadataInfo, cachedResult, totalCount } =
+ useQueryResultContext();
const listSelect = useListContext();
const {
selectedIds,
@@ -174,8 +174,8 @@ export const ItemList = (
const metadataByline = useMemo(() => {
if (cachedResult.loading) return "";
- return renderMetadataByline?.(cachedResult) ?? "";
- }, [renderMetadataByline, cachedResult]);
+ return renderMetadataByline?.(cachedResult, metadataInfo) ?? "";
+ }, [renderMetadataByline, cachedResult, metadataInfo]);
const pages = Math.ceil(totalCount / filter.itemsPerPage);
@@ -369,11 +369,16 @@ export const ItemList = (
);
};
-interface IItemListContextProps {
+interface IItemListContextProps<
+ T extends QueryResult,
+ E extends IHasID,
+ M = unknown
+> {
filterMode: GQL.FilterMode;
defaultSort?: string;
defaultFilter?: ListFilterModel;
useResult: (filter: ListFilterModel) => T;
+ useMetadataInfo?: (filter: ListFilterModel) => M;
getCount: (data: T) => number;
getItems: (data: T) => E[];
filterHook?: (filter: ListFilterModel) => ListFilterModel;
@@ -384,14 +389,19 @@ interface IItemListContextProps {
// Provides the contexts for the ItemList component. Includes functionality to scroll
// to top on page change.
-export const ItemListContext = (
- props: PropsWithChildren>
+export const ItemListContext = <
+ T extends QueryResult,
+ E extends IHasID,
+ M = unknown
+>(
+ props: PropsWithChildren>
) => {
const {
filterMode,
defaultSort,
defaultFilter: providedDefaultFilter,
useResult,
+ useMetadataInfo,
getCount,
getItems,
view,
@@ -425,6 +435,7 @@ export const ItemListContext = (
diff --git a/ui/v2.5/src/components/List/ListProvider.tsx b/ui/v2.5/src/components/List/ListProvider.tsx
index 2e8854586..0584a61c6 100644
--- a/ui/v2.5/src/components/List/ListProvider.tsx
+++ b/ui/v2.5/src/components/List/ListProvider.tsx
@@ -80,21 +80,25 @@ export function useListContextOptional() {
interface IQueryResultContextOptions<
T extends QueryResult,
- E extends IHasID = IHasID
+ E extends IHasID = IHasID,
+ M = unknown
> {
filterHook?: (filter: ListFilterModel) => ListFilterModel;
useResult: (filter: ListFilterModel) => T;
+ useMetadataInfo?: (filter: ListFilterModel) => M;
getCount: (data: T) => number;
getItems: (data: T) => E[];
}
export interface IQueryResultContextState<
T extends QueryResult = QueryResult,
- E extends IHasID = IHasID
+ E extends IHasID = IHasID,
+ M = unknown
> {
effectiveFilter: ListFilterModel;
result: T;
cachedResult: T;
+ metadataInfo?: M;
items: E[];
totalCount: number;
}
@@ -104,15 +108,23 @@ export const QueryResultStateContext =
export const QueryResultContext = <
T extends QueryResult,
- E extends IHasID = IHasID
+ E extends IHasID = IHasID,
+ M = unknown
>(
- props: IQueryResultContextOptions & {
+ props: IQueryResultContextOptions & {
children?:
- | ((props: IQueryResultContextState) => React.ReactNode)
+ | ((props: IQueryResultContextState) => React.ReactNode)
| React.ReactNode;
}
) => {
- const { filterHook, useResult, getItems, getCount, children } = props;
+ const {
+ filterHook,
+ useResult,
+ useMetadataInfo,
+ getItems,
+ getCount,
+ children,
+ } = props;
const { filter } = useFilter();
const effectiveFilter = useMemo(() => {
@@ -122,9 +134,16 @@ export const QueryResultContext = <
return filter;
}, [filter, filterHook]);
- const result = useResult(effectiveFilter);
+ // metadata filter is the effective filter with the sort, page size and page number removed
+ const metadataFilter = useMemo(
+ () => effectiveFilter.metadataInfo(),
+ [effectiveFilter]
+ );
- // use cached query result for pagination and metadata rendering
+ const result = useResult(effectiveFilter);
+ const metadataInfo = useMetadataInfo?.(metadataFilter);
+
+ // use cached query result for pagination
const cachedResult = useCachedQueryResult(effectiveFilter, result);
const items = useMemo(() => getItems(result), [getItems, result]);
@@ -133,12 +152,13 @@ export const QueryResultContext = <
[getCount, cachedResult]
);
- const state: IQueryResultContextState = {
+ const state: IQueryResultContextState = {
effectiveFilter,
result,
cachedResult,
items,
totalCount,
+ metadataInfo,
};
return (
@@ -154,7 +174,8 @@ export const QueryResultContext = <
export function useQueryResultContext<
T extends QueryResult,
- E extends IHasID = IHasID
+ E extends IHasID = IHasID,
+ M = unknown
>() {
const context = React.useContext(QueryResultStateContext);
@@ -164,5 +185,5 @@ export function useQueryResultContext<
);
}
- return context as IQueryResultContextState;
+ return context as IQueryResultContextState;
}
diff --git a/ui/v2.5/src/core/StashService.ts b/ui/v2.5/src/core/StashService.ts
index d43d87097..424301fe1 100644
--- a/ui/v2.5/src/core/StashService.ts
+++ b/ui/v2.5/src/core/StashService.ts
@@ -201,6 +201,15 @@ export const useFindImages = (filter?: ListFilterModel) =>
},
});
+export const useFindImagesMetadata = (filter?: ListFilterModel) =>
+ GQL.useFindImagesMetadataQuery({
+ skip: filter === undefined,
+ variables: {
+ filter: filter?.makeFindFilter(),
+ image_filter: filter?.makeFilter(),
+ },
+ });
+
export const queryFindImages = (filter: ListFilterModel) =>
client.query({
query: GQL.FindImagesDocument,
diff --git a/ui/v2.5/src/models/list-filter/filter.ts b/ui/v2.5/src/models/list-filter/filter.ts
index 4780f1ab6..d28107a12 100644
--- a/ui/v2.5/src/models/list-filter/filter.ts
+++ b/ui/v2.5/src/models/list-filter/filter.ts
@@ -103,6 +103,20 @@ export class ListFilterModel {
});
}
+ // returns a clone of the filter for metadata fetching
+ // this removes the sort, page size and page number and zoom index
+ public metadataInfo() {
+ const clone = this.clone();
+ clone.sortBy = undefined;
+ clone.randomSeed = -1;
+ clone.currentPage = 1;
+ clone.sortDirection = DEFAULT_PARAMS.sortDirection;
+ clone.itemsPerPage = 0;
+ clone.zoomIndex = 1;
+ clone.displayMode = DEFAULT_PARAMS.displayMode;
+ return clone;
+ }
+
// returns the number of filters applied
public count() {
// don't include search term