diff --git a/package-lock.json b/package-lock.json index 27ad4faf1..a35d956b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8341,9 +8341,9 @@ } }, "node_modules/@matterport/webcomponent": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/@matterport/webcomponent/-/webcomponent-0.1.13.tgz", - "integrity": "sha512-oBM+7Nf4D4N9H7eVxjoAJ+DJl2xvDN2uEKpWu5YO1mFyb2di+++xHZX4qYHFZ5vfiLNEJwFw9xLWqUiCAuCZfQ==", + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/@matterport/webcomponent/-/webcomponent-0.1.21.tgz", + "integrity": "sha512-7Mm6Y0DACTRHP4/D8C0Ao1cJbVtDi/TpKMxfhzVR5qAsA7OtySWcrZP4gfcxjtrgL10Bx7dn3TZlhx6JgXI0Iw==", "dependencies": { "lit": "^2.4.1" }, @@ -71211,7 +71211,7 @@ "@iot-app-kit/core": "3.0.0", "@iot-app-kit/related-table": "3.0.0", "@matterport/r3f": "^0.2.1", - "@matterport/webcomponent": "^0.1.13", + "@matterport/webcomponent": "^0.1.21", "@react-three/drei": "8.11.0", "@react-three/fiber": "7.0.26", "@react-three/postprocessing": "2.6.2", @@ -99089,7 +99089,7 @@ "@iot-app-kit/related-table": "3.0.0", "@iot-app-kit/source-iottwinmaker": "3.0.0", "@matterport/r3f": "^0.2.1", - "@matterport/webcomponent": "^0.1.13", + "@matterport/webcomponent": "^0.1.21", "@react-spring/three": "^9.6.1", "@react-three/drei": "8.11.0", "@react-three/fiber": "7.0.26", @@ -110196,9 +110196,9 @@ "integrity": "sha512-ilf3vaENa4lQ45FewiRonDOVejPrgdRANfLNauIKcPoS1dBIDuP2rDAmzkLDhUt29mtpzEauS1yuabu6Hd/kTQ==" }, "@matterport/webcomponent": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/@matterport/webcomponent/-/webcomponent-0.1.13.tgz", - "integrity": "sha512-oBM+7Nf4D4N9H7eVxjoAJ+DJl2xvDN2uEKpWu5YO1mFyb2di+++xHZX4qYHFZ5vfiLNEJwFw9xLWqUiCAuCZfQ==", + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/@matterport/webcomponent/-/webcomponent-0.1.21.tgz", + "integrity": "sha512-7Mm6Y0DACTRHP4/D8C0Ao1cJbVtDi/TpKMxfhzVR5qAsA7OtySWcrZP4gfcxjtrgL10Bx7dn3TZlhx6JgXI0Iw==", "requires": { "lit": "^2.4.1" } diff --git a/packages/scene-composer/package.json b/packages/scene-composer/package.json index 671f90427..69b2546e0 100644 --- a/packages/scene-composer/package.json +++ b/packages/scene-composer/package.json @@ -139,7 +139,7 @@ "@iot-app-kit/core": "3.0.0", "@iot-app-kit/related-table": "3.0.0", "@matterport/r3f": "^0.2.1", - "@matterport/webcomponent": "^0.1.13", + "@matterport/webcomponent": "^0.1.21", "@react-three/drei": "8.11.0", "@react-three/fiber": "7.0.26", "@react-three/postprocessing": "2.6.2", diff --git a/packages/scene-composer/src/common/GlobalSettings.ts b/packages/scene-composer/src/common/GlobalSettings.ts index e11d00af6..60bafb76a 100644 --- a/packages/scene-composer/src/common/GlobalSettings.ts +++ b/packages/scene-composer/src/common/GlobalSettings.ts @@ -13,7 +13,7 @@ const globalSettings: { featureConfig: FeatureConfig; getSceneObjectFunction: GetSceneObjectFunction | undefined; twinMakerSceneMetadataModule: TwinMakerSceneMetadataModule | undefined; - matterportSdks: Record; + matterportSdks: Record; } = { debugMode: false, dracoDecoder: { enable: true }, @@ -66,7 +66,7 @@ export const setTwinMakerSceneMetadataModule = (twinMakerSceneMetadataModule: Tw notifySubscribers(); }; -export const setMatterportSdk = (sceneId: string, sdk: MpSdk): void => { +export const setMatterportSdk = (sceneId: string, sdk?: MpSdk): void => { globalSettings.matterportSdks[sceneId] = sdk; notifySubscribers(); }; diff --git a/packages/scene-composer/src/components/panels/TopBar.tsx b/packages/scene-composer/src/components/panels/TopBar.tsx index 2dadbee30..2143fe671 100644 --- a/packages/scene-composer/src/components/panels/TopBar.tsx +++ b/packages/scene-composer/src/components/panels/TopBar.tsx @@ -9,6 +9,7 @@ import { ICameraComponentInternal, useStore } from '../../store'; import useActiveCamera from '../../hooks/useActiveCamera'; import { findComponentByType } from '../../utils/nodeUtils'; import { getCameraSettings } from '../../utils/cameraUtils'; +import { getMatterportSdk } from '../../common/GlobalSettings'; // TODO: SpaceBetween is not intended to have custom styles, we should refactor this to use our own spacing component // if these styles are really needed. @@ -23,6 +24,7 @@ export const TopBar: FC = () => { const getSceneNodeByRef = useStore(sceneComposerId)((state) => state.getSceneNodeByRef); const getObject3DBySceneNodeRef = useStore(sceneComposerId)((state) => state.getObject3DBySceneNodeRef); const { setActiveCameraSettings } = useActiveCamera(); + const matterportSdk = getMatterportSdk(sceneComposerId); const intl = useIntl(); const cameraItems = useMemo(() => { @@ -38,7 +40,7 @@ export const TopBar: FC = () => { }); }, [nodeMap]); - const hasCameraView = cameraItems.length > 0; + const hasCameraView = cameraItems.length > 0 && !matterportSdk; const showTopBar = hasCameraView; const setActiveCameraOnItemClick = useCallback( diff --git a/packages/scene-composer/src/components/three-fiber/EntityGroup/index.tsx b/packages/scene-composer/src/components/three-fiber/EntityGroup/index.tsx index 67273c71a..e4c7886ee 100644 --- a/packages/scene-composer/src/components/three-fiber/EntityGroup/index.tsx +++ b/packages/scene-composer/src/components/three-fiber/EntityGroup/index.tsx @@ -80,7 +80,7 @@ const EntityGroup = ({ node }: IEntityGroupProps) => { useEditorState(sceneComposerId); const { addNodeError } = useNodeErrorState(sceneComposerId); - const onClick = useCallback( + const onPointerUp = useCallback( (e) => { e.stopPropagation(); // the most nested object in the click scope should get selected, and not bubble up to the parent. @@ -150,7 +150,7 @@ const EntityGroup = ({ node }: IEntityGroupProps) => { rotation={new Euler(...rotation, 'XYZ')} scale={scale} dispose={null} - onClick={onClick} + onPointerUp={onPointerUp} onPointerEnter={onPointerEnter} onPointerLeave={onPointerLeave} userData={{ nodeRef: !isEnvironmentNode(node) ? nodeRef : undefined, componentTypes }} // Do not add ref for environment nodes diff --git a/packages/scene-composer/src/components/toolbars/floatingToolbar/items/AddObjectMenu.tsx b/packages/scene-composer/src/components/toolbars/floatingToolbar/items/AddObjectMenu.tsx index 8f7a3bdf1..74be09170 100644 --- a/packages/scene-composer/src/components/toolbars/floatingToolbar/items/AddObjectMenu.tsx +++ b/packages/scene-composer/src/components/toolbars/floatingToolbar/items/AddObjectMenu.tsx @@ -20,7 +20,7 @@ import { IColorOverlayComponentInternal, ISceneNodeInternal, useEditorState, use import { extractFileNameExtFromUrl, parseS3BucketFromArn } from '../../../../utils/pathUtils'; import { ToolbarItem } from '../../common/ToolbarItem'; import { ToolbarItemOptions } from '../../common/types'; -import { getGlobalSettings } from '../../../../common/GlobalSettings'; +import { getGlobalSettings, getMatterportSdk } from '../../../../common/GlobalSettings'; import useActiveCamera from '../../../../hooks/useActiveCamera'; import { createNodeWithTransform, findComponentByType, isEnvironmentNode } from '../../../../utils/nodeUtils'; @@ -71,7 +71,7 @@ type ToolbarItemOptionRaw = Omit { +export const AddObjectMenu = (): JSX.Element => { const sceneComposerId = useContext(sceneComposerIdContext); const addComponentInternal = useStore(sceneComposerId)((state) => state.addComponentInternal); const appendSceneNode = useStore(sceneComposerId)((state) => state.appendSceneNode); @@ -85,6 +85,7 @@ export const AddObjectMenu = () => { const enhancedEditingEnabled = getGlobalSettings().featureConfig[COMPOSER_FEATURES.ENHANCED_EDITING]; const { formatMessage } = useIntl(); const { activeCameraSettings, mainCameraObject } = useActiveCamera(); + const matterportSdk = getMatterportSdk(sceneComposerId); const selectedSceneNode = useMemo(() => { return getSceneNodeByRef(selectedSceneNodeRef); @@ -138,6 +139,7 @@ export const AddObjectMenu = () => { { uuid: ObjectTypes.ViewCamera, feature: { name: COMPOSER_FEATURES.CameraView }, + isDisabled: matterportSdk !== undefined, }, { uuid: ObjectTypes.Tag, @@ -154,7 +156,7 @@ export const AddObjectMenu = () => { uuid: ObjectTypes.MotionIndicator, }, ].map(mapToMenuItem), - [showAssetBrowserCallback, sceneContainsEnvironmentModel, selectedSceneNodeRef, isEnvironmentNode], + [showAssetBrowserCallback, sceneContainsEnvironmentModel, selectedSceneNodeRef, isEnvironmentNode, matterportSdk], ); const getRefForParenting = useCallback(() => { diff --git a/packages/scene-composer/src/hooks/useMatterportTags.spec.ts b/packages/scene-composer/src/hooks/useMatterportTags.spec.ts index 77bbdd325..ada1dd95e 100644 --- a/packages/scene-composer/src/hooks/useMatterportTags.spec.ts +++ b/packages/scene-composer/src/hooks/useMatterportTags.spec.ts @@ -1,12 +1,18 @@ import { renderHook } from '@testing-library/react-hooks'; import { MpSdk } from '@matterport/r3f/dist'; -import { IAnchorComponent, ISceneNode } from '../interfaces'; -import { ISceneNodeInternal, useStore } from '../store'; +import { generateUUID } from '../utils/mathUtils'; +import { IAnchorComponent, ISceneNode, KnownComponentType } from '../interfaces'; +import { IDataOverlayComponentInternal, ISceneNodeInternal, useStore } from '../store'; import { MattertagItem, TagItem } from '../utils/matterportTagUtils'; +import { Component } from '../models/SceneModels'; import useMatterportTags from './useMatterportTags'; +jest.mock('../utils/mathUtils', () => ({ + generateUUID: jest.fn(() => 'random-uuid'), +})); + describe('useMatterportTags', () => { const appendSceneNode = jest.fn(); const updateSceneNodeInternal = jest.fn(); @@ -54,9 +60,23 @@ describe('useMatterportTags', () => { offset: [mattertagItem.stemVector.x, mattertagItem.stemVector.y, mattertagItem.stemVector.z], }; + const dataOverlayComponent: IDataOverlayComponentInternal = { + ref: generateUUID(), + type: KnownComponentType.DataOverlay, + subType: Component.DataOverlaySubType.OverlayPanel, + valueDataBindings: [], + dataRows: [ + { + rowType: Component.DataOverlayRowType.Markdown, + content: `#### **${mattertagItem.label}** +${mattertagItem.description}`, + }, + ], + }; + const testNode = { name: mattertagItem.label, - components: [anchorComponent], + components: [anchorComponent, dataOverlayComponent], transform: { position: [mattertagItem.anchorPosition.x, mattertagItem.anchorPosition.y, mattertagItem.anchorPosition.z], rotation: [0, 0, 0], @@ -119,6 +139,16 @@ describe('useMatterportTags', () => { ref: '', offset: [mattertagItem.stemVector.x, mattertagItem.stemVector.y, mattertagItem.stemVector.z], }, + { + ...dataOverlayComponent, + dataRows: [ + { + rowType: Component.DataOverlayRowType.Markdown, + content: `#### **${mattertagItem.label}** +${mattertagItem.description}`, + }, + ], + }, ], }); }); @@ -138,6 +168,16 @@ describe('useMatterportTags', () => { ref: '', offset: [tagItem.stemVector.x, tagItem.stemVector.y, tagItem.stemVector.z], }, + { + ...dataOverlayComponent, + dataRows: [ + { + rowType: Component.DataOverlayRowType.Markdown, + content: `#### **${mattertagItem.label}** +${mattertagItem.description}`, + }, + ], + }, ], }); }); diff --git a/packages/scene-composer/src/hooks/useMatterportTags.ts b/packages/scene-composer/src/hooks/useMatterportTags.ts index 52b93b37d..b1578919e 100644 --- a/packages/scene-composer/src/hooks/useMatterportTags.ts +++ b/packages/scene-composer/src/hooks/useMatterportTags.ts @@ -2,9 +2,33 @@ import { useCallback } from 'react'; import { useSceneComposerId } from '../common/sceneComposerIdContext'; import { IAnchorComponent, ISceneNode, KnownComponentType } from '../interfaces'; -import { IAnchorComponentInternal, ISceneNodeInternal, useStore } from '../store'; +import { IAnchorComponentInternal, IDataOverlayComponentInternal, ISceneNodeInternal, useStore } from '../store'; import { MattertagItem, TagItem } from '../utils/matterportTagUtils'; import { RecursivePartial } from '../utils/typeUtils'; +import { Component } from '../models/SceneModels'; +import { generateUUID } from '../utils/mathUtils'; + +const getContentForOverlayComponent = (label: string, description: string): string => { + // Do not change indentation as it affects rendering + return `#### **${label}** +${description}`; +}; + +const getNewDataOverlayComponent = (item: MattertagItem | TagItem): IDataOverlayComponentInternal => { + const dataoverlayComponent: IDataOverlayComponentInternal = { + ref: generateUUID(), + type: KnownComponentType.DataOverlay, + subType: Component.DataOverlaySubType.OverlayPanel, + valueDataBindings: [], + dataRows: [ + { + rowType: Component.DataOverlayRowType.Markdown, + content: getContentForOverlayComponent(item.label, item.description), + }, + ], + }; + return dataoverlayComponent; +}; const addTag = ( addSceneNode: (node: ISceneNode, parentRef?: string) => Readonly, @@ -15,9 +39,10 @@ const addTag = ( type: KnownComponentType.Tag, offset: [item.stemVector.x, item.stemVector.y, item.stemVector.z], }; + const dataoverlayComponent = getNewDataOverlayComponent(item); const node = { name: item.label, - components: [anchorComponent], + components: [anchorComponent, dataoverlayComponent], transform: { position: [item.anchorPosition.x, item.anchorPosition.y, item.anchorPosition.z], rotation: [0, 0, 0], @@ -27,6 +52,7 @@ const addTag = ( matterportId: id, //mattertag uses item.sid and tag uses item.id so we just us the collection key for both }, } as ISceneNode; + addSceneNode(node); }; @@ -45,6 +71,21 @@ const updateTag = ( offset: [item.stemVector.x, item.stemVector.y, item.stemVector.z], } as IAnchorComponentInternal; } + const dataOverlayIndex = node.components.findIndex((elem) => elem.type === KnownComponentType.DataOverlay); + if (dataOverlayIndex !== -1) { + components[dataOverlayIndex] = { + ...components[dataOverlayIndex], + dataRows: [ + { + rowType: Component.DataOverlayRowType.Markdown, + content: getContentForOverlayComponent(item.label, item.description), + }, + ], + } as IDataOverlayComponentInternal; + } else { + const dataoverlayComponent = getNewDataOverlayComponent(item); + components.push(dataoverlayComponent); + } updateSceneNode(ref, { name: item.label, transform: { diff --git a/packages/scene-composer/src/layouts/SceneLayout/SceneLayout.tsx b/packages/scene-composer/src/layouts/SceneLayout/SceneLayout.tsx index 0fea9e918..fbf5c6566 100644 --- a/packages/scene-composer/src/layouts/SceneLayout/SceneLayout.tsx +++ b/packages/scene-composer/src/layouts/SceneLayout/SceneLayout.tsx @@ -84,6 +84,7 @@ const R3FWrapper = (props: { ); } else { + setMatterportSdk(sceneComposerId, undefined); return ( diff --git a/packages/scene-composer/stories/Matterport.stories.mdx b/packages/scene-composer/stories/Matterport.stories.mdx index ded936ee8..9b17cd6a0 100644 --- a/packages/scene-composer/stories/Matterport.stories.mdx +++ b/packages/scene-composer/stories/Matterport.stories.mdx @@ -21,7 +21,8 @@ export const defaultArgs = { COMPOSER_FEATURES.ENHANCED_EDITING, COMPOSER_FEATURES.CameraView, COMPOSER_FEATURES.OpacityRule, - COMPOSER_FEATURES.Matterport + COMPOSER_FEATURES.Matterport, + COMPOSER_FEATURES.Overlay ] }