feat(ui): create debounced metadata/workflow query hooks

Also added config options for metadata and workflow debounce times (`metadataFetchDebounce` & `workflowFetchDebounce`).

Falls back to 0 if not provided.

In OSS, because we have no major latency concerns, the debounce is 0. But in other environments, it may be desirable to set this to something like 300ms.
This commit is contained in:
psychedelicious 2023-10-18 21:23:09 +11:00
parent 2faed653d7
commit f04462973b
7 changed files with 62 additions and 42 deletions

View File

@ -59,6 +59,8 @@ export type AppConfig = {
nodesAllowlist: string[] | undefined; nodesAllowlist: string[] | undefined;
nodesDenylist: string[] | undefined; nodesDenylist: string[] | undefined;
maxUpscalePixels?: number; maxUpscalePixels?: number;
metadataFetchDebounce?: number;
workflowFetchDebounce?: number;
sd: { sd: {
defaultModel?: string; defaultModel?: string;
disabledControlNetModels: string[]; disabledControlNetModels: string[];

View File

@ -38,15 +38,12 @@ import {
FaSeedling, FaSeedling,
} from 'react-icons/fa'; } from 'react-icons/fa';
import { FaCircleNodes, FaEllipsis } from 'react-icons/fa6'; import { FaCircleNodes, FaEllipsis } from 'react-icons/fa6';
import { import { useGetImageDTOQuery } from 'services/api/endpoints/images';
useGetImageDTOQuery, import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata';
useGetImageMetadataQuery, import { useDebouncedWorkflow } from 'services/api/hooks/useDebouncedWorkflow';
} from 'services/api/endpoints/images';
import { menuListMotionProps } from 'theme/components/menu'; import { menuListMotionProps } from 'theme/components/menu';
import { useDebounce } from 'use-debounce';
import { sentImageToImg2Img } from '../../store/actions'; import { sentImageToImg2Img } from '../../store/actions';
import SingleSelectionMenuItems from '../ImageContextMenu/SingleSelectionMenuItems'; import SingleSelectionMenuItems from '../ImageContextMenu/SingleSelectionMenuItems';
import { useGetWorkflowQuery } from 'services/api/endpoints/workflows';
const currentImageButtonsSelector = createSelector( const currentImageButtonsSelector = createSelector(
[stateSelector, activeTabNameSelector], [stateSelector, activeTabNameSelector],
@ -105,17 +102,12 @@ const CurrentImageButtons = () => {
lastSelectedImage?.image_name ?? skipToken lastSelectedImage?.image_name ?? skipToken
); );
const [debouncedImageName] = useDebounce(lastSelectedImage?.image_name, 300); const { metadata, isLoading: isLoadingMetadata } = useDebouncedMetadata(
const [debouncedWorkflowId] = useDebounce( lastSelectedImage?.image_name
lastSelectedImage?.workflow_id,
300
); );
const { data: metadata, isLoading: isLoadingMetadata } = const { workflow, isLoading: isLoadingWorkflow } = useDebouncedWorkflow(
useGetImageMetadataQuery(debouncedImageName ?? skipToken); lastSelectedImage?.workflow_id
const { data: workflow, isLoading: isLoadingWorkflow } = useGetWorkflowQuery(
debouncedWorkflowId ?? skipToken
); );
const handleLoadWorkflow = useCallback(() => { const handleLoadWorkflow = useCallback(() => {

View File

@ -1,6 +1,5 @@
import { Flex, MenuItem, Spinner } from '@chakra-ui/react'; import { Flex, MenuItem, Spinner } from '@chakra-ui/react';
import { useStore } from '@nanostores/react'; import { useStore } from '@nanostores/react';
import { skipToken } from '@reduxjs/toolkit/dist/query';
import { useAppToaster } from 'app/components/Toaster'; import { useAppToaster } from 'app/components/Toaster';
import { $customStarUI } from 'app/store/nanostores/customStarUI'; import { $customStarUI } from 'app/store/nanostores/customStarUI';
import { useAppDispatch } from 'app/store/storeHooks'; import { useAppDispatch } from 'app/store/storeHooks';
@ -33,13 +32,12 @@ import {
import { FaCircleNodes } from 'react-icons/fa6'; import { FaCircleNodes } from 'react-icons/fa6';
import { MdStar, MdStarBorder } from 'react-icons/md'; import { MdStar, MdStarBorder } from 'react-icons/md';
import { import {
useGetImageMetadataQuery,
useStarImagesMutation, useStarImagesMutation,
useUnstarImagesMutation, useUnstarImagesMutation,
} from 'services/api/endpoints/images'; } from 'services/api/endpoints/images';
import { useGetWorkflowQuery } from 'services/api/endpoints/workflows'; import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata';
import { useDebouncedWorkflow } from 'services/api/hooks/useDebouncedWorkflow';
import { ImageDTO } from 'services/api/types'; import { ImageDTO } from 'services/api/types';
import { useDebounce } from 'use-debounce';
import { sentImageToCanvas, sentImageToImg2Img } from '../../store/actions'; import { sentImageToCanvas, sentImageToImg2Img } from '../../store/actions';
type SingleSelectionMenuItemsProps = { type SingleSelectionMenuItemsProps = {
@ -57,14 +55,11 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
const isCanvasEnabled = useFeatureStatus('unifiedCanvas').isFeatureEnabled; const isCanvasEnabled = useFeatureStatus('unifiedCanvas').isFeatureEnabled;
const customStarUi = useStore($customStarUI); const customStarUi = useStore($customStarUI);
const [debouncedImageName] = useDebounce(imageDTO?.image_name, 300); const { metadata, isLoading: isLoadingMetadata } = useDebouncedMetadata(
const [debouncedWorkflowId] = useDebounce(imageDTO?.workflow_id, 300); imageDTO?.image_name
);
const { data: metadata, isLoading: isLoadingMetadata } = const { workflow, isLoading: isLoadingWorkflow } = useDebouncedWorkflow(
useGetImageMetadataQuery(debouncedImageName ?? skipToken); imageDTO?.workflow_id
const { data: workflow, isLoading: isLoadingWorkflow } = useGetWorkflowQuery(
debouncedWorkflowId ?? skipToken
); );
const [starImages] = useStarImagesMutation(); const [starImages] = useStarImagesMutation();

View File

@ -9,15 +9,13 @@ import {
Tabs, Tabs,
Text, Text,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { skipToken } from '@reduxjs/toolkit/dist/query';
import { IAINoContentFallback } from 'common/components/IAIImageFallback'; import { IAINoContentFallback } from 'common/components/IAIImageFallback';
import ScrollableContent from 'features/nodes/components/sidePanel/ScrollableContent'; import ScrollableContent from 'features/nodes/components/sidePanel/ScrollableContent';
import { memo } from 'react'; import { memo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useGetImageMetadataQuery } from 'services/api/endpoints/images'; import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata';
import { useGetWorkflowQuery } from 'services/api/endpoints/workflows'; import { useDebouncedWorkflow } from 'services/api/hooks/useDebouncedWorkflow';
import { ImageDTO } from 'services/api/types'; import { ImageDTO } from 'services/api/types';
import { useDebounce } from 'use-debounce';
import DataViewer from './DataViewer'; import DataViewer from './DataViewer';
import ImageMetadataActions from './ImageMetadataActions'; import ImageMetadataActions from './ImageMetadataActions';
@ -33,16 +31,8 @@ const ImageMetadataViewer = ({ image }: ImageMetadataViewerProps) => {
// }); // });
const { t } = useTranslation(); const { t } = useTranslation();
const [debouncedImageName] = useDebounce(image.image_name, 300); const { metadata } = useDebouncedMetadata(image.image_name);
const [debouncedWorkflowId] = useDebounce(image.workflow_id, 300); const { workflow } = useDebouncedWorkflow(image.workflow_id);
const { data: metadata } = useGetImageMetadataQuery(
debouncedImageName ?? skipToken
);
const { data: workflow } = useGetWorkflowQuery(
debouncedWorkflowId ?? skipToken
);
return ( return (
<Flex <Flex

View File

@ -7,7 +7,6 @@ export const workflowsApi = api.injectEndpoints({
endpoints: (build) => ({ endpoints: (build) => ({
getWorkflow: build.query<Workflow | undefined, string>({ getWorkflow: build.query<Workflow | undefined, string>({
query: (workflow_id) => `workflows/i/${workflow_id}`, query: (workflow_id) => `workflows/i/${workflow_id}`,
keepUnusedDataFor: 86400, // 24 hours
providesTags: (result, error, workflow_id) => [ providesTags: (result, error, workflow_id) => [
{ type: 'Workflow', id: workflow_id }, { type: 'Workflow', id: workflow_id },
], ],

View File

@ -0,0 +1,21 @@
import { skipToken } from '@reduxjs/toolkit/query';
import { useDebounce } from 'use-debounce';
import { useGetImageMetadataQuery } from '../endpoints/images';
import { useAppSelector } from 'app/store/storeHooks';
export const useDebouncedMetadata = (imageName?: string | null) => {
const metadataFetchDebounce = useAppSelector(
(state) => state.config.metadataFetchDebounce
);
const [debouncedImageName] = useDebounce(
imageName,
metadataFetchDebounce ?? 0
);
const { data: metadata, isLoading } = useGetImageMetadataQuery(
debouncedImageName ?? skipToken
);
return { metadata, isLoading };
};

View File

@ -0,0 +1,21 @@
import { skipToken } from '@reduxjs/toolkit/query';
import { useAppSelector } from 'app/store/storeHooks';
import { useDebounce } from 'use-debounce';
import { useGetWorkflowQuery } from '../endpoints/workflows';
export const useDebouncedWorkflow = (workflowId?: string | null) => {
const workflowFetchDebounce = useAppSelector(
(state) => state.config.workflowFetchDebounce
);
const [debouncedWorkflowID] = useDebounce(
workflowId,
workflowFetchDebounce ?? 0
);
const { data: workflow, isLoading } = useGetWorkflowQuery(
debouncedWorkflowID ?? skipToken
);
return { workflow, isLoading };
};