Merge branch 'main' into dim/fe2/newsletter-registration
This commit is contained in:
Коммит
7962d56530
|
@ -14,7 +14,7 @@
|
|||
<!-- Comment bubbles -->
|
||||
<ViewerAnchoredPointThread
|
||||
v-for="thread in Object.values(commentThreads)"
|
||||
v-show="!hideBubbles || thread.isExpanded"
|
||||
v-show="!hideBubbles || isOpenThread(thread.id)"
|
||||
:key="thread.id"
|
||||
:model-value="thread"
|
||||
:class="openThread?.id === thread.id ? 'z-[12]' : 'z-[11]'"
|
||||
|
@ -110,11 +110,14 @@ import {
|
|||
useInjectedViewerInterfaceState,
|
||||
useInjectedViewerState
|
||||
} from '~~/lib/viewer/composables/setup'
|
||||
import { useThreadUtilities } from '~~/lib/viewer/composables/ui'
|
||||
|
||||
const parentEl = ref(null as Nullable<HTMLElement>)
|
||||
const { isLoggedIn } = useActiveUser()
|
||||
const { sessionId } = useInjectedViewerState()
|
||||
const { users } = useViewerUserActivityTracking({ parentEl })
|
||||
const { isOpenThread, open } = useThreadUtilities()
|
||||
|
||||
const canPostComment = useCheckViewerCommentingAccess()
|
||||
|
||||
const followers = computed(() => {
|
||||
|
@ -132,8 +135,7 @@ const {
|
|||
threads: {
|
||||
openThread: { thread: openThread },
|
||||
items: commentThreads,
|
||||
hideBubbles,
|
||||
open
|
||||
hideBubbles
|
||||
}
|
||||
} = useInjectedViewerInterfaceState()
|
||||
|
||||
|
|
|
@ -78,8 +78,8 @@ import {
|
|||
isValidCommentContentInput,
|
||||
convertCommentEditorValueToInput
|
||||
} from '~~/lib/viewer/helpers/comments'
|
||||
import { useInjectedViewerInterfaceState } from '~~/lib/viewer/composables/setup'
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
import { useThreadUtilities } from '~~/lib/viewer/composables/ui'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', v: ViewerNewThreadBubbleModel): void
|
||||
|
@ -90,9 +90,9 @@ const props = defineProps<{
|
|||
modelValue: ViewerNewThreadBubbleModel
|
||||
}>()
|
||||
|
||||
const ui = useInjectedViewerInterfaceState()
|
||||
const { onKeyDownHandler, updateIsTyping, pauseAutomaticUpdates } =
|
||||
useIsTypingUpdateEmitter()
|
||||
const { closeAllThreads, open } = useThreadUtilities()
|
||||
|
||||
const editor = ref(null as Nullable<{ openFilePicker: () => void }>)
|
||||
const commentValue = ref(<CommentEditorValue>{ doc: undefined, attachments: undefined })
|
||||
|
@ -137,7 +137,7 @@ const onSubmit = (comment?: CommentEditorValue) => {
|
|||
if (!threadId) return
|
||||
|
||||
// switch to new thread
|
||||
await ui.threads.open(threadId)
|
||||
await open(threadId)
|
||||
})
|
||||
.finally(() => {
|
||||
isPostingNewThread.value = false
|
||||
|
@ -167,7 +167,7 @@ watch(
|
|||
() => props.modelValue.isExpanded,
|
||||
async (newVal) => {
|
||||
if (newVal) {
|
||||
await ui.threads.closeAllThreads()
|
||||
await closeAllThreads()
|
||||
}
|
||||
commentValue.value = {
|
||||
doc: undefined,
|
||||
|
|
|
@ -189,6 +189,7 @@ import {
|
|||
} from '~~/lib/viewer/composables/serialization'
|
||||
import { useDisableGlobalTextSelection } from '~~/lib/common/composables/window'
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
import { useThreadUtilities } from '~~/lib/viewer/composables/ui'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', v: CommentBubbleModel): void
|
||||
|
@ -220,6 +221,7 @@ const markThreadViewed = useMarkThreadViewed()
|
|||
const { usersTyping } = useViewerThreadTypingTracking(threadId)
|
||||
const { ellipsis, controls } = useAnimatingEllipsis()
|
||||
const applyState = useApplySerializedState()
|
||||
const { isOpenThread, open, closeAllThreads } = useThreadUtilities()
|
||||
|
||||
const commentsContainer = ref(null as Nullable<HTMLElement>)
|
||||
const threadContainer = ref(null as Nullable<HTMLElement>)
|
||||
|
@ -239,7 +241,7 @@ const comments = computed(() => [
|
|||
// width: 320
|
||||
// })
|
||||
|
||||
const isExpanded = computed(() => props.modelValue.isExpanded)
|
||||
const isExpanded = computed(() => isOpenThread(props.modelValue.id))
|
||||
|
||||
const isTypingMessage = computed(() => {
|
||||
if (!usersTyping.value.length) return null
|
||||
|
@ -317,11 +319,13 @@ const threadAuthors = computed(() => {
|
|||
return authors
|
||||
})
|
||||
|
||||
const changeExpanded = (newVal: boolean) => {
|
||||
emit('update:modelValue', {
|
||||
...props.modelValue,
|
||||
isExpanded: newVal
|
||||
})
|
||||
const changeExpanded = async (newVal: boolean) => {
|
||||
if (newVal) {
|
||||
await open(props.modelValue.id)
|
||||
} else {
|
||||
await closeAllThreads()
|
||||
}
|
||||
|
||||
emit('update:expanded', newVal)
|
||||
mp.track('Comment Action', {
|
||||
type: 'action',
|
||||
|
|
|
@ -68,6 +68,7 @@ import { useArchiveComment } from '~~/lib/viewer/composables/commentManagement'
|
|||
import { ToastNotificationType, useGlobalToast } from '~~/lib/common/composables/toast'
|
||||
import { Roles } from '@speckle/shared'
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
import { useThreadUtilities } from '~~/lib/viewer/composables/ui'
|
||||
|
||||
const props = defineProps<{
|
||||
thread: LoadedCommentThread
|
||||
|
@ -75,8 +76,9 @@ const props = defineProps<{
|
|||
|
||||
const { resourceItems } = useInjectedViewerLoadedResources()
|
||||
const {
|
||||
threads: { open: openThreadRaw, openThread }
|
||||
threads: { openThread }
|
||||
} = useInjectedViewerInterfaceState()
|
||||
const { open: openThreadRaw } = useThreadUtilities()
|
||||
|
||||
const mp = useMixpanel()
|
||||
const open = (id: string) => {
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import { CSSProperties, Ref } from 'vue'
|
||||
import { Nullable, SpeckleViewer } from '@speckle/shared'
|
||||
import {
|
||||
InitialStateWithUrlHashState,
|
||||
LoadedCommentThread,
|
||||
useInjectedViewerInterfaceState,
|
||||
useInjectedViewerState,
|
||||
useResetUiState
|
||||
} from '~~/lib/viewer/composables/setup'
|
||||
import { graphql } from '~~/lib/common/generated/gql'
|
||||
import { reduce, difference, debounce } from 'lodash-es'
|
||||
import { debounce } from 'lodash-es'
|
||||
import { Vector3 } from 'three'
|
||||
import {
|
||||
useOnViewerLoadComplete,
|
||||
|
@ -22,7 +21,7 @@ import {
|
|||
useResponsiveHorizontalDirectionCalculation
|
||||
} from '~~/lib/common/composables/window'
|
||||
import { useViewerUserActivityBroadcasting } from '~~/lib/viewer/composables/activity'
|
||||
import { until, useIntervalFn } from '@vueuse/core'
|
||||
import { useIntervalFn } from '@vueuse/core'
|
||||
import {
|
||||
StateApplyMode,
|
||||
useApplySerializedState,
|
||||
|
@ -124,7 +123,6 @@ export type CommentBubbleModel = Merge<
|
|||
LoadedCommentThread,
|
||||
{ viewerState: Nullable<SpeckleViewer.ViewerState.SerializedViewerState> }
|
||||
> & {
|
||||
isExpanded: boolean
|
||||
isOccluded: boolean
|
||||
style: Partial<CSSProperties> & { x?: number; y?: number }
|
||||
}
|
||||
|
@ -161,129 +159,6 @@ export function useViewerCommentBubblesProjection(params: {
|
|||
})
|
||||
}
|
||||
|
||||
export function useViewerCommentBubbles(
|
||||
options?: Partial<{
|
||||
state: InitialStateWithUrlHashState
|
||||
}>
|
||||
) {
|
||||
const {
|
||||
resources: {
|
||||
response: { commentThreads: commentThreadsBase }
|
||||
},
|
||||
urlHashState: { focusedThreadId }
|
||||
} = options?.state || useInjectedViewerState()
|
||||
|
||||
const commentThreads = ref({} as Record<string, CommentBubbleModel>)
|
||||
const openThread = computed(() =>
|
||||
Object.values(commentThreads.value).find(
|
||||
(t) => t.isExpanded && t.id === focusedThreadId.value
|
||||
)
|
||||
)
|
||||
|
||||
useSelectionEvents(
|
||||
{
|
||||
singleClickCallback: (eventInfo) => {
|
||||
if ((eventInfo && eventInfo?.hits.length === 0) || !eventInfo) {
|
||||
// Close open thread
|
||||
// Object.values(commentThreads.value).forEach((t) => (t.isExpanded = false))
|
||||
}
|
||||
}
|
||||
},
|
||||
{ state: options?.state }
|
||||
)
|
||||
|
||||
const closeAllThreads = async () => {
|
||||
await focusedThreadId.update(null)
|
||||
}
|
||||
|
||||
const open = async (id: string) => {
|
||||
if (id === focusedThreadId.value) return
|
||||
await focusedThreadId.update(id)
|
||||
await Promise.all([
|
||||
until(focusedThreadId).toBe(id),
|
||||
until(openThread).toMatch((t) => t?.id === id)
|
||||
])
|
||||
}
|
||||
|
||||
// Shallow watcher, only for mapping `commentThreadsBase` -> `commentThreads`
|
||||
watch(
|
||||
commentThreadsBase,
|
||||
(newCommentThreads) => {
|
||||
const newModels = reduce(
|
||||
newCommentThreads,
|
||||
(results, item) => {
|
||||
const id = item.id
|
||||
results[id] = {
|
||||
...(commentThreads.value[id]
|
||||
? commentThreads.value[id]
|
||||
: {
|
||||
isExpanded: false,
|
||||
isOccluded: false,
|
||||
style: {}
|
||||
}),
|
||||
...item,
|
||||
isExpanded: !!(focusedThreadId.value && id === focusedThreadId.value),
|
||||
viewerState: SpeckleViewer.ViewerState.isSerializedViewerState(
|
||||
item.viewerState
|
||||
)
|
||||
? item.viewerState
|
||||
: null
|
||||
}
|
||||
return results
|
||||
},
|
||||
{} as Record<string, CommentBubbleModel>
|
||||
)
|
||||
commentThreads.value = newModels
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
// Making sure there's only ever 1 expanded thread & focusedThreadId is linked to these values
|
||||
watch(
|
||||
() =>
|
||||
Object.values(commentThreads.value)
|
||||
.filter((t) => t.isExpanded)
|
||||
.map((t) => t.id),
|
||||
async (newExpandedThreadIds, oldExpandedThreadIds) => {
|
||||
const completelyNewIds = difference(
|
||||
newExpandedThreadIds,
|
||||
oldExpandedThreadIds || []
|
||||
)
|
||||
const finalOpenThreadId =
|
||||
(completelyNewIds.length ? completelyNewIds[0] : newExpandedThreadIds[0]) ||
|
||||
null
|
||||
|
||||
for (const commentThread of Object.values(commentThreads.value)) {
|
||||
const shouldBeExpanded = commentThread.id === finalOpenThreadId
|
||||
if (commentThread.isExpanded !== shouldBeExpanded) {
|
||||
commentThreads.value[commentThread.id].isExpanded = shouldBeExpanded
|
||||
}
|
||||
}
|
||||
|
||||
if (focusedThreadId.value !== finalOpenThreadId) {
|
||||
await focusedThreadId.update(finalOpenThreadId)
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// Toggling isExpanded when threadIdToOpen changes
|
||||
watch(focusedThreadId, (id) => {
|
||||
if (id) {
|
||||
if (commentThreads.value[id]) commentThreads.value[id].isExpanded = true
|
||||
} else {
|
||||
Object.values(commentThreads.value).forEach((t) => (t.isExpanded = false))
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
commentThreads,
|
||||
openThread,
|
||||
closeAllThreads,
|
||||
open
|
||||
}
|
||||
}
|
||||
|
||||
export function useViewerOpenedThreadUpdateEmitter() {
|
||||
if (process.server) return
|
||||
|
||||
|
|
|
@ -50,10 +50,7 @@ import {
|
|||
} from '~~/lib/common/helpers/graphql'
|
||||
import { nanoid } from 'nanoid'
|
||||
import { ToastNotificationType, useGlobalToast } from '~~/lib/common/composables/toast'
|
||||
import {
|
||||
CommentBubbleModel,
|
||||
useViewerCommentBubbles
|
||||
} from '~~/lib/viewer/composables/commentBubbles'
|
||||
import { CommentBubbleModel } from '~~/lib/viewer/composables/commentBubbles'
|
||||
import { setupUrlHashState } from '~~/lib/viewer/composables/setup/urlHashState'
|
||||
import { SpeckleObject } from '~~/lib/common/helpers/sceneExplorer'
|
||||
import { Box3, Vector3 } from 'three'
|
||||
|
@ -69,6 +66,7 @@ import {
|
|||
} from '~~/lib/viewer/composables/setup/diff'
|
||||
import { useDiffUtilities, useFilterUtilities } from '~~/lib/viewer/composables/ui'
|
||||
import { reduce } from 'lodash-es'
|
||||
import { setupViewerCommentBubbles } from '~~/lib/viewer/composables/setup/comments'
|
||||
|
||||
export type LoadedModel = NonNullable<
|
||||
Get<ViewerLoadedResourcesQuery, 'project.models.items[0]'>
|
||||
|
@ -224,8 +222,6 @@ export type InjectableViewerState = Readonly<{
|
|||
isTyping: Ref<boolean>
|
||||
newThreadEditor: Ref<boolean>
|
||||
}
|
||||
closeAllThreads: () => Promise<void>
|
||||
open: (id: string) => Promise<void>
|
||||
hideBubbles: Ref<boolean>
|
||||
}
|
||||
spotlightUserSessionId: Ref<Nullable<string>>
|
||||
|
@ -803,9 +799,7 @@ function setupInterfaceState(
|
|||
/**
|
||||
* THREADS
|
||||
*/
|
||||
const { commentThreads, openThread, closeAllThreads, open } = useViewerCommentBubbles(
|
||||
{ state }
|
||||
)
|
||||
const { commentThreads, openThread } = setupViewerCommentBubbles({ state })
|
||||
const isTyping = ref(false)
|
||||
const newThreadEditor = ref(false)
|
||||
const hideBubbles = ref(false)
|
||||
|
@ -837,8 +831,6 @@ function setupInterfaceState(
|
|||
isTyping,
|
||||
newThreadEditor
|
||||
},
|
||||
closeAllThreads,
|
||||
open,
|
||||
hideBubbles
|
||||
},
|
||||
camera: {
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
import { CommentBubbleModel } from '~~/lib/viewer/composables/commentBubbles'
|
||||
import {
|
||||
InitialStateWithUrlHashState,
|
||||
useInjectedViewerState
|
||||
} from '~~/lib/viewer/composables/setup'
|
||||
import { useSelectionEvents } from '~~/lib/viewer/composables/viewer'
|
||||
import { reduce } from 'lodash-es'
|
||||
import { SpeckleViewer } from '@speckle/shared'
|
||||
|
||||
export function setupViewerCommentBubbles(
|
||||
options?: Partial<{
|
||||
state: InitialStateWithUrlHashState
|
||||
}>
|
||||
) {
|
||||
const {
|
||||
resources: {
|
||||
response: { commentThreads: commentThreadsBase }
|
||||
},
|
||||
urlHashState: { focusedThreadId }
|
||||
} = options?.state || useInjectedViewerState()
|
||||
|
||||
const commentThreads = ref({} as Record<string, CommentBubbleModel>)
|
||||
const openThread = computed(() => {
|
||||
const ot = Object.values(commentThreads.value).find(
|
||||
(t) => t.id === focusedThreadId.value
|
||||
)
|
||||
return ot
|
||||
})
|
||||
|
||||
useSelectionEvents(
|
||||
{
|
||||
singleClickCallback: (eventInfo) => {
|
||||
if ((eventInfo && eventInfo?.hits.length === 0) || !eventInfo) {
|
||||
// Close open thread
|
||||
// Object.values(commentThreads.value).forEach((t) => (t.isExpanded = false))
|
||||
}
|
||||
}
|
||||
},
|
||||
{ state: options?.state }
|
||||
)
|
||||
|
||||
// Shallow watcher, only for mapping `commentThreadsBase` -> `commentThreads`
|
||||
watch(
|
||||
commentThreadsBase,
|
||||
(newCommentThreads) => {
|
||||
const newModels = reduce(
|
||||
newCommentThreads,
|
||||
(results, item) => {
|
||||
const id = item.id
|
||||
results[id] = {
|
||||
...(commentThreads.value[id]
|
||||
? commentThreads.value[id]
|
||||
: {
|
||||
isOccluded: false,
|
||||
style: {}
|
||||
}),
|
||||
...item,
|
||||
viewerState: SpeckleViewer.ViewerState.isSerializedViewerState(
|
||||
item.viewerState
|
||||
)
|
||||
? item.viewerState
|
||||
: null
|
||||
}
|
||||
return results
|
||||
},
|
||||
{} as Record<string, CommentBubbleModel>
|
||||
)
|
||||
commentThreads.value = newModels
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
return {
|
||||
commentThreads,
|
||||
openThread
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import { ViewerEvent } from '@speckle/viewer'
|
||||
import { useInjectedViewerState } from '~~/lib/viewer/composables/setup'
|
||||
import { useViewerEventListener } from '~~/lib/viewer/composables/viewer'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function useDebugViewerEvents() {
|
||||
for (const [key, val] of Object.entries(ViewerEvent)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
useViewerEventListener(val, (...args) => console.log(key, ...args))
|
||||
}
|
||||
}
|
||||
|
||||
function useDebugViewer() {
|
||||
const state = useInjectedViewerState()
|
||||
const {
|
||||
viewer: { instance }
|
||||
} = state
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
window.VIEWER = instance
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
window.VIEWER_STATE = () => ({ ...state })
|
||||
}
|
||||
|
||||
export function setupDebugMode() {
|
||||
if (process.server) return
|
||||
if (!process.dev) return
|
||||
|
||||
// useDebugViewerEvents()
|
||||
useDebugViewer()
|
||||
}
|
|
@ -46,6 +46,7 @@ import { areVectorsLooselyEqual } from '~~/lib/viewer/helpers/three'
|
|||
import { Nullable } from '@speckle/shared'
|
||||
import { useCameraUtilities } from '~~/lib/viewer/composables/ui'
|
||||
import { watchTriggerable } from '@vueuse/core'
|
||||
import { setupDebugMode } from '~~/lib/viewer/composables/setup/dev'
|
||||
|
||||
function useViewerIsBusyEventHandler() {
|
||||
const state = useInjectedViewerState()
|
||||
|
@ -696,23 +697,6 @@ function useDiffingIntegration() {
|
|||
})
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function useDebugViewerEvents() {
|
||||
if (process.server) return
|
||||
const {
|
||||
viewer: { instance }
|
||||
} = useInjectedViewerState()
|
||||
|
||||
for (const [key, val] of Object.entries(ViewerEvent)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
useViewerEventListener(val, (...args) => console.log(key, ...args))
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
window.VIEWER = instance
|
||||
}
|
||||
|
||||
export function useViewerPostSetup() {
|
||||
if (process.server) return
|
||||
useViewerObjectAutoLoading()
|
||||
|
@ -727,7 +711,5 @@ export function useViewerPostSetup() {
|
|||
useLightConfigIntegration()
|
||||
useExplodeFactorIntegration()
|
||||
useDiffingIntegration()
|
||||
|
||||
// test
|
||||
// useDebugViewerEvents()
|
||||
setupDebugMode()
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { Nullable } from '@speckle/shared'
|
||||
import { SelectionEvent } from '@speckle/viewer'
|
||||
|
||||
import { SpeckleObject } from '~~/lib/common/helpers/sceneExplorer'
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
import { useInjectedViewerState } from '~~/lib/viewer/composables/setup'
|
||||
|
@ -12,11 +11,11 @@ function useCollectSelection() {
|
|||
ui: { selection }
|
||||
} = useInjectedViewerState()
|
||||
|
||||
const selectionCallback = (event: Nullable<SelectionEvent>) => {
|
||||
if (!event) return (selection.value = null) // reset selection location
|
||||
|
||||
const firstHit = event.hits[0]
|
||||
selection.value = firstHit.point
|
||||
const selectionCallback: Parameters<
|
||||
typeof useSelectionEvents
|
||||
>[0]['singleClickCallback'] = (_event, { firstVisibleSelectionHit }) => {
|
||||
if (!firstVisibleSelectionHit) return (selection.value = null) // reset selection location
|
||||
selection.value = firstVisibleSelectionHit.point
|
||||
}
|
||||
useSelectionEvents({
|
||||
singleClickCallback: selectionCallback,
|
||||
|
|
|
@ -311,3 +311,31 @@ export function useDiffUtilities() {
|
|||
areDiffsEqual
|
||||
}
|
||||
}
|
||||
|
||||
export function useThreadUtilities() {
|
||||
const {
|
||||
urlHashState: { focusedThreadId },
|
||||
ui: {
|
||||
threads: {
|
||||
openThread: { thread: openThread }
|
||||
}
|
||||
}
|
||||
} = useInjectedViewerState()
|
||||
|
||||
const isOpenThread = (id: string) => focusedThreadId.value === id
|
||||
|
||||
const closeAllThreads = async () => {
|
||||
await focusedThreadId.update(null)
|
||||
}
|
||||
|
||||
const open = async (id: string) => {
|
||||
if (id === focusedThreadId.value) return
|
||||
await focusedThreadId.update(id)
|
||||
await Promise.all([
|
||||
until(focusedThreadId).toBe(id),
|
||||
until(openThread).toMatch((t) => t?.id === id)
|
||||
])
|
||||
}
|
||||
|
||||
return { closeAllThreads, open, isOpenThread }
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</div>
|
||||
|
||||
<OnboardingDialogManager
|
||||
v-show="showManagerDownloadDialog"
|
||||
v-model:open="showManagerDownloadDialog"
|
||||
@done="showManagerDownloadDialog = false"
|
||||
@cancel="showManagerDownloadDialog = false"
|
||||
></OnboardingDialogManager>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
export default defineNuxtPlugin(() => {
|
||||
if (!process.dev) return
|
||||
if (!process.client) return
|
||||
|
||||
console.debug('🚧 Running FE2 in dev mode, extra debugging tools may be available...')
|
||||
})
|
|
@ -526,4 +526,6 @@ onMounted(() => {
|
|||
triggerSearch()
|
||||
}
|
||||
})
|
||||
|
||||
defineExpose({ triggerSearch })
|
||||
</script>
|
||||
|
|
|
@ -109,7 +109,7 @@ const getStream = () => {
|
|||
// prettier-ignore
|
||||
// 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8?c=%5B-7.66134,10.82932,6.41935,-0.07739,-13.88552,1.8697,0,1%5D'
|
||||
// Revit sample house (good for bim-like stuff with many display meshes)
|
||||
// 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8'
|
||||
'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8'
|
||||
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/6c6e43e5f3'
|
||||
// 'https://latest.speckle.dev/streams/58b5648c4d/commits/60371ecb2d'
|
||||
// 'Super' heavy revit shit
|
||||
|
@ -270,10 +270,11 @@ const getStream = () => {
|
|||
// 'https://latest.speckle.dev/streams/c1faab5c62/objects/d3466547df9df86397eb4dff7ac9713f'
|
||||
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/140c443886'
|
||||
// 'https://latest.speckle.dev/streams/e258b0e8db/commits/108971810d'
|
||||
// 'https://latest.speckle.dev/streams/e258b0e8db/objects/3fcd63d80cf791c3f554a795846e62f6'
|
||||
// 'https://latest.speckle.dev/streams/55cc1cbf0a/objects/d7ae178fb6a7b1f599a177486e14f9a6'
|
||||
// 'https://latest.speckle.dev/streams/e258b0e8db/objects/3fcd63d80cf791c3f554a795846e62f6'
|
||||
// 'https://latest.speckle.dev/streams/92b620fb17/commits/6adbcfa8dc'
|
||||
'https://latest.speckle.dev/streams/92b620fb17/commits/6adbcfa8dc'
|
||||
// 'https://latest.speckle.dev/streams/92b620fb17/commits/6adbcfa8dc'
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -164,12 +164,18 @@ export interface IViewer {
|
|||
transition?: boolean
|
||||
)
|
||||
|
||||
loadObject(url: string, token?: string, enableCaching?: boolean): Promise<void>
|
||||
loadObject(
|
||||
url: string,
|
||||
token?: string,
|
||||
enableCaching?: boolean,
|
||||
zoomToObject?: boolean
|
||||
): Promise<void>
|
||||
loadObjectAsync(
|
||||
url: string,
|
||||
token?: string,
|
||||
enableCaching?: boolean,
|
||||
priority?: number
|
||||
priority?: number,
|
||||
zoomToObject?: boolean
|
||||
): Promise<void>
|
||||
cancelLoad(url: string, unload?: boolean): Promise<void>
|
||||
unloadObject(url: string): Promise<void>
|
||||
|
|
|
@ -520,7 +520,11 @@ export default class SpeckleRenderer {
|
|||
this._needsRender = true
|
||||
}
|
||||
|
||||
public async addRenderTreeAsync(subtreeId: string, priority = 1) {
|
||||
public async addRenderTreeAsync(
|
||||
subtreeId: string,
|
||||
priority = 1,
|
||||
zoomToObject = true
|
||||
) {
|
||||
this.cancel[subtreeId] = false
|
||||
const subtreeGroup = new Group()
|
||||
subtreeGroup.name = subtreeId
|
||||
|
@ -537,11 +541,9 @@ export default class SpeckleRenderer {
|
|||
if (!batch) continue
|
||||
|
||||
this.addBatch(batch, subtreeGroup)
|
||||
this.zoom()
|
||||
if (zoomToObject) this.zoom()
|
||||
if (batch.geometryType === GeometryType.MESH) {
|
||||
this.updateDirectLights()
|
||||
/** Updating the shadowcatcher after each batch is a bit too much. Stalls a lot */
|
||||
// this.updateShadowCatcher()
|
||||
}
|
||||
this._needsRender = true
|
||||
if (this.cancel[subtreeId]) {
|
||||
|
@ -1176,6 +1178,7 @@ export default class SpeckleRenderer {
|
|||
view.view.target['z'],
|
||||
transition
|
||||
)
|
||||
this.viewer.cameraHandler.enableRotations()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1191,12 +1194,14 @@ export default class SpeckleRenderer {
|
|||
|
||||
switch (side) {
|
||||
case 'front':
|
||||
this.zoomExtents()
|
||||
this.viewer.cameraHandler.controls.rotateTo(0, DEG90, transition)
|
||||
if (this.viewer.cameraHandler.activeCam.name === 'ortho')
|
||||
this.viewer.cameraHandler.disableRotations()
|
||||
break
|
||||
|
||||
case 'back':
|
||||
this.zoomExtents()
|
||||
this.viewer.cameraHandler.controls.rotateTo(DEG180, DEG90, transition)
|
||||
if (this.viewer.cameraHandler.activeCam.name === 'ortho')
|
||||
this.viewer.cameraHandler.disableRotations()
|
||||
|
@ -1204,6 +1209,7 @@ export default class SpeckleRenderer {
|
|||
|
||||
case 'up':
|
||||
case 'top':
|
||||
this.zoomExtents()
|
||||
this.viewer.cameraHandler.controls.rotateTo(0, 0, transition)
|
||||
if (this.viewer.cameraHandler.activeCam.name === 'ortho')
|
||||
this.viewer.cameraHandler.disableRotations()
|
||||
|
@ -1211,18 +1217,21 @@ export default class SpeckleRenderer {
|
|||
|
||||
case 'down':
|
||||
case 'bottom':
|
||||
this.zoomExtents()
|
||||
this.viewer.cameraHandler.controls.rotateTo(0, DEG180, transition)
|
||||
if (this.viewer.cameraHandler.activeCam.name === 'ortho')
|
||||
this.viewer.cameraHandler.disableRotations()
|
||||
break
|
||||
|
||||
case 'right':
|
||||
this.zoomExtents()
|
||||
this.viewer.cameraHandler.controls.rotateTo(DEG90, DEG90, transition)
|
||||
if (this.viewer.cameraHandler.activeCam.name === 'ortho')
|
||||
this.viewer.cameraHandler.disableRotations()
|
||||
break
|
||||
|
||||
case 'left':
|
||||
this.zoomExtents()
|
||||
this.viewer.cameraHandler.controls.rotateTo(-DEG90, DEG90, transition)
|
||||
if (this.viewer.cameraHandler.activeCam.name === 'ortho')
|
||||
this.viewer.cameraHandler.disableRotations()
|
||||
|
@ -1261,10 +1270,12 @@ export default class SpeckleRenderer {
|
|||
view.target.z,
|
||||
transition
|
||||
)
|
||||
this.viewer.cameraHandler.enableRotations()
|
||||
}
|
||||
|
||||
private setViewPolar(view: PolarView, transition = true) {
|
||||
this.viewer.cameraHandler.controls.rotate(view.azimuth, view.polar, transition)
|
||||
this.viewer.cameraHandler.enableRotations()
|
||||
}
|
||||
|
||||
public screenToNDC(
|
||||
|
|
|
@ -467,7 +467,12 @@ export class Viewer extends EventEmitter implements IViewer {
|
|||
await loader.load()
|
||||
}
|
||||
|
||||
public async loadObject(url: string, token: string = null, enableCaching = true) {
|
||||
public async loadObject(
|
||||
url: string,
|
||||
token: string = null,
|
||||
enableCaching = true,
|
||||
zoomToObject = true
|
||||
) {
|
||||
if (++this.inProgressOperations === 1)
|
||||
(this as EventEmitter).emit(ViewerEvent.Busy, true)
|
||||
await this.downloadObject(url, token, enableCaching)
|
||||
|
@ -480,7 +485,8 @@ export class Viewer extends EventEmitter implements IViewer {
|
|||
await this.speckleRenderer.addRenderTree(url)
|
||||
Logger.log('SYNC batch build time -> ', performance.now() - t0)
|
||||
|
||||
this.zoom()
|
||||
if (zoomToObject) this.zoom()
|
||||
|
||||
this.speckleRenderer.resetPipeline(true)
|
||||
this.emit(ViewerEvent.LoadComplete, url)
|
||||
this.loaders[url].dispose()
|
||||
|
@ -493,7 +499,8 @@ export class Viewer extends EventEmitter implements IViewer {
|
|||
url: string,
|
||||
token: string = null,
|
||||
enableCaching = true,
|
||||
priority = 1
|
||||
priority = 1,
|
||||
zoomToObject = true
|
||||
) {
|
||||
if (++this.inProgressOperations === 1)
|
||||
(this as EventEmitter).emit(ViewerEvent.Busy, true)
|
||||
|
@ -505,7 +512,7 @@ export class Viewer extends EventEmitter implements IViewer {
|
|||
|
||||
if (treeBuilt) {
|
||||
t0 = performance.now()
|
||||
await this.speckleRenderer.addRenderTreeAsync(url, priority)
|
||||
await this.speckleRenderer.addRenderTreeAsync(url, priority, zoomToObject)
|
||||
Logger.log('ASYNC batch build time -> ', performance.now() - t0)
|
||||
this.speckleRenderer.resetPipeline(true)
|
||||
this.emit(ViewerEvent.LoadComplete, url)
|
||||
|
|
|
@ -118,10 +118,9 @@ export default class CameraHandler {
|
|||
|
||||
const lineOfSight = new THREE.Vector3()
|
||||
this.camera.getWorldDirection(lineOfSight)
|
||||
const target = new THREE.Vector3()
|
||||
this.controls.getTarget(target)
|
||||
const target = new THREE.Vector3().copy(this.viewer.World.worldOrigin)
|
||||
const distance = target.clone().sub(this.camera.position)
|
||||
const depth = distance.dot(lineOfSight)
|
||||
const depth = distance.length()
|
||||
const dims = {
|
||||
x: this.viewer.container.offsetWidth,
|
||||
y: this.viewer.container.offsetHeight
|
||||
|
@ -141,21 +140,22 @@ export default class CameraHandler {
|
|||
this.orthoCamera.updateProjectionMatrix()
|
||||
this.orthoCamera.position.copy(this.camera.position)
|
||||
this.orthoCamera.quaternion.copy(this.camera.quaternion)
|
||||
this.orthoCamera.updateProjectionMatrix()
|
||||
|
||||
this.controls.camera = this.orthoCamera
|
||||
|
||||
// fit the camera inside, so we don't have clipping plane issues.
|
||||
// WIP implementation
|
||||
const camPos = this.orthoCamera.position
|
||||
const box = new THREE.Box3().setFromObject(this.viewer.speckleRenderer.allObjects)
|
||||
const sphere = new THREE.Sphere()
|
||||
box.getBoundingSphere(sphere)
|
||||
// const camPos = this.orthoCamera.position
|
||||
// const box = new THREE.Box3().setFromObject(this.viewer.speckleRenderer.allObjects)
|
||||
// const sphere = new THREE.Sphere()
|
||||
// box.getBoundingSphere(sphere)
|
||||
|
||||
let dist = sphere.distanceToPoint(camPos)
|
||||
if (dist < 0) {
|
||||
dist *= -1
|
||||
this.controls.setPosition(camPos.x + dist, camPos.y + dist, camPos.z + dist)
|
||||
}
|
||||
// let dist = sphere.distanceToPoint(camPos)
|
||||
// if (dist < 0) {
|
||||
// dist *= -1
|
||||
// this.controls.setPosition(camPos.x + dist, camPos.y + dist, camPos.z + dist)
|
||||
// }
|
||||
|
||||
this.viewer.emit('projection-change', 'ortho')
|
||||
}
|
||||
|
@ -165,7 +165,7 @@ export default class CameraHandler {
|
|||
this.camera.position.copy(this.orthoCamera.position)
|
||||
this.camera.quaternion.copy(this.orthoCamera.quaternion)
|
||||
this.camera.updateProjectionMatrix()
|
||||
this.controls.distance = this.previousDistance
|
||||
// this.controls.distance = this.previousDistance
|
||||
this.controls.camera = this.camera
|
||||
this.controls.zoomTo(1)
|
||||
this.enableRotations()
|
||||
|
|
|
@ -586,7 +586,7 @@ export class FilteringManager extends EventEmitter {
|
|||
const allIds: string[] = []
|
||||
const key = objectIds.join(',')
|
||||
|
||||
if (this.idCache[key]) return this.idCache[key]
|
||||
if (this.idCache[key] && this.idCache[key].length) return this.idCache[key]
|
||||
|
||||
this.WTI.walk((node: TreeNode) => {
|
||||
if (objectIds.includes(node.model.raw.id) && node.model.raw.__closure) {
|
||||
|
|
|
@ -47,6 +47,7 @@ export class SpeckleCameraControls extends CameraControls {
|
|||
private _didZoom = false
|
||||
private overrideDollyLerpRatio = 0
|
||||
private overrideZoomLerpRatio = 0
|
||||
|
||||
static install() {
|
||||
_v3A = new Vector3()
|
||||
_v3B = new Vector3()
|
||||
|
@ -101,6 +102,10 @@ export class SpeckleCameraControls extends CameraControls {
|
|||
|
||||
protected _zoomInternal = (delta: number, x: number, y: number): void => {
|
||||
const zoomScale = Math.pow(0.95, delta * this.dollySpeed)
|
||||
/** We need to move the camera as well when zooming in orthographic mode */
|
||||
const dollyScale = Math.pow(0.95, -delta * this.dollySpeed)
|
||||
const distance = this._sphericalEnd.radius * dollyScale
|
||||
this.dollyTo(distance, true, 0.9)
|
||||
|
||||
// for both PerspectiveCamera and OrthographicCamera
|
||||
this.zoomTo(this._zoom * zoomScale, false, 1)
|
||||
|
|
|
@ -9,8 +9,8 @@ metadata:
|
|||
{{- if .Values.cert_manager_issuer }}
|
||||
cert-manager.io/cluster-issuer: {{ .Values.cert_manager_issuer }}
|
||||
{{- end }}
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
|
||||
nginx.org/client-max-body-size: "100m"
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: {{ (printf "%dm" (int .Values.file_size_limit_mb)) | quote }}
|
||||
nginx.org/client-max-body-size: {{ (printf "%dm" (int .Values.file_size_limit_mb)) | quote }}
|
||||
nginx.ingress.kubernetes.io/use-regex: "true"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
|
|
2201
yarn.lock
2201
yarn.lock
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче