mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-23 01:55:00 +02:00
211 lines
6.4 KiB
Svelte
211 lines
6.4 KiB
Svelte
<!--
|
|
// Copyright © 2023 Hardcore Engineering Inc.
|
|
//
|
|
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License. You may
|
|
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
//
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
-->
|
|
<script lang="ts">
|
|
import activity, { ActivityMessage } from '@hcengineering/activity'
|
|
import { Analytics } from '@hcengineering/analytics'
|
|
import { AttachmentRefInput } from '@hcengineering/attachment-resources'
|
|
import chunter, { ChatMessage, ThreadMessage } from '@hcengineering/chunter'
|
|
import { PersonAccount } from '@hcengineering/contact'
|
|
import { Class, Doc, generateId, getCurrentAccount, Ref, type CommitResult } from '@hcengineering/core'
|
|
import { createQuery, DraftController, draftsStore, getClient, isSpace } from '@hcengineering/presentation'
|
|
import { EmptyMarkup } from '@hcengineering/text-editor'
|
|
import { createEventDispatcher } from 'svelte'
|
|
|
|
export let object: Doc
|
|
export let chatMessage: ChatMessage | undefined = undefined
|
|
export let shouldSaveDraft: boolean = true
|
|
export let focusIndex: number = -1
|
|
export let boundary: HTMLElement | undefined = undefined
|
|
export let loading = false
|
|
export let collection: string = 'comments'
|
|
export let autofocus = false
|
|
|
|
type MessageDraft = Pick<ChatMessage, '_id' | 'message' | 'attachments'>
|
|
|
|
const dispatch = createEventDispatcher()
|
|
|
|
const client = getClient()
|
|
const hierarchy = client.getHierarchy()
|
|
const _class: Ref<Class<ChatMessage>> = hierarchy.isDerived(object._class, activity.class.ActivityMessage)
|
|
? chunter.class.ThreadMessage
|
|
: chunter.class.ChatMessage
|
|
const createdMessageQuery = createQuery()
|
|
const account = getCurrentAccount() as PersonAccount
|
|
|
|
const draftKey = `${object._id}_${_class}`
|
|
const draftController = new DraftController<MessageDraft>(draftKey)
|
|
const currentDraft = shouldSaveDraft ? $draftsStore[draftKey] : undefined
|
|
|
|
const emptyMessage: Pick<MessageDraft, 'message' | 'attachments'> = {
|
|
message: EmptyMarkup,
|
|
attachments: 0
|
|
}
|
|
|
|
let inputRef: AttachmentRefInput
|
|
let currentMessage: MessageDraft = chatMessage ?? currentDraft ?? getDefault()
|
|
let _id = currentMessage._id
|
|
let inputContent = currentMessage.message
|
|
|
|
$: if (currentDraft != null) {
|
|
createdMessageQuery.query(_class, { _id }, (result: ChatMessage[]) => {
|
|
if (result.length > 0 && _id !== chatMessage?._id) {
|
|
// Ouch we have got comment with same id created already.
|
|
clear()
|
|
}
|
|
})
|
|
} else {
|
|
createdMessageQuery.unsubscribe()
|
|
}
|
|
|
|
function clear (): void {
|
|
currentMessage = getDefault()
|
|
_id = currentMessage._id
|
|
inputRef.removeDraft(false)
|
|
}
|
|
|
|
function objectChange (draft: MessageDraft, empty: Partial<MessageDraft>): void {
|
|
if (shouldSaveDraft) {
|
|
draftController.save(draft, empty)
|
|
}
|
|
}
|
|
|
|
$: objectChange(currentMessage, emptyMessage)
|
|
|
|
function getDefault (): MessageDraft {
|
|
return {
|
|
_id: generateId(),
|
|
...emptyMessage
|
|
}
|
|
}
|
|
|
|
function onUpdate (event: CustomEvent): void {
|
|
if (!shouldSaveDraft) {
|
|
return
|
|
}
|
|
const { message, attachments } = event.detail
|
|
currentMessage.message = message
|
|
currentMessage.attachments = attachments
|
|
}
|
|
|
|
async function handleCreate (event: CustomEvent, _id: Ref<ChatMessage>): Promise<void> {
|
|
try {
|
|
const res = await createMessage(event, _id, `chunter.create.${_class} ${object._class}`)
|
|
|
|
console.log(`create.${_class} measure`, res.serverTime, res.time)
|
|
} catch (err: any) {
|
|
Analytics.handleError(err)
|
|
console.error(err)
|
|
}
|
|
}
|
|
|
|
async function handleEdit (event: CustomEvent): Promise<void> {
|
|
try {
|
|
await editMessage(event)
|
|
} catch (err: any) {
|
|
Analytics.handleError(err)
|
|
console.error(err)
|
|
}
|
|
}
|
|
|
|
async function onMessage (event: CustomEvent): Promise<void> {
|
|
draftController.remove()
|
|
inputRef.removeDraft(false)
|
|
|
|
if (chatMessage !== undefined) {
|
|
loading = true
|
|
await handleEdit(event)
|
|
} else {
|
|
void handleCreate(event, _id)
|
|
}
|
|
|
|
// Remove draft from Local Storage
|
|
clear()
|
|
dispatch('submit', false)
|
|
loading = false
|
|
}
|
|
|
|
async function createMessage (event: CustomEvent, _id: Ref<ChatMessage>, msg: string): Promise<CommitResult> {
|
|
const { message, attachments } = event.detail
|
|
const operations = client.apply(_id, msg)
|
|
|
|
if (_class === chunter.class.ThreadMessage) {
|
|
const parentMessage = object as ActivityMessage
|
|
|
|
await operations.addCollection<ActivityMessage, ThreadMessage>(
|
|
chunter.class.ThreadMessage,
|
|
parentMessage.space,
|
|
parentMessage._id,
|
|
parentMessage._class,
|
|
'replies',
|
|
{
|
|
message,
|
|
attachments,
|
|
objectClass: parentMessage.attachedToClass,
|
|
objectId: parentMessage.attachedTo
|
|
},
|
|
_id as Ref<ThreadMessage>
|
|
)
|
|
|
|
await operations.update(parentMessage, { lastReply: Date.now() })
|
|
|
|
const hasPerson = !!parentMessage.repliedPersons?.includes(account.person)
|
|
|
|
if (!hasPerson) {
|
|
await operations.update(parentMessage, { $push: { repliedPersons: account.person } })
|
|
}
|
|
} else {
|
|
await operations.addCollection<Doc, ChatMessage>(
|
|
_class,
|
|
isSpace(object) ? object._id : object.space,
|
|
object._id,
|
|
object._class,
|
|
collection,
|
|
{ message, attachments },
|
|
_id
|
|
)
|
|
}
|
|
return await operations.commit()
|
|
}
|
|
|
|
async function editMessage (event: CustomEvent): Promise<void> {
|
|
if (chatMessage === undefined) {
|
|
return
|
|
}
|
|
const { message, attachments } = event.detail
|
|
await client.update(chatMessage, { message, attachments, editedOn: Date.now() })
|
|
}
|
|
export function submit (): void {
|
|
inputRef.submit()
|
|
}
|
|
</script>
|
|
|
|
<AttachmentRefInput
|
|
{focusIndex}
|
|
bind:this={inputRef}
|
|
bind:content={inputContent}
|
|
{_class}
|
|
space={isSpace(object) ? object._id : object.space}
|
|
skipAttachmentsPreload={(currentMessage.attachments ?? 0) === 0}
|
|
bind:objectId={_id}
|
|
{shouldSaveDraft}
|
|
{boundary}
|
|
{autofocus}
|
|
on:message={onMessage}
|
|
on:update={onUpdate}
|
|
on:focus
|
|
on:blur
|
|
bind:loading
|
|
/>
|