Files
speckle-server/packages/server/modules/comments/services/data.ts
T
Kristaps Fabians Geikins a6287fc06d feat(fe2 & server): saved views foundation (list & view) + bits n bobs (#5163)
* init db migration

* WIP store view

* create service call

* WIP insertion

* insert sort of works

* moving code arounmd

* creation tests

* avoid duplicate entries

* fixes from main

* basic group retrieval works

* group filtering works

* WIP view listing

* filter by acl

* fixes + WIP single group retrieval

* wip pivot

* more pivot query fixes

* tests fixed after pivot

* views list tests

* fixing test command

* business plan only checks

* more tests for coverage

* .dts import fix

* cli fix

* anutha one

* auth policy tests for business plan access

* WIP saved views panel base

* BE listing adjustments

* WIP group rendering

* group render done

* WIP post create cache updates

* listing fine?

* my vs theirs

* auto open

* minor fixes

* click load omg

* nicely loading views

* type fix

* less spammy loading

* another type fix:

* more lint fix

* test fix

* codecov disable

* moar coverage

* fix sidebar flashin

* more test coverage

* more test cvoverage

* minor adfjustments

* adj

* saved view wipe fixes

* CSR viewer

* more improvements

* extra feature flag checks

* lint fix

* feature flags fix

* more test fixes
2025-08-05 11:52:50 +03:00

180 lines
5.3 KiB
TypeScript

import type {
ConvertLegacyDataToState,
GetViewerResourcesForComments
} from '@/modules/comments/domain/operations'
import type { LegacyCommentViewerData } from '@/modules/core/graph/generated/graphql'
import { viewerResourcesToString } from '@/modules/viewer/services/viewerResources'
import type {
VersionedSerializedViewerState,
SerializedViewerState
} from '@speckle/shared/viewer/state'
import {
formatSerializedViewerState,
isVersionedSerializedViewerState,
inputToVersionedState
} from '@speckle/shared/viewer/state'
import { intersection, isObjectLike } from 'lodash-es'
export type LegacyData = Partial<LegacyCommentViewerData>
export type DataStruct = VersionedSerializedViewerState
export { formatSerializedViewerState }
export const inputToDataStruct = inputToVersionedState
export const isDataStruct = isVersionedSerializedViewerState
export function isLegacyData(data: unknown): data is LegacyData {
if (!data) return false
const keys: Array<keyof LegacyData> = [
'camPos',
'filters',
'location',
'sectionBox',
'selection'
]
if (!isObjectLike(data)) return false
const valKeys = Object.keys(data as Record<string, unknown>)
if (intersection(valKeys, keys).length !== keys.length) return false
return true
}
export function convertStateToLegacyData(state: SerializedViewerState): LegacyData {
const camPos = state.ui.camera.position
const camTarget = state.ui.camera.target
const zoom = state.ui.camera.zoom
const isOrtho = state.ui.camera.isOrthoProjection
const selection = state.ui.selection
const selectionLocation = selection || camTarget
const ret: LegacyData = {
camPos: [
camPos[0] || 0,
camPos[1] || 0,
camPos[2] || 0,
camTarget[0] || 0,
camTarget[1] || 0,
camTarget[2] || 0,
zoom || 1,
isOrtho ? 1 : 0
],
filters: {
passMin: state.viewer.metadata.filteringState?.passMin || null,
passMax: state.viewer.metadata.filteringState?.passMax || null,
hiddenIds: state.ui.filters.hiddenObjectIds.slice(),
isolatedIds: state.ui.filters.isolatedObjectIds.slice(),
sectionBox: {
min: (state.ui?.sectionBox?.min as number[]) || [0, 0, 0],
max: (state.ui?.sectionBox?.max as number[]) || [0, 0, 0],
rotation: (state.ui?.sectionBox?.rotation as number[]) || [
1, 0, 0, 0, 1, 0, 0, 0, 1
]
},
propertyInfoKey: state.ui.filters.propertyFilter.key
},
location: {
x: selectionLocation[0] || 0,
y: selectionLocation[1] || 0,
z: selectionLocation[2] || 0
},
sectionBox: {
min: (state.ui?.sectionBox?.min as number[]) || [0, 0, 0],
max: (state.ui?.sectionBox?.max as number[]) || [0, 0, 0],
rotation: (state.ui?.sectionBox?.rotation as number[]) || [
1, 0, 0, 0, 1, 0, 0, 0, 1
]
},
selection: null
}
return ret
}
export const convertLegacyDataToStateFactory =
(deps: {
getViewerResourcesForComments: GetViewerResourcesForComments
}): ConvertLegacyDataToState =>
async (data, comment) => {
const resources = await deps.getViewerResourcesForComments(comment.streamId, [
comment.id
])
const sectionBox = data.filters?.sectionBox || data.sectionBox
const ret: SerializedViewerState = {
projectId: comment.streamId,
sessionId: 'legacy-sessionId',
viewer: {
metadata: {
filteringState: {
passMax: data.filters?.passMax,
passMin: data.filters?.passMin
}
}
},
resources: {
request: {
resourceIdString: viewerResourcesToString(resources),
threadFilters: {
includeArchived: false,
loadedVersionsOnly: false
}
}
},
ui: {
threads: {
openThread: {
threadId: null,
isTyping: false,
newThreadEditor: true
}
},
spotlightUserSessionId: null,
explodeFactor: 0,
filters: {
isolatedObjectIds: data.filters?.isolatedIds || [],
hiddenObjectIds: data.filters?.hiddenIds || [],
selectedObjectApplicationIds: {},
propertyFilter: {
key: data.filters?.propertyInfoKey || null,
isApplied: true
}
},
camera: {
position: [
data.camPos?.[0] || 0,
data.camPos?.[1] || 0,
data.camPos?.[2] || 0
],
target: [data.camPos?.[3] || 0, data.camPos?.[4] || 0, data.camPos?.[5] || 0],
isOrthoProjection: !!data.camPos?.[6],
zoom: data.camPos?.[7] || 1
},
viewMode: 0,
sectionBox: sectionBox
? {
min: (sectionBox.min as number[]) || [0, 0, 0],
max: (sectionBox.max as number[]) || [0, 0, 0],
rotation: (sectionBox.rotation as number[]) || [1, 0, 0, 0, 1, 0, 0, 0, 1]
}
: null,
lightConfig: {},
selection: data.location
? [
data.location.x as number,
data.location.y as number,
data.location.z as number
]
: null,
diff: {
command: null,
mode: 1,
time: 0.5
},
measurement: {
enabled: false,
options: null
}
}
}
return ret
}