TSK-461: Refactor Tracker/Remember Issues (#2425)

* Refactor: Move drafts.ts to /presentation

Signed-off-by: Denis Maslennikov <denis.maslennikov@gmail.com>

* Refactor remember issues

Signed-off-by: Denis Maslennikov <denis.maslennikov@gmail.com>

* Fix close issue bug

Signed-off-by: Denis Maslennikov <denis.maslennikov@gmail.com>

* Refactor and fix draft clean

Signed-off-by: Denis Maslennikov <denis.maslennikov@gmail.com>

* Minor handle method fix

Signed-off-by: Denis Maslennikov <denis.maslennikov@gmail.com>

Signed-off-by: Denis Maslennikov <denis.maslennikov@gmail.com>
This commit is contained in:
Denis Maslennikov 2022-12-08 08:17:53 +07:00 committed by GitHub
parent 04206adaef
commit 8865006d3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 137 additions and 64 deletions

View File

@ -0,0 +1,67 @@
import { fetchMetadataLocalStorage, setMetadataLocalStorage } from '@hcengineering/ui'
import { writable, get } from 'svelte/store'
import { getClient } from '.'
import presentation from './plugin'
/**
* @public
*/
// eslint-disable-next-line
export const draftStore = writable<Record<string, any>>(fetchMetadataLocalStorage(presentation.metadata.Draft) || {})
/**
* @public
*/
export function updateDraftStore (id: string, draft: any): void {
draftStore.update((drafts) => {
drafts[id] = draft
setMetadataLocalStorage(presentation.metadata.Draft, drafts)
return drafts
})
}
/**
* @public
*/
export function updateUserDraft (id: string, draft: any): void {
const client = getClient()
draftStore.update((drafts) => {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
const userDrafts: Record<string, any> = drafts[client.user] || {}
userDrafts[id] = draft
drafts[client.user] = userDrafts
setMetadataLocalStorage(presentation.metadata.Draft, drafts)
return drafts
})
}
/**
* @public
*/
export function getUserDraft (id: string): any {
const client = getClient()
const drafts: Record<string, any> = get(draftStore)
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
const userDrafts: Record<string, any> = drafts[client.user] || {}
const draft: Record<string, any> = userDrafts[id]
return draft
}
/**
* @public
*/
export function isUserDraftExists (id: string): boolean {
const client = getClient()
const drafts: Record<string, any> = get(draftStore)
const userDrafts: Record<string, any> = drafts[client.user]
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (!userDrafts) {
return false
}
const draftRecord: Record<string, any> = userDrafts[id]
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (!draftRecord) {
return false
}
return true
}

View File

@ -47,6 +47,7 @@ export { connect, versionError } from './connect'
export { default } from './plugin'
export * from './types'
export * from './utils'
export * from './drafts'
export { presentationId }
addStringsLoader(presentationId, async (lang: string) => {

View File

@ -59,6 +59,7 @@ export default plugin(presentationId, {
GravatarsManaged: '' as IntlString
},
metadata: {
RequiredVersion: '' as Metadata<string>
RequiredVersion: '' as Metadata<string>,
Draft: '' as Metadata<Record<string, any>>
}
})

View File

@ -16,10 +16,9 @@
<script lang="ts">
import { Comment } from '@hcengineering/chunter'
import { Doc, generateId, Ref } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import { getClient, draftStore, updateDraftStore } from '@hcengineering/presentation'
import { AttachmentRefInput } from '@hcengineering/attachment-resources'
import { createBacklinks } from '../backlinks'
import { draftStore, updateDraftStore } from '../drafts'
import chunter from '../plugin'
export let object: Doc

View File

@ -1,18 +0,0 @@
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
})
}

View File

@ -15,7 +15,7 @@
import chunter, { chunterId } from '@hcengineering/chunter'
import type { Client, Space } from '@hcengineering/core'
import type { IntlString, Metadata, Resource } from '@hcengineering/platform'
import type { IntlString, Resource } from '@hcengineering/platform'
import { mergeIds } from '@hcengineering/platform'
import type { AnyComponent } from '@hcengineering/ui'
import { ViewAction } from '@hcengineering/view'
@ -87,8 +87,5 @@ export default mergeIds(chunterId, chunter, {
Messages: '' as IntlString,
NoResults: '' as IntlString,
CopyLink: '' as IntlString
},
metadata: {
Draft: '' as Metadata<Record<string, any>>
}
})

View File

@ -28,7 +28,16 @@
WithLookup
} from '@hcengineering/core'
import { getResource, translate } from '@hcengineering/platform'
import { Card, createQuery, getClient, KeyedAttribute, MessageBox, SpaceSelector } from '@hcengineering/presentation'
import {
Card,
createQuery,
getClient,
getUserDraft,
KeyedAttribute,
MessageBox,
SpaceSelector,
updateUserDraft
} from '@hcengineering/presentation'
import tags, { TagElement, TagReference } from '@hcengineering/tags'
import {
calcRank,
@ -55,7 +64,6 @@
IconMoreH,
Label,
Menu,
setMetadataLocalStorage,
showPopup,
Spinner
} from '@hcengineering/ui'
@ -85,17 +93,19 @@
export let sprint: Ref<Sprint> | null = $activeSprint ?? null
export let relatedTo: Doc | undefined
export let shouldSaveDraft: boolean = false
export let draft: IssueDraft | null
export let parentIssue: Issue | undefined
export let originalIssue: Issue | undefined
export let onDraftChanged: (draft: Data<IssueDraft>) => void
export let onDraftChanged: () => void
const draft: IssueDraft | undefined = getUserDraft(tracker.class.IssueDraft)
let issueStatuses: WithLookup<IssueStatus>[] | undefined
let labels: TagReference[] = draft?.labels || []
let objectId: Ref<Issue> = draft?.issueId || generateId()
let saveTimer: number | undefined
let currentTeam: Team | undefined
function toIssue (initials: AttachedData<Issue>, draft: IssueDraft | null): AttachedData<Issue> {
function toIssue (initials: AttachedData<Issue>, draft: IssueDraft | undefined): AttachedData<Issue> {
if (draft == null) {
return { ...initials }
}
@ -269,6 +279,36 @@
$: originalIssue && setPropsFromOriginalIssue()
$: draft && setPropsFromDraft()
$: object && updateDraft()
async function updateDraft () {
if (saveTimer) {
clearTimeout(saveTimer)
}
saveTimer = setTimeout(() => {
saveDraft()
}, 200)
}
async function saveDraft () {
if (!shouldSaveDraft) {
return
}
await descriptionBox?.createAttachments()
let newDraft: Data<IssueDraft> | undefined = createDraftFromObject()
const isEmpty = await isDraftEmpty(newDraft)
if (isEmpty) {
newDraft = undefined
}
updateUserDraft(tracker.class.IssueDraft, newDraft)
if (onDraftChanged) {
return onDraftChanged()
}
}
async function updateIssueStatusId (teamId: Ref<Team>, issueStatusId?: Ref<IssueStatus>) {
if (issueStatusId !== undefined) {
@ -319,13 +359,17 @@
return false
}
if (draft.status === '') {
return true
}
const team = await client.findOne(tracker.class.Team, { _id: _space })
if (team?.defaultIssueStatus) {
return draft.status === team.defaultIssueStatus
}
return status === ''
return false
}
export function canClose (): boolean {
@ -336,7 +380,7 @@
const newDraft: Data<IssueDraft> = {
issueId: objectId,
title: getTitle(object.title),
description: object.description,
description: (object.description as string).replaceAll('<p></p>', ''),
assignee: object.assignee,
project: object.project,
sprint: object.sprint,
@ -355,23 +399,10 @@
}
export async function onOutsideClick () {
if (!shouldSaveDraft) {
return
}
await descriptionBox?.createAttachments()
const newDraft = createDraftFromObject()
const isEmpty = await isDraftEmpty(newDraft)
if (isEmpty) {
return
}
setMetadataLocalStorage(tracker.metadata.CreateIssueDraft, newDraft)
saveDraft()
if (onDraftChanged) {
return onDraftChanged(newDraft)
return onDraftChanged()
}
}
@ -523,6 +554,7 @@
objectId = generateId()
resetObject()
saveDraft()
}
async function showMoreActions (ev: Event) {
@ -631,6 +663,8 @@
(result?: boolean) => {
if (result === true) {
dispatch('close')
resetObject()
saveDraft()
}
}
)

View File

@ -13,10 +13,9 @@
// limitations under the License.
-->
<script lang="ts">
import { Data, Ref, Space } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import { IssueDraft } from '@hcengineering/tracker'
import { Button, fetchMetadataLocalStorage, setMetadataLocalStorage, showPopup } from '@hcengineering/ui'
import { Ref, Space } from '@hcengineering/core'
import { getClient, isUserDraftExists } from '@hcengineering/presentation'
import { Button, showPopup } from '@hcengineering/ui'
import tracker from '../plugin'
import CreateIssue from './CreateIssue.svelte'
@ -27,10 +26,10 @@
let space: Ref<Space> | undefined
$: updateSpace(currentSpace)
let issueDraft: Data<IssueDraft> | null = fetchMetadataLocalStorage(tracker.metadata.CreateIssueDraft)
let draftExists: boolean = isUserDraftExists(tracker.class.IssueDraft)
const handleDraftChanged = (draft: Data<IssueDraft>) => {
issueDraft = draft
const handleDraftChanged = () => {
draftExists = isUserDraftExists(tracker.class.IssueDraft)
}
async function updateSpace (spaceId: Ref<Space> | undefined): Promise<void> {
@ -49,14 +48,7 @@
space = team?._id
}
showPopup(
CreateIssue,
{ space, shouldSaveDraft: true, draft: issueDraft, onDraftChanged: handleDraftChanged },
'top'
)
setMetadataLocalStorage(tracker.metadata.CreateIssueDraft, null)
issueDraft = null
showPopup(CreateIssue, { space, shouldSaveDraft: true, onDraftChanged: handleDraftChanged }, 'top')
}
</script>
@ -64,13 +56,13 @@
<div class="flex-grow text-md">
<Button
icon={tracker.icon.NewIssue}
label={issueDraft ? tracker.string.ResumeDraft : tracker.string.NewIssue}
label={draftExists ? tracker.string.ResumeDraft : tracker.string.NewIssue}
justify={'left'}
width={'100%'}
on:click={newIssue}
>
<div slot="content" class="draft-circle-container">
{#if issueDraft}
{#if draftExists}
<div class="draft-circle" />
{/if}
</div>