mirror of
https://github.com/hcengineering/platform.git
synced 2025-01-03 17:05:16 +03:00
TSK-343: Remember unfinished comment per document (#2400)
* Add draft for comments Signed-off-by: Denis Maslennikov <denis.maslennikov@gmail.com> * Fix missed code Signed-off-by: Denis Maslennikov <denis.maslennikov@gmail.com> * Refactoring draft and attachments Signed-off-by: Denis Maslennikov <denis.maslennikov@gmail.com> Signed-off-by: Denis Maslennikov <denis.maslennikov@gmail.com>
This commit is contained in:
parent
d4d3502a3b
commit
2ed837ac59
@ -54,6 +54,11 @@
|
||||
let isSelectionEmpty = true
|
||||
let isEmpty = true
|
||||
|
||||
$: setContent(content)
|
||||
|
||||
function setContent (content: string) {
|
||||
textEditor?.setContent(content)
|
||||
}
|
||||
interface RefAction {
|
||||
label: IntlString
|
||||
icon: Asset | AnySvelteComponent
|
||||
@ -286,6 +291,7 @@
|
||||
}}
|
||||
extensions={editorExtensions}
|
||||
on:selection-update={updateFormattingState}
|
||||
on:update
|
||||
/>
|
||||
</div>
|
||||
{#if showSend}
|
||||
|
@ -48,7 +48,13 @@
|
||||
dispatch('content', content)
|
||||
}
|
||||
}
|
||||
|
||||
export function setContent (newContent: string): void {
|
||||
if (content !== newContent) {
|
||||
content = newContent
|
||||
editor.commands.setContent(content)
|
||||
isEmpty = editor.isEmpty
|
||||
}
|
||||
}
|
||||
export function clear (): void {
|
||||
content = ''
|
||||
editor.commands.clearContent(false)
|
||||
@ -181,6 +187,9 @@
|
||||
Placeholder.configure({ placeholder: placeHolderStr }),
|
||||
...extensions
|
||||
],
|
||||
parseOptions: {
|
||||
preserveWhitespace: 'full'
|
||||
},
|
||||
onTransaction: () => {
|
||||
// force re-render so `editor.isActive` works as expected
|
||||
editor = editor
|
||||
@ -199,6 +208,9 @@
|
||||
dispatch('value', content)
|
||||
dispatch('update', content)
|
||||
},
|
||||
onCreate: () => {
|
||||
isEmpty = editor.isEmpty
|
||||
},
|
||||
onSelectionUpdate: () => dispatch('selection-update')
|
||||
})
|
||||
})
|
||||
|
@ -142,7 +142,7 @@
|
||||
</Scroller>
|
||||
{#if showCommenInput}
|
||||
<div class="ref-input">
|
||||
<Component is={chunter.component.CommentInput} props={{ object }} />
|
||||
<Component is={chunter.component.CommentInput} props={{ object, shouldUseDraft: true }} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@ -164,7 +164,7 @@
|
||||
</div>
|
||||
{#if showCommenInput}
|
||||
<div class="ref-input">
|
||||
<Component is={chunter.component.CommentInput} props={{ object }} />
|
||||
<Component is={chunter.component.CommentInput} props={{ object, shouldUseDraft: true }} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="p-activity select-text" id={activity.string.Activity}>
|
||||
|
@ -28,6 +28,7 @@
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let content: string = ''
|
||||
export let showSend = true
|
||||
export let shouldUseDraft: boolean = false
|
||||
export function submit (): void {
|
||||
refInput.submit()
|
||||
}
|
||||
@ -39,6 +40,7 @@
|
||||
|
||||
const client = getClient()
|
||||
const query = createQuery()
|
||||
|
||||
let attachments: Map<Ref<Attachment>, Attachment> = new Map<Ref<Attachment>, Attachment>()
|
||||
let originalAttachments: Set<Ref<Attachment>> = new Set<Ref<Attachment>>()
|
||||
const newAttachments: Set<Ref<Attachment>> = new Set<Ref<Attachment>>()
|
||||
@ -79,6 +81,9 @@
|
||||
})
|
||||
newAttachments.add(_id)
|
||||
attachments = attachments
|
||||
if (shouldUseDraft) {
|
||||
await createAttachments()
|
||||
}
|
||||
} catch (err: any) {
|
||||
setPlatformStatus(unknownError(err))
|
||||
}
|
||||
@ -110,6 +115,9 @@
|
||||
async function removeAttachment (attachment: Attachment): Promise<void> {
|
||||
removedAttachments.add(attachment)
|
||||
attachments.delete(attachment._id)
|
||||
if (shouldUseDraft) {
|
||||
await createAttachments()
|
||||
}
|
||||
attachments = attachments
|
||||
}
|
||||
|
||||
@ -139,7 +147,7 @@
|
||||
}
|
||||
})
|
||||
|
||||
async function onMessage (event: CustomEvent) {
|
||||
export function createAttachments (): Promise<void> {
|
||||
saved = true
|
||||
const promises: Promise<any>[] = []
|
||||
newAttachments.forEach((p) => {
|
||||
@ -151,10 +159,18 @@
|
||||
removedAttachments.forEach((p) => {
|
||||
promises.push(deleteAttachment(p))
|
||||
})
|
||||
await Promise.all(promises)
|
||||
return Promise.all(promises).then()
|
||||
}
|
||||
|
||||
async function onMessage (event: CustomEvent) {
|
||||
await createAttachments()
|
||||
dispatch('message', { message: event.detail, attachments: attachments.size })
|
||||
}
|
||||
|
||||
async function onUpdate (event: CustomEvent) {
|
||||
dispatch('update', { message: event.detail, attachments: attachments.size })
|
||||
}
|
||||
|
||||
function pasteAction (evt: ClipboardEvent): void {
|
||||
let t: HTMLElement | null = evt.target as HTMLElement
|
||||
let allowed = false
|
||||
@ -224,6 +240,7 @@
|
||||
on:attach={() => {
|
||||
inputFile.click()
|
||||
}}
|
||||
on:update={onUpdate}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 Hardcore Engineering Inc.
|
||||
// Copyright © 2021, 2022 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
|
||||
@ -19,12 +19,101 @@
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { AttachmentRefInput } from '@hcengineering/attachment-resources'
|
||||
import { createBacklinks } from '../backlinks'
|
||||
import { draftStore, updateDraftStore } from '../drafts'
|
||||
import chunter from '../plugin'
|
||||
|
||||
const client = getClient()
|
||||
export let object: Doc
|
||||
export let shouldUseDraft: boolean = false
|
||||
|
||||
const client = getClient()
|
||||
const _class = chunter.class.Comment
|
||||
|
||||
let _id: Ref<Comment> = generateId()
|
||||
let inputContent: string = ''
|
||||
let commentInputBox: AttachmentRefInput
|
||||
let draftComment: Comment | undefined = undefined
|
||||
let saveTimer: number | undefined
|
||||
|
||||
$: updateDraft(object)
|
||||
$: updateCommentFromDraft(draftComment)
|
||||
|
||||
async function updateDraft (object: Doc) {
|
||||
if (!shouldUseDraft) {
|
||||
return
|
||||
}
|
||||
draftComment = $draftStore[object._id]
|
||||
if (!draftComment) {
|
||||
_id = generateId()
|
||||
}
|
||||
}
|
||||
|
||||
async function updateCommentFromDraft (draftComment: Comment | undefined) {
|
||||
if (!shouldUseDraft) {
|
||||
return
|
||||
}
|
||||
inputContent = draftComment ? draftComment.message : ''
|
||||
_id = draftComment ? draftComment._id : _id
|
||||
}
|
||||
|
||||
function commentIsEmpty (message: string, attachments: number): boolean {
|
||||
return (message === '<p></p>' || message === '') && !(attachments > 0)
|
||||
}
|
||||
|
||||
async function saveDraft (object: Doc) {
|
||||
if (draftComment) {
|
||||
draftComment._id = _id
|
||||
$draftStore[object._id] = draftComment
|
||||
} else {
|
||||
delete $draftStore[object._id]
|
||||
}
|
||||
updateDraftStore(object._id, draftComment)
|
||||
}
|
||||
|
||||
async function handleCommentUpdate (message: string, attachments: number) {
|
||||
if (commentIsEmpty(message, attachments)) {
|
||||
draftComment = undefined
|
||||
saveDraft(object)
|
||||
_id = generateId()
|
||||
return
|
||||
}
|
||||
if (!draftComment) {
|
||||
draftComment = createDraftFromObject()
|
||||
}
|
||||
draftComment.message = message
|
||||
draftComment.attachments = attachments
|
||||
|
||||
await commentInputBox.createAttachments()
|
||||
saveDraft(object)
|
||||
}
|
||||
|
||||
async function onUpdate (event: CustomEvent) {
|
||||
if (!shouldUseDraft) {
|
||||
return
|
||||
}
|
||||
const { message, attachments } = event.detail
|
||||
if (saveTimer) {
|
||||
clearTimeout(saveTimer)
|
||||
}
|
||||
saveTimer = setTimeout(() => {
|
||||
handleCommentUpdate(message, attachments)
|
||||
}, 200)
|
||||
}
|
||||
|
||||
function createDraftFromObject () {
|
||||
const newDraft: Comment = {
|
||||
_id,
|
||||
_class: chunter.class.Comment,
|
||||
space: object.space,
|
||||
modifiedOn: Date.now(),
|
||||
modifiedBy: object.modifiedBy,
|
||||
attachedTo: object._id,
|
||||
attachedToClass: object._class,
|
||||
collection: 'comments',
|
||||
message: '',
|
||||
attachments: 0
|
||||
}
|
||||
return newDraft
|
||||
}
|
||||
|
||||
async function onMessage (event: CustomEvent) {
|
||||
const { message, attachments } = event.detail
|
||||
@ -40,8 +129,21 @@
|
||||
|
||||
// Create an backlink to document
|
||||
await createBacklinks(client, object._id, object._class, _id, message)
|
||||
|
||||
// Remove draft from Local Storage
|
||||
_id = generateId()
|
||||
draftComment = undefined
|
||||
await saveDraft(object)
|
||||
}
|
||||
</script>
|
||||
|
||||
<AttachmentRefInput {_class} space={object.space} objectId={_id} on:message={onMessage} />
|
||||
<AttachmentRefInput
|
||||
bind:this={commentInputBox}
|
||||
bind:content={inputContent}
|
||||
{_class}
|
||||
space={object.space}
|
||||
bind:objectId={_id}
|
||||
shouldUseDraft
|
||||
on:message={onMessage}
|
||||
on:update={onUpdate}
|
||||
/>
|
||||
|
18
plugins/chunter-resources/src/drafts.ts
Normal file
18
plugins/chunter-resources/src/drafts.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { fetchMetadataLocalStorage, setMetadataLocalStorage } from '@hcengineering/ui'
|
||||
import { writable } from 'svelte/store'
|
||||
import chunter from './plugin'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
// eslint-disable-next-line
|
||||
export const draftStore = writable<Record<string, any>>(fetchMetadataLocalStorage(chunter.metadata.Draft) || {})
|
||||
console.log('draft store', draftStore)
|
||||
|
||||
export function updateDraftStore (id: string, draft: any): void {
|
||||
draftStore.update((drafts) => {
|
||||
drafts[id] = draft
|
||||
setMetadataLocalStorage(chunter.metadata.Draft, drafts)
|
||||
return drafts
|
||||
})
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
|
||||
import chunter, { chunterId } from '@hcengineering/chunter'
|
||||
import type { Client, Space } from '@hcengineering/core'
|
||||
import type { IntlString, Resource } from '@hcengineering/platform'
|
||||
import type { IntlString, Metadata, Resource } from '@hcengineering/platform'
|
||||
import { mergeIds } from '@hcengineering/platform'
|
||||
import type { AnyComponent } from '@hcengineering/ui'
|
||||
import { ViewAction } from '@hcengineering/view'
|
||||
@ -87,5 +87,8 @@ export default mergeIds(chunterId, chunter, {
|
||||
Messages: '' as IntlString,
|
||||
NoResults: '' as IntlString,
|
||||
CopyLink: '' as IntlString
|
||||
},
|
||||
metadata: {
|
||||
Draft: '' as Metadata<Record<string, any>>
|
||||
}
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user