83d8035dc2
* root + server * frontend * frontend-2 * dui3 * dui3 * tailwind theme * ui-components * preview service * viewer * viewer-sandbox * fileimport-service * webhook service * objectloader * shared * ui-components-nuxt * WIP full config * WIP full linter * eslint projectwide util * minor fix * removing redundant ci * clean up test errors * fixed prettier formatting * CI improvements * TSC lint fix * 'buildBatch' needs to be async since some batch types (like Text) require it. Removed a disabled liniting rule from ObjLoader * removed unnecessary void --------- Co-authored-by: AlexandruPopovici <alexandrupopoviciioan@gmail.com>
236 lines
6.5 KiB
TypeScript
236 lines
6.5 KiB
TypeScript
import type { ApolloCache } from '@apollo/client/cache'
|
|
import type { JSONContent } from '@tiptap/core'
|
|
import { useApolloClient, useSubscription } from '@vue/apollo-composable'
|
|
import type { MaybeRef } from '@vueuse/core'
|
|
import dayjs from 'dayjs'
|
|
import type { Get } from 'type-fest'
|
|
import { useActiveUser } from '~~/lib/auth/composables/activeUser'
|
|
import type {
|
|
CommentContentInput,
|
|
CreateCommentReplyInput,
|
|
OnViewerCommentsUpdatedSubscription
|
|
} from '~~/lib/common/generated/gql/graphql'
|
|
import {
|
|
convertThrowIntoFetchResult,
|
|
getCacheId,
|
|
getFirstErrorMessage
|
|
} from '~~/lib/common/helpers/graphql'
|
|
import {
|
|
archiveCommentMutation,
|
|
createCommentReplyMutation,
|
|
createCommentThreadMutation,
|
|
markCommentViewedMutation
|
|
} from '~~/lib/viewer/graphql/mutations'
|
|
import { onViewerCommentsUpdatedSubscription } from '~~/lib/viewer/graphql/subscriptions'
|
|
import { useInjectedViewerState } from '~~/lib/viewer/composables/setup'
|
|
import type { MaybeNullOrUndefined } from '@speckle/shared'
|
|
import { ToastNotificationType, useGlobalToast } from '~~/lib/common/composables/toast'
|
|
import type { SuccessfullyUploadedFileItem } from '~~/lib/core/api/blobStorage'
|
|
import { isValidCommentContentInput } from '~~/lib/viewer/helpers/comments'
|
|
import { useStateSerialization } from '~~/lib/viewer/composables/serialization'
|
|
|
|
export function useViewerCommentUpdateTracking(
|
|
params: {
|
|
projectId: MaybeRef<string>
|
|
resourceIdString: MaybeRef<string>
|
|
loadedVersionsOnly?: MaybeRef<MaybeNullOrUndefined<boolean>>
|
|
},
|
|
handler?: (
|
|
data: NonNullable<
|
|
Get<OnViewerCommentsUpdatedSubscription, 'projectCommentsUpdated'>
|
|
>,
|
|
cache: ApolloCache<unknown>
|
|
) => void
|
|
) {
|
|
const apollo = useApolloClient().client
|
|
const { onResult: onViewerCommentUpdated } = useSubscription(
|
|
onViewerCommentsUpdatedSubscription,
|
|
() => ({
|
|
target: {
|
|
projectId: unref(params.projectId),
|
|
resourceIdString: unref(params.resourceIdString),
|
|
loadedVersionsOnly: unref(params.loadedVersionsOnly)
|
|
}
|
|
})
|
|
)
|
|
|
|
onViewerCommentUpdated((res) => {
|
|
if (!res.data?.projectCommentsUpdated) return
|
|
|
|
const event = res.data.projectCommentsUpdated
|
|
const cache = apollo.cache
|
|
|
|
handler?.(event, cache)
|
|
})
|
|
}
|
|
|
|
export function useMarkThreadViewed() {
|
|
const apollo = useApolloClient().client
|
|
const { isLoggedIn } = useActiveUser()
|
|
const logger = useLogger()
|
|
|
|
return async (projectId: string, threadId: string) => {
|
|
if (!isLoggedIn.value) return false
|
|
const { data, errors } = await apollo
|
|
.mutate({
|
|
mutation: markCommentViewedMutation,
|
|
variables: {
|
|
projectId,
|
|
threadId
|
|
},
|
|
update: (cache, { data }) => {
|
|
if (!data?.commentMutations.markViewed) return
|
|
|
|
cache.modify({
|
|
id: getCacheId('Comment', threadId),
|
|
fields: {
|
|
viewedAt: () => dayjs().toISOString()
|
|
}
|
|
})
|
|
}
|
|
})
|
|
.catch(convertThrowIntoFetchResult)
|
|
|
|
if (errors) {
|
|
logger.error('Marking thread as viewed failed', errors)
|
|
}
|
|
|
|
return !!data?.commentMutations.markViewed
|
|
}
|
|
}
|
|
|
|
export type CommentEditorValue = {
|
|
doc?: JSONContent | null
|
|
attachments?: SuccessfullyUploadedFileItem[] | null
|
|
}
|
|
|
|
export function useSubmitComment() {
|
|
const {
|
|
projectId,
|
|
resources: {
|
|
request: { resourceIdString }
|
|
},
|
|
viewer: { instance: viewerInstance }
|
|
} = useInjectedViewerState()
|
|
const { isLoggedIn } = useActiveUser()
|
|
const client = useApolloClient().client
|
|
const { triggerNotification } = useGlobalToast()
|
|
const { serialize } = useStateSerialization()
|
|
|
|
return async (content: CommentContentInput) => {
|
|
if (!isLoggedIn.value) return null
|
|
if (!isValidCommentContentInput(content)) return null
|
|
const screenshot = await viewerInstance.screenshot()
|
|
|
|
const { data, errors } = await client
|
|
.mutate({
|
|
mutation: createCommentThreadMutation,
|
|
variables: {
|
|
input: {
|
|
projectId: projectId.value,
|
|
resourceIdString: resourceIdString.value,
|
|
content,
|
|
viewerState: serialize({ concreteResourceIdString: true }),
|
|
screenshot
|
|
}
|
|
}
|
|
})
|
|
.catch(convertThrowIntoFetchResult)
|
|
|
|
if (data?.commentMutations.create) {
|
|
return data.commentMutations.create
|
|
}
|
|
|
|
const errMsg = getFirstErrorMessage(errors)
|
|
triggerNotification({
|
|
type: ToastNotificationType.Danger,
|
|
title: 'Comment creation failed',
|
|
description: errMsg
|
|
})
|
|
|
|
return null
|
|
}
|
|
}
|
|
|
|
export function useSubmitReply() {
|
|
const { isLoggedIn } = useActiveUser()
|
|
const client = useApolloClient().client
|
|
const { triggerNotification } = useGlobalToast()
|
|
|
|
return async (input: CreateCommentReplyInput) => {
|
|
if (!isLoggedIn.value) return null
|
|
if (!isValidCommentContentInput(input.content)) return null
|
|
|
|
const { data, errors } = await client
|
|
.mutate({
|
|
mutation: createCommentReplyMutation,
|
|
variables: {
|
|
input
|
|
}
|
|
})
|
|
.catch(convertThrowIntoFetchResult)
|
|
|
|
if (data?.commentMutations.reply) {
|
|
return data.commentMutations.reply
|
|
}
|
|
|
|
const errMsg = getFirstErrorMessage(errors)
|
|
triggerNotification({
|
|
type: ToastNotificationType.Danger,
|
|
title: 'Reply creation failed',
|
|
description: errMsg
|
|
})
|
|
|
|
return null
|
|
}
|
|
}
|
|
|
|
export function useArchiveComment() {
|
|
const { isLoggedIn } = useActiveUser()
|
|
const client = useApolloClient().client
|
|
const { triggerNotification } = useGlobalToast()
|
|
|
|
return async (commentId: string, archived = true) => {
|
|
if (!isLoggedIn.value || !commentId) return false
|
|
|
|
const { data, errors } = await client
|
|
.mutate({
|
|
mutation: archiveCommentMutation,
|
|
variables: {
|
|
commentId,
|
|
archived
|
|
}
|
|
})
|
|
.catch(convertThrowIntoFetchResult)
|
|
|
|
if (data?.commentMutations.archive) return true
|
|
|
|
const errMsg = getFirstErrorMessage(errors)
|
|
triggerNotification({
|
|
type: ToastNotificationType.Danger,
|
|
title: 'Comment archival failed',
|
|
description: errMsg
|
|
})
|
|
|
|
return false
|
|
}
|
|
}
|
|
|
|
export function useCheckViewerCommentingAccess() {
|
|
const {
|
|
resources: {
|
|
response: { project }
|
|
}
|
|
} = useInjectedViewerState()
|
|
const { activeUser } = useActiveUser()
|
|
|
|
return computed(() => {
|
|
if (!activeUser.value) return false
|
|
|
|
const hasRole = !!project.value?.role
|
|
const allowPublicComments = !!project.value?.allowPublicComments
|
|
|
|
return hasRole || allowPublicComments
|
|
})
|
|
}
|