Support card drafts (#10004)

Signed-off-by: Artem Savchenko <armisav@gmail.com>
Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Artyom Savchenko
2025-10-02 22:31:33 +07:00
committed by GitHub
co-authored by Copilot
parent 082e36e796
commit 0f7a111d7f
2 changed files with 86 additions and 4 deletions
@@ -13,7 +13,13 @@
// limitations under the License.
-->
<script lang="ts">
import { getClient, getCommunicationClient, SpaceSelector } from '@hcengineering/presentation'
import {
getClient,
getCommunicationClient,
SpaceSelector,
DraftController,
draftsStore
} from '@hcengineering/presentation'
import { ModernButton, ModernEditbox, ButtonIcon, IconSend, IconMinimize } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
@@ -24,6 +30,7 @@
import { getResource } from '@hcengineering/platform'
import { EmptyMarkup, markupToText, isEmptyMarkup, markupToJSON } from '@hcengineering/text'
import { createCard } from '../utils'
import { getCardDraftKey, getEmptyCardDraft, type CardDraft } from '../draft'
import { AttachmentStyledBox } from '@hcengineering/attachment-resources'
import { AttachIcon } from '@hcengineering/text-editor-resources'
import { AttachmentID, BlobParams } from '@hcengineering/communication-types'
@@ -31,9 +38,9 @@
import textEditor, { type RefAction } from '@hcengineering/text-editor'
import { defaultMessageInputActions } from '@hcengineering/communication-resources'
import chat from '@hcengineering/chat'
import { Analytics } from '@hcengineering/analytics'
import EditorActions from './EditorActions.svelte'
import { Analytics } from '@hcengineering/analytics'
const dispatch = createEventDispatcher()
const communicationClient = getCommunicationClient()
@@ -43,12 +50,39 @@
export let type: Ref<MasterTag> = threadMasterTag
export let space: Ref<CardSpace> | undefined = undefined
let title: string = ''
let description = EmptyMarkup
const draftKey = getCardDraftKey()
const draftController = new DraftController<CardDraft>(draftKey)
$: currentDraft = $draftsStore[draftKey] as CardDraft | undefined
let _id = generateId<Card>()
let isExpanded = false
let descriptionBox: AttachmentStyledBox
// Initialize form state from draft or defaults
let title: string = ''
let description: Markup = EmptyMarkup
let initialized = false
function initializeFromDraft (draft: CardDraft): void {
title = draft.title
description = draft.description
if (draft.type !== undefined) {
type = draft.type
}
if (draft.space !== undefined) {
space = draft.space
}
if (draft.title !== '' || !isEmptyMarkup(draft.description)) {
isExpanded = true
}
initialized = true
}
$: if (currentDraft != null && !initialized) {
initializeFromDraft(currentDraft)
}
$: draftController.save({ title, description, type, space }, getEmptyCardDraft())
let creating = false
$: applyDisabled = (title.trim() === '' && isEmptyMarkup(description)) || space == null || type == null || creating
@@ -137,6 +171,7 @@
title = ''
description = EmptyMarkup
_id = generateId<Card>()
draftController.remove()
}
function expand (): void {
+47
View File
@@ -0,0 +1,47 @@
// Copyright © 2025 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.
import { type Ref, type Markup } from '@hcengineering/core'
import { type CardSpace, type MasterTag } from '@hcengineering/card'
import { EmptyMarkup } from '@hcengineering/text'
import { getCurrentLocation, type Location } from '@hcengineering/ui'
/**
* Card draft interface for DraftController
*/
export interface CardDraft {
title: string
description: Markup
type: Ref<MasterTag> | undefined
space: Ref<CardSpace> | undefined
}
/**
* Generate a location-specific draft key for card drafts
*/
export function getCardDraftKey (location?: Location): string {
const currentLocation = location ?? getCurrentLocation()
const locationKey = currentLocation.path.slice(2).join('.') ?? 'root'
const fragmentKey =
currentLocation.fragment != null && currentLocation.fragment !== '' ? `.${currentLocation.fragment}` : ''
return `card_draft.${locationKey}${fragmentKey}`
}
export function getEmptyCardDraft (): CardDraft {
return {
title: '',
description: EmptyMarkup,
type: undefined,
space: undefined
}
}