Merge pull request #55 from AuraOfDivinity/fix/concurrent-update-requests-on-zoom-and-offset

fix: add request queue to handle concurrent zoom & offset requests
This commit is contained in:
Mohamed Marrouchi 2024-09-23 10:42:18 +01:00 committed by GitHub
commit 37f20f7d79
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 92 additions and 25 deletions

View File

@ -22,7 +22,6 @@ import {
Tab, Tab,
Tabs, Tabs,
Tooltip, Tooltip,
debounce,
tabsClasses, tabsClasses,
} from "@mui/material"; } from "@mui/material";
import { import {
@ -32,7 +31,13 @@ import {
DiagramModel, DiagramModel,
DiagramModelGenerics, DiagramModelGenerics,
} from "@projectstorm/react-diagrams"; } from "@projectstorm/react-diagrams";
import { SyntheticEvent, useEffect, useRef, useState } from "react"; import {
SyntheticEvent,
useCallback,
useEffect,
useRef,
useState,
} from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { DeleteDialog } from "@/app-components/dialogs"; import { DeleteDialog } from "@/app-components/dialogs";
@ -41,6 +46,7 @@ import { useDelete, useDeleteFromCache } from "@/hooks/crud/useDelete";
import { useFind } from "@/hooks/crud/useFind"; import { useFind } from "@/hooks/crud/useFind";
import { useGetFromCache } from "@/hooks/crud/useGet"; import { useGetFromCache } from "@/hooks/crud/useGet";
import { useUpdate, useUpdateCache } from "@/hooks/crud/useUpdate"; import { useUpdate, useUpdateCache } from "@/hooks/crud/useUpdate";
import useDebouncedUpdate from "@/hooks/useDebouncedUpdate";
import { getDisplayDialogs, useDialog } from "@/hooks/useDialog"; import { getDisplayDialogs, useDialog } from "@/hooks/useDialog";
import { useSearch } from "@/hooks/useSearch"; import { useSearch } from "@/hooks/useSearch";
import { EntityType, Format } from "@/services/types"; import { EntityType, Format } from "@/services/types";
@ -108,29 +114,36 @@ const Diagrams = () => {
const { mutateAsync: updateBlock } = useUpdate(EntityType.BLOCK, { const { mutateAsync: updateBlock } = useUpdate(EntityType.BLOCK, {
invalidate: false, invalidate: false,
}); });
const debouncedZoomEvent = debounce((event) => { const debouncedUpdateCategory = useDebouncedUpdate(updateCategory, 300);
if (selectedCategoryId) { const debouncedZoomEvent = useCallback(
engine?.repaintCanvas(); (event: any) => {
updateCategory({ if (selectedCategoryId) {
id: selectedCategoryId, engine?.repaintCanvas();
params: { debouncedUpdateCategory({
zoom: event.zoom, id: selectedCategoryId,
}, params: {
}); zoom: event.zoom,
} },
event.stopPropagation(); });
}, 200); }
const debouncedOffsetEvent = debounce((event) => { event.stopPropagation();
if (selectedCategoryId) { },
updateCategory({ [selectedCategoryId, engine, debouncedUpdateCategory],
id: selectedCategoryId, );
params: { const debouncedOffsetEvent = useCallback(
offset: [event.offsetX, event.offsetY], (event: any) => {
}, if (selectedCategoryId) {
}); debouncedUpdateCategory({
} id: selectedCategoryId,
event.stopPropagation(); params: {
}, 200); offset: [event.offsetX, event.offsetY],
},
});
}
event.stopPropagation();
},
[selectedCategoryId, debouncedUpdateCategory],
);
const getBlockFromCache = useGetFromCache(EntityType.BLOCK); const getBlockFromCache = useGetFromCache(EntityType.BLOCK);
const updateCachedBlock = useUpdateCache(EntityType.BLOCK); const updateCachedBlock = useUpdateCache(EntityType.BLOCK);
const deleteCachedBlock = useDeleteFromCache(EntityType.BLOCK); const deleteCachedBlock = useDeleteFromCache(EntityType.BLOCK);

View File

@ -0,0 +1,54 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
*/
import { debounce } from "@mui/material";
import { useCallback, useEffect, useRef } from "react";
type DebouncedUpdateParams = {
id: string;
params: Record<string, any>;
};
function useDebouncedUpdate(
apiUpdate: (params: DebouncedUpdateParams) => void,
delay: number = 300,
) {
const accumulatedUpdates = useRef<DebouncedUpdateParams | null>(null);
const processUpdates = useRef(
debounce(() => {
if (accumulatedUpdates.current) {
apiUpdate(accumulatedUpdates.current);
accumulatedUpdates.current = null;
}
}, delay),
).current;
const handleUpdate = useCallback(
(params: DebouncedUpdateParams) => {
accumulatedUpdates.current = {
id: params.id,
params: {
...(accumulatedUpdates.current?.params || {}),
...params.params,
},
};
processUpdates();
},
[processUpdates],
);
useEffect(() => {
return () => {
processUpdates.clear();
};
}, [processUpdates]);
return handleUpdate;
}
export default useDebouncedUpdate;