mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 16:34:02 +01:00
Merge 5eae9c223e into 061d21dede
This commit is contained in:
commit
2d55c11ea5
6 changed files with 109 additions and 28 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 <span className="images-stats"> (...)</span>;
|
||||
}
|
||||
|
||||
if (!megapixels && !size) {
|
||||
return;
|
||||
|
|
@ -450,6 +462,7 @@ export const ImageList: React.FC<IImageList> = ({
|
|||
<ItemListContext
|
||||
filterMode={filterMode}
|
||||
useResult={useFindImages}
|
||||
useMetadataInfo={useFindImagesMetadata}
|
||||
getItems={getItems}
|
||||
getCount={getCount}
|
||||
alterQuery={alterQuery}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ export function useFilteredItemList<
|
|||
};
|
||||
}
|
||||
|
||||
interface IItemListProps<T extends QueryResult, E extends IHasID> {
|
||||
interface IItemListProps<T extends QueryResult, E extends IHasID, M = unknown> {
|
||||
view?: View;
|
||||
otherOperations?: IItemListOperation<T>[];
|
||||
renderContent: (
|
||||
|
|
@ -123,7 +123,7 @@ interface IItemListProps<T extends QueryResult, E extends IHasID> {
|
|||
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<T extends QueryResult, E extends IHasID> {
|
|||
renderToolbar?: (props: IFilteredListToolbar) => React.ReactNode;
|
||||
}
|
||||
|
||||
export const ItemList = <T extends QueryResult, E extends IHasID>(
|
||||
props: IItemListProps<T, E>
|
||||
export const ItemList = <T extends QueryResult, E extends IHasID, M = unknown>(
|
||||
props: IItemListProps<T, E, M>
|
||||
) => {
|
||||
const {
|
||||
view,
|
||||
|
|
@ -155,8 +155,8 @@ export const ItemList = <T extends QueryResult, E extends IHasID>(
|
|||
} = props;
|
||||
|
||||
const { filter, setFilter: updateFilter } = useFilter();
|
||||
const { effectiveFilter, result, cachedResult, totalCount } =
|
||||
useQueryResultContext<T, E>();
|
||||
const { effectiveFilter, result, metadataInfo, cachedResult, totalCount } =
|
||||
useQueryResultContext<T, E, M>();
|
||||
const listSelect = useListContext<E>();
|
||||
const {
|
||||
selectedIds,
|
||||
|
|
@ -174,8 +174,8 @@ export const ItemList = <T extends QueryResult, E extends IHasID>(
|
|||
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 = <T extends QueryResult, E extends IHasID>(
|
|||
);
|
||||
};
|
||||
|
||||
interface IItemListContextProps<T extends QueryResult, E extends IHasID> {
|
||||
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<T extends QueryResult, E extends IHasID> {
|
|||
|
||||
// Provides the contexts for the ItemList component. Includes functionality to scroll
|
||||
// to top on page change.
|
||||
export const ItemListContext = <T extends QueryResult, E extends IHasID>(
|
||||
props: PropsWithChildren<IItemListContextProps<T, E>>
|
||||
export const ItemListContext = <
|
||||
T extends QueryResult,
|
||||
E extends IHasID,
|
||||
M = unknown
|
||||
>(
|
||||
props: PropsWithChildren<IItemListContextProps<T, E, M>>
|
||||
) => {
|
||||
const {
|
||||
filterMode,
|
||||
defaultSort,
|
||||
defaultFilter: providedDefaultFilter,
|
||||
useResult,
|
||||
useMetadataInfo,
|
||||
getCount,
|
||||
getItems,
|
||||
view,
|
||||
|
|
@ -425,6 +435,7 @@ export const ItemListContext = <T extends QueryResult, E extends IHasID>(
|
|||
<QueryResultContext
|
||||
filterHook={filterHook}
|
||||
useResult={useResult}
|
||||
useMetadataInfo={useMetadataInfo}
|
||||
getCount={getCount}
|
||||
getItems={getItems}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -80,21 +80,25 @@ export function useListContextOptional<T extends IHasID = IHasID>() {
|
|||
|
||||
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<T, E> & {
|
||||
props: IQueryResultContextOptions<T, E, M> & {
|
||||
children?:
|
||||
| ((props: IQueryResultContextState<T, E>) => React.ReactNode)
|
||||
| ((props: IQueryResultContextState<T, E, M>) => 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<T, E> = {
|
||||
const state: IQueryResultContextState<T, E, M> = {
|
||||
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<T, E>;
|
||||
return context as IQueryResultContextState<T, E, M>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<GQL.FindImagesQuery>({
|
||||
query: GQL.FindImagesDocument,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue