Files
speckle-server/packages/frontend-2/lib/viewer/composables/savedViews/validation.ts
T
Kristaps Fabians Geikins 185806ec64 feat: embedding saved views (#5690)
* init + new API query

* embed dialog works

* embed actions menu

* embed label

* WIP positioning fix:

* cleanup

* embed options finalized

* prevent reset in embed mode

* more embed UX improverments

* tests fix
2025-10-08 10:49:26 +03:00

192 lines
5.2 KiB
TypeScript

import type { MaybeNullOrUndefined, Optional } from '@speckle/shared'
import type { GenericValidateFunction } from 'vee-validate'
import { graphql } from '~/lib/common/generated/gql/gql'
import {
SavedViewVisibility,
type FullPermissionCheckResultFragment,
type UseSavedViewValidationHelpers_SavedViewFragment
} from '~/lib/common/generated/gql/graphql'
import { Globe, User } from 'lucide-vue-next'
import type { FormRadioGroupItem } from '@speckle/ui-components'
import { useMutationLoading } from '@vue/apollo-composable'
import { useInjectedViewerState } from '~/lib/viewer/composables/setup'
graphql(`
fragment UseSavedViewValidationHelpers_SavedView on SavedView {
id
isHomeView
visibility
permissions {
canUpdate {
...FullPermissionCheckResult
}
canMove {
...FullPermissionCheckResult
}
canEditTitle {
...FullPermissionCheckResult
}
canEditDescription {
...FullPermissionCheckResult
}
canSetAsHomeView {
...FullPermissionCheckResult
}
}
}
`)
export const useSavedViewValidationHelpers = (params: {
view: ComputedRef<
MaybeNullOrUndefined<UseSavedViewValidationHelpers_SavedViewFragment>
>
}) => {
const homeViewPrivateError = 'A home view must be shared'
const isLoading = useMutationLoading()
const {
resources: {
response: { isFederatedView }
}
} = useInjectedViewerState()
const permissions = computed(() => params.view.value?.permissions)
const canUpdate = computed(() => permissions.value?.canUpdate)
const canMove = computed(() => permissions.value?.canMove)
const canEditTitle = computed(() => permissions.value?.canEditTitle)
const canEditDescription = computed(() => permissions.value?.canEditDescription)
const canEmbed = computed((): FullPermissionCheckResultFragment | undefined => {
if (isLoading.value) {
return {
authorized: false,
errorMessage: undefined,
code: 'LOADING',
message: ''
}
}
if (params.view.value?.visibility !== SavedViewVisibility.Public) {
return {
authorized: false,
errorMessage: 'Only shared views can be embedded',
code: 'FORBIDDEN',
message: 'Only shared views can be embedded'
}
}
return { authorized: true, code: 'OK', message: '' }
})
const canOpenEditDialog = computed(
(): FullPermissionCheckResultFragment | undefined => {
if (isLoading.value) {
return {
authorized: false,
errorMessage: undefined,
code: 'LOADING',
message: ''
}
}
if (canUpdate.value?.authorized) return canUpdate.value
if (canEditTitle.value?.authorized) return canEditTitle.value
if (canEditDescription.value?.authorized) return canEditDescription.value
if (canMove.value?.authorized) return canMove.value
return canMove.value
}
)
const isOnlyVisibleToMe = computed(
() => params.view.value?.visibility === SavedViewVisibility.AuthorOnly
)
const isHomeView = computed(() => params.view.value?.isHomeView)
/**
* Visibility options for visibility radio group
*/
const visibilityOptions = computed((): FormRadioGroupItem<SavedViewVisibility>[] => [
{
value: SavedViewVisibility.Public,
title: 'Shared',
introduction: 'Visible to anyone with access to the model',
icon: Globe
},
{
value: SavedViewVisibility.AuthorOnly,
title: 'Private',
introduction: 'Visible only to the view author',
icon: User,
...(params.view.value?.isHomeView
? {
disabled: true,
help: homeViewPrivateError
}
: {})
}
])
const canSetHomeView = computed(
(): { authorized: boolean; message: Optional<string> } => {
if (!permissions.value?.canSetAsHomeView.authorized || isLoading.value) {
return {
authorized: false,
message: permissions.value?.canSetAsHomeView.errorMessage || undefined
}
}
if (isFederatedView.value) {
return {
authorized: false,
message: "Home view settings can't be updated while in a federated view"
}
}
return { authorized: true, message: undefined }
}
)
const canToggleVisibility = computed(() => {
if (!canUpdate.value?.authorized || isLoading.value) {
return {
authorized: false,
message: canUpdate.value?.errorMessage || undefined
}
}
if (isHomeView.value && !isOnlyVisibleToMe.value) {
return {
authorized: false,
message: homeViewPrivateError
}
}
return { authorized: true, message: undefined }
})
/**
* Vee-validate rule for visibility checks
*/
const validateVisibility: GenericValidateFunction<SavedViewVisibility> = (value) => {
if (!params.view.value) return true
if (!params.view.value.isHomeView) return true
return value === SavedViewVisibility.AuthorOnly ? homeViewPrivateError : true
}
return {
validateVisibility,
visibilityOptions,
canUpdate,
isOnlyVisibleToMe,
canSetHomeView,
isHomeView,
canToggleVisibility,
canMove,
canEditTitle,
canEditDescription,
canOpenEditDialog,
canEmbed
}
}