7b037352df
* Fixed an issue with curves doubling up on geometry and also not being selectable after the last filtering changes. Added the options to make lines transparent. Added lines to diffing * Points now are diff-able and support proper visual diff-ing. Visual diff filters are now chosen internally by the Differ. Fixed an issue with LineBatch and transparency * Implemented PLAIN visual diff mode, where all objects keep their original materil, but opacity is manipulated via the diff time. Added API member function to switch between the PLAIN and COLORED visual diff modes * feat(fe2): diffs wip * Diffing fixes for instances and blocks. Things seem to be working fine, but there are some caveats. Additionally, some older issues were fixed and diffing now works better on all the rest of the streams * feat(fe2): de-dupes diff results * feat(fe2): wip diffs * feat(fe2): diff transparency goes from 0 to 1 * feat(fe2): diff results display work * feat(fe2): diff results display work * feat(fe2): diff panel work * feat(fe2): diff work: various display changes, coloring toggle, selection logic, selection object display wip * feat(fe2): diff work: cleaned up old/new version, fixed minor bug in viewer diff time when swapping color mode * feat(fe2): diff work: implements custom selection logic and selection display for modified objects (they come in pairs now) * feat(fe2): diff minor fix in selected object display * feat(fe2): wip; trying to fix diff order to be consistent (ordered by date) * feat(fe2): wip, broken state right now * feat(fe2): fixes scrollbars in viewer * feat(fe2): fixes slider sync with diff time * feat(fe2): WIP syncs of diffs (threads, refreshes, etc.) * feat(fe2): diffing polish * speckle shared fix * speckle shared fix * more bugfixes * linter fixess * more CI fixes * fix viewerState serialization * more linting fixess * template fixes * moving tailwind classes to theme package * migrated away from diffString + simplified postSetup * moved diff new/old version resolution to use state.resources * cleanup * updating url threadId & diff command correctly * minor improvements to diff state --------- Co-authored-by: AlexandruPopovici <alexandrupopoviciioan@gmail.com> Co-authored-by: Kristaps Fabians Geikins <fabis94@live.com>
153 lines
4.4 KiB
TypeScript
153 lines
4.4 KiB
TypeScript
import { Nullable, Optional, SpeckleViewer } from '@speckle/shared'
|
|
import { DiffResult, VisualDiffMode } from '@speckle/viewer'
|
|
import { ViewerModelVersionCardItemFragment } from '~~/lib/common/generated/gql/graphql'
|
|
import {
|
|
InitialStateWithUrlHashState,
|
|
InjectableViewerState
|
|
} from '~~/lib/viewer/composables/setup'
|
|
import {
|
|
isObjectLike,
|
|
has,
|
|
get,
|
|
isArray,
|
|
differenceBy,
|
|
sortBy,
|
|
isString
|
|
} from 'lodash-es'
|
|
|
|
export function setupUiDiffState(
|
|
state: InitialStateWithUrlHashState
|
|
): InjectableViewerState['ui']['diff'] {
|
|
const {
|
|
urlHashState: { diff },
|
|
resources: {
|
|
response: { availableModelsAndVersions }
|
|
}
|
|
} = state
|
|
const result = shallowRef(undefined as Optional<DiffResult>)
|
|
const time = ref(0.5)
|
|
const mode = ref<VisualDiffMode>(VisualDiffMode.COLORED)
|
|
|
|
// TODO: Only single diff for now
|
|
const getVersion = (type: keyof DiffInstruction) => {
|
|
const instruction = diff.value?.diffs[0]
|
|
if (!instruction) return undefined
|
|
|
|
const model = availableModelsAndVersions.value.find(
|
|
(m) => m.model.id === instruction[type].modelId
|
|
)
|
|
if (!model) return undefined
|
|
return model.versions.find((v) => v.id === instruction[type].versionId)
|
|
}
|
|
|
|
const versionA = computed(
|
|
(): Optional<ViewerModelVersionCardItemFragment> => getVersion('versionA')
|
|
)
|
|
const versionB = computed(
|
|
(): Optional<ViewerModelVersionCardItemFragment> => getVersion('versionB')
|
|
)
|
|
|
|
const sortedActiveVersions = computed(() => {
|
|
if (!versionA.value || !versionB.value) return null
|
|
const sorted = sortBy([versionA.value, versionB.value], (v) =>
|
|
new Date(v.createdAt).getTime()
|
|
)
|
|
|
|
return {
|
|
oldVersion: sorted[0],
|
|
newVersion: sorted[1]
|
|
}
|
|
})
|
|
|
|
const enabled = computed(() => !!(diff.value && sortedActiveVersions.value))
|
|
|
|
return {
|
|
newVersion: computed(() => sortedActiveVersions.value?.newVersion),
|
|
oldVersion: computed(() => sortedActiveVersions.value?.oldVersion),
|
|
time,
|
|
mode,
|
|
enabled,
|
|
result
|
|
}
|
|
}
|
|
|
|
export type DiffInstruction = {
|
|
versionA: SpeckleViewer.ViewerRoute.ViewerVersionResource
|
|
versionB: SpeckleViewer.ViewerRoute.ViewerVersionResource
|
|
}
|
|
|
|
export type DiffStateCommand = {
|
|
diffs: DiffInstruction[]
|
|
}
|
|
|
|
export function useDiffBuilderUtilities() {
|
|
const serializeDiffCommand = (command: DiffStateCommand): string =>
|
|
JSON.stringify(command)
|
|
|
|
const deserializeDiffCommand = (
|
|
command: Nullable<string>
|
|
): Nullable<DiffStateCommand> => {
|
|
if (!command) return null
|
|
|
|
try {
|
|
const deserializedCommand = JSON.parse(command) as unknown
|
|
if (!isObjectLike(deserializedCommand)) throw new Error('Invalid structure')
|
|
if (!has(deserializedCommand, 'diffs')) throw new Error('Invalid structure')
|
|
|
|
const diffs = get(deserializedCommand, 'diffs') as unknown
|
|
if (!isArray(diffs)) throw new Error('Invalid structure')
|
|
|
|
const finalDiffs: DiffInstruction[] = []
|
|
for (const diff of diffs) {
|
|
const getResource = (
|
|
val: unknown
|
|
): Nullable<SpeckleViewer.ViewerRoute.ViewerVersionResource> => {
|
|
const valString = isString(val) ? val : null
|
|
if (!valString) return null
|
|
|
|
const [resource] = SpeckleViewer.ViewerRoute.parseUrlParameters(valString)
|
|
if (!resource || !SpeckleViewer.ViewerRoute.isModelResource(resource))
|
|
return null
|
|
|
|
const modelId = resource.modelId
|
|
const versionId = resource.versionId
|
|
if (!modelId || !versionId) return null
|
|
|
|
return new SpeckleViewer.ViewerRoute.ViewerVersionResource(modelId, versionId)
|
|
}
|
|
|
|
const versionA = getResource(get(diff, 'versionA'))
|
|
const versionB = getResource(get(diff, 'versionB'))
|
|
if (!versionA || !versionB) continue
|
|
|
|
finalDiffs.push({
|
|
versionB,
|
|
versionA
|
|
})
|
|
}
|
|
|
|
if (!finalDiffs.length) throw new Error('No valid resource referneces found')
|
|
return { diffs: finalDiffs }
|
|
} catch (e) {
|
|
console.warn('Diff command deserialization failed', e)
|
|
return null
|
|
}
|
|
}
|
|
|
|
const areDiffsEqual = (a: DiffStateCommand, b: DiffStateCommand): boolean => {
|
|
const differentInstructions = differenceBy(
|
|
a.diffs,
|
|
b.diffs,
|
|
(instruction) =>
|
|
`${instruction.versionA.toString()}->${instruction.versionB.toString()}`
|
|
)
|
|
return differentInstructions.length < 1
|
|
}
|
|
|
|
return {
|
|
serializeDiffCommand,
|
|
deserializeDiffCommand,
|
|
areDiffsEqual
|
|
}
|
|
}
|