Files
nocodb/packages/nc-gui/composables/useRowComments.ts
mertmit 69a29568c7 chore: sync
Signed-off-by: mertmit <mertmit99@gmail.com>
2026-01-10 00:21:02 +03:00

303 lines
8.3 KiB
TypeScript

import type { ColumnType, CommentType, MetaType, TableType } from 'nocodb-sdk'
import { NcMarkdownParser } from '~/helpers/tiptap'
export interface CommentTypeExtended extends CommentType {
created_display_name?: string | null
created_display_name_short?: string
resolved_display_name?: string | null
resolved_display_name_short?: string
created_by_meta?: MetaType
resolved_by_meta?: MetaType
}
const [useProvideRowComments, useRowComments] = useInjectionState((meta: Ref<TableType>, row: Ref<Row>) => {
const isCommentsLoading = ref(false)
const { user } = useGlobal()
const { isUIAllowed } = useRoles()
const { $e, $state, $api } = useNuxtApp()
const basesStore = useBases()
const { basesUser } = storeToRefs(basesStore)
const baseUsers = computed(() => (meta.value.base_id ? basesUser.value.get(meta.value.base_id) || [] : []))
const comments = ref<Array<CommentTypeExtended>>([])
const parsedHtmlComments = computed(() => {
return comments.value.reduce((acc, comment) => {
if (comment.id) {
let commentValue = unref(comment.comment)
if (comment.updated_at !== comment.created_at && comment.updated_at) {
const str = timeAgo(comment.updated_at).replace(' ', '_')
commentValue += ` [(edited)](a~~~###~~~Edited_${str}) `
}
acc[comment.id] =
NcMarkdownParser.parse(
commentValue,
{
enableMention: !!isEeUI,
users: unref(baseUsers.value),
currentUser: unref(user.value),
},
true,
) ?? ''
}
return acc
}, {} as Record<string, any>)
})
const loadComments = async (_rowId?: string, ignoreLoadingIndicator = true) => {
if (!isUIAllowed('commentList') || (!row.value && !_rowId)) return
const rowId = _rowId ?? extractPkFromRow(row.value.row, meta.value.columns as ColumnType[])
if (!rowId) return
try {
if (!ignoreLoadingIndicator) isCommentsLoading.value = true
const res = ((
await $api.internal.getOperation(meta.value!.fk_workspace_id!, meta.value!.base_id!, {
operation: 'commentList',
row_id: rowId,
fk_model_id: meta.value.id as string,
})
).list || []) as Array<CommentTypeExtended>
comments.value = res.map((comment) => {
const user = baseUsers.value.find((u) => u.id === comment.created_by)
const resolvedUser = comment.resolved_by ? baseUsers.value.find((u) => u.id === comment.resolved_by) : null
return {
...comment,
created_display_name: user?.display_name,
created_display_name_short: user?.display_name ?? extractNameFromEmail(user?.email),
resolved_display_name: resolvedUser?.display_name,
resolved_display_name_short: resolvedUser?.display_name ?? extractNameFromEmail(resolvedUser?.email),
created_by_meta: user?.meta,
resolved_by_meta: resolvedUser?.meta,
}
})
} catch (e: unknown) {
message.error(
await extractSdkResponseErrorMsg(
e as Error & {
response: any
},
),
)
} finally {
if (!ignoreLoadingIndicator) isCommentsLoading.value = false
}
}
const deleteComment = async (commentId: string) => {
if (!isUIAllowed('commentDelete')) return
const tempC = comments.value.find((c) => c.id === commentId)
if (!tempC) return
try {
comments.value = comments.value.filter((c) => c.id !== commentId)
await $api.internal.postOperation(
(meta.value as any).fk_workspace_id!,
meta.value!.base_id!,
{
operation: 'commentDelete',
},
{
commentId,
},
)
// update comment count in rowMeta
Object.assign(row.value, {
...row.value,
rowMeta: {
...row.value.rowMeta,
commentCount: (row.value.rowMeta.commentCount ?? 1) - 1,
},
})
} catch (e: unknown) {
message.error(
await extractSdkResponseErrorMsg(
e as Error & {
response: any
},
),
)
comments.value = [...comments.value, tempC]
}
}
const resolveComment = async (commentId: string) => {
if (!isUIAllowed('commentResolve')) return
const tempC = comments.value.find((c) => c.id === commentId)
if (!tempC) return
try {
comments.value = comments.value.map((c) => {
if (c.id === commentId) {
return {
...c,
resolved_by: tempC.resolved_by ? undefined : $state.user?.value?.id,
resolved_by_email: tempC.resolved_by ? undefined : $state.user?.value?.email,
resolved_display_name: tempC.resolved_by ? undefined : $state.user?.value?.display_name,
resolved_display_name_short: tempC.resolved_by
? undefined
: $state.user?.value?.display_name ?? extractNameFromEmail($state.user?.value?.email),
resolved_by_meta: tempC.resolved_by ? undefined : $state.user?.value?.meta,
}
}
return c
})
await $api.internal.postOperation(
(meta.value as any).fk_workspace_id!,
meta.value!.base_id!,
{
operation: 'commentResolve',
},
{
commentId,
},
)
} catch (e: unknown) {
comments.value = comments.value.map((c) => {
if (c.id === commentId) {
return tempC
}
return c
})
message.error(
await extractSdkResponseErrorMsg(
e as Error & {
response: any
},
),
)
}
}
const saveComment = async (comment: string) => {
try {
if (!row.value || !comment) {
comments.value = comments.value.filter((c) => !c.id?.startsWith('temp-'))
return
}
const rowId = extractPkFromRow(row.value.row, meta.value.columns as ColumnType[])
if (!rowId) return
await $api.internal.postOperation(
(meta.value as any).fk_workspace_id!,
meta.value!.base_id!,
{
operation: 'commentRow',
},
{
fk_model_id: meta.value?.id as string,
row_id: rowId,
comment: `${comment}`.replace(/(<br \/>)+$/g, ''),
},
)
// Increase Comment Count in rowMeta
Object.assign(row.value, {
rowMeta: {
...row.value.rowMeta,
commentCount: (row.value.rowMeta.commentCount ?? 0) + 1,
},
})
// reloadTrigger?.trigger()
await loadComments()
} catch (e: any) {
comments.value = comments.value.filter((c) => !(c.id ?? '').startsWith('temp-'))
message.error(
await extractSdkResponseErrorMsg(
e as Error & {
response: any
},
),
)
}
$e('a:row-expand:comment')
}
const updateComment = async (commentId: string, comment: Partial<CommentType>) => {
const tempEdit = comments.value.find((c) => c.id === commentId)
if (!tempEdit) return
try {
comments.value = comments.value.map((c) => {
if (c.id === commentId) {
return {
...c,
...comment,
updated_at: new Date().toISOString(),
}
}
return c
})
await $api.internal.postOperation(
(meta.value as any).fk_workspace_id!,
meta.value!.base_id!,
{
operation: 'commentUpdate',
},
{
commentId,
...comment,
},
)
} catch (e: any) {
comments.value = comments.value.map((c) => {
if (c.id === commentId) {
return tempEdit
}
return c
})
message.error(
await extractSdkResponseErrorMsg(
e as Error & {
response: any
},
),
)
}
}
const primaryKey = computed(() => {
return extractPkFromRow(row.value.row, meta.value.columns as ColumnType[])
})
return {
comments,
loadComments,
saveComment,
updateComment,
resolveComment,
deleteComment,
isCommentsLoading,
primaryKey,
parsedHtmlComments,
}
})
export { useProvideRowComments }
export function useRowCommentsOrThrow() {
const rowComments = useRowComments()
if (!rowComments) {
throw new Error('useRowComments is not provided')
}
return rowComments
}