feat: improve collaboration feedback and previews
This commit is contained in:
@@ -449,14 +449,17 @@ const PointerMissedHandler = ({
|
||||
|
||||
const OutlinerSync = () => {
|
||||
const selection = useViewer((s) => s.selection)
|
||||
const externalSelectedIds = useViewer((s) => s.externalSelectedIds)
|
||||
const hoveredId = useViewer((s) => s.hoveredId)
|
||||
const outliner = useViewer((s) => s.outliner)
|
||||
const geometryRevision = useViewer((s) => s.geometryRevision)
|
||||
const nodes = useScene((s) => s.nodes)
|
||||
|
||||
useEffect(() => {
|
||||
void geometryRevision
|
||||
// Sync selected objects
|
||||
outliner.selectedObjects.length = 0
|
||||
for (const id of selection.selectedIds) {
|
||||
for (const id of new Set([...selection.selectedIds, ...externalSelectedIds])) {
|
||||
const node = nodes[id as AnyNodeId]
|
||||
if (node?.type === 'slab') continue
|
||||
const obj = sceneRegistry.nodes.get(id)
|
||||
@@ -471,7 +474,7 @@ const OutlinerSync = () => {
|
||||
const obj = sceneRegistry.nodes.get(hoveredId)
|
||||
if (obj) outliner.hoveredObjects.push(obj)
|
||||
}
|
||||
}, [selection, hoveredId, outliner, nodes])
|
||||
}, [selection, externalSelectedIds, hoveredId, outliner, nodes, geometryRevision])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
+2
@@ -15,6 +15,8 @@ type ViewerState = {
|
||||
selection: SelectionPath
|
||||
previewSelectedIds: BaseNode['id'][]
|
||||
setPreviewSelectedIds: (ids: BaseNode['id'][]) => void
|
||||
externalSelectedIds: BaseNode['id'][]
|
||||
setExternalSelectedIds: (ids: BaseNode['id'][]) => void
|
||||
hoverHighlightMode: string
|
||||
setHoverHighlightMode: (mode: string) => void
|
||||
hoveredId: AnyNode['id'] | ZoneNode['id'] | null
|
||||
|
||||
@@ -5,6 +5,7 @@ import useViewer from './use-viewer'
|
||||
|
||||
const resetMeasurementPreferences = () => {
|
||||
useViewer.setState({
|
||||
externalSelectedIds: [],
|
||||
projectId: null,
|
||||
projectPreferences: {},
|
||||
showMeasurements: true,
|
||||
@@ -49,3 +50,14 @@ describe('measurement display preferences', () => {
|
||||
expect(useViewer.getState().showMeasurements).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('external selection highlights', () => {
|
||||
test('tracks host-owned highlights without changing the local selection', () => {
|
||||
const localSelection = useViewer.getState().selection
|
||||
|
||||
useViewer.getState().setExternalSelectedIds(['wall_remote'])
|
||||
|
||||
expect(useViewer.getState().externalSelectedIds).toEqual(['wall_remote'])
|
||||
expect(useViewer.getState().selection).toBe(localSelection)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -27,6 +27,10 @@ type ViewerState = {
|
||||
selection: SelectionPath
|
||||
previewSelectedIds: BaseNode['id'][]
|
||||
setPreviewSelectedIds: (ids: BaseNode['id'][]) => void
|
||||
/** Host-owned selection highlights rendered through the viewer's native
|
||||
* selection paths without changing the local user's editable selection. */
|
||||
externalSelectedIds: BaseNode['id'][]
|
||||
setExternalSelectedIds: (ids: BaseNode['id'][]) => void
|
||||
hoverHighlightMode: string
|
||||
setHoverHighlightMode: (mode: string) => void
|
||||
hoveredId: AnyNode['id'] | ZoneNode['id'] | null
|
||||
@@ -314,6 +318,17 @@ const useViewer = create<ViewerState>()(
|
||||
selection: { buildingId: null, levelId: null, zoneId: null, selectedIds: [] },
|
||||
previewSelectedIds: [],
|
||||
setPreviewSelectedIds: (ids) => set({ previewSelectedIds: ids }),
|
||||
externalSelectedIds: [],
|
||||
setExternalSelectedIds: (ids) =>
|
||||
set((state) => {
|
||||
if (
|
||||
state.externalSelectedIds.length === ids.length &&
|
||||
state.externalSelectedIds.every((id, index) => id === ids[index])
|
||||
) {
|
||||
return state
|
||||
}
|
||||
return { externalSelectedIds: ids }
|
||||
}),
|
||||
hoverHighlightMode: 'default',
|
||||
setHoverHighlightMode: (mode) =>
|
||||
set((state) => (state.hoverHighlightMode === mode ? state : { hoverHighlightMode: mode })),
|
||||
|
||||
Reference in New Issue
Block a user