Fix OS switching not working on tree view UI

This commit resolves a rendering bug in the tree view component.
Previously, updating the tree collection prior to node updates led to
rendering errors due to the presence of non-existent nodes in the new
collection.

Changes:

- Implement manual control over the rendering process in tree view. This
  includes clearing the rendering queue and currently rendered nodes
  before updates, aligning the rendering process with the updated
  collection.
- Add Cypress E2E tests to test switching between all operating systems
  and script views, ensuring no uncaught errors and preventing
  regression.
- Replace hardcoded operating system lists in the download URL list view
  with a unified `getSupportedOsList()` method from the application,
  reducing duplication and simplifying future updates.
- Rename `initial-nodes` to `nodes` in `TreeView.vue` to reflect their
  mutable nature.
- Centralize the function for getting operating system names into
  `OperatingSystemNames.ts`, improving reusability in E2E tests.
This commit is contained in:
undergroundwires
2023-12-14 09:51:42 +01:00
parent fe3de498c8
commit 3457fe18cf
13 changed files with 285 additions and 76 deletions

View File

@@ -5,12 +5,19 @@ import { ReadOnlyTreeNode } from '../Node/TreeNode';
import { useNodeStateChangeAggregator } from '../UseNodeStateChangeAggregator';
import { TreeRoot } from '../TreeRoot/TreeRoot';
import { useCurrentTreeNodes } from '../UseCurrentTreeNodes';
import { QueryableNodes } from '../TreeRoot/NodeCollection/Query/QueryableNodes';
import { NodeRenderingStrategy } from './Scheduling/NodeRenderingStrategy';
import { DelayScheduler } from './DelayScheduler';
import { TimeoutDelayScheduler } from './Scheduling/TimeoutDelayScheduler';
import { RenderQueueOrderer } from './Ordering/RenderQueueOrderer';
import { CollapsedParentOrderer } from './Ordering/CollapsedParentOrderer';
export interface NodeRenderingControl {
readonly renderingStrategy: NodeRenderingStrategy;
clearRenderingStates(): void;
notifyRenderingUpdates(): void;
}
/**
* Renders tree nodes gradually to prevent UI freeze when loading large amounts of nodes.
*/
@@ -22,7 +29,7 @@ export function useGradualNodeRendering(
initialBatchSize = 30,
subsequentBatchSize = 5,
orderer: RenderQueueOrderer = new CollapsedParentOrderer(),
): NodeRenderingStrategy {
): NodeRenderingControl {
const nodesToRender = new Set<ReadOnlyTreeNode>();
const nodesBeingRendered = shallowRef(new Set<ReadOnlyTreeNode>());
let isRenderingInProgress = false;
@@ -31,6 +38,10 @@ export function useGradualNodeRendering(
const { onNodeStateChange } = useChangeAggregator(treeRootRef);
const { nodes } = useTreeNodes(treeRootRef);
function notifyRenderingUpdates() {
triggerRef(nodesBeingRendered);
}
function updateNodeRenderQueue(node: ReadOnlyTreeNode, isVisible: boolean) {
if (isVisible
&& !nodesToRender.has(node)
@@ -43,16 +54,20 @@ export function useGradualNodeRendering(
}
if (nodesBeingRendered.value.has(node)) {
nodesBeingRendered.value.delete(node);
triggerRef(nodesBeingRendered);
notifyRenderingUpdates();
}
}
}
watch(nodes, (newNodes) => {
function clearRenderingStates() {
nodesToRender.clear();
nodesBeingRendered.value.clear();
}
function initializeAndRenderNodes(newNodes: QueryableNodes) {
clearRenderingStates();
if (!newNodes || newNodes.flattenedNodes.length === 0) {
triggerRef(nodesBeingRendered);
notifyRenderingUpdates();
return;
}
newNodes
@@ -60,6 +75,10 @@ export function useGradualNodeRendering(
.filter((node) => node.state.current.isVisible)
.forEach((node) => nodesToRender.add(node));
beginRendering();
}
watch(nodes, (newNodes) => {
initializeAndRenderNodes(newNodes);
}, { immediate: true });
onNodeStateChange((change) => {
@@ -91,7 +110,7 @@ export function useGradualNodeRendering(
nodesToRender.delete(node);
nodesBeingRendered.value.add(node);
});
triggerRef(nodesBeingRendered);
notifyRenderingUpdates();
scheduler.scheduleNext(
() => renderNextBatch(subsequentBatchSize),
renderingDelayInMs,
@@ -103,6 +122,10 @@ export function useGradualNodeRendering(
}
return {
shouldRender: shouldNodeBeRendered,
renderingStrategy: {
shouldRender: shouldNodeBeRendered,
},
clearRenderingStates,
notifyRenderingUpdates,
};
}