mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-14 12:25:17 +00:00
Fix attachments in drafts (#2451)
Signed-off-by: Denis Maslennikov <denis.maslennikov@gmail.com>
This commit is contained in:
parent
12292dbf46
commit
69a229efef
@ -14,7 +14,12 @@ export const draftStore = writable<Record<string, any>>(fetchMetadataLocalStorag
|
||||
*/
|
||||
export function updateDraftStore (id: string, draft: any): void {
|
||||
draftStore.update((drafts) => {
|
||||
drafts[id] = draft
|
||||
if (draft === undefined) {
|
||||
drafts[id] = draft
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete drafts[id]
|
||||
}
|
||||
setMetadataLocalStorage(presentation.metadata.Draft, drafts)
|
||||
return drafts
|
||||
})
|
||||
|
@ -142,7 +142,7 @@
|
||||
</Scroller>
|
||||
{#if showCommenInput}
|
||||
<div class="ref-input">
|
||||
<Component is={chunter.component.CommentInput} props={{ object, shouldUseDraft: true }} />
|
||||
<Component is={chunter.component.CommentInput} props={{ object, shouldSaveDraft: true }} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@ -164,7 +164,7 @@
|
||||
</div>
|
||||
{#if showCommenInput}
|
||||
<div class="ref-input">
|
||||
<Component is={chunter.component.CommentInput} props={{ object, shouldUseDraft: true }} />
|
||||
<Component is={chunter.component.CommentInput} props={{ object, shouldSaveDraft: true }} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="p-activity select-text" id={activity.string.Activity}>
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { createQuery, getClient, draftStore, updateDraftStore } from '@hcengineering/presentation'
|
||||
import { ReferenceInput } from '@hcengineering/text-editor'
|
||||
import { deleteFile, uploadFile } from '../utils'
|
||||
import attachment from '../plugin'
|
||||
@ -28,7 +28,8 @@
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let content: string = ''
|
||||
export let showSend = true
|
||||
export let shouldUseDraft: boolean = false
|
||||
export let shouldSaveDraft: boolean = false
|
||||
export let attachments: Map<Ref<Attachment>, Attachment> = new Map<Ref<Attachment>, Attachment>()
|
||||
export function submit (): void {
|
||||
refInput.submit()
|
||||
}
|
||||
@ -41,24 +42,48 @@
|
||||
const client = getClient()
|
||||
const query = createQuery()
|
||||
|
||||
let attachments: Map<Ref<Attachment>, Attachment> = new Map<Ref<Attachment>, Attachment>()
|
||||
let draftAttachments: Record<Ref<Attachment>, Attachment> | undefined = undefined
|
||||
let originalAttachments: Set<Ref<Attachment>> = new Set<Ref<Attachment>>()
|
||||
const newAttachments: Set<Ref<Attachment>> = new Set<Ref<Attachment>>()
|
||||
const removedAttachments: Set<Attachment> = new Set<Attachment>()
|
||||
|
||||
let refContainer: HTMLElement
|
||||
|
||||
$: objectId &&
|
||||
query.query(
|
||||
attachment.class.Attachment,
|
||||
{
|
||||
attachedTo: objectId
|
||||
},
|
||||
(res) => {
|
||||
originalAttachments = new Set(res.map((p) => p._id))
|
||||
attachments = new Map(res.map((p) => [p._id, p]))
|
||||
}
|
||||
)
|
||||
$: objectId && updateAttachments(objectId)
|
||||
|
||||
async function updateAttachments (objectId: Ref<Doc>) {
|
||||
draftAttachments = $draftStore[objectId]
|
||||
if (draftAttachments && shouldSaveDraft) {
|
||||
attachments.clear()
|
||||
newAttachments.clear()
|
||||
Object.entries(draftAttachments).map((file) => {
|
||||
return attachments.set(file[0] as Ref<Attachment>, file[1])
|
||||
})
|
||||
Object.entries(draftAttachments).map((file) => {
|
||||
return newAttachments.add(file[0] as Ref<Attachment>)
|
||||
})
|
||||
originalAttachments.clear()
|
||||
removedAttachments.clear()
|
||||
} else {
|
||||
query.query(
|
||||
attachment.class.Attachment,
|
||||
{
|
||||
attachedTo: objectId
|
||||
},
|
||||
(res) => {
|
||||
originalAttachments = new Set(res.map((p) => p._id))
|
||||
attachments = new Map(res.map((p) => [p._id, p]))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function saveDraft () {
|
||||
if (objectId && shouldSaveDraft) {
|
||||
draftAttachments = Object.fromEntries(attachments)
|
||||
updateDraftStore(objectId, draftAttachments)
|
||||
}
|
||||
}
|
||||
|
||||
async function createAttachment (file: File) {
|
||||
try {
|
||||
@ -81,9 +106,7 @@
|
||||
})
|
||||
newAttachments.add(_id)
|
||||
attachments = attachments
|
||||
if (shouldUseDraft) {
|
||||
await createAttachments()
|
||||
}
|
||||
saveDraft()
|
||||
} catch (err: any) {
|
||||
setPlatformStatus(unknownError(err))
|
||||
}
|
||||
@ -115,10 +138,11 @@
|
||||
async function removeAttachment (attachment: Attachment): Promise<void> {
|
||||
removedAttachments.add(attachment)
|
||||
attachments.delete(attachment._id)
|
||||
if (shouldUseDraft) {
|
||||
if (shouldSaveDraft) {
|
||||
await createAttachments()
|
||||
}
|
||||
attachments = attachments
|
||||
saveDraft()
|
||||
}
|
||||
|
||||
async function deleteAttachment (attachment: Attachment): Promise<void> {
|
||||
@ -137,7 +161,7 @@
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
if (!saved) {
|
||||
if (!saved && !shouldSaveDraft) {
|
||||
newAttachments.forEach(async (p) => {
|
||||
const attachment = attachments.get(p)
|
||||
if (attachment !== undefined) {
|
||||
@ -147,6 +171,20 @@
|
||||
}
|
||||
})
|
||||
|
||||
export function removeDraft (removeFiles: boolean) {
|
||||
if (objectId) {
|
||||
updateDraftStore(objectId, undefined)
|
||||
}
|
||||
if (removeFiles) {
|
||||
newAttachments.forEach(async (p) => {
|
||||
const attachment = attachments.get(p)
|
||||
if (attachment !== undefined) {
|
||||
await deleteFile(attachment.file)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function createAttachments (): Promise<void> {
|
||||
saved = true
|
||||
const promises: Promise<any>[] = []
|
||||
|
@ -16,7 +16,7 @@
|
||||
import { Attachment } from '@hcengineering/attachment'
|
||||
import { Account, Class, Doc, generateId, Ref, Space } from '@hcengineering/core'
|
||||
import { IntlString, setPlatformStatus, unknownError } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { createQuery, getClient, draftStore, updateDraftStore } from '@hcengineering/presentation'
|
||||
import { StyledTextBox } from '@hcengineering/text-editor'
|
||||
import { IconSize } from '@hcengineering/ui'
|
||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||
@ -37,6 +37,7 @@
|
||||
export let focusable: boolean = false
|
||||
export let fakeAttach: 'fake' | 'hidden' | 'normal' = 'normal'
|
||||
export let refContainer: HTMLElement | undefined = undefined
|
||||
export let shouldSaveDraft: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@ -62,6 +63,7 @@
|
||||
let refInput: StyledTextBox
|
||||
|
||||
let inputFile: HTMLInputElement
|
||||
let draftAttachments: Record<Ref<Attachment>, Attachment> | undefined = undefined
|
||||
let saved = false
|
||||
|
||||
const client = getClient()
|
||||
@ -71,17 +73,41 @@
|
||||
const newAttachments: Set<Ref<Attachment>> = new Set<Ref<Attachment>>()
|
||||
const removedAttachments: Set<Attachment> = new Set<Attachment>()
|
||||
|
||||
$: objectId &&
|
||||
query.query(
|
||||
attachment.class.Attachment,
|
||||
{
|
||||
attachedTo: objectId
|
||||
},
|
||||
(res) => {
|
||||
originalAttachments = new Set(res.map((p) => p._id))
|
||||
attachments = new Map(res.map((p) => [p._id, p]))
|
||||
}
|
||||
)
|
||||
$: objectId && updateAttachments(objectId)
|
||||
|
||||
async function updateAttachments (objectId: Ref<Doc>) {
|
||||
draftAttachments = $draftStore[objectId]
|
||||
if (draftAttachments && shouldSaveDraft) {
|
||||
attachments.clear()
|
||||
newAttachments.clear()
|
||||
Object.entries(draftAttachments).map((file) => {
|
||||
return attachments.set(file[0] as Ref<Attachment>, file[1])
|
||||
})
|
||||
Object.entries(draftAttachments).map((file) => {
|
||||
return newAttachments.add(file[0] as Ref<Attachment>)
|
||||
})
|
||||
originalAttachments.clear()
|
||||
removedAttachments.clear()
|
||||
} else {
|
||||
query.query(
|
||||
attachment.class.Attachment,
|
||||
{
|
||||
attachedTo: objectId
|
||||
},
|
||||
(res) => {
|
||||
originalAttachments = new Set(res.map((p) => p._id))
|
||||
attachments = new Map(res.map((p) => [p._id, p]))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function saveDraft () {
|
||||
if (objectId && shouldSaveDraft) {
|
||||
draftAttachments = Object.fromEntries(attachments)
|
||||
updateDraftStore(objectId, draftAttachments)
|
||||
}
|
||||
}
|
||||
|
||||
async function createAttachment (file: File) {
|
||||
if (space === undefined || objectId === undefined || _class === undefined) return
|
||||
@ -105,6 +131,7 @@
|
||||
})
|
||||
newAttachments.add(_id)
|
||||
attachments = attachments
|
||||
saveDraft()
|
||||
} catch (err: any) {
|
||||
setPlatformStatus(unknownError(err))
|
||||
}
|
||||
@ -139,6 +166,7 @@
|
||||
removedAttachments.add(attachment)
|
||||
attachments.delete(attachment._id)
|
||||
attachments = attachments
|
||||
saveDraft()
|
||||
}
|
||||
|
||||
async function deleteAttachment (attachment: Attachment): Promise<void> {
|
||||
@ -157,7 +185,7 @@
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
if (!saved) {
|
||||
if (!saved && !shouldSaveDraft) {
|
||||
newAttachments.forEach(async (p) => {
|
||||
const attachment = attachments.get(p)
|
||||
if (attachment !== undefined) {
|
||||
@ -167,6 +195,20 @@
|
||||
}
|
||||
})
|
||||
|
||||
export function removeDraft (removeFiles: boolean) {
|
||||
if (objectId) {
|
||||
updateDraftStore(objectId, undefined)
|
||||
}
|
||||
if (removeFiles) {
|
||||
newAttachments.forEach(async (p) => {
|
||||
const attachment = attachments.get(p)
|
||||
if (attachment !== undefined) {
|
||||
await deleteFile(attachment.file)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function createAttachments (): Promise<void> {
|
||||
saved = true
|
||||
const promises: Promise<any>[] = []
|
||||
|
@ -22,7 +22,7 @@
|
||||
import chunter from '../plugin'
|
||||
|
||||
export let object: Doc
|
||||
export let shouldUseDraft: boolean = false
|
||||
export let shouldSaveDraft: boolean = false
|
||||
|
||||
const client = getClient()
|
||||
const _class = chunter.class.Comment
|
||||
@ -37,7 +37,7 @@
|
||||
$: updateCommentFromDraft(draftComment)
|
||||
|
||||
async function updateDraft (object: Doc) {
|
||||
if (!shouldUseDraft) {
|
||||
if (!shouldSaveDraft) {
|
||||
return
|
||||
}
|
||||
draftComment = $draftStore[object._id]
|
||||
@ -47,7 +47,7 @@
|
||||
}
|
||||
|
||||
async function updateCommentFromDraft (draftComment: Comment | undefined) {
|
||||
if (!shouldUseDraft) {
|
||||
if (!shouldSaveDraft) {
|
||||
return
|
||||
}
|
||||
inputContent = draftComment ? draftComment.message : ''
|
||||
@ -59,12 +59,6 @@
|
||||
}
|
||||
|
||||
async function saveDraft (object: Doc) {
|
||||
if (draftComment) {
|
||||
draftComment._id = _id
|
||||
$draftStore[object._id] = draftComment
|
||||
} else {
|
||||
delete $draftStore[object._id]
|
||||
}
|
||||
updateDraftStore(object._id, draftComment)
|
||||
}
|
||||
|
||||
@ -86,7 +80,7 @@
|
||||
}
|
||||
|
||||
async function onUpdate (event: CustomEvent) {
|
||||
if (!shouldUseDraft) {
|
||||
if (!shouldSaveDraft) {
|
||||
return
|
||||
}
|
||||
const { message, attachments } = event.detail
|
||||
@ -133,6 +127,7 @@
|
||||
_id = generateId()
|
||||
draftComment = undefined
|
||||
await saveDraft(object)
|
||||
commentInputBox.removeDraft(false)
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -142,7 +137,7 @@
|
||||
{_class}
|
||||
space={object.space}
|
||||
bind:objectId={_id}
|
||||
shouldUseDraft
|
||||
{shouldSaveDraft}
|
||||
on:message={onMessage}
|
||||
on:update={onUpdate}
|
||||
/>
|
||||
|
@ -82,7 +82,6 @@
|
||||
import SetParentIssueActionPopup from './SetParentIssueActionPopup.svelte'
|
||||
import SprintSelector from './sprints/SprintSelector.svelte'
|
||||
import IssueTemplateChilds from './templates/IssueTemplateChilds.svelte'
|
||||
import attachment from '@hcengineering/attachment-resources/src/plugin'
|
||||
import IssueNotification from './issues/IssueNotification.svelte'
|
||||
|
||||
export let space: Ref<Team>
|
||||
@ -97,7 +96,7 @@
|
||||
export let originalIssue: Issue | undefined
|
||||
export let onDraftChanged: () => void
|
||||
|
||||
const draft: IssueDraft | undefined = getUserDraft(tracker.class.IssueDraft)
|
||||
const draft: IssueDraft | undefined = shouldSaveDraft ? getUserDraft(tracker.class.IssueDraft) : undefined
|
||||
|
||||
let issueStatuses: WithLookup<IssueStatus>[] | undefined
|
||||
let labels: TagReference[] = draft?.labels || []
|
||||
@ -106,7 +105,7 @@
|
||||
let currentTeam: Team | undefined
|
||||
|
||||
function toIssue (initials: AttachedData<Issue>, draft: IssueDraft | undefined): AttachedData<Issue> {
|
||||
if (draft == null) {
|
||||
if (draft === undefined) {
|
||||
return { ...initials }
|
||||
}
|
||||
const { labels, subIssues, ...issue } = draft
|
||||
@ -125,6 +124,7 @@
|
||||
priority,
|
||||
dueDate: null,
|
||||
comments: 0,
|
||||
attachments: 0,
|
||||
subIssues: 0,
|
||||
parents: [],
|
||||
reportedTime: 0,
|
||||
@ -295,8 +295,6 @@
|
||||
return
|
||||
}
|
||||
|
||||
await descriptionBox?.createAttachments()
|
||||
|
||||
let newDraft: Data<IssueDraft> | undefined = createDraftFromObject()
|
||||
const isEmpty = await isDraftEmpty(newDraft)
|
||||
|
||||
@ -337,6 +335,7 @@
|
||||
description: '',
|
||||
dueDate: null,
|
||||
estimation: 0,
|
||||
attachments: 0,
|
||||
labels: [],
|
||||
parentIssue: undefined,
|
||||
priority: 0,
|
||||
@ -351,11 +350,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
const attachmentResult = await client.findOne(attachment.class.Attachment, { attachedTo: objectId })
|
||||
|
||||
if (attachmentResult) {
|
||||
return false
|
||||
}
|
||||
// if (object.attachments && object.attachments > 0) {
|
||||
// return false
|
||||
// }
|
||||
|
||||
if (draft.project && draft.project !== defaultIssue.project) {
|
||||
return false
|
||||
@ -395,6 +392,7 @@
|
||||
dueDate: object.dueDate,
|
||||
estimation: object.estimation,
|
||||
template: object.template,
|
||||
attachments: object.attachments,
|
||||
labels,
|
||||
parentIssue: parentIssue?._id,
|
||||
team: _space,
|
||||
@ -562,6 +560,7 @@
|
||||
objectId = generateId()
|
||||
resetObject()
|
||||
saveDraft()
|
||||
descriptionBox?.removeDraft(false)
|
||||
}
|
||||
|
||||
async function showMoreActions (ev: Event) {
|
||||
@ -672,6 +671,7 @@
|
||||
dispatch('close')
|
||||
resetObject()
|
||||
saveDraft()
|
||||
descriptionBox?.removeDraft(true)
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -738,6 +738,7 @@
|
||||
<AttachmentStyledBox
|
||||
bind:this={descriptionBox}
|
||||
{objectId}
|
||||
{shouldSaveDraft}
|
||||
_class={tracker.class.Issue}
|
||||
space={_space}
|
||||
alwaysEdit
|
||||
@ -746,6 +747,11 @@
|
||||
bind:content={object.description}
|
||||
placeholder={tracker.string.IssueDescriptionPlaceholder}
|
||||
on:changeSize={() => dispatch('changeContent')}
|
||||
on:attach={(ev) => {
|
||||
if (ev.detail.action === 'saved') {
|
||||
object.attachments = ev.detail.value
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/key}
|
||||
<IssueTemplateChilds bind:children={subIssues} sprint={object.sprint} project={object.project} isScrollable />
|
||||
|
@ -222,6 +222,7 @@ export interface IssueDraft extends Doc {
|
||||
// Estimation in man days
|
||||
estimation: number
|
||||
parentIssue?: string
|
||||
attachments?: number
|
||||
labels?: TagReference[]
|
||||
subIssues?: IssueTemplateChild[]
|
||||
template?: {
|
||||
|
Loading…
Reference in New Issue
Block a user