Draft tabs sync (#3030)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2023-04-21 08:50:32 +06:00 committed by GitHub
parent 67d361d856
commit 01afbab81d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 104 additions and 46 deletions

View File

@ -68,7 +68,6 @@
let refInput: StyledTextBox
let inputFile: HTMLInputElement
let draftAttachments: Record<Ref<Attachment>, Attachment> | undefined = undefined
let saved = false
const client = getClient()
@ -78,11 +77,14 @@
const newAttachments: Set<Ref<Attachment>> = new Set<Ref<Attachment>>()
const removedAttachments: Set<Attachment> = new Set<Attachment>()
$: objectId && draftKey && updateAttachments(objectId, draftKey)
$: draftKey && updateAttachments(objectId, $draftsStore[draftKey])
async function updateAttachments (objectId: Ref<Doc>, draftKey: string) {
draftAttachments = $draftsStore[draftKey]
async function updateAttachments (
objectId: Ref<Doc> | undefined,
draftAttachments: Record<Ref<Attachment>, Attachment> | undefined
) {
if (draftAttachments && shouldSaveDraft) {
query.unsubscribe()
attachments.clear()
newAttachments.clear()
Object.entries(draftAttachments).map((file) => {
@ -93,7 +95,8 @@
})
originalAttachments.clear()
removedAttachments.clear()
} else {
attachments = attachments
} else if (objectId) {
query.query(
attachment.class.Attachment,
{
@ -102,6 +105,7 @@
(res) => {
originalAttachments = new Set(res.map((p) => p._id))
attachments = toIdMap(res)
dispatch('attach', { action: 'saved', value: attachments.size })
}
)
}
@ -109,7 +113,7 @@
async function saveDraft () {
if (draftKey && shouldSaveDraft) {
draftAttachments = Object.fromEntries(attachments)
const draftAttachments = Object.fromEntries(attachments)
DraftController.save(draftKey, draftAttachments)
}
}
@ -138,6 +142,7 @@
attachments = attachments
saved = false
saveDraft()
dispatch('attach', { action: 'saved', value: attachments.size })
dispatch('attached', _id)
} catch (err: any) {
setPlatformStatus(unknownError(err))
@ -255,10 +260,6 @@
removedAttachments.clear()
}
$: if (attachments.size || newAttachments.size || removedAttachments.size) {
dispatch('attach', { action: 'saved', value: attachments.size })
}
function isAllowedPaste (evt: ClipboardEvent) {
let t: HTMLElement | null = evt.target as HTMLElement

View File

@ -50,15 +50,15 @@
Component,
createFocusManager,
EditBox,
IconFile as FileIcon,
FocusHandler,
getColorNumberByText,
IconFile as FileIcon,
IconInfo,
Label,
showPopup,
Spinner
} from '@hcengineering/ui'
import { createEventDispatcher, onDestroy } from 'svelte'
import { createEventDispatcher } from 'svelte'
import recruit from '../plugin'
import FileUpload from './icons/FileUpload.svelte'
import YesNo from './YesNo.svelte'
@ -81,10 +81,29 @@
const empty = {}
const client = getClient()
const hierarchy = client.getHierarchy()
const ignoreKeys = ['onsite', 'remote']
const ignoreKeys = ['onsite', 'remote', 'title']
const draft = shouldSaveDraft ? draftController.get() : undefined
let draft = shouldSaveDraft ? ($draftsStore[recruit.mixin.Candidate] as CandidateDraft) : undefined
$: draft = shouldSaveDraft ? ($draftsStore[recruit.mixin.Candidate] as CandidateDraft) : undefined
let object = draft ?? getEmptyCandidate()
function draftChange (draft: CandidateDraft | undefined) {
if (draft === undefined) {
object = getEmptyCandidate()
} else {
object = draft
}
}
function objectChange (object: CandidateDraft, empty: any) {
if (shouldSaveDraft) {
draftController.save(object, empty)
}
}
$: objectChange(object, empty)
$: draftChange(draft)
type resumeFile = {
name: string
uuid: string
@ -113,12 +132,6 @@
}
}
if (shouldSaveDraft) {
draftController.watch(object, empty)
}
onDestroy(() => draftController.unsubscribe())
const dispatch = createEventDispatcher()
let inputFile: HTMLInputElement
@ -643,6 +656,9 @@
toClass={contact.class.Contact}
{ignoreKeys}
extraProps={{ showNavigate: false }}
on:update={() => {
object = object
}}
/>
</div>
</svelte:fragment>

View File

@ -55,7 +55,6 @@
} from '@hcengineering/ui'
import view from '@hcengineering/view'
import { ObjectBox } from '@hcengineering/view-resources'
import { onDestroy } from 'svelte'
import { createEventDispatcher } from 'svelte'
import { activeComponent, activeSprint, generateIssueShortLink, getIssueId, updateIssueRelation } from '../issues'
import tracker from '../plugin'
@ -84,7 +83,8 @@
const draftController = new DraftController<any>(tracker.ids.IssueDraft)
const draft: IssueDraft | undefined = shouldSaveDraft ? draftController.get() : undefined
let draft = shouldSaveDraft ? ($draftsStore[tracker.ids.IssueDraft] as IssueDraft) : undefined
$: draft = shouldSaveDraft ? ($draftsStore[tracker.ids.IssueDraft] as IssueDraft) : undefined
const client = getClient()
const hierarchy = client.getHierarchy()
const parentQuery = createQuery()
@ -92,6 +92,24 @@
let object = draft ?? getDefaultObject()
function draftChange (draft: IssueDraft | undefined) {
if (draft === undefined) {
object = getDefaultObject()
} else {
object = draft
descriptionBox?.setContent(object.description)
}
}
function objectChange (object: IssueDraft, empty: any) {
if (shouldSaveDraft) {
draftController.save(object, empty)
}
}
$: objectChange(object, empty)
$: draftChange(draft)
$: if (object.parentIssue) {
parentQuery.query(
tracker.class.Issue,
@ -297,12 +315,6 @@
}
}
$: watch(empty)
function watch (empty: Record<string, any>): void {
if (!shouldSaveDraft) return
draftController.watch(object, empty)
}
async function createIssue () {
const _id: Ref<Issue> = generateId()
if (!canSave || object.status === undefined) {
@ -390,6 +402,7 @@
draftController.remove()
resetObject()
descriptionBox?.removeDraft(false)
subIssuesComponent.removeChildDraft()
}
async function showMoreActions (ev: Event) {
@ -503,6 +516,7 @@
if (result === true) {
dispatch('close')
resetObject()
subIssuesComponent.removeChildDraft()
draftController.remove()
descriptionBox?.removeDraft(true)
}
@ -511,8 +525,6 @@
}
}
onDestroy(() => draftController.unsubscribe())
$: objectId = object._id
</script>

View File

@ -36,7 +36,9 @@
let lastProject = project
let isCollapsed = false
let isCreating = $draftsStore[tracker.ids.IssueDraftChild] !== undefined
$: isCreatingMode = $draftsStore[tracker.ids.IssueDraftChild] !== undefined
let isManualCreating = false
$: isCreating = isCreatingMode || isManualCreating
async function handleIssueSwap (ev: CustomEvent<{ fromIndex: number; toIndex: number }>) {
if (subIssues) {
@ -170,7 +172,13 @@
}
}
export function removeChildDraft () {
draftChild?.removeDraft()
}
$: hasSubIssues = subIssues.length > 0
let draftChild: DraftIssueChildEditor
</script>
<div class="flex-between clear-mins">
@ -200,7 +208,7 @@
showTooltip={{ label: tracker.string.AddSubIssues, props: { subIssues: 1 } }}
on:click={() => {
closeTooltip()
isCreating = true
isManualCreating = true
isCollapsed = false
}}
/>
@ -224,12 +232,13 @@
{#if isCreating && project}
<ExpandCollapse isExpanded={!isCollapsed} on:changeContent>
<DraftIssueChildEditor
bind:this={draftChild}
{project}
{component}
{sprint}
{shouldSaveDraft}
on:close={() => {
isCreating = false
isManualCreating = false
}}
on:create={(evt) => {
if (subIssues === undefined) {

View File

@ -13,8 +13,8 @@
// limitations under the License.
-->
<script lang="ts">
import contact, { Employee, EmployeeAccount } from '@hcengineering/contact'
import { AssigneeBox } from '@hcengineering/contact-resources'
import { Employee, EmployeeAccount } from '@hcengineering/contact'
import { AssigneeBox, employeeAccountByIdStore } from '@hcengineering/contact-resources'
import { AttachedData, Ref } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import { Issue, IssueDraft, IssueTemplateData } from '@hcengineering/tracker'
@ -58,9 +58,9 @@
if (hasSpace(issue)) {
const project = await client.findOne(tracker.class.Project, { _id: issue.space })
if (project !== undefined) {
const accounts = await client.findAll(contact.class.EmployeeAccount, {
_id: { $in: project.members as Ref<EmployeeAccount>[] }
})
const accounts = project.members
.map((p) => $employeeAccountByIdStore.get(p as Ref<EmployeeAccount>))
.filter((p) => p !== undefined) as EmployeeAccount[]
members = accounts.map((p) => p.employee)
} else {
members = []

View File

@ -15,11 +15,11 @@
<script lang="ts">
import { AttachmentStyledBox } from '@hcengineering/attachment-resources'
import { Account, Doc, generateId, Ref } from '@hcengineering/core'
import presentation, { DraftController, getClient, KeyedAttribute } from '@hcengineering/presentation'
import presentation, { DraftController, draftsStore, getClient, KeyedAttribute } from '@hcengineering/presentation'
import tags, { TagElement, TagReference } from '@hcengineering/tags'
import { Component as ComponentType, IssueDraft, IssuePriority, Project, Sprint } from '@hcengineering/tracker'
import { Button, Component, EditBox } from '@hcengineering/ui'
import { createEventDispatcher, onDestroy } from 'svelte'
import { createEventDispatcher } from 'svelte'
import tracker from '../../plugin'
import AssigneeEditor from '../issues/AssigneeEditor.svelte'
import PriorityEditor from '../issues/PriorityEditor.svelte'
@ -36,17 +36,28 @@
const dispatch = createEventDispatcher()
const client = getClient()
const draftController = new DraftController<IssueDraft>(tracker.ids.IssueDraftChild)
const draft = shouldSaveDraft ? draftController.get() : undefined
const draft = shouldSaveDraft ? ($draftsStore[tracker.ids.IssueDraftChild] as IssueDraft) : undefined
let object = childIssue !== undefined ? childIssue : draft ?? getIssueDefaults()
let thisRef: HTMLDivElement
let focusIssueTitle: () => void
onDestroy(() => draftController.unsubscribe())
const key: KeyedAttribute = {
key: 'labels',
attr: client.getHierarchy().getAttribute(tracker.class.IssueTemplate, 'labels')
}
let descriptionBox: AttachmentStyledBox
function draftChange (draft: IssueDraft | undefined) {
if (draft === undefined) {
object = childIssue !== undefined ? childIssue : getIssueDefaults()
} else {
object = draft
descriptionBox?.setContent(object.description)
}
}
$: shouldSaveDraft && draftChange($draftsStore[tracker.ids.IssueDraftChild])
function getIssueDefaults (): IssueDraft {
return {
_id: generateId(),
@ -75,10 +86,14 @@
sprint
}
if (shouldSaveDraft) {
draftController.watch(object, empty)
function objectChange (object: IssueDraft, empty: any) {
if (shouldSaveDraft) {
draftController.save(object, empty)
}
}
$: objectChange(object, empty)
function resetToDefaults () {
object = getIssueDefaults()
focusIssueTitle?.()
@ -88,8 +103,12 @@
return value.trim()
}
function close () {
export function removeDraft () {
draftController.remove()
}
function close () {
removeDraft()
dispatch('close')
}
@ -100,7 +119,7 @@
dispatch(childIssue ? 'close' : 'create', object)
draftController.remove()
removeDraft()
resetToDefaults()
}
@ -149,6 +168,7 @@
<div class="mt-4 clear-mins" id="sub-issue-description">
{#key objectId}
<AttachmentStyledBox
bind:this={descriptionBox}
objectId={object._id}
space={project._id}
_class={tracker.class.Issue}