From b0a67cb87d8532f743d26ce45cb737da16a35059 Mon Sep 17 00:00:00 2001 From: roluk <70327485+roluk@users.noreply.github.com> Date: Fri, 22 Nov 2024 14:34:44 +0200 Subject: [PATCH 01/21] Organise imports --- .vscode/settings.json | 7 ++++ packages/changed-elements-react/src/index.ts | 25 +++++++---- .../src/widgets/ChangedElementsWidget.tsx | 29 ++++++++----- .../common/versionCompareToasts.ts | 15 ++++--- .../common/versionCompareV2WidgetUtils.ts | 17 ++++---- .../VersionCompareManageNamedVersions.tsx | 3 +- .../VersionCompareSelectComponent.tsx | 12 +++--- .../components/VersionCompareSelectModal.tsx | 41 +++++++++++-------- .../VersionCompareSelectorInner.tsx | 10 ++--- .../components/VersionEntries.tsx | 12 +++--- .../components/VersionList.tsx | 10 ++--- .../hooks/useNamedVersionLoader.tsx | 17 ++++---- 12 files changed, 119 insertions(+), 79 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 541f6f77..087cbae5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -23,6 +23,13 @@ "typescript.format.semicolons": "insert", "typescript.preferences.importModuleSpecifierEnding": "js", + "typescript.preferences.preferTypeOnlyAutoImports": true, + "typescript.preferences.organizeImports": { + "caseFirst": "lower", + "caseSensitivity": "caseSensitive", + "numericCollation": true, + "typeOrder": "last", + }, "typescript.reportStyleChecksAsWarnings": true, "[javascript][typescript][typescriptreact]": { diff --git a/packages/changed-elements-react/src/index.ts b/packages/changed-elements-react/src/index.ts index 63044185..9a9d2e09 100644 --- a/packages/changed-elements-react/src/index.ts +++ b/packages/changed-elements-react/src/index.ts @@ -2,8 +2,6 @@ * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -export { type FilterData, type FilterOptions, type SavedFiltersManager } from "./SavedFiltersManager.js"; -export { VersionCompareContext, type VersionCompareContextValue } from "./VersionCompareContext.js"; export { type ChangedElementEntry } from "./api/ChangedElementEntryCache.js"; export * from "./api/ChangedElementsApiClient.js"; export * from "./api/ChangedElementsClientBase.js"; @@ -16,17 +14,26 @@ export { VersionCompareManager } from "./api/VersionCompareManager.js"; export * from "./api/VersionCompareTiles.js"; export * from "./api/VersionCompareVisualization.js"; export type { MainVisualizationOptions, VisualizationHandler } from "./api/VisualizationHandler.js"; +export { + ComparisonJobClient, type ComparisonJobClientParams +} from "./clients/ComparisonJobClient.js"; +export type { + ComparisonJob, ComparisonJobCompleted, ComparisonJobFailed, ComparisonJobQueued, + ComparisonJobStarted +} from "./clients/IComparisonJobClient.js"; export type { - Changeset, GetChangesetsParams, GetNamedVersionsParams, IModelsClient, NamedVersion, + Changeset, GetChangesetsParams, GetNamedVersionsParams, IModelsClient, NamedVersion } from "./clients/iModelsClient.js"; export { ITwinIModelsClient, type ITwinIModelsClientParams } from "./clients/iTwinIModelsClient.js"; -export { ComparisonJobClient, type ComparisonJobClientParams } from "./clients/ComparisonJobClient.js"; export * from "./contentviews/PropertyComparisonTable.js"; +export type { FilterData, FilterOptions, SavedFiltersManager } from "./SavedFiltersManager.js"; +export { VersionCompareContext, type VersionCompareContextValue } from "./VersionCompareContext.js"; export * from "./widgets/ChangedElementsWidget.js"; +export * from "./widgets/comparisonJobWidget/common/versionCompareToasts.js"; +export * from "./widgets/comparisonJobWidget/components/VersionCompareDialogProvider.js"; +export * from "./widgets/comparisonJobWidget/components/VersionCompareSelectModal.js"; +export type { + JobAndNamedVersions +} from "./widgets/comparisonJobWidget/models/ComparisonJobModels.js"; export { ChangedElementsListComponent } from "./widgets/EnhancedElementsInspector.js"; export * from "./widgets/VersionCompareSelectWidget.js"; -export * from "./widgets/comparisonJobWidget/components/VersionCompareSelectModal.js" -export * from "./widgets/comparisonJobWidget/components/VersionCompareDialogProvider.js" -export * from "./widgets/comparisonJobWidget/common/versionCompareToasts.js" -export type {JobAndNamedVersions} from "./widgets/comparisonJobWidget/models/ComparisonJobModels.js" -export type {ComparisonJob, ComparisonJobCompleted, ComparisonJobFailed, ComparisonJobQueued, ComparisonJobStarted} from "./clients/IComparisonJobClient.js"; diff --git a/packages/changed-elements-react/src/widgets/ChangedElementsWidget.tsx b/packages/changed-elements-react/src/widgets/ChangedElementsWidget.tsx index beb6e813..81c55d04 100644 --- a/packages/changed-elements-react/src/widgets/ChangedElementsWidget.tsx +++ b/packages/changed-elements-react/src/widgets/ChangedElementsWidget.tsx @@ -4,30 +4,37 @@ *--------------------------------------------------------------------------------------------*/ import { BeEvent, Logger, type Id64String } from "@itwin/core-bentley"; import { - IModelApp, IModelConnection, NotifyMessageDetails, OutputMessagePriority, ScreenViewport + IModelApp, NotifyMessageDetails, OutputMessagePriority, type IModelConnection, + type ScreenViewport, } from "@itwin/core-frontend"; import { SvgAdd, SvgCompare, SvgExport, SvgStop } from "@itwin/itwinui-icons-react"; import { IconButton, ProgressRadial } from "@itwin/itwinui-react"; -import { Component, ReactElement, ReactNode } from "react"; -import { FilterOptions } from "../SavedFiltersManager.js"; -import { type ChangedElementEntry } from "../api/ChangedElementEntryCache.js"; -import { ReportProperty } from "../api/ReportGenerator.js"; +import { Component, type ReactElement, type ReactNode } from "react"; + +import type { FilterOptions } from "../SavedFiltersManager.js"; +import type { ChangedElementEntry } from "../api/ChangedElementEntryCache.js"; +import type { ReportProperty } from "../api/ReportGenerator.js"; import { VersionCompareUtils, VersionCompareVerboseMessages } from "../api/VerboseMessages.js"; import { VersionCompare } from "../api/VersionCompare.js"; -import { VersionCompareManager } from "../api/VersionCompareManager.js"; +import type { VersionCompareManager } from "../api/VersionCompareManager.js"; import { CenteredDiv } from "../common/CenteredDiv.js"; import { EmptyStateComponent } from "../common/EmptyStateComponent.js"; import { Widget as WidgetComponent } from "../common/Widget/Widget.js"; import { PropertyLabelCache } from "../dialogs/PropertyLabelCache.js"; import { ReportGeneratorDialog } from "../dialogs/ReportGeneratorDialog.js"; import { ChangedElementsInspector } from "./EnhancedElementsInspector.js"; -import "./ChangedElementsWidget.scss"; -import InfoButton from "./InformationButton.js"; -import { VersionCompareSelectDialogV2 } from "./comparisonJobWidget/components/VersionCompareSelectModal.js"; import { FeedbackButton } from "./FeedbackButton.js"; +import InfoButton from "./InformationButton.js"; import { VersionCompareSelectDialog } from "./VersionCompareSelectWidget.js"; -import { ComparisonJobUpdateType, VersionCompareSelectProviderV2 } from "./comparisonJobWidget/components/VersionCompareDialogProvider.js"; -import { JobAndNamedVersions } from "./comparisonJobWidget/models/ComparisonJobModels.js"; +import { + VersionCompareSelectProviderV2, type ComparisonJobUpdateType, +} from "./comparisonJobWidget/components/VersionCompareDialogProvider.js"; +import { + VersionCompareSelectDialogV2 +} from "./comparisonJobWidget/components/VersionCompareSelectModal.js"; +import type { JobAndNamedVersions } from "./comparisonJobWidget/models/ComparisonJobModels.js"; + +import "./ChangedElementsWidget.scss"; export const changedElementsWidgetAttachToViewportEvent = new BeEvent<(vp: ScreenViewport) => void>(); diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareToasts.ts b/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareToasts.ts index 7d54cee7..d47506d6 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareToasts.ts +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareToasts.ts @@ -2,13 +2,18 @@ * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { IModelApp, IModelConnection, NotifyMessageDetails, OutputMessagePriority, OutputMessageType } from "@itwin/core-frontend"; -import { IModelsClient, NamedVersion } from "../../../clients/iModelsClient"; +import { + IModelApp, NotifyMessageDetails, OutputMessagePriority, OutputMessageType, type IModelConnection, +} from "@itwin/core-frontend"; import { toaster } from "@itwin/itwinui-react"; + +import type { + ComparisonJobCompleted, IComparisonJobClient, +} from "../../../clients/IComparisonJobClient"; +import type { IModelsClient, NamedVersion } from "../../../clients/iModelsClient"; +import type { ComparisonJobUpdateType } from "../components/VersionCompareDialogProvider"; +import type { JobAndNamedVersions } from "../models/ComparisonJobModels"; import { runManagerStartComparisonV2 } from "./versionCompareV2WidgetUtils"; -import { ComparisonJobCompleted, IComparisonJobClient } from "../../../clients/IComparisonJobClient"; -import { ComparisonJobUpdateType } from "../components/VersionCompareDialogProvider"; -import { JobAndNamedVersions } from "../models/ComparisonJobModels"; /** Toast Comparison Job Processing. * Outputs toast message following the pattern: diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareV2WidgetUtils.ts b/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareV2WidgetUtils.ts index 0dc5ef5a..8eeaec19 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareV2WidgetUtils.ts +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareV2WidgetUtils.ts @@ -2,15 +2,18 @@ * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { IModelConnection } from "@itwin/core-frontend"; -import { ComparisonJobCompleted, ComparisonJobStarted, IComparisonJobClient } from "../../../clients/IComparisonJobClient"; -import { IModelsClient, NamedVersion } from "../../../clients/iModelsClient"; +import { Logger } from "@itwin/core-bentley"; +import type { IModelConnection } from "@itwin/core-frontend"; + import { VersionCompare } from "../../../api/VersionCompare"; +import type { + ComparisonJobCompleted, ComparisonJobStarted, IComparisonJobClient, +} from "../../../clients/IComparisonJobClient"; +import type { IModelsClient, NamedVersion } from "../../../clients/iModelsClient"; +import type { ComparisonJobUpdateType } from "../components/VersionCompareDialogProvider"; +import type { JobAndNamedVersions, JobStatusAndJobProgress } from "../models/ComparisonJobModels"; +import type { VersionState } from "../models/VersionState"; import { toastComparisonVisualizationStarting } from "./versionCompareToasts"; -import { Logger } from "@itwin/core-bentley"; -import { JobAndNamedVersions, JobStatusAndJobProgress } from "../models/ComparisonJobModels"; -import { VersionState } from "../models/VersionState"; -import { ComparisonJobUpdateType } from "../components/VersionCompareDialogProvider"; export type ManagerStartComparisonV2Args = { comparisonJob: ComparisonJobCompleted; diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareManageNamedVersions.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareManageNamedVersions.tsx index dbbf1a56..71fcad08 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareManageNamedVersions.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareManageNamedVersions.tsx @@ -2,8 +2,7 @@ * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { ReactNode } from "react"; -import "./styles/ComparisonJobWidget.scss"; +import type { ReactNode } from "react"; interface ManageNamedVersionsProps { children: ReactNode; diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectComponent.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectComponent.tsx index f854a2fd..40c533a4 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectComponent.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectComponent.tsx @@ -2,14 +2,14 @@ * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { IModelConnection } from "@itwin/core-frontend"; -import { ReactNode, useState } from "react"; +import type { IModelConnection } from "@itwin/core-frontend"; import { ProgressRadial } from "@itwin/itwinui-react"; +import { useState, type ReactNode } from "react"; + +import type { ChangesetChunk } from "../../../api/ChangedElementsApiClient"; +import type { NamedVersion } from "../../../clients/iModelsClient"; +import type { CurrentNamedVersionAndNamedVersions } from "../models/NamedVersions"; import { VersionCompareSelectorInner } from "./VersionCompareSelectorInner"; -import { CurrentNamedVersionAndNamedVersions } from "../models/NamedVersions"; -import { NamedVersion } from "../../../clients/iModelsClient"; -import { ChangesetChunk } from "../../../api/ChangedElementsApiClient"; -import "./styles/ComparisonJobWidget.scss"; /** Options for VersionCompareSelectComponent. */ export interface VersionCompareSelectorProps { diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx index 2e2016af..574e5258 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx @@ -2,25 +2,34 @@ * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { Modal, ModalContent, ModalButtonBar, Button } from "@itwin/itwinui-react"; -import { ReactNode, useEffect, useState } from "react"; -import { IModelApp, IModelConnection } from "@itwin/core-frontend"; -import React from "react"; -import { VersionCompareSelectComponent } from "./VersionCompareSelectComponent"; -import { NamedVersionLoaderState, useNamedVersionLoader } from "../hooks/useNamedVersionLoader"; -import { IComparisonJobClient, ComparisonJob, ComparisonJobCompleted } from "../../../clients/IComparisonJobClient"; -import { useVersionCompare } from "../../../VersionCompareContext"; +import { IModelApp, type IModelConnection } from "@itwin/core-frontend"; +import { Button, Modal, ModalButtonBar, ModalContent } from "@itwin/itwinui-react"; +import { useContext, useEffect, useState, type ReactNode, } from "react"; + import { VersionCompareUtils, VersionCompareVerboseMessages } from "../../../api/VerboseMessages"; -import { IModelsClient, NamedVersion } from "../../../clients/iModelsClient"; import { VersionCompare } from "../../../api/VersionCompare"; -import "./styles/ComparisonJobWidget.scss"; +import { + ComparisonJob, ComparisonJobCompleted, IComparisonJobClient, +} from "../../../clients/IComparisonJobClient"; +import type { IModelsClient, NamedVersion } from "../../../clients/iModelsClient"; import { arrayToMap, tryXTimes } from "../../../utils/utils"; -import { VersionState } from "../models/VersionState"; -import { JobAndNamedVersions, JobStatusAndJobProgress } from "../models/ComparisonJobModels"; +import { useVersionCompare } from "../../../VersionCompareContext"; +import { + toastComparisonJobComplete, toastComparisonJobError, toastComparisonJobProcessing, +} from "../common/versionCompareToasts"; +import { + createJobId, getJobStatusAndJobProgress, runManagerStartComparisonV2, +} from "../common/versionCompareV2WidgetUtils"; +import { + useNamedVersionLoader, type NamedVersionLoaderState, +} from "../hooks/useNamedVersionLoader"; +import type { JobAndNamedVersions, JobStatusAndJobProgress } from "../models/ComparisonJobModels"; import { VersionProcessedState } from "../models/VersionProcessedState"; -import { toastComparisonJobComplete, toastComparisonJobError, toastComparisonJobProcessing } from "../common/versionCompareToasts"; -import { createJobId, getJobStatusAndJobProgress, runManagerStartComparisonV2 } from "../common/versionCompareV2WidgetUtils"; -import { ComparisonJobUpdateType, V2DialogContext } from "./VersionCompareDialogProvider"; +import type { VersionState } from "../models/VersionState"; +import { V2DialogContext, type ComparisonJobUpdateType } from "./VersionCompareDialogProvider"; +import { VersionCompareSelectComponent } from "./VersionCompareSelectComponent"; + +import "./styles/ComparisonJobWidget.scss"; /** Options for VersionCompareSelectDialogV2. */ export interface VersionCompareSelectDialogV2Props { @@ -59,7 +68,7 @@ export function VersionCompareSelectDialogV2(props: VersionCompareSelectDialogV2 throw new Error("V1 Client Is Not Initialized In Given Context."); } const { openDialog, closedDialog, getDialogOpen, addRunningJob, removeRunningJob, getRunningJobs - , getPendingJobs, removePendingJob, addPendingJob, getToastsEnabled, runOnJobUpdate } = React.useContext(V2DialogContext); + , getPendingJobs, removePendingJob, addPendingJob, getToastsEnabled, runOnJobUpdate } = useContext(V2DialogContext); const [targetVersion, setTargetVersion] = useState(undefined); const [currentVersion, setCurrentVersion] = useState(undefined); const [result, setResult] = useState(); diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectorInner.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectorInner.tsx index ce716568..063e09bf 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectorInner.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectorInner.tsx @@ -5,12 +5,12 @@ import { IModelApp } from "@itwin/core-frontend"; import { Text } from "@itwin/itwinui-react"; import { ReactNode } from "react"; -import { VersionList } from "./VersionList"; -import { CurrentVersionEntry } from "./VersionEntries"; -import { VersionState } from "../models/VersionState"; -import { NamedVersion } from "../../../clients/iModelsClient"; -import "./styles/ComparisonJobWidget.scss"; + +import type { NamedVersion } from "../../../clients/iModelsClient"; +import type { VersionState } from "../models/VersionState"; import { ManageNamedVersions } from "./VersionCompareManageNamedVersions"; +import { CurrentVersionEntry } from "./VersionEntries"; +import { VersionList } from "./VersionList"; interface VersionCompareSelectorInnerProps { entries: VersionState[]; diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionEntries.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionEntries.tsx index 78803fe4..9d911467 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionEntries.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionEntries.tsx @@ -2,14 +2,14 @@ * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { ReactElement, ReactNode } from "react"; -import { ProgressLinear, Radio, Badge, Text } from "@itwin/itwinui-react"; import { IModelApp } from "@itwin/core-frontend"; -import { JobStatus, JobProgress } from "../models/ComparisonJobModels"; +import { Badge, ProgressLinear, Radio, Text } from "@itwin/itwinui-react"; +import type { ReactElement, ReactNode } from "react"; + +import type { NamedVersion } from "../../../clients/iModelsClient"; +import type { JobProgress, JobStatus } from "../models/ComparisonJobModels"; import { VersionProcessedState } from "../models/VersionProcessedState"; -import { NamedVersion } from "../../../clients/iModelsClient"; -import { VersionState } from "../models/VersionState"; -import "./styles/ComparisonJobWidget.scss"; +import type { VersionState } from "../models/VersionState"; interface CurrentVersionEntryProps { versionState: VersionState; diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionList.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionList.tsx index 2b54d501..fd27ca8c 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionList.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionList.tsx @@ -3,12 +3,12 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import { IModelApp } from "@itwin/core-frontend"; -import { ReactElement } from "react"; -import { VersionListEntry } from "./VersionEntries"; -import { VersionState } from "../models/VersionState"; -import { NamedVersion } from "../../../clients/iModelsClient"; -import "./styles/ComparisonJobWidget.scss"; import { LoadingSpinner } from "@itwin/core-react"; +import type { ReactElement } from "react"; + +import type { NamedVersion } from "../../../clients/iModelsClient"; +import type { VersionState } from "../models/VersionState"; +import { VersionListEntry } from "./VersionEntries"; interface VersionListProps { entries: VersionState[]; diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/hooks/useNamedVersionLoader.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/hooks/useNamedVersionLoader.tsx index 9bd0ca11..daf6846c 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/hooks/useNamedVersionLoader.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/hooks/useNamedVersionLoader.tsx @@ -2,15 +2,18 @@ * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { IModelApp, IModelConnection } from "@itwin/core-frontend"; +import { IModelApp, type IModelConnection } from "@itwin/core-frontend"; import { useEffect, useState } from "react"; -import { JobStatus, JobProgress, JobStatusAndJobProgress, JobAndNamedVersions } from "../models/ComparisonJobModels"; -import { VersionProcessedState } from "../models/VersionProcessedState"; -import { CurrentNamedVersionAndNamedVersions } from "../models/NamedVersions"; -import { IComparisonJobClient } from "../../../clients/IComparisonJobClient"; -import { IModelsClient, NamedVersion } from "../../../clients/iModelsClient"; -import { createJobId, getJobStatusAndJobProgress } from "../common/versionCompareV2WidgetUtils"; + +import type { IComparisonJobClient } from "../../../clients/IComparisonJobClient"; +import type { IModelsClient, NamedVersion } from "../../../clients/iModelsClient"; import { arrayToMap } from "../../../utils/utils"; +import { createJobId, getJobStatusAndJobProgress } from "../common/versionCompareV2WidgetUtils"; +import type { + JobAndNamedVersions, JobProgress, JobStatus, JobStatusAndJobProgress, +} from "../models/ComparisonJobModels"; +import type { CurrentNamedVersionAndNamedVersions } from "../models/NamedVersions"; +import { VersionProcessedState } from "../models/VersionProcessedState"; /** * Result type for versionLoader. From 7592a21c04ca20d5ace64a87cc9925d88f67c76d Mon Sep 17 00:00:00 2001 From: roluk <70327485+roluk@users.noreply.github.com> Date: Wed, 20 Nov 2024 15:55:30 +0200 Subject: [PATCH 02/21] Adjust whitespace and comments --- .../common/versionCompareV2WidgetUtils.ts | 45 ++- .../VersionCompareDialogProvider.tsx | 94 +++--- .../VersionCompareSelectComponent.tsx | 46 +-- .../components/VersionCompareSelectModal.tsx | 296 +++++++++++------- .../components/VersionEntries.tsx | 85 +++-- .../components/VersionList.tsx | 35 ++- .../hooks/useNamedVersionLoader.tsx | 188 +++++++---- 7 files changed, 518 insertions(+), 271 deletions(-) diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareV2WidgetUtils.ts b/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareV2WidgetUtils.ts index 8eeaec19..5f7c3ca2 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareV2WidgetUtils.ts +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareV2WidgetUtils.ts @@ -22,7 +22,10 @@ export type ManagerStartComparisonV2Args = { targetVersion: NamedVersion; currentVersion: NamedVersion; getToastsEnabled?: () => boolean; - runOnJobUpdate?: (comparisonEventType: ComparisonJobUpdateType, jobAndNamedVersions?: JobAndNamedVersions) => Promise; + runOnJobUpdate?: ( + comparisonEventType: ComparisonJobUpdateType, + jobAndNamedVersions?: JobAndNamedVersions, + ) => Promise; iModelsClient: IModelsClient; }; @@ -30,6 +33,7 @@ export const runManagerStartComparisonV2 = async (args: ManagerStartComparisonV2 if (VersionCompare.manager?.isComparing) { await VersionCompare.manager?.stopComparison(); } + if (args.getToastsEnabled?.()) { toastComparisonVisualizationStarting(); } @@ -42,29 +46,41 @@ export const runManagerStartComparisonV2 = async (args: ManagerStartComparisonV2 if (args.runOnJobUpdate) { void args.runOnJobUpdate("ComparisonVisualizationStarting", jobAndNamedVersion); } + const changedElements = await args.comparisonJobClient.getComparisonJobResult(args.comparisonJob); VersionCompare.manager?.startComparisonV2( args.iModelConnection, args.currentVersion, await updateTargetVersion(args.iModelConnection, args.targetVersion, args.iModelsClient), - [changedElements.changedElements]).catch((e) => { - Logger.logError(VersionCompare.logCategory, "Could not start version comparison: " + e); - }); + [changedElements.changedElements], + ).catch((e) => { + Logger.logError(VersionCompare.logCategory, "Could not start version comparison: " + e); + }); }; -const updateTargetVersion = async (iModelConnection: IModelConnection, targetVersion: NamedVersion, iModelsClient: IModelsClient) => { - // we need to update the changesetId and index of the target version. - // earlier we updated all named versions to have an offset of 1, so we undo this offset to get the proper results from any VersionCompare.manager?.startComparisonV2 calls - // on this target version - // the change elements API requires an offset, but the IModels API does not. +const updateTargetVersion = async ( + iModelConnection: IModelConnection, + targetVersion: NamedVersion, + iModelsClient: IModelsClient, +) => { + // We need to update the changesetId and index of the target version. Earlier + // we updated all named versions to have an offset of 1, so we undo this offset + // to get the proper results from any VersionCompare.manager?.startComparisonV2 + // calls on this target version. The change elements API requires an offset, but + // the IModels API does not. const iModelId = iModelConnection?.iModelId as string; const updatedTargetVersion = { ...targetVersion }; updatedTargetVersion.changesetIndex = targetVersion.changesetIndex - 1; - const changeSets = await iModelsClient.getChangesets({ iModelId }).then((changesets) => changesets.slice().reverse()); - const actualChangeSet = changeSets.find((changeset) => updatedTargetVersion.changesetIndex === changeset.index); + const changeSets = await iModelsClient + .getChangesets({ iModelId }) + .then((changesets) => changesets.slice().reverse()); + const actualChangeSet = changeSets.find( + (changeset) => updatedTargetVersion.changesetIndex === changeset.index, + ); if (actualChangeSet) { updatedTargetVersion.changesetId = actualChangeSet.id; } + return updatedTargetVersion; }; @@ -76,7 +92,9 @@ export type GetJobStatusAndJobProgress = { currentChangesetId: string; }; -export const getJobStatusAndJobProgress = async (args: GetJobStatusAndJobProgress): Promise => { +export const getJobStatusAndJobProgress = async ( + args: GetJobStatusAndJobProgress, +): Promise => { try { const res = await args.comparisonJobClient.getComparisonJob({ iTwinId: args.iTwinId, @@ -94,6 +112,7 @@ export const getJobStatusAndJobProgress = async (args: GetJobStatusAndJobProgres }, }; } + case "Queued": { return { jobStatus: "Queued", @@ -103,6 +122,7 @@ export const getJobStatusAndJobProgress = async (args: GetJobStatusAndJobProgres }, }; } + case "Started": { const progressingJob = res as ComparisonJobStarted; return { @@ -113,6 +133,7 @@ export const getJobStatusAndJobProgress = async (args: GetJobStatusAndJobProgres }, }; } + case "Failed": return { jobStatus: "Error", diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareDialogProvider.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareDialogProvider.tsx index 04cc7a16..feda217b 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareDialogProvider.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareDialogProvider.tsx @@ -5,12 +5,13 @@ import React from "react"; import { JobAndNamedVersions } from "../models/ComparisonJobModels"; -/** Comparison Job Update Type -* - "JobComplete" = job is completed -* - "JobError" = job error -* - "JobProcessing" = job is started -* - "ComparisonVisualizationStarting" = version compare visualization is starting -*/ +/** + * Comparison Job Update Type + * - "JobComplete" = job is completed + * - "JobError" = job error + * - "JobProcessing" = job is started + * - "ComparisonVisualizationStarting" = version compare visualization is starting + */ export type ComparisonJobUpdateType = "JobComplete" | "JobError" | "JobProcessing" | "ComparisonVisualizationStarting"; export type V2Context = { @@ -24,49 +25,69 @@ export type V2Context = { addPendingJob: (jobId: string, comparisonJob: JobAndNamedVersions) => void; removePendingJob: (jobId: string) => void; getToastsEnabled: () => boolean; - runOnJobUpdate: (comparisonJobUpdateType: ComparisonJobUpdateType, jobAndNamedVersions?: JobAndNamedVersions) => Promise; + runOnJobUpdate: ( + comparisonJobUpdateType: ComparisonJobUpdateType, + jobAndNamedVersions?: JobAndNamedVersions, + ) => Promise; }; export const V2DialogContext = React.createContext({} as V2Context); export type V2DialogProviderProps = { children: React.ReactNode; - // Optional. When enabled will toast messages regarding job status. If not defined will default to false and will not show toasts. + + /** + * Optional. When enabled will toast messages regarding job status. If not defined, + * will default to false and will not show toasts. + */ enableComparisonJobUpdateToasts?: boolean; - /** On Job Update - * Optional. a call back function for handling job updates. - * @param comparisonJobUpdateType param for the type of update: - * - "JobComplete" = invoked when job is completed - * - "JobError" = invoked on job error - * - "JobProcessing" = invoked on job is started - * - "ComparisonVisualizationStarting" = invoked on when version compare visualization is starting - * @param jobAndNamedVersion param contain job and named version info to be passed to call back -*/ - onJobUpdate?: (comparisonJobUpdateType: ComparisonJobUpdateType, jobAndNamedVersions?: JobAndNamedVersions) => Promise; + + /** + * Optional. A call back function for handling job updates. + * @param comparisonJobUpdateType param for the type of update: + * - "JobComplete" = invoked when job is completed + * - "JobError" = invoked on job error + * - "JobProcessing" = invoked on job is started + * - "ComparisonVisualizationStarting" = invoked on when version compare visualization is starting + * @param jobAndNamedVersion param contain job and named version info to be passed to call back + */ + onJobUpdate?: ( + comparisonJobUpdateType: ComparisonJobUpdateType, + jobAndNamedVersions?: JobAndNamedVersions, + ) => Promise; }; -/** V2DialogProvider use comparison jobs for processing. - * Used for tracking if the dialog is open or closed. - * This is useful for managing toast messages associated with dialog. - * Also caches comparison jobs that are pending creation or are currently running. To help populate new modal ref. - * Example: - * - *{(isOpenCondition) && - * } - * -*/ -export function VersionCompareSelectProviderV2({ children, enableComparisonJobUpdateToasts, onJobUpdate }: V2DialogProviderProps) { +/** + * V2DialogProvider use comparison jobs for processing. Used for tracking if the + * dialog is open or closed. This is useful for managing toast messages associated + * with dialog. Also caches comparison jobs that are pending creation or are currently + * running. To help populate new modal ref. + * + * @exmaple + * + * { + * isOpenCondition && + * + * } + * + */ +export function VersionCompareSelectProviderV2( + { children, enableComparisonJobUpdateToasts, onJobUpdate }: V2DialogProviderProps, +) { const dialogRunningJobs = React.useRef>(new Map()); const dialogPendingJobs = React.useRef>(new Map()); const addRunningJob = (jobId: string, jobAndNamedVersions: JobAndNamedVersions) => { - dialogRunningJobs.current.set(jobId, { + dialogRunningJobs.current.set( + jobId, + { comparisonJob: jobAndNamedVersions.comparisonJob, targetNamedVersion: jobAndNamedVersions.targetNamedVersion, currentNamedVersion: jobAndNamedVersions.currentNamedVersion, - }); + }, + ); }; const removeRunningJob = (jobId: string) => { dialogRunningJobs.current.delete(jobId); @@ -100,7 +121,10 @@ export function VersionCompareSelectProviderV2({ children, enableComparisonJobUp const getToastsEnabled = () => { return enableComparisonJobUpdateToasts ?? false; }; - const runOnJobUpdate = async (comparisonEventType: ComparisonJobUpdateType, jobAndNamedVersions?: JobAndNamedVersions) => { + const runOnJobUpdate = async ( + comparisonEventType: ComparisonJobUpdateType, + jobAndNamedVersions?: JobAndNamedVersions, + ) => { if (onJobUpdate) { void onJobUpdate(comparisonEventType, jobAndNamedVersions); } diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectComponent.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectComponent.tsx index 40c533a4..9b0a0d71 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectComponent.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectComponent.tsx @@ -17,7 +17,11 @@ export interface VersionCompareSelectorProps { iModelConnection: IModelConnection; /** Optional handler for when a version is selected. */ - onVersionSelected: (currentVersion: NamedVersion, targetVersion: NamedVersion, chunks?: ChangesetChunk[]) => void; + onVersionSelected: ( + currentVersion: NamedVersion, + targetVersion: NamedVersion, + chunks?: ChangesetChunk[], + ) => void; /** Whether to show a title for the component or not. */ wantTitle?: boolean; @@ -25,38 +29,38 @@ export interface VersionCompareSelectorProps { /** Named Versions to be displayed */ namedVersions: CurrentNamedVersionAndNamedVersions | undefined; - /** Optional prop for a user supplied component to handle managing named versions.*/ + /** Optional prop for a user supplied component to handle managing named versions. */ manageNamedVersionsSlot?: ReactNode | undefined; - /** If true display loading spinner to indicate we are receiving more named versions*/ + /** If true display loading spinner to indicate we are receiving more named versions. */ isLoading: boolean; } -/** - * Component that lets the user select which named version to compare to. - */ +/** Component that lets the user select which named version to compare to. */ export function VersionCompareSelectComponent(props: VersionCompareSelectorProps) { const [targetVersion, setTargetVersion] = useState(); const handleVersionClicked = (targetVersion: NamedVersion) => { setTargetVersion(targetVersion); if (props.namedVersions && props.namedVersions.currentVersion) { - props.onVersionSelected?.( - props.namedVersions.currentVersion.version, - targetVersion, - ); + props.onVersionSelected?.(props.namedVersions.currentVersion.version, targetVersion); } }; - return props.namedVersions ? :
- -
; + return props.namedVersions + ? ( + + ) : ( +
+ +
+ ); } diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx index 574e5258..02db2006 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx @@ -35,28 +35,36 @@ import "./styles/ComparisonJobWidget.scss"; export interface VersionCompareSelectDialogV2Props { /** IModel Connection that is being visualized. */ iModelConnection: IModelConnection; - /** onClose triggered when user clicks start comparison or closes dialog.*/ + + /** onClose triggered when user clicks start comparison or closes dialog. */ onClose: (() => void) | undefined; + "data-testid"?: string; - /** Optional prop for a user supplied component to handle managing named versions.*/ + + /** Optional prop for a user supplied component to handle managing named versions. */ manageNamedVersionsSlot?: ReactNode | undefined; } -/** VersionCompareSelectDialogV2 use comparison jobs for processing. - * Requires context of: - * - * ... - * - *------------------------------------------------------------------------------------------------ +/** + * VersionCompareSelectDialogV2 use comparison jobs for processing. Requires context of: + * + * ... + * + * ------------------------------------------------------------------------------------------------ * Should be used with provider. Example: - * - *{(isOpenCondition) && - * } - * - * provider should be supplied with new dialog based on condition in order to keep track of toast and polling information. + * + * { + * (isOpenCondition) && + * + * } + * + * + * Provider should be supplied with new dialog based on condition in order to keep + * track of toast and polling information. + * * @throws Exception if context does not include iModelsClient and comparisonJobClient. */ export function VersionCompareSelectDialogV2(props: VersionCompareSelectDialogV2Props) { @@ -64,43 +72,57 @@ export function VersionCompareSelectDialogV2(props: VersionCompareSelectDialogV2 if (!comparisonJobClient) { throw new Error("V2 Client Is Not Initialized In Given Context."); } + if (!iModelsClient) { throw new Error("V1 Client Is Not Initialized In Given Context."); } - const { openDialog, closedDialog, getDialogOpen, addRunningJob, removeRunningJob, getRunningJobs - , getPendingJobs, removePendingJob, addPendingJob, getToastsEnabled, runOnJobUpdate } = useContext(V2DialogContext); + + const { + openDialog, closedDialog, getDialogOpen, addRunningJob, removeRunningJob, getRunningJobs, + getPendingJobs, removePendingJob, addPendingJob, getToastsEnabled, runOnJobUpdate, + } = useContext(V2DialogContext); const [targetVersion, setTargetVersion] = useState(undefined); const [currentVersion, setCurrentVersion] = useState(undefined); const [result, setResult] = useState(); - const { isLoading } = useNamedVersionLoader(props.iModelConnection, iModelsClient, comparisonJobClient, setResult, getPendingJobs); - useEffect(() => { - let isDisposed = false; - const getIsDisposed = () => { - return isDisposed; - }; - openDialog(); - if (result && result?.namedVersions.entries) { - void pollForInProgressJobs({ - iTwinId: props.iModelConnection.iTwinId as string, - iModelId: props.iModelConnection.iModelId as string, - namedVersionLoaderState: result, - comparisonJobClient: comparisonJobClient, - iModelConnection: props.iModelConnection, - setResult: setResult, - removeRunningJob: removeRunningJob, - getRunningJobs: getRunningJobs, - getDialogOpen: getDialogOpen, - getIsDisposed, - getToastsEnabled, - runOnJobUpdate, - iModelsClient, - }); - } - return () => { - isDisposed = true; - }; + const { isLoading } = useNamedVersionLoader( + props.iModelConnection, + iModelsClient, + comparisonJobClient, + setResult, + getPendingJobs, + ); + useEffect( + () => { + let isDisposed = false; + const getIsDisposed = () => { + return isDisposed; + }; + openDialog(); + if (result && result?.namedVersions.entries) { + void pollForInProgressJobs({ + iTwinId: props.iModelConnection.iTwinId as string, + iModelId: props.iModelConnection.iModelId as string, + namedVersionLoaderState: result, + comparisonJobClient: comparisonJobClient, + iModelConnection: props.iModelConnection, + setResult: setResult, + removeRunningJob: removeRunningJob, + getRunningJobs: getRunningJobs, + getDialogOpen: getDialogOpen, + getIsDisposed, + getToastsEnabled, + runOnJobUpdate, + iModelsClient, + }); + } + + return () => { + isDisposed = true; + }; + }, // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isLoading]); + [isLoading], + ); const _handleOk = async (): Promise => { if (comparisonJobClient && result?.namedVersions && targetVersion && currentVersion) { const getIsDisposed = () => true; @@ -120,25 +142,28 @@ export function VersionCompareSelectDialogV2(props: VersionCompareSelectDialogV2 iModelsClient, }); if (startResult?.comparisonJob) { - addRunningJob(createJobId(targetVersion, currentVersion), { - comparisonJob: startResult.comparisonJob, - targetNamedVersion: { - id: targetVersion.id, - displayName: targetVersion.displayName, - changesetId: targetVersion.changesetId, - changesetIndex: targetVersion.changesetIndex, - description: targetVersion.description, - createdDateTime: targetVersion.createdDateTime, - }, - currentNamedVersion: { - id: currentVersion.id, - displayName: currentVersion.displayName, - changesetId: currentVersion.changesetId, - changesetIndex: currentVersion.changesetIndex, - description: currentVersion.description, - createdDateTime: currentVersion.createdDateTime, + addRunningJob( + createJobId(targetVersion, currentVersion), + { + comparisonJob: startResult.comparisonJob, + targetNamedVersion: { + id: targetVersion.id, + displayName: targetVersion.displayName, + changesetId: targetVersion.changesetId, + changesetIndex: targetVersion.changesetIndex, + description: targetVersion.description, + createdDateTime: targetVersion.createdDateTime, + }, + currentNamedVersion: { + id: currentVersion.id, + displayName: currentVersion.displayName, + changesetId: currentVersion.changesetId, + changesetIndex: currentVersion.changesetIndex, + description: currentVersion.description, + createdDateTime: currentVersion.createdDateTime, + }, }, - }); + ); void pollForInProgressJobs({ iTwinId: props.iModelConnection.iTwinId as string, iModelId: props.iModelConnection.iModelId as string, @@ -173,7 +198,9 @@ export function VersionCompareSelectDialogV2(props: VersionCompareSelectDialogV2 @@ -204,7 +231,8 @@ export function VersionCompareSelectDialogV2(props: VersionCompareSelectDialogV2 ); } -//todo refactor all types in this file they are not dry. We want "type" space to be as clean as "value" space. +// TODO: refactor all types in this file they are not dry. We want "type" space +// to be as clean as "value" space. type RunStartComparisonV2Args = { targetVersion: NamedVersion; comparisonJobClient: IComparisonJobClient; @@ -214,7 +242,10 @@ type RunStartComparisonV2Args = { addPendingJob: (jobId: string, comparisonJob: JobAndNamedVersions) => void; getDialogOpen: () => boolean; getToastsEnabled: () => boolean; - runOnJobUpdate: (comparisonJobUpdateType: ComparisonJobUpdateType, jobAndNamedVersions?: JobAndNamedVersions) => Promise; + runOnJobUpdate: ( + comparisonJobUpdateType: ComparisonJobUpdateType, + jobAndNamedVersions?: JobAndNamedVersions, + ) => Promise; iModelsClient: IModelsClient; }; @@ -223,27 +254,36 @@ type PostOrRunComparisonJobResult = { comparisonJob?: ComparisonJob; }; -const createOrRunManagerStartComparisonV2 = async (args: RunStartComparisonV2Args): Promise => { +const createOrRunManagerStartComparisonV2 = async ( + args: RunStartComparisonV2Args, +): Promise => { const jobId = createJobId(args.targetVersion, args.currentVersion); try { - args.addPendingJob(jobId, { - targetNamedVersion: args.targetVersion, - currentNamedVersion: args.currentVersion, - }); - let comparisonJob = await tryXTimes(async () => { - const job = (await postOrGetComparisonJob({ - changedElementsClient: args.comparisonJobClient, - iTwinId: args.iModelConnection?.iTwinId as string, - iModelId: args.iModelConnection?.iModelId as string, - startChangesetId: args.targetVersion.changesetId as string, - endChangesetId: args.currentVersion.changesetId as string, - })); - args.removePendingJob(jobId); - return job; - }, 3); + args.addPendingJob( + jobId, + { + targetNamedVersion: args.targetVersion, + currentNamedVersion: args.currentVersion, + }, + ); + let comparisonJob = await tryXTimes( + async () => { + const job = await postOrGetComparisonJob({ + changedElementsClient: args.comparisonJobClient, + iTwinId: args.iModelConnection?.iTwinId as string, + iModelId: args.iModelConnection?.iModelId as string, + startChangesetId: args.targetVersion.changesetId as string, + endChangesetId: args.currentVersion.changesetId as string, + }); + args.removePendingJob(jobId); + return job; + }, + 3, + ); if (comparisonJob.comparisonJob.status === "Failed") { comparisonJob = await handleJobError({ ...args, comparisonJob: comparisonJob }); } + if (comparisonJob.comparisonJob.status === "Completed") { void runManagerStartComparisonV2({ comparisonJob: comparisonJob as ComparisonJobCompleted, @@ -257,14 +297,17 @@ const createOrRunManagerStartComparisonV2 = async (args: RunStartComparisonV2Arg }); return { startedComparison: true }; } + if (args.getToastsEnabled() && !args.getDialogOpen()) { toastComparisonJobProcessing(args.currentVersion, args.targetVersion); } + const jobAndNamedVersion: JobAndNamedVersions = { comparisonJob: comparisonJob, targetNamedVersion: args.targetVersion, currentNamedVersion: args.currentVersion, }; + void args.runOnJobUpdate("JobProcessing", jobAndNamedVersion); return { startedComparison: false, comparisonJob: comparisonJob }; @@ -273,6 +316,7 @@ const createOrRunManagerStartComparisonV2 = async (args: RunStartComparisonV2Arg if (args.getToastsEnabled()) { toastComparisonJobError(args.currentVersion, args.targetVersion); } + const jobAndNamedVersion: JobAndNamedVersions = { comparisonJob: undefined, targetNamedVersion: args.targetVersion, @@ -283,7 +327,10 @@ const createOrRunManagerStartComparisonV2 = async (args: RunStartComparisonV2Arg } }; -type handleJobErrorArgs = Omit & { +type handleJobErrorArgs = Omit< + RunStartComparisonV2Args, + "getDialogOpen" | "getToastsEnabled" | "runOnJobUpdate" | "iModelsClient" +> & { comparisonJob: ComparisonJob; }; @@ -297,17 +344,20 @@ const handleJobError: (args: handleJobErrorArgs) => Promise = asy iModelId: args.comparisonJob.comparisonJob.iModelId, jobId: args.comparisonJob.comparisonJob.jobId, }); - return tryXTimes(async () => { - const job = (await postOrGetComparisonJob({ - changedElementsClient: args.comparisonJobClient, - iTwinId: args.iModelConnection?.iTwinId as string, - iModelId: args.iModelConnection?.iModelId as string, - startChangesetId: args.targetVersion.changesetId as string, - endChangesetId: args.currentVersion.changesetId as string, - })); - args.removePendingJob(job.comparisonJob.jobId); - return job; - }, 3); + return tryXTimes( + async () => { + const job = (await postOrGetComparisonJob({ + changedElementsClient: args.comparisonJobClient, + iTwinId: args.iModelConnection?.iTwinId as string, + iModelId: args.iModelConnection?.iModelId as string, + startChangesetId: args.targetVersion.changesetId as string, + endChangesetId: args.currentVersion.changesetId as string, + })); + args.removePendingJob(job.comparisonJob.jobId); + return job; + }, + 3, + ); }; type PollForInProgressJobsArgs = { @@ -323,13 +373,22 @@ type PollForInProgressJobsArgs = { getIsDisposed: () => boolean; targetVersion?: NamedVersion; getToastsEnabled: () => boolean; - runOnJobUpdate: (comparisonJobUpdateType: ComparisonJobUpdateType, jobAndNamedVersions?: JobAndNamedVersions) => Promise; + runOnJobUpdate: ( + comparisonJobUpdateType: ComparisonJobUpdateType, + jobAndNamedVersions?: JobAndNamedVersions, + ) => Promise; iModelsClient: IModelsClient; }; -export const pollForInProgressJobs: (args: PollForInProgressJobsArgs) => Promise = async (args: PollForInProgressJobsArgs) => { +export const pollForInProgressJobs: ( + args: PollForInProgressJobsArgs, +) => Promise = async (args: PollForInProgressJobsArgs) => { void pollUntilCurrentRunningJobsCompleteAndToast(args); - if (args.namedVersionLoaderState && args.namedVersionLoaderState.namedVersions.entries.length > 0 && args.getDialogOpen() && !args.getIsDisposed()) + if (( + args.namedVersionLoaderState && + args.namedVersionLoaderState.namedVersions.entries.length > 0 && + args.getDialogOpen() && !args.getIsDisposed()) + ) void pollUpdateCurrentEntriesForModal(args); }; @@ -350,6 +409,7 @@ const pollUntilCurrentRunningJobsCompleteAndToast = async (args: PollForInProgre args.removeRunningJob(runningJob?.comparisonJob?.comparisonJob.jobId as string); continue; } + notifyComparisonCompletion({ isConnectionClosed: isConnectionClosed, getRunningJobs: args.getRunningJobs, @@ -390,7 +450,10 @@ type ConditionallyToastCompletionArgs = { comparisonJobClient: IComparisonJobClient; iModelConnection: IModelConnection; getToastsEnabled: () => boolean; - runOnJobUpdate: (comparisonJobUpdateType: ComparisonJobUpdateType, jobAndNamedVersions?: JobAndNamedVersions) => Promise; + runOnJobUpdate: ( + comparisonJobUpdateType: ComparisonJobUpdateType, + jobAndNamedVersions?: JobAndNamedVersions, + ) => Promise; iModelsClient: IModelsClient; }; const notifyComparisonCompletion = (args: ConditionallyToastCompletionArgs) => { @@ -409,6 +472,7 @@ const notifyComparisonCompletion = (args: ConditionallyToastCompletionArgs) => { iModelsClient: args.iModelsClient, }); } + const jobAndNamedVersion: JobAndNamedVersions = { comparisonJob: args.currentJobRsp, targetNamedVersion: args.runningJob.targetNamedVersion, @@ -422,7 +486,10 @@ const notifyComparisonCompletion = (args: ConditionallyToastCompletionArgs) => { const pollUpdateCurrentEntriesForModal = async (args: PollForInProgressJobsArgs) => { const currentVersionId = args.iModelConnection?.changeset.id; let entries = args.namedVersionLoaderState!.namedVersions.entries.slice(); - const currentRunningJobsMap = arrayToMap(args.getRunningJobs(), (job: JobAndNamedVersions) => { return job.comparisonJob?.comparisonJob.jobId as string; }); + const currentRunningJobsMap = arrayToMap( + args.getRunningJobs(), + (job: JobAndNamedVersions) => { return job.comparisonJob?.comparisonJob.jobId as string; }, + ); if (areJobsInProgress(entries, args.getRunningJobs)) { const idEntryMap = arrayToMap(entries, (entry: VersionState) => { return entry.version.id; }); let updatingEntries = getUpdatingEntries(entries, currentVersionId, currentRunningJobsMap); @@ -448,12 +515,16 @@ const pollUpdateCurrentEntriesForModal = async (args: PollForInProgressJobsArgs) args.removeRunningJob(`${entry.version.changesetId}-${currentVersionId}`); } } + entries = [...idEntryMap.values()]; updatingEntries = getUpdatingEntries(entries, currentVersionId, currentRunningJobsMap); if (isDialogOpenAndNotDisposed(args.getDialogOpen, args.getIsDisposed)) { const updatedState = { - namedVersions: { currentVersion: args.namedVersionLoaderState!.namedVersions.currentVersion, entries: entries }, + namedVersions: { + currentVersion: args.namedVersionLoaderState!.namedVersions.currentVersion, + entries: entries, + }, }; args.setResult(updatedState); } @@ -465,11 +536,20 @@ const isDialogOpenAndNotDisposed = (getDialogOpen: () => boolean, getIsDisposed: return getDialogOpen() && !getIsDisposed(); }; -const areJobsInProgress = (entries: VersionState[], getRunningJobs: () => JobAndNamedVersions[]) => { - return entries.find(entry => entry.jobStatus === "Processing" || entry.jobStatus === "Queued") !== undefined || getRunningJobs().length > 0; +const areJobsInProgress = ( + entries: VersionState[], + getRunningJobs: () => JobAndNamedVersions[], +) => { + return entries.find( + entry => entry.jobStatus === "Processing" || entry.jobStatus === "Queued", + ) !== undefined || getRunningJobs().length > 0; }; -const getUpdatingEntries = (entries: VersionState[], currentVersionId: string, currentRunningJobsMap: Map) => { +const getUpdatingEntries = ( + entries: VersionState[], + currentVersionId: string, + currentRunningJobsMap: Map, +) => { return entries.filter((entry) => { if (entry.jobStatus === "Processing" || entry.jobStatus === "Queued") return true; @@ -487,7 +567,7 @@ type PostOrGetComparisonJobParams = { }; /** -* post or gets comparison job. +* Post or gets comparison job. * @returns ComparisonJob * @throws on a non 2XX response. */ @@ -503,7 +583,9 @@ async function postOrGetComparisonJob(args: PostOrGetComparisonJobParams): Promi }, }); } catch (error: unknown) { - if (error && typeof error === "object" && "code" in error && error.code === "ComparisonNotFound") { + if ( + error && typeof error === "object" && "code" in error && error.code === "ComparisonNotFound" + ) { result = await args.changedElementsClient.postComparisonJob({ iTwinId: args.iTwinId, iModelId: args.iModelId, @@ -513,7 +595,9 @@ async function postOrGetComparisonJob(args: PostOrGetComparisonJobParams): Promi }); return result; } + throw error; } + return result; } diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionEntries.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionEntries.tsx index 9d911467..fc22604b 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionEntries.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionEntries.tsx @@ -15,18 +15,22 @@ interface CurrentVersionEntryProps { versionState: VersionState; } -/** - * Component for current version. - * Displays the current version's name date description. - */ +/** Component for current version. Displays the current version's name date description. */ export function CurrentVersionEntry(props: CurrentVersionEntryProps): ReactElement { const isProcessed = props.versionState.state === VersionProcessedState.Processed; return (
- +
- {props.versionState.version.createdDateTime ? new Date(props.versionState.version.createdDateTime).toDateString() : ""} + { + props.versionState.version.createdDateTime + ? new Date(props.versionState.version.createdDateTime).toDateString() + : "" + }
{IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.current")} @@ -49,12 +53,24 @@ function DateCurrentAndJobInfo(props: DateAndCurrentProps): ReactElement { return (
{props.children} - {props.jobStatus === undefined || props.jobStatus === "Unknown" ? <> : - {`${getLocalizedJobStatusText(props.jobStatus)}`}} - {props.jobProgress === undefined || props.jobProgress.maxProgress === 0 ? <> - : - {`${IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.progress")}: ${Math.floor((props.jobProgress.currentProgress / props.jobProgress.maxProgress) * 100)}%`} - } + { + props.jobStatus === undefined || props.jobStatus === "Unknown" + ? <> + : ( + + {`${getLocalizedJobStatusText(props.jobStatus)}`} + + ) + } + { + props.jobProgress === undefined || props.jobProgress.maxProgress === 0 + ? <> + : ( + + {`${IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.progress")}: ${Math.floor((props.jobProgress.currentProgress / props.jobProgress.maxProgress) * 100)}%`} + + ) + }
); } @@ -66,14 +82,19 @@ const getJobBackgroundColor = (jobStatus: JobStatus): string => { switch (jobStatus) { case "Available": return green; + case "Queued": return teal; + case "Processing": return teal; + case "Not Processed": return ""; + case "Error": return red; + default: return ""; } @@ -83,14 +104,19 @@ const getLocalizedJobStatusText = (jobStatus: JobStatus): string => { switch (jobStatus) { case "Available": return IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.available"); + case "Queued": return IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.queued"); + case "Processing": return IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.processing"); + case "Not Processed": return IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.notProcessed"); + case "Error": return IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.error"); + default: return IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.notProcessed"); } @@ -123,13 +149,17 @@ interface VersionListEntryProps { } /** - * Named Version List Entry. - * Displays the job information. The job will be between this version and the current version. - * Displays the description and name of the version as well. + * Named Version List Entry. Displays the job information. The job will be between + * this version and the current version. Displays the description and name of the + * version as well. */ export function VersionListEntry(props: VersionListEntryProps): ReactElement { const handleClick = async () => { - if (props.versionState.state !== VersionProcessedState.Processed || props.versionState.jobStatus === "Processing" || props.versionState.jobStatus === "Queued") { + if ( + props.versionState.state !== VersionProcessedState.Processed || + props.versionState.jobStatus === "Processing" || + props.versionState.jobStatus === "Queued" + ) { return; } @@ -140,8 +170,10 @@ export function VersionListEntry(props: VersionListEntryProps): ReactElement { switch (props.versionState.state) { case VersionProcessedState.Processed: return "current-empty"; + case VersionProcessedState.Processing: return "state-processing"; + case VersionProcessedState.Unavailable: default: return "state-unavailable"; @@ -151,11 +183,10 @@ export function VersionListEntry(props: VersionListEntryProps): ReactElement { switch (props.versionState.state) { case VersionProcessedState.Processed: return ""; - case VersionProcessedState.Processing: { - return IModelApp.localization.getLocalizedString( - "VersionCompare:versionCompare.processed", - ); - } + + case VersionProcessedState.Processing: + return IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.processed"); + case VersionProcessedState.Unavailable: default: return IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.unavailable"); @@ -163,9 +194,11 @@ export function VersionListEntry(props: VersionListEntryProps): ReactElement { }; const getAvailableDate = () => { return ( - + jobProgress={props.versionState.jobProgress} + >
{getStateDivMessage()}
@@ -173,7 +206,8 @@ export function VersionListEntry(props: VersionListEntryProps): ReactElement { ); }; - const isProcessed = props.versionState.state === VersionProcessedState.Processed || (props.versionState.jobStatus !== "Processing" && props.versionState.jobStatus !== "Queued"); + const isProcessed = props.versionState.state === VersionProcessedState.Processed || + (props.versionState.jobStatus !== "Processing" && props.versionState.jobStatus !== "Queued"); return (
- diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionList.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionList.tsx index fd27ca8c..cbd72698 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionList.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionList.tsx @@ -12,17 +12,18 @@ import { VersionListEntry } from "./VersionEntries"; interface VersionListProps { entries: VersionState[]; + currentVersion: VersionState; + selectedVersionChangesetId: string | undefined; + onVersionClicked: (targetVersion: NamedVersion) => void; /** If true display loading spinner to indicate we are receiving more named versions*/ isLoading: boolean; } -/** - * Component that displays named versions (non current). - */ +/** Component that displays named versions (non current). */ export function VersionList(props: VersionListProps): ReactElement { return (
@@ -32,22 +33,24 @@ export function VersionList(props: VersionListProps): ReactElement { {IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.versions")}
- {"Comparison Status"} + Comparison Status
- {props.entries.map((versionState) => { - const isSelected = props.selectedVersionChangesetId !== undefined && - versionState.version.changesetId === props.selectedVersionChangesetId; - return ( - - ); - })} + { + props.entries.map((versionState) => { + const isSelected = props.selectedVersionChangesetId !== undefined && + versionState.version.changesetId === props.selectedVersionChangesetId; + return ( + + ); + }) + } {props.isLoading && }
diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/hooks/useNamedVersionLoader.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/hooks/useNamedVersionLoader.tsx index daf6846c..3d676d34 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/hooks/useNamedVersionLoader.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/hooks/useNamedVersionLoader.tsx @@ -15,9 +15,7 @@ import type { import type { CurrentNamedVersionAndNamedVersions } from "../models/NamedVersions"; import { VersionProcessedState } from "../models/VersionProcessedState"; -/** - * Result type for versionLoader. - */ +/** Result type for versionLoader. */ export type NamedVersionLoaderState = { /** Named versions to display in the list. */ namedVersions: CurrentNamedVersionAndNamedVersions; @@ -28,9 +26,10 @@ interface UseNamedVersionLoaderResult { } /** - * Loads name versions and their job status compared to current version iModel is targeting. - * Returns a result object with current version and namedVersion with there job status sorted from newest to oldest. - * This is run during the initial load of the widget. + * Loads name versions and their job status compared to current version iModel is + * targeting. Returns a result object with current version and namedVersion with + * there job status sorted from newest to oldest. This is run during the initial + * load of the widget. */ export const useNamedVersionLoader = ( iModelConnection: IModelConnection, @@ -65,21 +64,27 @@ export const useNamedVersionLoader = ( while (!disposed) { try { // Get a page of named versions - const namedVersions = await iModelsClient.getNamedVersions( - { - iModelId, - top: pageSize, - skip: currentPage * pageSize, - orderby: "changesetIndex", - ascendingOrDescending: "desc", - }); + const namedVersions = await iModelsClient.getNamedVersions({ + iModelId, + top: pageSize, + skip: currentPage * pageSize, + orderby: "changesetIndex", + ascendingOrDescending: "desc", + }); if (!currentNamedVersion) - currentNamedVersion = await getOrCreateCurrentNamedVersion(namedVersions, currentChangeSetId, iModelsClient, iModelId, currentChangeSetIndex); + currentNamedVersion = await getOrCreateCurrentNamedVersion( + namedVersions, + currentChangeSetId, + iModelsClient, + iModelId, + currentChangeSetIndex, + ); if (namedVersions.length === 0) { setIsLoading(false); break; // No more named versions to process } + // Process the named versions const processedNamedVersionsState = await processNamedVersions({ currentNamedVersion: currentNamedVersion, @@ -98,7 +103,9 @@ export const useNamedVersionLoader = ( if (currentState) { const updatedState: NamedVersionLoaderState = { namedVersions: { - entries: currentState.namedVersions.entries.concat(processedNamedVersionsState.namedVersions.entries), + entries: currentState.namedVersions.entries.concat( + processedNamedVersionsState.namedVersions.entries, + ), currentVersion: processedNamedVersionsState?.namedVersions.currentVersion, }, }; @@ -106,8 +113,10 @@ export const useNamedVersionLoader = ( } else { currentState = processedNamedVersionsState; } + setNamedVersionResult(currentState); } + currentPage++; } catch (error) { setIsLoading(false); @@ -152,12 +161,19 @@ const processNamedVersions = async (args: ProcessNamedVersionsArgs) => { getPendingJobs, currentNamedVersion, } = args; - const sortedAndOffsetNamedVersions = await sortAndSetIndexOfNamedVersions(namedVersions, currentNamedVersion, setResultNoNamedVersions, iModelsClient, iModelId); + const sortedAndOffsetNamedVersions = await sortAndSetIndexOfNamedVersions( + namedVersions, + currentNamedVersion, + setResultNoNamedVersions, + iModelsClient, + iModelId, + ); if (!sortedAndOffsetNamedVersions || sortedAndOffsetNamedVersions.length === 0) { setResultNoNamedVersions(); updatePaging(false); return; } + const initialComparisonJobStatus: JobStatus = "Unknown"; const initialJobProgress: JobProgress = { currentProgress: 0, @@ -190,16 +206,34 @@ const processNamedVersions = async (args: ProcessNamedVersionsArgs) => { }; // create faked named version if current version is not a named version -const getOrCreateCurrentNamedVersion = async (namedVersions: NamedVersion[], currentChangeSetId: string, iModelsClient: IModelsClient, iModelId?: string, currentChangeSetIndex?: number): Promise => { - const currentFromNamedVersion = getCurrentFromNamedVersions(namedVersions, currentChangeSetId, currentChangeSetIndex); +const getOrCreateCurrentNamedVersion = async ( + namedVersions: NamedVersion[], + currentChangeSetId: string, + iModelsClient: IModelsClient, + iModelId?: string, + currentChangeSetIndex?: number, +): Promise => { + const currentFromNamedVersion = getCurrentFromNamedVersions( + namedVersions, + currentChangeSetId, + currentChangeSetIndex, + ); if (currentFromNamedVersion) return currentFromNamedVersion; - const currentFromChangeSet = await getCurrentFromChangeSet(currentChangeSetId, iModelsClient, iModelId); + + const currentFromChangeSet = await getCurrentFromChangeSet( + currentChangeSetId, + iModelsClient, + iModelId, + ); if (currentFromChangeSet) return currentFromChangeSet; + return { id: currentChangeSetId, - displayName: IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.currentChangeset"), + displayName: IModelApp.localization.getLocalizedString( + "VersionCompare:versionCompare.currentChangeset", + ), changesetId: currentChangeSetId, changesetIndex: currentChangeSetIndex ?? 0, description: "", @@ -207,18 +241,36 @@ const getOrCreateCurrentNamedVersion = async (namedVersions: NamedVersion[], cur }; }; -const getCurrentFromNamedVersions = (namedVersions: NamedVersion[], currentChangeSetId: string, currentChangeSetIndex?: number) => { - const currentNamedVersion = namedVersions.find(version => (version.changesetId === currentChangeSetId || version.changesetIndex === currentChangeSetIndex)); +const getCurrentFromNamedVersions = ( + namedVersions: NamedVersion[], + currentChangeSetId: string, + currentChangeSetIndex?: number, +) => { + const currentNamedVersion = namedVersions.find((version) => { + return ( + version.changesetId === currentChangeSetId || + version.changesetIndex === currentChangeSetIndex + ); + }); if (currentNamedVersion) { return currentNamedVersion; } + return undefined; }; -const getCurrentFromChangeSet = async (currentChangeSetId: string, iModelsClient: IModelsClient, iModelId?: string): Promise => { +const getCurrentFromChangeSet = async ( + currentChangeSetId: string, + iModelsClient: IModelsClient, + iModelId?: string, +): Promise => { if (!iModelId) return undefined; - const currentChangeSet = await iModelsClient.getChangeset({ iModelId: iModelId, changesetId: currentChangeSetId }); + + const currentChangeSet = await iModelsClient.getChangeset({ + iModelId: iModelId, + changesetId: currentChangeSetId, + }); if (currentChangeSet) { return { id: currentChangeSet.id, @@ -229,28 +281,41 @@ const getCurrentFromChangeSet = async (currentChangeSetId: string, iModelsClient createdDateTime: currentChangeSet.pushDateTime, }; } + return undefined; }; -const sortAndSetIndexOfNamedVersions = async (namedVersions: NamedVersion[], currentNamedVersion: NamedVersion, onError: () => void, iModelsClient: IModelsClient, iModelId: string) => { +const sortAndSetIndexOfNamedVersions = async ( + namedVersions: NamedVersion[], + currentNamedVersion: NamedVersion, + onError: () => void, + iModelsClient: IModelsClient, + iModelId: string, +) => { //if current index is 0 then no need to filter. All change sets are older than current. - const namedVersionsOlderThanCurrentVersion = currentNamedVersion.changesetIndex !== 0 ? namedVersions.filter(version => version.changesetIndex <= currentNamedVersion.changesetIndex) : - namedVersions; + const namedVersionsOlderThanCurrentVersion = currentNamedVersion.changesetIndex !== 0 + ? namedVersions.filter(version => version.changesetIndex <= currentNamedVersion.changesetIndex) + : namedVersions; if (namedVersionsOlderThanCurrentVersion.length === 0) { onError(); return; } + const reversedNamedVersions = namedVersionsOlderThanCurrentVersion; if (reversedNamedVersions[0].changesetIndex === currentNamedVersion.changesetIndex) { reversedNamedVersions.shift(); //remove current named version } + // we must offset the named versions , because that changeset is "already applied" to the named version, see this: // https://developer.bentley.com/tutorials/changed-elements-api/#221-using-the-api-to-get-changed-elements // this assuming latest is current const promises = reversedNamedVersions.map(async (nameVersion) => { nameVersion.changesetIndex = nameVersion.changesetIndex + 1; const changesetId = nameVersion.changesetIndex.toString(); - const changeSet = await iModelsClient.getChangeset({ iModelId: iModelId, changesetId: changesetId }); + const changeSet = await iModelsClient.getChangeset({ + iModelId: iModelId, + changesetId: changesetId, + }); nameVersion.changesetId = changeSet?.id ?? nameVersion.changesetId; return nameVersion; }); @@ -268,38 +333,49 @@ type ProcessChangesetsArgs = { }; const getComparisonJobInfoForNamedVersions = async (args: ProcessChangesetsArgs) => { - const pendingJobsMap = arrayToMap(args.getPendingJobs(), (job: JobAndNamedVersions) => { return createJobId(job.targetNamedVersion, job.currentNamedVersion); }); - const currentVersionId = args.namedVersionLoaderState.namedVersions.currentVersion?.version.changesetId ?? - args.iModelConnection?.changeset.id; - const newEntries = await Promise.all(args.namedVersionLoaderState.namedVersions.entries.map(async (entry) => { - const jobStatusAndJobProgress: JobStatusAndJobProgress = await getJobStatusAndJobProgress({ - comparisonJobClient: args.comparisonJobClient, - entry: entry, - iTwinId: args.iTwinId, - iModelId: args.iModelId, - currentChangesetId: currentVersionId, - }); - if (pendingJobsMap.has(`${entry.version.changesetId}-${currentVersionId}`)) { - const jobStatus: JobStatus = "Processing"; + const pendingJobsMap = arrayToMap( + args.getPendingJobs(), + (job: JobAndNamedVersions) => { + return createJobId(job.targetNamedVersion, job.currentNamedVersion); + }, + ); + const currentVersionId = args.namedVersionLoaderState.namedVersions + .currentVersion?.version.changesetId ?? args.iModelConnection?.changeset.id; + const newEntries = await Promise.all( + args.namedVersionLoaderState.namedVersions.entries.map(async (entry) => { + const jobStatusAndJobProgress: JobStatusAndJobProgress = await getJobStatusAndJobProgress({ + comparisonJobClient: args.comparisonJobClient, + entry: entry, + iTwinId: args.iTwinId, + iModelId: args.iModelId, + currentChangesetId: currentVersionId, + }); + if (pendingJobsMap.has(`${entry.version.changesetId}-${currentVersionId}`)) { + const jobStatus: JobStatus = "Processing"; + return { + version: entry.version, + state: VersionProcessedState.Processed, + jobStatus: jobStatus, + jobProgress: { + currentProgress: 0, + maxProgress: 1, + }, + }; + } + return { version: entry.version, state: VersionProcessedState.Processed, - jobStatus: jobStatus, - jobProgress: { - currentProgress: 0, - maxProgress: 1, - }, + jobStatus: jobStatusAndJobProgress.jobStatus, + jobProgress: jobStatusAndJobProgress.jobProgress, }; - } - return { - version: entry.version, - state: VersionProcessedState.Processed, - jobStatus: jobStatusAndJobProgress.jobStatus, - jobProgress: jobStatusAndJobProgress.jobProgress, - }; - })); + }), + ); const updatedState = { - namedVersions: { currentVersion: args.namedVersionLoaderState.namedVersions.currentVersion, entries: newEntries }, + namedVersions: { + currentVersion: args.namedVersionLoaderState.namedVersions.currentVersion, + entries: newEntries, + }, }; return updatedState; }; From b33fd8e5bb211a081ca1fc512b8de9cf29ae8496 Mon Sep 17 00:00:00 2001 From: roluk <70327485+roluk@users.noreply.github.com> Date: Wed, 20 Nov 2024 16:57:21 +0200 Subject: [PATCH 03/21] Remove zero-width space --- .../src/widgets/ChangedElementsWidget.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/changed-elements-react/src/widgets/ChangedElementsWidget.tsx b/packages/changed-elements-react/src/widgets/ChangedElementsWidget.tsx index 81c55d04..f9a85467 100644 --- a/packages/changed-elements-react/src/widgets/ChangedElementsWidget.tsx +++ b/packages/changed-elements-react/src/widgets/ChangedElementsWidget.tsx @@ -420,7 +420,7 @@ export class ChangedElementsWidget extends Component {this.state.versionSelectDialogVisible && Date: Wed, 20 Nov 2024 16:32:01 +0200 Subject: [PATCH 04/21] Apply trivial simplifications --- .../src/widgets/ChangedElementsWidget.tsx | 39 ++- .../common/versionCompareV2WidgetUtils.ts | 151 ++++----- .../VersionCompareDialogProvider.tsx | 50 ++- .../VersionCompareSelectComponent.tsx | 27 +- .../components/VersionCompareSelectModal.tsx | 303 ++++++++---------- .../components/VersionEntries.tsx | 247 +++++++------- .../components/VersionList.tsx | 23 +- .../hooks/useNamedVersionLoader.tsx | 199 ++++++------ 8 files changed, 486 insertions(+), 553 deletions(-) diff --git a/packages/changed-elements-react/src/widgets/ChangedElementsWidget.tsx b/packages/changed-elements-react/src/widgets/ChangedElementsWidget.tsx index f9a85467..79b53deb 100644 --- a/packages/changed-elements-react/src/widgets/ChangedElementsWidget.tsx +++ b/packages/changed-elements-react/src/widgets/ChangedElementsWidget.tsx @@ -416,22 +416,31 @@ export class ChangedElementsWidget extends Component } - {this.props.useV2Widget ? - - {this.state.versionSelectDialogVisible && - } - : + { this.state.versionSelectDialogVisible && - } + ( + this.props.useV2Widget + ? ( + + + + ) : ( + + ) + ) + } ); } diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareV2WidgetUtils.ts b/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareV2WidgetUtils.ts index 5f7c3ca2..f009eab8 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareV2WidgetUtils.ts +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareV2WidgetUtils.ts @@ -7,7 +7,7 @@ import type { IModelConnection } from "@itwin/core-frontend"; import { VersionCompare } from "../../../api/VersionCompare"; import type { - ComparisonJobCompleted, ComparisonJobStarted, IComparisonJobClient, + ComparisonJob, ComparisonJobCompleted, ComparisonJobStarted, IComparisonJobClient, } from "../../../clients/IComparisonJobClient"; import type { IModelsClient, NamedVersion } from "../../../clients/iModelsClient"; import type { ComparisonJobUpdateType } from "../components/VersionCompareDialogProvider"; @@ -15,7 +15,7 @@ import type { JobAndNamedVersions, JobStatusAndJobProgress } from "../models/Com import type { VersionState } from "../models/VersionState"; import { toastComparisonVisualizationStarting } from "./versionCompareToasts"; -export type ManagerStartComparisonV2Args = { +export interface ManagerStartComparisonV2Args { comparisonJob: ComparisonJobCompleted; comparisonJobClient: IComparisonJobClient; iModelConnection: IModelConnection; @@ -25,11 +25,13 @@ export type ManagerStartComparisonV2Args = { runOnJobUpdate?: ( comparisonEventType: ComparisonJobUpdateType, jobAndNamedVersions?: JobAndNamedVersions, - ) => Promise; + ) => void; iModelsClient: IModelsClient; -}; +} -export const runManagerStartComparisonV2 = async (args: ManagerStartComparisonV2Args) => { +export async function runManagerStartComparisonV2( + args: ManagerStartComparisonV2Args, +): Promise { if (VersionCompare.manager?.isComparing) { await VersionCompare.manager?.stopComparison(); } @@ -43,9 +45,7 @@ export const runManagerStartComparisonV2 = async (args: ManagerStartComparisonV2 targetNamedVersion: args.targetVersion, currentNamedVersion: args.currentVersion, }; - if (args.runOnJobUpdate) { - void args.runOnJobUpdate("ComparisonVisualizationStarting", jobAndNamedVersion); - } + args.runOnJobUpdate?.("ComparisonVisualizationStarting", jobAndNamedVersion); const changedElements = await args.comparisonJobClient.getComparisonJobResult(args.comparisonJob); VersionCompare.manager?.startComparisonV2( @@ -56,13 +56,13 @@ export const runManagerStartComparisonV2 = async (args: ManagerStartComparisonV2 ).catch((e) => { Logger.logError(VersionCompare.logCategory, "Could not start version comparison: " + e); }); -}; +} -const updateTargetVersion = async ( +async function updateTargetVersion( iModelConnection: IModelConnection, targetVersion: NamedVersion, iModelsClient: IModelsClient, -) => { +): Promise { // We need to update the changesetId and index of the target version. Earlier // we updated all named versions to have an offset of 1, so we undo this offset // to get the proper results from any VersionCompare.manager?.startComparisonV2 @@ -71,10 +71,8 @@ const updateTargetVersion = async ( const iModelId = iModelConnection?.iModelId as string; const updatedTargetVersion = { ...targetVersion }; updatedTargetVersion.changesetIndex = targetVersion.changesetIndex - 1; - const changeSets = await iModelsClient - .getChangesets({ iModelId }) - .then((changesets) => changesets.slice().reverse()); - const actualChangeSet = changeSets.find( + const changesets = await iModelsClient.getChangesets({ iModelId }); + const actualChangeSet = changesets.slice().reverse().find( (changeset) => updatedTargetVersion.changesetIndex === changeset.index, ); if (actualChangeSet) { @@ -82,75 +80,26 @@ const updateTargetVersion = async ( } return updatedTargetVersion; -}; +} -export type GetJobStatusAndJobProgress = { +export interface GetJobStatusAndJobProgress { comparisonJobClient: IComparisonJobClient; entry: VersionState; iTwinId: string; iModelId: string; currentChangesetId: string; -}; +} -export const getJobStatusAndJobProgress = async ( +export async function getJobStatusAndJobProgress( args: GetJobStatusAndJobProgress, -): Promise => { +): Promise { + let res: ComparisonJob; try { - const res = await args.comparisonJobClient.getComparisonJob({ + res = await args.comparisonJobClient.getComparisonJob({ iTwinId: args.iTwinId, iModelId: args.iModelId, jobId: `${args.entry.version.changesetId}-${args.currentChangesetId}`, }); - if (res) { - switch (res.comparisonJob.status) { - case "Completed": { - return { - jobStatus: "Available", - jobProgress: { - currentProgress: 0, - maxProgress: 0, - }, - }; - } - - case "Queued": { - return { - jobStatus: "Queued", - jobProgress: { - currentProgress: 0, - maxProgress: 0, - }, - }; - } - - case "Started": { - const progressingJob = res as ComparisonJobStarted; - return { - jobStatus: "Processing", - jobProgress: { - currentProgress: progressingJob.comparisonJob.currentProgress, - maxProgress: progressingJob.comparisonJob.maxProgress, - }, - }; - } - - case "Failed": - return { - jobStatus: "Error", - jobProgress: { - currentProgress: 0, - maxProgress: 0, - }, - }; - } - } - return { - jobStatus: "Not Processed", - jobProgress: { - currentProgress: 0, - maxProgress: 0, - }, - }; } catch { return { jobStatus: "Not Processed", @@ -160,8 +109,62 @@ export const getJobStatusAndJobProgress = async ( }, }; } -}; -export const createJobId = (startNamedVersion: NamedVersion, endNamedVersion: NamedVersion) => { + switch (res.comparisonJob.status) { + case "Completed": { + return { + jobStatus: "Available", + jobProgress: { + currentProgress: 0, + maxProgress: 0, + }, + }; + } + + case "Queued": { + return { + jobStatus: "Queued", + jobProgress: { + currentProgress: 0, + maxProgress: 0, + }, + }; + } + + case "Started": { + const progressingJob = res as ComparisonJobStarted; + return { + jobStatus: "Processing", + jobProgress: { + currentProgress: progressingJob.comparisonJob.currentProgress, + maxProgress: progressingJob.comparisonJob.maxProgress, + }, + }; + } + + case "Failed": + return { + jobStatus: "Error", + jobProgress: { + currentProgress: 0, + maxProgress: 0, + }, + }; + + default: + return { + jobStatus: "Not Processed", + jobProgress: { + currentProgress: 0, + maxProgress: 0, + }, + }; + } +} + +export function createJobId( + startNamedVersion: NamedVersion, + endNamedVersion: NamedVersion, +): string { return `${startNamedVersion.changesetId}-${endNamedVersion.changesetId}`; -}; +} diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareDialogProvider.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareDialogProvider.tsx index feda217b..2aae718f 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareDialogProvider.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareDialogProvider.tsx @@ -2,8 +2,9 @@ * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import React from "react"; -import { JobAndNamedVersions } from "../models/ComparisonJobModels"; +import { createContext, type ReactElement, type ReactNode, useRef } from "react"; + +import type { JobAndNamedVersions } from "../models/ComparisonJobModels"; /** * Comparison Job Update Type @@ -12,9 +13,10 @@ import { JobAndNamedVersions } from "../models/ComparisonJobModels"; * - "JobProcessing" = job is started * - "ComparisonVisualizationStarting" = version compare visualization is starting */ -export type ComparisonJobUpdateType = "JobComplete" | "JobError" | "JobProcessing" | "ComparisonVisualizationStarting"; +export type ComparisonJobUpdateType = "JobComplete" | "JobError" | "JobProcessing" + | "ComparisonVisualizationStarting"; -export type V2Context = { +export interface V2Context { getDialogOpen: () => boolean; openDialog: () => void; closedDialog: () => void; @@ -29,12 +31,12 @@ export type V2Context = { comparisonJobUpdateType: ComparisonJobUpdateType, jobAndNamedVersions?: JobAndNamedVersions, ) => Promise; -}; +} -export const V2DialogContext = React.createContext({} as V2Context); +export const V2DialogContext = createContext({} as V2Context); export type V2DialogProviderProps = { - children: React.ReactNode; + children: ReactNode; /** * Optional. When enabled will toast messages regarding job status. If not defined, @@ -76,25 +78,23 @@ export type V2DialogProviderProps = { */ export function VersionCompareSelectProviderV2( { children, enableComparisonJobUpdateToasts, onJobUpdate }: V2DialogProviderProps, -) { - const dialogRunningJobs = React.useRef>(new Map()); - const dialogPendingJobs = React.useRef>(new Map()); +): ReactElement { + const dialogRunningJobs = useRef(new Map()); + const dialogPendingJobs = useRef(new Map()); const addRunningJob = (jobId: string, jobAndNamedVersions: JobAndNamedVersions) => { dialogRunningJobs.current.set( jobId, { - comparisonJob: jobAndNamedVersions.comparisonJob, - targetNamedVersion: jobAndNamedVersions.targetNamedVersion, - currentNamedVersion: jobAndNamedVersions.currentNamedVersion, + comparisonJob: jobAndNamedVersions.comparisonJob, + targetNamedVersion: jobAndNamedVersions.targetNamedVersion, + currentNamedVersion: jobAndNamedVersions.currentNamedVersion, }, ); }; const removeRunningJob = (jobId: string) => { dialogRunningJobs.current.delete(jobId); }; - const getRunningJobs = () => { - return Array.from(dialogRunningJobs.current.values()); - }; + const getRunningJobs = () => Array.from(dialogRunningJobs.current.values()); const addPendingJob = (jobId: string, jobAndNamedVersions: JobAndNamedVersions) => { dialogPendingJobs.current.set(jobId, { comparisonJob: jobAndNamedVersions.comparisonJob, @@ -105,29 +105,21 @@ export function VersionCompareSelectProviderV2( const removePendingJob = (jobId: string) => { dialogPendingJobs.current.delete(jobId); }; - const getPendingJobs = () => { - return Array.from(dialogPendingJobs.current.values()); - }; - const dialogOpenRef = React.useRef(false); + const getPendingJobs = () => Array.from(dialogPendingJobs.current.values()); + const dialogOpenRef = useRef(false); const openDialog = () => { dialogOpenRef.current = true; }; const closedDialog = () => { dialogOpenRef.current = false; }; - const getDialogOpen = () => { - return dialogOpenRef.current; - }; - const getToastsEnabled = () => { - return enableComparisonJobUpdateToasts ?? false; - }; + const getDialogOpen = () => dialogOpenRef.current; + const getToastsEnabled = () => enableComparisonJobUpdateToasts ?? false; const runOnJobUpdate = async ( comparisonEventType: ComparisonJobUpdateType, jobAndNamedVersions?: JobAndNamedVersions, ) => { - if (onJobUpdate) { - void onJobUpdate(comparisonEventType, jobAndNamedVersions); - } + await onJobUpdate?.(comparisonEventType, jobAndNamedVersions); }; return ( - ) : ( + if (!props.namedVersions) { + return (
); + } + + return ( + + ); } diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx index 02db2006..26745dea 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx @@ -23,7 +23,7 @@ import { import { useNamedVersionLoader, type NamedVersionLoaderState, } from "../hooks/useNamedVersionLoader"; -import type { JobAndNamedVersions, JobStatusAndJobProgress } from "../models/ComparisonJobModels"; +import type { JobAndNamedVersions } from "../models/ComparisonJobModels"; import { VersionProcessedState } from "../models/VersionProcessedState"; import type { VersionState } from "../models/VersionState"; import { V2DialogContext, type ComparisonJobUpdateType } from "./VersionCompareDialogProvider"; @@ -70,19 +70,15 @@ export interface VersionCompareSelectDialogV2Props { export function VersionCompareSelectDialogV2(props: VersionCompareSelectDialogV2Props) { const { comparisonJobClient, iModelsClient } = useVersionCompare(); if (!comparisonJobClient) { - throw new Error("V2 Client Is Not Initialized In Given Context."); - } - - if (!iModelsClient) { - throw new Error("V1 Client Is Not Initialized In Given Context."); + throw new Error("V2 Client is not initialized in given context."); } const { openDialog, closedDialog, getDialogOpen, addRunningJob, removeRunningJob, getRunningJobs, getPendingJobs, removePendingJob, addPendingJob, getToastsEnabled, runOnJobUpdate, } = useContext(V2DialogContext); - const [targetVersion, setTargetVersion] = useState(undefined); - const [currentVersion, setCurrentVersion] = useState(undefined); + const [targetVersion, setTargetVersion] = useState(); + const [currentVersion, setCurrentVersion] = useState(); const [result, setResult] = useState(); const { isLoading } = useNamedVersionLoader( props.iModelConnection, @@ -94,21 +90,19 @@ export function VersionCompareSelectDialogV2(props: VersionCompareSelectDialogV2 useEffect( () => { let isDisposed = false; - const getIsDisposed = () => { - return isDisposed; - }; + const getIsDisposed = () => isDisposed; openDialog(); - if (result && result?.namedVersions.entries) { - void pollForInProgressJobs({ + if (result?.namedVersions.entries) { + pollForInProgressJobs({ iTwinId: props.iModelConnection.iTwinId as string, iModelId: props.iModelConnection.iModelId as string, namedVersionLoaderState: result, - comparisonJobClient: comparisonJobClient, + comparisonJobClient, iModelConnection: props.iModelConnection, - setResult: setResult, - removeRunningJob: removeRunningJob, - getRunningJobs: getRunningJobs, - getDialogOpen: getDialogOpen, + setResult, + removeRunningJob, + getRunningJobs, + getDialogOpen, getIsDisposed, getToastsEnabled, runOnJobUpdate, @@ -124,62 +118,63 @@ export function VersionCompareSelectDialogV2(props: VersionCompareSelectDialogV2 [isLoading], ); const _handleOk = async (): Promise => { - if (comparisonJobClient && result?.namedVersions && targetVersion && currentVersion) { - const getIsDisposed = () => true; - props.onClose?.(); - closedDialog(); - VersionCompareUtils.outputVerbose(VersionCompareVerboseMessages.selectDialogClosed); - const startResult = await createOrRunManagerStartComparisonV2({ - targetVersion: targetVersion, - comparisonJobClient: comparisonJobClient, + if (!comparisonJobClient || !result?.namedVersions || !targetVersion || !currentVersion) { + return; + } + + props.onClose?.(); + closedDialog(); + VersionCompareUtils.outputVerbose(VersionCompareVerboseMessages.selectDialogClosed); + const startResult = await createOrRunManagerStartComparisonV2({ + targetVersion, + comparisonJobClient, + iModelConnection: props.iModelConnection, + currentVersion, + addPendingJob, + removePendingJob, + getDialogOpen, + getToastsEnabled, + runOnJobUpdate, + iModelsClient, + }); + if (startResult?.comparisonJob) { + addRunningJob( + createJobId(targetVersion, currentVersion), + { + comparisonJob: startResult.comparisonJob, + targetNamedVersion: { + id: targetVersion.id, + displayName: targetVersion.displayName, + changesetId: targetVersion.changesetId, + changesetIndex: targetVersion.changesetIndex, + description: targetVersion.description, + createdDateTime: targetVersion.createdDateTime, + }, + currentNamedVersion: { + id: currentVersion.id, + displayName: currentVersion.displayName, + changesetId: currentVersion.changesetId, + changesetIndex: currentVersion.changesetIndex, + description: currentVersion.description, + createdDateTime: currentVersion.createdDateTime, + }, + }, + ); + pollForInProgressJobs({ + iTwinId: props.iModelConnection.iTwinId as string, + iModelId: props.iModelConnection.iModelId as string, + namedVersionLoaderState: result, + comparisonJobClient, iModelConnection: props.iModelConnection, - currentVersion: currentVersion, - addPendingJob, - removePendingJob, + setResult, + removeRunningJob, + getRunningJobs, getDialogOpen, + getIsDisposed: () => true, getToastsEnabled, runOnJobUpdate, iModelsClient, }); - if (startResult?.comparisonJob) { - addRunningJob( - createJobId(targetVersion, currentVersion), - { - comparisonJob: startResult.comparisonJob, - targetNamedVersion: { - id: targetVersion.id, - displayName: targetVersion.displayName, - changesetId: targetVersion.changesetId, - changesetIndex: targetVersion.changesetIndex, - description: targetVersion.description, - createdDateTime: targetVersion.createdDateTime, - }, - currentNamedVersion: { - id: currentVersion.id, - displayName: currentVersion.displayName, - changesetId: currentVersion.changesetId, - changesetIndex: currentVersion.changesetIndex, - description: currentVersion.description, - createdDateTime: currentVersion.createdDateTime, - }, - }, - ); - void pollForInProgressJobs({ - iTwinId: props.iModelConnection.iTwinId as string, - iModelId: props.iModelConnection.iModelId as string, - namedVersionLoaderState: result, - comparisonJobClient: comparisonJobClient, - iModelConnection: props.iModelConnection, - setResult: setResult, - removeRunningJob: removeRunningJob, - getRunningJobs: getRunningJobs, - getDialogOpen: getDialogOpen, - getIsDisposed, - getToastsEnabled, - runOnJobUpdate, - iModelsClient, - }); - } } }; @@ -217,9 +212,7 @@ export function VersionCompareSelectDialogV2(props: VersionCompareSelectDialogV2 @@ -233,7 +226,7 @@ export function VersionCompareSelectDialogV2(props: VersionCompareSelectDialogV2 // TODO: refactor all types in this file they are not dry. We want "type" space // to be as clean as "value" space. -type RunStartComparisonV2Args = { +interface RunStartComparisonV2Args { targetVersion: NamedVersion; comparisonJobClient: IComparisonJobClient; iModelConnection: IModelConnection; @@ -247,16 +240,16 @@ type RunStartComparisonV2Args = { jobAndNamedVersions?: JobAndNamedVersions, ) => Promise; iModelsClient: IModelsClient; -}; +} -type PostOrRunComparisonJobResult = { +interface PostOrRunComparisonJobResult { startedComparison: boolean; comparisonJob?: ComparisonJob; -}; +} -const createOrRunManagerStartComparisonV2 = async ( +async function createOrRunManagerStartComparisonV2( args: RunStartComparisonV2Args, -): Promise => { +): Promise { const jobId = createJobId(args.targetVersion, args.currentVersion); try { args.addPendingJob( @@ -302,30 +295,33 @@ const createOrRunManagerStartComparisonV2 = async ( toastComparisonJobProcessing(args.currentVersion, args.targetVersion); } - const jobAndNamedVersion: JobAndNamedVersions = { - comparisonJob: comparisonJob, - targetNamedVersion: args.targetVersion, - currentNamedVersion: args.currentVersion, - }; - - void args.runOnJobUpdate("JobProcessing", jobAndNamedVersion); + void args.runOnJobUpdate( + "JobProcessing", + { + comparisonJob, + targetNamedVersion: args.targetVersion, + currentNamedVersion: args.currentVersion, + }, + ); - return { startedComparison: false, comparisonJob: comparisonJob }; + return { startedComparison: false, comparisonJob }; } catch (error) { args.removePendingJob(jobId); if (args.getToastsEnabled()) { toastComparisonJobError(args.currentVersion, args.targetVersion); } - const jobAndNamedVersion: JobAndNamedVersions = { - comparisonJob: undefined, - targetNamedVersion: args.targetVersion, - currentNamedVersion: args.currentVersion, - }; - void args.runOnJobUpdate("JobError", jobAndNamedVersion); + void args.runOnJobUpdate( + "JobError", + { + comparisonJob: undefined, + targetNamedVersion: args.targetVersion, + currentNamedVersion: args.currentVersion, + }, + ); return undefined; } -}; +} type handleJobErrorArgs = Omit< RunStartComparisonV2Args, @@ -334,7 +330,7 @@ type handleJobErrorArgs = Omit< comparisonJob: ComparisonJob; }; -const handleJobError: (args: handleJobErrorArgs) => Promise = async (args) => { +async function handleJobError(args: handleJobErrorArgs): Promise { args.addPendingJob(args.comparisonJob.comparisonJob.jobId, { targetNamedVersion: args.targetVersion, currentNamedVersion: args.currentVersion, @@ -358,9 +354,9 @@ const handleJobError: (args: handleJobErrorArgs) => Promise = asy }, 3, ); -}; +} -type PollForInProgressJobsArgs = { +interface PollForInProgressJobsArgs { iTwinId: string; iModelId: string; namedVersionLoaderState?: NamedVersionLoaderState; @@ -371,32 +367,33 @@ type PollForInProgressJobsArgs = { getRunningJobs: () => JobAndNamedVersions[]; getDialogOpen: () => boolean; getIsDisposed: () => boolean; - targetVersion?: NamedVersion; getToastsEnabled: () => boolean; runOnJobUpdate: ( comparisonJobUpdateType: ComparisonJobUpdateType, jobAndNamedVersions?: JobAndNamedVersions, ) => Promise; iModelsClient: IModelsClient; -}; +} -export const pollForInProgressJobs: ( - args: PollForInProgressJobsArgs, -) => Promise = async (args: PollForInProgressJobsArgs) => { +export function pollForInProgressJobs(args: PollForInProgressJobsArgs): void { void pollUntilCurrentRunningJobsCompleteAndToast(args); - if (( + if ( args.namedVersionLoaderState && args.namedVersionLoaderState.namedVersions.entries.length > 0 && - args.getDialogOpen() && !args.getIsDisposed()) - ) + args.getDialogOpen() && + !args.getIsDisposed() + ) { void pollUpdateCurrentEntriesForModal(args); -}; + } +} -const pollUntilCurrentRunningJobsCompleteAndToast = async (args: PollForInProgressJobsArgs) => { +async function pollUntilCurrentRunningJobsCompleteAndToast( + args: PollForInProgressJobsArgs, +): Promise { let isConnectionClosed = false; args.iModelConnection.onClose.addListener(() => { isConnectionClosed = true; }); - const loopDelayInMilliseconds = 5000; - while (shouldProcessRunningJobs({ getRunningJobs: args.getRunningJobs, isConnectionClosed })) { + while (args.getRunningJobs().length > 0 && !isConnectionClosed) { + const loopDelayInMilliseconds = 5000; await new Promise((resolve) => setTimeout(resolve, loopDelayInMilliseconds)); for (const runningJob of args.getRunningJobs()) { try { @@ -414,7 +411,7 @@ const pollUntilCurrentRunningJobsCompleteAndToast = async (args: PollForInProgre isConnectionClosed: isConnectionClosed, getRunningJobs: args.getRunningJobs, getDialogOpen: args.getDialogOpen, - runningJob: runningJob, + runningJob, currentJobRsp: completedJob, removeRunningJob: args.removeRunningJob, comparisonJobClient: args.comparisonJobClient, @@ -429,18 +426,9 @@ const pollUntilCurrentRunningJobsCompleteAndToast = async (args: PollForInProgre } } } -}; - -type ShouldProcessRunningJobArgs = { - isConnectionClosed: boolean; - getRunningJobs: () => JobAndNamedVersions[]; -}; - -const shouldProcessRunningJobs = (args: ShouldProcessRunningJobArgs) => { - return args.getRunningJobs().length > 0 && !args.isConnectionClosed; -}; +} -type ConditionallyToastCompletionArgs = { +interface ConditionallyToastCompletionArgs { isConnectionClosed: boolean; getRunningJobs: () => JobAndNamedVersions[]; getDialogOpen: () => boolean; @@ -455,8 +443,9 @@ type ConditionallyToastCompletionArgs = { jobAndNamedVersions?: JobAndNamedVersions, ) => Promise; iModelsClient: IModelsClient; -}; -const notifyComparisonCompletion = (args: ConditionallyToastCompletionArgs) => { +} + +function notifyComparisonCompletion(args: ConditionallyToastCompletionArgs): void { if (args.currentJobRsp.comparisonJob.status === "Completed") { args.removeRunningJob(args.runningJob?.comparisonJob?.comparisonJob.jobId as string); if (!VersionCompare.manager?.isComparing && !args.getDialogOpen()) { @@ -481,25 +470,28 @@ const notifyComparisonCompletion = (args: ConditionallyToastCompletionArgs) => { void args.runOnJobUpdate("JobComplete", jobAndNamedVersion); } } -}; +} -const pollUpdateCurrentEntriesForModal = async (args: PollForInProgressJobsArgs) => { +async function pollUpdateCurrentEntriesForModal(args: PollForInProgressJobsArgs): Promise { const currentVersionId = args.iModelConnection?.changeset.id; let entries = args.namedVersionLoaderState!.namedVersions.entries.slice(); const currentRunningJobsMap = arrayToMap( args.getRunningJobs(), - (job: JobAndNamedVersions) => { return job.comparisonJob?.comparisonJob.jobId as string; }, + (job) => job.comparisonJob?.comparisonJob.jobId as string, ); - if (areJobsInProgress(entries, args.getRunningJobs)) { - const idEntryMap = arrayToMap(entries, (entry: VersionState) => { return entry.version.id; }); + const jobsAreInProgress = entries.some( + ({ jobStatus }) => jobStatus === "Processing" || jobStatus === "Queued", + ) || args.getRunningJobs().length > 0; + if (jobsAreInProgress) { + const idEntryMap = arrayToMap(entries, (entry) => entry.version.id); let updatingEntries = getUpdatingEntries(entries, currentVersionId, currentRunningJobsMap); const loopDelayInMilliseconds = 5000; - while (isDialogOpenAndNotDisposed(args.getDialogOpen, args.getIsDisposed)) { + while (args.getDialogOpen() && !args.getIsDisposed()) { for (let entry of updatingEntries) { await new Promise((resolve) => setTimeout(resolve, loopDelayInMilliseconds)); - const jobStatusAndJobProgress: JobStatusAndJobProgress = await getJobStatusAndJobProgress({ + const jobStatusAndJobProgress = await getJobStatusAndJobProgress({ comparisonJobClient: args.comparisonJobClient, - entry: entry, + entry, iTwinId: args.iTwinId, iModelId: args.iModelId, currentChangesetId: currentVersionId, @@ -516,65 +508,47 @@ const pollUpdateCurrentEntriesForModal = async (args: PollForInProgressJobsArgs) } } - entries = [...idEntryMap.values()]; + entries = Array.from(idEntryMap.values()); updatingEntries = getUpdatingEntries(entries, currentVersionId, currentRunningJobsMap); - if (isDialogOpenAndNotDisposed(args.getDialogOpen, args.getIsDisposed)) { - const updatedState = { + if (args.getDialogOpen() && !args.getIsDisposed()) { + args.setResult({ namedVersions: { currentVersion: args.namedVersionLoaderState!.namedVersions.currentVersion, - entries: entries, + entries, }, - }; - args.setResult(updatedState); + }); } } } -}; - -const isDialogOpenAndNotDisposed = (getDialogOpen: () => boolean, getIsDisposed: () => boolean) => { - return getDialogOpen() && !getIsDisposed(); -}; - -const areJobsInProgress = ( - entries: VersionState[], - getRunningJobs: () => JobAndNamedVersions[], -) => { - return entries.find( - entry => entry.jobStatus === "Processing" || entry.jobStatus === "Queued", - ) !== undefined || getRunningJobs().length > 0; -}; +} -const getUpdatingEntries = ( +function getUpdatingEntries( entries: VersionState[], currentVersionId: string, currentRunningJobsMap: Map, -) => { +): VersionState[] { return entries.filter((entry) => { - if (entry.jobStatus === "Processing" || entry.jobStatus === "Queued") + if (entry.jobStatus === "Processing" || entry.jobStatus === "Queued") { return true; + } + const jobId = `${entry.version.changesetId}-${currentVersionId}`; return currentRunningJobsMap.has(jobId); }); -}; +} -type PostOrGetComparisonJobParams = { +interface PostOrGetComparisonJobParams { changedElementsClient: IComparisonJobClient; iTwinId: string; iModelId: string; startChangesetId: string; endChangesetId: string; -}; +} -/** -* Post or gets comparison job. -* @returns ComparisonJob -* @throws on a non 2XX response. -*/ async function postOrGetComparisonJob(args: PostOrGetComparisonJobParams): Promise { - let result: ComparisonJob; try { - result = await args.changedElementsClient.getComparisonJob({ + return await args.changedElementsClient.getComparisonJob({ iTwinId: args.iTwinId, iModelId: args.iModelId, jobId: `${args.startChangesetId}-${args.endChangesetId}`, @@ -586,18 +560,15 @@ async function postOrGetComparisonJob(args: PostOrGetComparisonJobParams): Promi if ( error && typeof error === "object" && "code" in error && error.code === "ComparisonNotFound" ) { - result = await args.changedElementsClient.postComparisonJob({ + return args.changedElementsClient.postComparisonJob({ iTwinId: args.iTwinId, iModelId: args.iModelId, startChangesetId: args.startChangesetId, endChangesetId: args.endChangesetId, headers: { "Content-Type": "application/json" }, }); - return result; } throw error; } - - return result; } diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionEntries.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionEntries.tsx index fc22604b..ab66fc90 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionEntries.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionEntries.tsx @@ -27,9 +27,8 @@ export function CurrentVersionEntry(props: CurrentVersionEntryProps): ReactEleme >
{ - props.versionState.version.createdDateTime - ? new Date(props.versionState.version.createdDateTime).toDateString() - : "" + props.versionState.version.createdDateTime && + new Date(props.versionState.version.createdDateTime).toDateString() }
@@ -40,88 +39,6 @@ export function CurrentVersionEntry(props: CurrentVersionEntryProps): ReactEleme ); } -interface DateAndCurrentProps { - createdDate?: string; - children: ReactNode; - jobStatus?: JobStatus; - jobProgress?: JobProgress; -} - -function DateCurrentAndJobInfo(props: DateAndCurrentProps): ReactElement { - const jobBadgeBackground = getJobBackgroundColor(props.jobStatus ?? "Unknown"); - - return ( -
- {props.children} - { - props.jobStatus === undefined || props.jobStatus === "Unknown" - ? <> - : ( - - {`${getLocalizedJobStatusText(props.jobStatus)}`} - - ) - } - { - props.jobProgress === undefined || props.jobProgress.maxProgress === 0 - ? <> - : ( - - {`${IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.progress")}: ${Math.floor((props.jobProgress.currentProgress / props.jobProgress.maxProgress) * 100)}%`} - - ) - } -
- ); -} - -const getJobBackgroundColor = (jobStatus: JobStatus): string => { - const green = "#c3e1af"; - const teal = "#b7e0f2"; - const red = "#efa9a9"; - switch (jobStatus) { - case "Available": - return green; - - case "Queued": - return teal; - - case "Processing": - return teal; - - case "Not Processed": - return ""; - - case "Error": - return red; - - default: - return ""; - } -}; - -const getLocalizedJobStatusText = (jobStatus: JobStatus): string => { - switch (jobStatus) { - case "Available": - return IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.available"); - - case "Queued": - return IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.queued"); - - case "Processing": - return IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.processing"); - - case "Not Processed": - return IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.notProcessed"); - - case "Error": - return IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.error"); - - default: - return IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.notProcessed"); - } -}; - interface VersionNameAndDescriptionProps { version: NamedVersion; isProcessed: boolean; @@ -134,9 +51,10 @@ function VersionNameAndDescription(props: VersionNameAndDescriptionProps): React {props.version.displayName}
- {props.version.description === "" - ? IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.noDescription") - : props.version.description} + { + props.version.description || + IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.noDescription") + }
); @@ -154,6 +72,9 @@ interface VersionListEntryProps { * version as well. */ export function VersionListEntry(props: VersionListEntryProps): ReactElement { + const isProcessed = props.versionState.state === VersionProcessedState.Processed || + (props.versionState.jobStatus !== "Processing" && props.versionState.jobStatus !== "Queued"); + const handleClick = async () => { if ( props.versionState.state !== VersionProcessedState.Processed || @@ -166,55 +87,34 @@ export function VersionListEntry(props: VersionListEntryProps): ReactElement { props.onClicked(props.versionState.version); }; - const getStateDivClassname = () => { - switch (props.versionState.state) { - case VersionProcessedState.Processed: - return "current-empty"; - - case VersionProcessedState.Processing: - return "state-processing"; - - case VersionProcessedState.Unavailable: - default: - return "state-unavailable"; - } + const versionStateMap = { + [VersionProcessedState.Verifying]: { + className: "state-unavailable", + message: "VersionCompare:versionCompare.unavailable", + }, + [VersionProcessedState.Processed]: { + className: "current-empty", + message: "", + }, + [VersionProcessedState.Processing]: { + className: "state-processing", + message: "VersionCompare:versionCompare.processed", + }, + [VersionProcessedState.Unavailable]: { + className: "state-unavailable", + message: "VersionCompare:versionCompare.unavailable", + }, }; - const getStateDivMessage = () => { - switch (props.versionState.state) { - case VersionProcessedState.Processed: - return ""; - case VersionProcessedState.Processing: - return IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.processed"); + const { className, message } = versionStateMap[ + props.versionState.state ?? VersionProcessedState.Unavailable + ]; - case VersionProcessedState.Unavailable: - default: - return IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.unavailable"); - } - }; - const getAvailableDate = () => { - return ( - -
-
{getStateDivMessage()}
-
-
- ); - }; - - const isProcessed = props.versionState.state === VersionProcessedState.Processed || - (props.versionState.jobStatus !== "Processing" && props.versionState.jobStatus !== "Queued"); return (
{ props.versionState.state === VersionProcessedState.Verifying - ? <> + ? ( + <> + + {IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.verifying")} + + + + ) : ( - {IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.verifying")} +
+
{message}
+
- - - : getAvailableDate() + ) + } +
+ ); +} + +interface DateAndCurrentProps { + createdDate?: string; + children: ReactNode; + jobStatus?: JobStatus; + jobProgress?: JobProgress; +} + +function DateCurrentAndJobInfo(props: DateAndCurrentProps): ReactElement { + const colorMap = { + red: "#efa9a9", + green: "#c3e1af", + teal: "#b7e0f2", + }; + + const jobStatusMap = { + "Available": { + backgroundColor: colorMap.green, + text: "VersionCompare:versionCompare.available", + }, + "Queued": { + backgroundColor: colorMap.teal, + text: "VersionCompare:versionCompare.queued", + }, + "Processing": { + backgroundColor: colorMap.teal, + text: "VersionCompare:versionCompare.processing", + }, + "Not Processed": { + backgroundColor: "", + text: "VersionCompare:versionCompare.notProcessed", + }, + "Error": { + backgroundColor: colorMap.red, + text: "VersionCompare:versionCompare.error", + }, + "Unknown": { + backgroundColor: "", + text: "VersionCompare:versionCompare.notProcessed", + }, + }; + + const { backgroundColor, text } = jobStatusMap[props.jobStatus ?? "Unknown"]; + const progress = props.jobProgress && Math.floor( + 100 * props.jobProgress.currentProgress / props.jobProgress.maxProgress, + ); + return ( +
+ {props.children} + { + props.jobStatus && props.jobStatus !== "Unknown" && + + {IModelApp.localization.getLocalizedString(text)} + + } + { + props.jobProgress && props.jobProgress.maxProgress > 0 && + + {`${IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.progress")}: ${progress}`}% + }
); diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionList.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionList.tsx index cbd72698..f09b7019 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionList.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionList.tsx @@ -38,18 +38,17 @@ export function VersionList(props: VersionListProps): ReactElement {
{ - props.entries.map((versionState) => { - const isSelected = props.selectedVersionChangesetId !== undefined && - versionState.version.changesetId === props.selectedVersionChangesetId; - return ( - - ); - }) + props.entries.map((versionState) => ( + + )) } {props.isLoading && }
diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/hooks/useNamedVersionLoader.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/hooks/useNamedVersionLoader.tsx index 3d676d34..01476cdd 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/hooks/useNamedVersionLoader.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/hooks/useNamedVersionLoader.tsx @@ -16,10 +16,10 @@ import type { CurrentNamedVersionAndNamedVersions } from "../models/NamedVersion import { VersionProcessedState } from "../models/VersionProcessedState"; /** Result type for versionLoader. */ -export type NamedVersionLoaderState = { +export interface NamedVersionLoaderState { /** Named versions to display in the list. */ namedVersions: CurrentNamedVersionAndNamedVersions; -}; +} interface UseNamedVersionLoaderResult { isLoading: boolean; @@ -31,14 +31,14 @@ interface UseNamedVersionLoaderResult { * there job status sorted from newest to oldest. This is run during the initial * load of the widget. */ -export const useNamedVersionLoader = ( +export function useNamedVersionLoader( iModelConnection: IModelConnection, iModelsClient: IModelsClient, comparisonJobClient: IComparisonJobClient, setNamedVersionResult: (state: NamedVersionLoaderState) => void, getPendingJobs: () => JobAndNamedVersions[], pageSize: number = 20, -): UseNamedVersionLoaderResult => { +): UseNamedVersionLoaderResult { const [isLoading, setIsLoading] = useState(true); useEffect( () => { @@ -71,7 +71,7 @@ export const useNamedVersionLoader = ( orderby: "changesetIndex", ascendingOrDescending: "desc", }); - if (!currentNamedVersion) + if (!currentNamedVersion) { currentNamedVersion = await getOrCreateCurrentNamedVersion( namedVersions, currentChangeSetId, @@ -79,6 +79,7 @@ export const useNamedVersionLoader = ( iModelId, currentChangeSetIndex, ); + } if (namedVersions.length === 0) { setIsLoading(false); @@ -87,21 +88,21 @@ export const useNamedVersionLoader = ( // Process the named versions const processedNamedVersionsState = await processNamedVersions({ - currentNamedVersion: currentNamedVersion, - namedVersions: namedVersions, - setResultNoNamedVersions: setResultNoNamedVersions, - iModelsClient: iModelsClient, - iModelId: iModelId, + currentNamedVersion, + namedVersions, + setResultNoNamedVersions, + iModelsClient, + iModelId, updatePaging: setIsLoading, - iTwinId: iTwinId, - iModelConnection: iModelConnection, - comparisonJobClient: comparisonJobClient, - getPendingJobs: getPendingJobs, + iTwinId, + iModelConnection, + comparisonJobClient, + getPendingJobs, }); if (processedNamedVersionsState) { if (currentState) { - const updatedState: NamedVersionLoaderState = { + currentState = { namedVersions: { entries: currentState.namedVersions.entries.concat( processedNamedVersionsState.namedVersions.entries, @@ -109,7 +110,6 @@ export const useNamedVersionLoader = ( currentVersion: processedNamedVersionsState?.namedVersions.currentVersion, }, }; - currentState = updatedState; } else { currentState = processedNamedVersionsState; } @@ -132,9 +132,9 @@ export const useNamedVersionLoader = ( [comparisonJobClient, iModelConnection, iModelsClient], ); return { isLoading }; -}; +} -type ProcessNamedVersionsArgs = { +interface ProcessNamedVersionsArgs { namedVersions: NamedVersion[]; currentNamedVersion: NamedVersion; setResultNoNamedVersions: () => void; @@ -145,10 +145,12 @@ type ProcessNamedVersionsArgs = { iModelConnection: IModelConnection; comparisonJobClient: IComparisonJobClient; getPendingJobs: () => JobAndNamedVersions[]; -}; +} -const processNamedVersions = async (args: ProcessNamedVersionsArgs) => { +async function processNamedVersions( + args: ProcessNamedVersionsArgs, +): Promise { const { namedVersions, setResultNoNamedVersions, @@ -171,7 +173,7 @@ const processNamedVersions = async (args: ProcessNamedVersionsArgs) => { if (!sortedAndOffsetNamedVersions || sortedAndOffsetNamedVersions.length === 0) { setResultNoNamedVersions(); updatePaging(false); - return; + return undefined; } const initialComparisonJobStatus: JobStatus = "Unknown"; @@ -196,38 +198,39 @@ const processNamedVersions = async (args: ProcessNamedVersionsArgs) => { }, }; return getComparisonJobInfoForNamedVersions({ - iModelConnection: iModelConnection, - iTwinId: iTwinId, - iModelId: iModelId, + iModelConnection, + iTwinId, + iModelId, namedVersionLoaderState: namedVersionState, - comparisonJobClient: comparisonJobClient, + comparisonJobClient, getPendingJobs, }); -}; +} // create faked named version if current version is not a named version -const getOrCreateCurrentNamedVersion = async ( +async function getOrCreateCurrentNamedVersion( namedVersions: NamedVersion[], currentChangeSetId: string, iModelsClient: IModelsClient, iModelId?: string, currentChangeSetIndex?: number, -): Promise => { - const currentFromNamedVersion = getCurrentFromNamedVersions( - namedVersions, - currentChangeSetId, - currentChangeSetIndex, - ); - if (currentFromNamedVersion) +): Promise { + const currentFromNamedVersion = namedVersions.find((version) => ( + version.changesetId === currentChangeSetId || + version.changesetIndex === currentChangeSetIndex + )); + if (currentFromNamedVersion) { return currentFromNamedVersion; + } const currentFromChangeSet = await getCurrentFromChangeSet( currentChangeSetId, iModelsClient, iModelId, ); - if (currentFromChangeSet) + if (currentFromChangeSet) { return currentFromChangeSet; + } return { id: currentChangeSetId, @@ -239,105 +242,88 @@ const getOrCreateCurrentNamedVersion = async ( description: "", createdDateTime: "", }; -}; - -const getCurrentFromNamedVersions = ( - namedVersions: NamedVersion[], - currentChangeSetId: string, - currentChangeSetIndex?: number, -) => { - const currentNamedVersion = namedVersions.find((version) => { - return ( - version.changesetId === currentChangeSetId || - version.changesetIndex === currentChangeSetIndex - ); - }); - if (currentNamedVersion) { - return currentNamedVersion; - } - - return undefined; -}; +} -const getCurrentFromChangeSet = async ( +async function getCurrentFromChangeSet( currentChangeSetId: string, iModelsClient: IModelsClient, iModelId?: string, -): Promise => { - if (!iModelId) +): Promise { + if (!iModelId) { return undefined; + } const currentChangeSet = await iModelsClient.getChangeset({ - iModelId: iModelId, + iModelId, changesetId: currentChangeSetId, }); - if (currentChangeSet) { - return { - id: currentChangeSet.id, - displayName: currentChangeSet.displayName, - changesetId: currentChangeSet.id, - changesetIndex: currentChangeSet.index, - description: currentChangeSet.description, - createdDateTime: currentChangeSet.pushDateTime, - }; + if (!currentChangeSet) { + return undefined; } - return undefined; -}; + return { + id: currentChangeSet.id, + displayName: currentChangeSet.displayName, + changesetId: currentChangeSet.id, + changesetIndex: currentChangeSet.index, + description: currentChangeSet.description, + createdDateTime: currentChangeSet.pushDateTime, + }; +} -const sortAndSetIndexOfNamedVersions = async ( +async function sortAndSetIndexOfNamedVersions( namedVersions: NamedVersion[], currentNamedVersion: NamedVersion, onError: () => void, iModelsClient: IModelsClient, iModelId: string, -) => { - //if current index is 0 then no need to filter. All change sets are older than current. - const namedVersionsOlderThanCurrentVersion = currentNamedVersion.changesetIndex !== 0 - ? namedVersions.filter(version => version.changesetIndex <= currentNamedVersion.changesetIndex) - : namedVersions; +): Promise { + // If current index is 0, then no need to filter. All change sets are older than current. + const namedVersionsOlderThanCurrentVersion = currentNamedVersion.changesetIndex === 0 + ? namedVersions + : namedVersions.filter( + (version) => version.changesetIndex <= currentNamedVersion.changesetIndex, + ); if (namedVersionsOlderThanCurrentVersion.length === 0) { onError(); - return; + return undefined; } const reversedNamedVersions = namedVersionsOlderThanCurrentVersion; if (reversedNamedVersions[0].changesetIndex === currentNamedVersion.changesetIndex) { - reversedNamedVersions.shift(); //remove current named version + reversedNamedVersions.shift(); // Remove current named version } - // we must offset the named versions , because that changeset is "already applied" to the named version, see this: + // We must offset the named versions, because that changeset is "already applied" + // to the named version, see this: // https://developer.bentley.com/tutorials/changed-elements-api/#221-using-the-api-to-get-changed-elements - // this assuming latest is current - const promises = reversedNamedVersions.map(async (nameVersion) => { - nameVersion.changesetIndex = nameVersion.changesetIndex + 1; - const changesetId = nameVersion.changesetIndex.toString(); - const changeSet = await iModelsClient.getChangeset({ - iModelId: iModelId, - changesetId: changesetId, - }); - nameVersion.changesetId = changeSet?.id ?? nameVersion.changesetId; - return nameVersion; - }); - - return Promise.all(promises); -}; + // This assuming latest is current + return Promise.all( + reversedNamedVersions.map(async (nameVersion) => { + nameVersion.changesetIndex = nameVersion.changesetIndex + 1; + const changesetId = nameVersion.changesetIndex.toString(); + const changeSet = await iModelsClient.getChangeset({ iModelId, changesetId }); + nameVersion.changesetId = changeSet?.id ?? nameVersion.changesetId; + return nameVersion; + }), + ); +} -type ProcessChangesetsArgs = { +interface ProcessChangesetsArgs { iTwinId: string; iModelId: string; namedVersionLoaderState: NamedVersionLoaderState; iModelConnection: IModelConnection; comparisonJobClient: IComparisonJobClient; getPendingJobs: () => JobAndNamedVersions[]; -}; +} -const getComparisonJobInfoForNamedVersions = async (args: ProcessChangesetsArgs) => { +async function getComparisonJobInfoForNamedVersions( + args: ProcessChangesetsArgs, +): Promise { const pendingJobsMap = arrayToMap( args.getPendingJobs(), - (job: JobAndNamedVersions) => { - return createJobId(job.targetNamedVersion, job.currentNamedVersion); - }, + (job) => createJobId(job.targetNamedVersion, job.currentNamedVersion), ); const currentVersionId = args.namedVersionLoaderState.namedVersions .currentVersion?.version.changesetId ?? args.iModelConnection?.changeset.id; @@ -345,22 +331,18 @@ const getComparisonJobInfoForNamedVersions = async (args: ProcessChangesetsArgs) args.namedVersionLoaderState.namedVersions.entries.map(async (entry) => { const jobStatusAndJobProgress: JobStatusAndJobProgress = await getJobStatusAndJobProgress({ comparisonJobClient: args.comparisonJobClient, - entry: entry, + entry, iTwinId: args.iTwinId, iModelId: args.iModelId, currentChangesetId: currentVersionId, }); if (pendingJobsMap.has(`${entry.version.changesetId}-${currentVersionId}`)) { - const jobStatus: JobStatus = "Processing"; return { version: entry.version, state: VersionProcessedState.Processed, - jobStatus: jobStatus, - jobProgress: { - currentProgress: 0, - maxProgress: 1, - }, - }; + jobStatus: "Processing", + jobProgress: { currentProgress: 0, maxProgress: 1 }, + } as const; } return { @@ -371,11 +353,10 @@ const getComparisonJobInfoForNamedVersions = async (args: ProcessChangesetsArgs) }; }), ); - const updatedState = { + return { namedVersions: { currentVersion: args.namedVersionLoaderState.namedVersions.currentVersion, entries: newEntries, }, }; - return updatedState; -}; +} From ee8f1520cfc936eebee790b06ef8489e552c45a4 Mon Sep 17 00:00:00 2001 From: roluk <70327485+roluk@users.noreply.github.com> Date: Wed, 20 Nov 2024 18:51:25 +0200 Subject: [PATCH 05/21] Find all places where state could be externally mutated and remove unnecesary cloning --- .../src/api/VersionCompareManager.ts | 16 ++++++++++------ .../VersionCompareDialogProvider.tsx | 15 ++------------- .../components/VersionCompareSelectModal.tsx | 18 ++---------------- 3 files changed, 14 insertions(+), 35 deletions(-) diff --git a/packages/changed-elements-react/src/api/VersionCompareManager.ts b/packages/changed-elements-react/src/api/VersionCompareManager.ts index 9a60cec8..393d31d0 100644 --- a/packages/changed-elements-react/src/api/VersionCompareManager.ts +++ b/packages/changed-elements-react/src/api/VersionCompareManager.ts @@ -279,9 +279,11 @@ export class VersionCompareManager { IModelVersion.asOfChangeSet(changesetId), ); - // Keep metadata around for UI uses and other queries - this.currentVersion = currentVersion; - this.targetVersion = targetVersion; + // Keep metadata around for UI uses and other queries. We may receive an + // immutable React state, thus a copy is needed in case user ever atttempts + // to mutate the objects. + this.currentVersion = structuredClone(currentVersion); + this.targetVersion = structuredClone(targetVersion); this.loadingProgressEvent.raiseEvent( IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.msg_getChangedElements"), @@ -414,9 +416,11 @@ export class VersionCompareManager { IModelVersion.asOfChangeSet(changesetId), ); - // Keep metadata around for UI uses and other queries - this.currentVersion = currentVersion; - this.targetVersion = targetVersion; + // Keep metadata around for UI uses and other queries. We may receive an + // immutable React state, thus a copy is needed in case user ever atttempts + // to mutate the objects. + this.currentVersion = structuredClone(currentVersion); + this.targetVersion = structuredClone(targetVersion); this.loadingProgressEvent.raiseEvent( IModelApp.localization.getLocalizedString("VersionCompare:versionCompare.msg_getChangedElements"), diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareDialogProvider.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareDialogProvider.tsx index 2aae718f..03872b07 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareDialogProvider.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareDialogProvider.tsx @@ -82,25 +82,14 @@ export function VersionCompareSelectProviderV2( const dialogRunningJobs = useRef(new Map()); const dialogPendingJobs = useRef(new Map()); const addRunningJob = (jobId: string, jobAndNamedVersions: JobAndNamedVersions) => { - dialogRunningJobs.current.set( - jobId, - { - comparisonJob: jobAndNamedVersions.comparisonJob, - targetNamedVersion: jobAndNamedVersions.targetNamedVersion, - currentNamedVersion: jobAndNamedVersions.currentNamedVersion, - }, - ); + dialogRunningJobs.current.set(jobId, jobAndNamedVersions); }; const removeRunningJob = (jobId: string) => { dialogRunningJobs.current.delete(jobId); }; const getRunningJobs = () => Array.from(dialogRunningJobs.current.values()); const addPendingJob = (jobId: string, jobAndNamedVersions: JobAndNamedVersions) => { - dialogPendingJobs.current.set(jobId, { - comparisonJob: jobAndNamedVersions.comparisonJob, - targetNamedVersion: jobAndNamedVersions.targetNamedVersion, - currentNamedVersion: jobAndNamedVersions.currentNamedVersion, - }); + dialogPendingJobs.current.set(jobId, jobAndNamedVersions); }; const removePendingJob = (jobId: string) => { dialogPendingJobs.current.delete(jobId); diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx index 26745dea..57247a38 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx @@ -142,22 +142,8 @@ export function VersionCompareSelectDialogV2(props: VersionCompareSelectDialogV2 createJobId(targetVersion, currentVersion), { comparisonJob: startResult.comparisonJob, - targetNamedVersion: { - id: targetVersion.id, - displayName: targetVersion.displayName, - changesetId: targetVersion.changesetId, - changesetIndex: targetVersion.changesetIndex, - description: targetVersion.description, - createdDateTime: targetVersion.createdDateTime, - }, - currentNamedVersion: { - id: currentVersion.id, - displayName: currentVersion.displayName, - changesetId: currentVersion.changesetId, - changesetIndex: currentVersion.changesetIndex, - description: currentVersion.description, - createdDateTime: currentVersion.createdDateTime, - }, + targetNamedVersion: targetVersion, + currentNamedVersion: currentVersion, }, ); pollForInProgressJobs({ From 320ac08611754d023e09767e1fd41ac9a0405fb6 Mon Sep 17 00:00:00 2001 From: roluk <70327485+roluk@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:46:56 +0200 Subject: [PATCH 06/21] Make useNamedVersionLoader own loading state --- .../components/VersionCompareSelectModal.tsx | 34 +++++- .../hooks/useNamedVersionLoader.tsx | 114 +++++++++--------- 2 files changed, 86 insertions(+), 62 deletions(-) diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx index 57247a38..b55e8ddf 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx @@ -20,10 +20,9 @@ import { import { createJobId, getJobStatusAndJobProgress, runManagerStartComparisonV2, } from "../common/versionCompareV2WidgetUtils"; -import { - useNamedVersionLoader, type NamedVersionLoaderState, -} from "../hooks/useNamedVersionLoader"; +import { useNamedVersionLoader } from "../hooks/useNamedVersionLoader"; import type { JobAndNamedVersions } from "../models/ComparisonJobModels"; +import { CurrentNamedVersionAndNamedVersions } from "../models/NamedVersions.js"; import { VersionProcessedState } from "../models/VersionProcessedState"; import type { VersionState } from "../models/VersionState"; import { V2DialogContext, type ComparisonJobUpdateType } from "./VersionCompareDialogProvider"; @@ -80,13 +79,33 @@ export function VersionCompareSelectDialogV2(props: VersionCompareSelectDialogV2 const [targetVersion, setTargetVersion] = useState(); const [currentVersion, setCurrentVersion] = useState(); const [result, setResult] = useState(); - const { isLoading } = useNamedVersionLoader( + const { isLoading, ...rest } = useNamedVersionLoader( props.iModelConnection, iModelsClient, comparisonJobClient, - setResult, getPendingJobs, ); + + // Synchronise with previous result store, WIP + useEffect( + () => { + if (!rest.currentVersion) { + return; + } + + setResult((prev) => ({ + ...prev, + namedVersions: { + currentVersion: rest.currentVersion, + entries: (prev?.namedVersions.entries ?? []).concat( + rest.entries.slice(prev?.namedVersions.entries.length ?? 0), + ), + }, + })); + }, + [rest.currentVersion, rest.entries], + ); + useEffect( () => { let isDisposed = false; @@ -210,6 +229,11 @@ export function VersionCompareSelectDialogV2(props: VersionCompareSelectDialogV2 ); } +interface NamedVersionLoaderState { + /** Named versions to display in the list. */ + namedVersions: CurrentNamedVersionAndNamedVersions; +} + // TODO: refactor all types in this file they are not dry. We want "type" space // to be as clean as "value" space. interface RunStartComparisonV2Args { diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/hooks/useNamedVersionLoader.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/hooks/useNamedVersionLoader.tsx index 01476cdd..320e6318 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/hooks/useNamedVersionLoader.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/hooks/useNamedVersionLoader.tsx @@ -12,17 +12,14 @@ import { createJobId, getJobStatusAndJobProgress } from "../common/versionCompar import type { JobAndNamedVersions, JobProgress, JobStatus, JobStatusAndJobProgress, } from "../models/ComparisonJobModels"; -import type { CurrentNamedVersionAndNamedVersions } from "../models/NamedVersions"; import { VersionProcessedState } from "../models/VersionProcessedState"; - -/** Result type for versionLoader. */ -export interface NamedVersionLoaderState { - /** Named versions to display in the list. */ - namedVersions: CurrentNamedVersionAndNamedVersions; -} +import type { VersionState } from "../models/VersionState.js"; interface UseNamedVersionLoaderResult { isLoading: boolean; + isError: boolean; + entries: VersionState[]; + currentVersion: VersionState | undefined; } /** @@ -35,31 +32,35 @@ export function useNamedVersionLoader( iModelConnection: IModelConnection, iModelsClient: IModelsClient, comparisonJobClient: IComparisonJobClient, - setNamedVersionResult: (state: NamedVersionLoaderState) => void, getPendingJobs: () => JobAndNamedVersions[], pageSize: number = 20, ): UseNamedVersionLoaderResult { - const [isLoading, setIsLoading] = useState(true); + const [result, setResult] = useState({ + isLoading: true, + isError: false, + entries: [], + currentVersion: undefined, + }); useEffect( () => { const setResultNoNamedVersions = () => { - setNamedVersionResult({ - namedVersions: { entries: [], currentVersion: undefined }, + setResult({ + isLoading: false, + isError: false, + entries: [], + currentVersion: undefined, }); }; - const iTwinId = iModelConnection?.iTwinId; - const iModelId = iModelConnection?.iModelId; - const currentChangeSetId = iModelConnection?.changeset.id; - const currentChangeSetIndex = iModelConnection?.changeset.index; - let disposed = false; - if (!iTwinId || !iModelId || !currentChangeSetId) { + const { iTwinId, iModelId, changeset } = iModelConnection; + if (!iTwinId || !iModelId) { setResultNoNamedVersions(); return; } + let disposed = false; void (async () => { let currentNamedVersion: NamedVersion | undefined; - let currentState: NamedVersionLoaderState | undefined = undefined; + let currentState: Pick | undefined = undefined; let currentPage = 0; while (!disposed) { try { @@ -74,16 +75,17 @@ export function useNamedVersionLoader( if (!currentNamedVersion) { currentNamedVersion = await getOrCreateCurrentNamedVersion( namedVersions, - currentChangeSetId, + changeset.id, iModelsClient, iModelId, - currentChangeSetIndex, + changeset.index, ); } if (namedVersions.length === 0) { - setIsLoading(false); - break; // No more named versions to process + // No more named versions to process + setResult((prev) => ({ ...prev, isLoading: false })); + break; } // Process the named versions @@ -93,7 +95,7 @@ export function useNamedVersionLoader( setResultNoNamedVersions, iModelsClient, iModelId, - updatePaging: setIsLoading, + updatePaging: (isPaging) => setResult((prev) => ({ ...prev, isLoading: isPaging })), iTwinId, iModelConnection, comparisonJobClient, @@ -103,23 +105,26 @@ export function useNamedVersionLoader( if (processedNamedVersionsState) { if (currentState) { currentState = { - namedVersions: { - entries: currentState.namedVersions.entries.concat( - processedNamedVersionsState.namedVersions.entries, - ), - currentVersion: processedNamedVersionsState?.namedVersions.currentVersion, - }, + entries: currentState.entries.concat(processedNamedVersionsState.entries), + currentVersion: processedNamedVersionsState?.currentVersion, }; } else { currentState = processedNamedVersionsState; } - setNamedVersionResult(currentState); + const localCurrentState = currentState; + setResult((prev) => ({ + ...prev, + currentVersion: localCurrentState.currentVersion, + entries: localCurrentState.entries, + })); } currentPage++; } catch (error) { - setIsLoading(false); + // eslint-disable-next-line no-console + console.error(error); + setResult((prev) => ({ ...prev, isError: true, isLoading: false })); break; } } @@ -131,7 +136,7 @@ export function useNamedVersionLoader( // eslint-disable-next-line react-hooks/exhaustive-deps [comparisonJobClient, iModelConnection, iModelsClient], ); - return { isLoading }; + return result; } interface ProcessNamedVersionsArgs { @@ -147,10 +152,9 @@ interface ProcessNamedVersionsArgs { getPendingJobs: () => JobAndNamedVersions[]; } - async function processNamedVersions( args: ProcessNamedVersionsArgs, -): Promise { +): Promise | undefined> { const { namedVersions, setResultNoNamedVersions, @@ -181,20 +185,18 @@ async function processNamedVersions( currentProgress: 0, maxProgress: 0, }; - const namedVersionState: NamedVersionLoaderState = { - namedVersions: { - entries: sortedAndOffsetNamedVersions.map((namedVersion) => ({ - version: namedVersion, - state: VersionProcessedState.Verifying, - jobStatus: initialComparisonJobStatus, - jobProgress: initialJobProgress, - })), - currentVersion: { - version: currentNamedVersion, - state: VersionProcessedState.Processed, - jobStatus: initialComparisonJobStatus, - jobProgress: initialJobProgress, - }, + const namedVersionState: Pick = { + entries: sortedAndOffsetNamedVersions.map((namedVersion) => ({ + version: namedVersion, + state: VersionProcessedState.Verifying, + jobStatus: initialComparisonJobStatus, + jobProgress: initialJobProgress, + })), + currentVersion: { + version: currentNamedVersion, + state: VersionProcessedState.Processed, + jobStatus: initialComparisonJobStatus, + jobProgress: initialJobProgress, }, }; return getComparisonJobInfoForNamedVersions({ @@ -312,7 +314,7 @@ async function sortAndSetIndexOfNamedVersions( interface ProcessChangesetsArgs { iTwinId: string; iModelId: string; - namedVersionLoaderState: NamedVersionLoaderState; + namedVersionLoaderState: Pick; iModelConnection: IModelConnection; comparisonJobClient: IComparisonJobClient; getPendingJobs: () => JobAndNamedVersions[]; @@ -320,15 +322,15 @@ interface ProcessChangesetsArgs { async function getComparisonJobInfoForNamedVersions( args: ProcessChangesetsArgs, -): Promise { +): Promise> { const pendingJobsMap = arrayToMap( args.getPendingJobs(), (job) => createJobId(job.targetNamedVersion, job.currentNamedVersion), ); - const currentVersionId = args.namedVersionLoaderState.namedVersions - .currentVersion?.version.changesetId ?? args.iModelConnection?.changeset.id; + const currentVersionId = args.namedVersionLoaderState.currentVersion?.version.changesetId + ?? args.iModelConnection?.changeset.id; const newEntries = await Promise.all( - args.namedVersionLoaderState.namedVersions.entries.map(async (entry) => { + args.namedVersionLoaderState.entries.map(async (entry) => { const jobStatusAndJobProgress: JobStatusAndJobProgress = await getJobStatusAndJobProgress({ comparisonJobClient: args.comparisonJobClient, entry, @@ -354,9 +356,7 @@ async function getComparisonJobInfoForNamedVersions( }), ); return { - namedVersions: { - currentVersion: args.namedVersionLoaderState.namedVersions.currentVersion, - entries: newEntries, - }, + currentVersion: args.namedVersionLoaderState.currentVersion, + entries: newEntries, }; } From a253dc3a5cb9d186e8871399f51f62beaad0a171 Mon Sep 17 00:00:00 2001 From: roluk <70327485+roluk@users.noreply.github.com> Date: Fri, 22 Nov 2024 14:31:52 +0200 Subject: [PATCH 07/21] Simplify file hierarchy --- packages/changed-elements-react/src/index.ts | 10 ++--- .../src/widgets/ChangedElementsWidget.tsx | 6 +-- ...omparisonJobModels.ts => NamedVersions.ts} | 38 +++++++++++++------ .../VersionCompareDialogProvider.tsx | 2 +- .../VersionCompareSelectComponent.tsx | 2 +- ...scss => VersionCompareSelectDialogV2.scss} | 0 ...l.tsx => VersionCompareSelectDialogV2.tsx} | 16 ++++---- .../VersionCompareSelectorInner.tsx | 2 +- .../components/VersionEntries.tsx | 6 +-- .../components/VersionList.tsx | 2 +- .../models/NamedVersions.ts | 13 ------- .../models/VersionProcessedState.ts | 15 -------- .../models/VersionState.ts | 19 ---------- .../{hooks => }/useNamedVersionLoader.tsx | 17 ++++----- .../{common => }/versionCompareToasts.ts | 10 ++--- .../versionCompareV2WidgetUtils.ts | 13 ++++--- 16 files changed, 69 insertions(+), 102 deletions(-) rename packages/changed-elements-react/src/widgets/comparisonJobWidget/{models/ComparisonJobModels.ts => NamedVersions.ts} (61%) rename packages/changed-elements-react/src/widgets/comparisonJobWidget/components/{styles/ComparisonJobWidget.scss => VersionCompareSelectDialogV2.scss} (100%) rename packages/changed-elements-react/src/widgets/comparisonJobWidget/components/{VersionCompareSelectModal.tsx => VersionCompareSelectDialogV2.tsx} (97%) delete mode 100644 packages/changed-elements-react/src/widgets/comparisonJobWidget/models/NamedVersions.ts delete mode 100644 packages/changed-elements-react/src/widgets/comparisonJobWidget/models/VersionProcessedState.ts delete mode 100644 packages/changed-elements-react/src/widgets/comparisonJobWidget/models/VersionState.ts rename packages/changed-elements-react/src/widgets/comparisonJobWidget/{hooks => }/useNamedVersionLoader.tsx (95%) rename packages/changed-elements-react/src/widgets/comparisonJobWidget/{common => }/versionCompareToasts.ts (94%) rename packages/changed-elements-react/src/widgets/comparisonJobWidget/{common => }/versionCompareV2WidgetUtils.ts (91%) diff --git a/packages/changed-elements-react/src/index.ts b/packages/changed-elements-react/src/index.ts index 9a9d2e09..7b6c19ad 100644 --- a/packages/changed-elements-react/src/index.ts +++ b/packages/changed-elements-react/src/index.ts @@ -29,11 +29,11 @@ export * from "./contentviews/PropertyComparisonTable.js"; export type { FilterData, FilterOptions, SavedFiltersManager } from "./SavedFiltersManager.js"; export { VersionCompareContext, type VersionCompareContextValue } from "./VersionCompareContext.js"; export * from "./widgets/ChangedElementsWidget.js"; -export * from "./widgets/comparisonJobWidget/common/versionCompareToasts.js"; +export * from "./widgets/comparisonJobWidget/versionCompareToasts.js"; export * from "./widgets/comparisonJobWidget/components/VersionCompareDialogProvider.js"; -export * from "./widgets/comparisonJobWidget/components/VersionCompareSelectModal.js"; -export type { - JobAndNamedVersions -} from "./widgets/comparisonJobWidget/models/ComparisonJobModels.js"; +export { + VersionCompareSelectDialogV2, pollForInProgressJobs, +} from "./widgets/comparisonJobWidget/components/VersionCompareSelectDialogV2.js"; +export type { JobAndNamedVersions } from "./widgets/comparisonJobWidget/NamedVersions.js"; export { ChangedElementsListComponent } from "./widgets/EnhancedElementsInspector.js"; export * from "./widgets/VersionCompareSelectWidget.js"; diff --git a/packages/changed-elements-react/src/widgets/ChangedElementsWidget.tsx b/packages/changed-elements-react/src/widgets/ChangedElementsWidget.tsx index 79b53deb..4e2199d4 100644 --- a/packages/changed-elements-react/src/widgets/ChangedElementsWidget.tsx +++ b/packages/changed-elements-react/src/widgets/ChangedElementsWidget.tsx @@ -26,13 +26,13 @@ import { ChangedElementsInspector } from "./EnhancedElementsInspector.js"; import { FeedbackButton } from "./FeedbackButton.js"; import InfoButton from "./InformationButton.js"; import { VersionCompareSelectDialog } from "./VersionCompareSelectWidget.js"; +import type { JobAndNamedVersions } from "./comparisonJobWidget/NamedVersions.js"; import { VersionCompareSelectProviderV2, type ComparisonJobUpdateType, } from "./comparisonJobWidget/components/VersionCompareDialogProvider.js"; import { - VersionCompareSelectDialogV2 -} from "./comparisonJobWidget/components/VersionCompareSelectModal.js"; -import type { JobAndNamedVersions } from "./comparisonJobWidget/models/ComparisonJobModels.js"; + VersionCompareSelectDialogV2, +} from "./comparisonJobWidget/components/VersionCompareSelectDialogV2.js"; import "./ChangedElementsWidget.scss"; diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/models/ComparisonJobModels.ts b/packages/changed-elements-react/src/widgets/comparisonJobWidget/NamedVersions.ts similarity index 61% rename from packages/changed-elements-react/src/widgets/comparisonJobWidget/models/ComparisonJobModels.ts rename to packages/changed-elements-react/src/widgets/comparisonJobWidget/NamedVersions.ts index 0843c24c..5d40fd0d 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/models/ComparisonJobModels.ts +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/NamedVersions.ts @@ -2,8 +2,32 @@ * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { ComparisonJob } from "../../../clients/IComparisonJobClient"; -import { NamedVersion } from "../../../clients/iModelsClient"; +import type { ComparisonJob } from "../../clients/IComparisonJobClient.js"; +import type { NamedVersion } from "../../clients/iModelsClient.js"; + +/** + * Holds the version state of named versions and the current version. +*/ +export interface CurrentNamedVersionAndNamedVersions { + entries: VersionState[]; + currentVersion: VersionState | undefined; +} + +export type VersionState = { + version: NamedVersion; + state: VersionProcessedState; + // nullable because we don't run jobs in V1. For v2 use only. + jobStatus?: JobStatus; + // nullable because we don't run jobs in V1. For v2 use only. + jobProgress?: JobProgress; +}; + +export enum VersionProcessedState { + Verifying, + Processed, + Processing, + Unavailable, +} /** * Job status used for identification of job progress @@ -17,26 +41,16 @@ import { NamedVersion } from "../../../clients/iModelsClient"; */ export type JobStatus = "Unknown" | "Available" | "Not Processed" | "Processing" | "Error" | "Queued"; -/** - * Used to display progress of a job. - * current progress / maximum progress. -*/ export type JobProgress = { currentProgress: number; maxProgress: number; }; -/** - * Holds both the job progress and job status. -*/ export type JobStatusAndJobProgress = { jobStatus: JobStatus; jobProgress: JobProgress; }; -/** - * Holds comparison job and its named versions. -*/ export type JobAndNamedVersions = { comparisonJob?: ComparisonJob; targetNamedVersion: NamedVersion; diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareDialogProvider.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareDialogProvider.tsx index 03872b07..7b0fc74c 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareDialogProvider.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareDialogProvider.tsx @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { createContext, type ReactElement, type ReactNode, useRef } from "react"; -import type { JobAndNamedVersions } from "../models/ComparisonJobModels"; +import type { JobAndNamedVersions } from "../NamedVersions.js"; /** * Comparison Job Update Type diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectComponent.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectComponent.tsx index c37faa77..e7790a3d 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectComponent.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectComponent.tsx @@ -8,7 +8,7 @@ import { useState, type ReactNode } from "react"; import type { ChangesetChunk } from "../../../api/ChangedElementsApiClient"; import type { NamedVersion } from "../../../clients/iModelsClient"; -import type { CurrentNamedVersionAndNamedVersions } from "../models/NamedVersions"; +import type { CurrentNamedVersionAndNamedVersions } from "../NamedVersions"; import { VersionCompareSelectorInner } from "./VersionCompareSelectorInner"; /** Options for VersionCompareSelectComponent. */ diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/styles/ComparisonJobWidget.scss b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectDialogV2.scss similarity index 100% rename from packages/changed-elements-react/src/widgets/comparisonJobWidget/components/styles/ComparisonJobWidget.scss rename to packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectDialogV2.scss diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectDialogV2.tsx similarity index 97% rename from packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx rename to packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectDialogV2.tsx index b55e8ddf..7fc5fc5f 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectModal.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectDialogV2.tsx @@ -14,21 +14,21 @@ import { import type { IModelsClient, NamedVersion } from "../../../clients/iModelsClient"; import { arrayToMap, tryXTimes } from "../../../utils/utils"; import { useVersionCompare } from "../../../VersionCompareContext"; +import { + VersionProcessedState, type CurrentNamedVersionAndNamedVersions, type JobAndNamedVersions, + type VersionState, +} from "../NamedVersions.js"; +import { useNamedVersionLoader } from "../useNamedVersionLoader.js"; import { toastComparisonJobComplete, toastComparisonJobError, toastComparisonJobProcessing, -} from "../common/versionCompareToasts"; +} from "../versionCompareToasts"; import { createJobId, getJobStatusAndJobProgress, runManagerStartComparisonV2, -} from "../common/versionCompareV2WidgetUtils"; -import { useNamedVersionLoader } from "../hooks/useNamedVersionLoader"; -import type { JobAndNamedVersions } from "../models/ComparisonJobModels"; -import { CurrentNamedVersionAndNamedVersions } from "../models/NamedVersions.js"; -import { VersionProcessedState } from "../models/VersionProcessedState"; -import type { VersionState } from "../models/VersionState"; +} from "../versionCompareV2WidgetUtils"; import { V2DialogContext, type ComparisonJobUpdateType } from "./VersionCompareDialogProvider"; import { VersionCompareSelectComponent } from "./VersionCompareSelectComponent"; -import "./styles/ComparisonJobWidget.scss"; +import "./VersionCompareSelectDialogV2.scss"; /** Options for VersionCompareSelectDialogV2. */ export interface VersionCompareSelectDialogV2Props { diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectorInner.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectorInner.tsx index 063e09bf..a4b97862 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectorInner.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectorInner.tsx @@ -7,7 +7,7 @@ import { Text } from "@itwin/itwinui-react"; import { ReactNode } from "react"; import type { NamedVersion } from "../../../clients/iModelsClient"; -import type { VersionState } from "../models/VersionState"; +import type { VersionState } from "../NamedVersions.js"; import { ManageNamedVersions } from "./VersionCompareManageNamedVersions"; import { CurrentVersionEntry } from "./VersionEntries"; import { VersionList } from "./VersionList"; diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionEntries.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionEntries.tsx index ab66fc90..93296c56 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionEntries.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionEntries.tsx @@ -7,9 +7,9 @@ import { Badge, ProgressLinear, Radio, Text } from "@itwin/itwinui-react"; import type { ReactElement, ReactNode } from "react"; import type { NamedVersion } from "../../../clients/iModelsClient"; -import type { JobProgress, JobStatus } from "../models/ComparisonJobModels"; -import { VersionProcessedState } from "../models/VersionProcessedState"; -import type { VersionState } from "../models/VersionState"; +import { + VersionProcessedState, type JobProgress, type JobStatus, type VersionState, +} from "../NamedVersions.js"; interface CurrentVersionEntryProps { versionState: VersionState; diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionList.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionList.tsx index f09b7019..e10378c5 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionList.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionList.tsx @@ -7,7 +7,7 @@ import { LoadingSpinner } from "@itwin/core-react"; import type { ReactElement } from "react"; import type { NamedVersion } from "../../../clients/iModelsClient"; -import type { VersionState } from "../models/VersionState"; +import type { VersionState } from "../NamedVersions.js"; import { VersionListEntry } from "./VersionEntries"; interface VersionListProps { diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/models/NamedVersions.ts b/packages/changed-elements-react/src/widgets/comparisonJobWidget/models/NamedVersions.ts deleted file mode 100644 index 0d4051a1..00000000 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/models/NamedVersions.ts +++ /dev/null @@ -1,13 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Bentley Systems, Incorporated. All rights reserved. -* See LICENSE.md in the project root for license terms and full copyright notice. -*--------------------------------------------------------------------------------------------*/ -import { VersionState } from "./VersionState"; - -/** - * Holds the version state of named versions and the current version. -*/ -export interface CurrentNamedVersionAndNamedVersions { - entries: VersionState[]; - currentVersion: VersionState | undefined; -} diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/models/VersionProcessedState.ts b/packages/changed-elements-react/src/widgets/comparisonJobWidget/models/VersionProcessedState.ts deleted file mode 100644 index 96622c88..00000000 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/models/VersionProcessedState.ts +++ /dev/null @@ -1,15 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Bentley Systems, Incorporated. All rights reserved. -* See LICENSE.md in the project root for license terms and full copyright notice. -*--------------------------------------------------------------------------------------------*/ - -/** - * Give state of named version. - * Helps with finding data seeding and processing state of named version. -*/ -export enum VersionProcessedState { - Verifying, - Processed, - Processing, - Unavailable, -} diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/models/VersionState.ts b/packages/changed-elements-react/src/widgets/comparisonJobWidget/models/VersionState.ts deleted file mode 100644 index fff94d29..00000000 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/models/VersionState.ts +++ /dev/null @@ -1,19 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Bentley Systems, Incorporated. All rights reserved. -* See LICENSE.md in the project root for license terms and full copyright notice. -*--------------------------------------------------------------------------------------------*/ -import { NamedVersion } from "../../../clients/iModelsClient"; -import { VersionProcessedState } from "./VersionProcessedState"; -import { JobProgress, JobStatus } from "./ComparisonJobModels"; - -/** - * Holds the state of of the version and its subsequent meta data. -*/ -export type VersionState = { - version: NamedVersion; - state: VersionProcessedState; - // nullable because we don't run jobs in V1. For v2 use only. - jobStatus?: JobStatus; - // nullable because we don't run jobs in V1. For v2 use only. - jobProgress?: JobProgress; -}; diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/hooks/useNamedVersionLoader.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/useNamedVersionLoader.tsx similarity index 95% rename from packages/changed-elements-react/src/widgets/comparisonJobWidget/hooks/useNamedVersionLoader.tsx rename to packages/changed-elements-react/src/widgets/comparisonJobWidget/useNamedVersionLoader.tsx index 320e6318..d7e3e4bf 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/hooks/useNamedVersionLoader.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/useNamedVersionLoader.tsx @@ -5,15 +5,14 @@ import { IModelApp, type IModelConnection } from "@itwin/core-frontend"; import { useEffect, useState } from "react"; -import type { IComparisonJobClient } from "../../../clients/IComparisonJobClient"; -import type { IModelsClient, NamedVersion } from "../../../clients/iModelsClient"; -import { arrayToMap } from "../../../utils/utils"; -import { createJobId, getJobStatusAndJobProgress } from "../common/versionCompareV2WidgetUtils"; -import type { - JobAndNamedVersions, JobProgress, JobStatus, JobStatusAndJobProgress, -} from "../models/ComparisonJobModels"; -import { VersionProcessedState } from "../models/VersionProcessedState"; -import type { VersionState } from "../models/VersionState.js"; +import type { IComparisonJobClient } from "../../clients/IComparisonJobClient"; +import type { IModelsClient, NamedVersion } from "../../clients/iModelsClient"; +import { arrayToMap } from "../../utils/utils"; +import { + VersionProcessedState, type JobAndNamedVersions, type JobProgress, type JobStatus, + type JobStatusAndJobProgress, type VersionState, +} from "./NamedVersions.js"; +import { createJobId, getJobStatusAndJobProgress } from "./versionCompareV2WidgetUtils"; interface UseNamedVersionLoaderResult { isLoading: boolean; diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareToasts.ts b/packages/changed-elements-react/src/widgets/comparisonJobWidget/versionCompareToasts.ts similarity index 94% rename from packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareToasts.ts rename to packages/changed-elements-react/src/widgets/comparisonJobWidget/versionCompareToasts.ts index d47506d6..60d083e4 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareToasts.ts +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/versionCompareToasts.ts @@ -9,11 +9,11 @@ import { toaster } from "@itwin/itwinui-react"; import type { ComparisonJobCompleted, IComparisonJobClient, -} from "../../../clients/IComparisonJobClient"; -import type { IModelsClient, NamedVersion } from "../../../clients/iModelsClient"; -import type { ComparisonJobUpdateType } from "../components/VersionCompareDialogProvider"; -import type { JobAndNamedVersions } from "../models/ComparisonJobModels"; -import { runManagerStartComparisonV2 } from "./versionCompareV2WidgetUtils"; +} from "../../clients/IComparisonJobClient"; +import type { IModelsClient, NamedVersion } from "../../clients/iModelsClient"; +import type { ComparisonJobUpdateType } from "./components/VersionCompareDialogProvider"; +import type { JobAndNamedVersions } from "./NamedVersions.js"; +import { runManagerStartComparisonV2 } from "./versionCompareV2WidgetUtils.js"; /** Toast Comparison Job Processing. * Outputs toast message following the pattern: diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareV2WidgetUtils.ts b/packages/changed-elements-react/src/widgets/comparisonJobWidget/versionCompareV2WidgetUtils.ts similarity index 91% rename from packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareV2WidgetUtils.ts rename to packages/changed-elements-react/src/widgets/comparisonJobWidget/versionCompareV2WidgetUtils.ts index f009eab8..18a8e7c1 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/common/versionCompareV2WidgetUtils.ts +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/versionCompareV2WidgetUtils.ts @@ -5,14 +5,15 @@ import { Logger } from "@itwin/core-bentley"; import type { IModelConnection } from "@itwin/core-frontend"; -import { VersionCompare } from "../../../api/VersionCompare"; +import { VersionCompare } from "../../api/VersionCompare"; import type { ComparisonJob, ComparisonJobCompleted, ComparisonJobStarted, IComparisonJobClient, -} from "../../../clients/IComparisonJobClient"; -import type { IModelsClient, NamedVersion } from "../../../clients/iModelsClient"; -import type { ComparisonJobUpdateType } from "../components/VersionCompareDialogProvider"; -import type { JobAndNamedVersions, JobStatusAndJobProgress } from "../models/ComparisonJobModels"; -import type { VersionState } from "../models/VersionState"; +} from "../../clients/IComparisonJobClient"; +import type { IModelsClient, NamedVersion } from "../../clients/iModelsClient"; +import type { ComparisonJobUpdateType } from "./components/VersionCompareDialogProvider"; +import type { + JobAndNamedVersions, JobStatusAndJobProgress, VersionState, +} from "./NamedVersions.js"; import { toastComparisonVisualizationStarting } from "./versionCompareToasts"; export interface ManagerStartComparisonV2Args { From 1a0cfd9bee2699003eb398d709972c1b7511ace8 Mon Sep 17 00:00:00 2001 From: roluk <70327485+roluk@users.noreply.github.com> Date: Mon, 25 Nov 2024 14:46:02 +0200 Subject: [PATCH 08/21] Store NamedVersion list and state separately --- .../VersionCompareSelectDialogV2.tsx | 120 ++++++------ .../useNamedVersionLoader.tsx | 183 ++++++++---------- .../versionCompareV2WidgetUtils.ts | 10 +- 3 files changed, 147 insertions(+), 166 deletions(-) diff --git a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectDialogV2.tsx b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectDialogV2.tsx index 7fc5fc5f..295d4a66 100644 --- a/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectDialogV2.tsx +++ b/packages/changed-elements-react/src/widgets/comparisonJobWidget/components/VersionCompareSelectDialogV2.tsx @@ -15,8 +15,7 @@ import type { IModelsClient, NamedVersion } from "../../../clients/iModelsClient import { arrayToMap, tryXTimes } from "../../../utils/utils"; import { useVersionCompare } from "../../../VersionCompareContext"; import { - VersionProcessedState, type CurrentNamedVersionAndNamedVersions, type JobAndNamedVersions, - type VersionState, + VersionProcessedState, type JobAndNamedVersions, type VersionState } from "../NamedVersions.js"; import { useNamedVersionLoader } from "../useNamedVersionLoader.js"; import { @@ -78,40 +77,19 @@ export function VersionCompareSelectDialogV2(props: VersionCompareSelectDialogV2 } = useContext(V2DialogContext); const [targetVersion, setTargetVersion] = useState(); const [currentVersion, setCurrentVersion] = useState(); - const [result, setResult] = useState(); - const { isLoading, ...rest } = useNamedVersionLoader( + const { isLoading, result, setResult } = useNamedVersionLoader( props.iModelConnection, iModelsClient, comparisonJobClient, getPendingJobs, ); - // Synchronise with previous result store, WIP - useEffect( - () => { - if (!rest.currentVersion) { - return; - } - - setResult((prev) => ({ - ...prev, - namedVersions: { - currentVersion: rest.currentVersion, - entries: (prev?.namedVersions.entries ?? []).concat( - rest.entries.slice(prev?.namedVersions.entries.length ?? 0), - ), - }, - })); - }, - [rest.currentVersion, rest.entries], - ); - useEffect( () => { let isDisposed = false; const getIsDisposed = () => isDisposed; openDialog(); - if (result?.namedVersions.entries) { + if (result?.entries) { pollForInProgressJobs({ iTwinId: props.iModelConnection.iTwinId as string, iModelId: props.iModelConnection.iModelId as string, @@ -137,7 +115,7 @@ export function VersionCompareSelectDialogV2(props: VersionCompareSelectDialogV2 [isLoading], ); const _handleOk = async (): Promise => { - if (!comparisonJobClient || !result?.namedVersions || !targetVersion || !currentVersion) { + if (!comparisonJobClient || !result || !targetVersion || !currentVersion) { return; } @@ -208,7 +186,15 @@ export function VersionCompareSelectDialogV2(props: VersionCompareSelectDialogV2 ({ version: entry, ...result.versionState[i] })), + }} manageNamedVersionsSlot={props.manageNamedVersionsSlot} isLoading={isLoading} /> @@ -216,7 +202,7 @@ export function VersionCompareSelectDialogV2(props: VersionCompareSelectDialogV2