mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-13 03:40:48 +00:00
TSK-1342: Reduce number of transfer data and improve Kanban initial render speed (#3078)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
40d13681d6
commit
42e320f425
@ -619,7 +619,6 @@ export function createModel (builder: Builder): void {
|
||||
space: recruit.class.Vacancy
|
||||
}
|
||||
],
|
||||
assignee: contact.class.Employee,
|
||||
_id: {
|
||||
related: [tracker.class.Issue, 'relations._id']
|
||||
}
|
||||
|
@ -595,7 +595,7 @@ export function createModel (builder: Builder): void {
|
||||
props: { listProps: { key: 'modified', fixed: 'left' } }
|
||||
},
|
||||
{
|
||||
key: '$lookup.assignee',
|
||||
key: 'assignee',
|
||||
presenter: tracker.component.AssigneePresenter,
|
||||
props: { defaultClass: contact.class.Employee, shouldShowLabel: false }
|
||||
}
|
||||
@ -675,7 +675,7 @@ export function createModel (builder: Builder): void {
|
||||
props: { listProps: { fixed: 'right', optional: true } }
|
||||
},
|
||||
{
|
||||
key: '$lookup.assignee',
|
||||
key: 'assignee',
|
||||
presenter: tracker.component.AssigneePresenter,
|
||||
props: { defaultClass: contact.class.Employee, shouldShowLabel: false }
|
||||
}
|
||||
@ -726,7 +726,7 @@ export function createModel (builder: Builder): void {
|
||||
props: { listProps: { fixed: 'right' } }
|
||||
},
|
||||
{
|
||||
key: '$lookup.assignee',
|
||||
key: 'assignee',
|
||||
presenter: tracker.component.AssigneePresenter,
|
||||
props: { defaultClass: contact.class.Employee, shouldShowLabel: false }
|
||||
}
|
||||
@ -1783,7 +1783,7 @@ export function createModel (builder: Builder): void {
|
||||
{ key: '', presenter: tracker.component.SprintDatePresenter, props: { field: 'startDate' } },
|
||||
{ key: '', presenter: tracker.component.SprintDatePresenter, props: { field: 'targetDate' } },
|
||||
{
|
||||
key: '$lookup.lead',
|
||||
key: 'lead',
|
||||
presenter: tracker.component.SprintLeadPresenter,
|
||||
props: {
|
||||
_class: tracker.class.Sprint,
|
||||
|
@ -325,6 +325,7 @@
|
||||
<KanbanRow
|
||||
bind:this={stateRows[si]}
|
||||
on:obj-focus
|
||||
index={si}
|
||||
{groupByDocs}
|
||||
{stateObjects}
|
||||
{isDragging}
|
||||
|
@ -17,7 +17,8 @@
|
||||
import ui, { Button, IconMoreH, mouseAttractor } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { slide } from 'svelte/transition'
|
||||
import { CardDragEvent, Item } from '../types'
|
||||
import { CardDragEvent, DocWithRank, Item } from '../types'
|
||||
import Spinner from '@hcengineering/ui/src/components/Spinner.svelte'
|
||||
|
||||
export let stateObjects: Item[]
|
||||
export let isDragging: boolean
|
||||
@ -27,6 +28,7 @@
|
||||
export let selection: number | undefined = undefined
|
||||
export let checkedSet: Set<Ref<Doc>>
|
||||
export let state: CategoryType
|
||||
export let index: number
|
||||
|
||||
export let cardDragOver: (evt: CardDragEvent, object: Item) => void
|
||||
export let cardDrop: (evt: CardDragEvent, object: Item) => void
|
||||
@ -53,7 +55,20 @@
|
||||
|
||||
let limit = 50
|
||||
|
||||
$: limitedObjects = stateObjects.slice(0, limit)
|
||||
let limitedObjects: DocWithRank[] = []
|
||||
let loading = false
|
||||
|
||||
function nop (op: () => void, timeout: number) {
|
||||
op()
|
||||
}
|
||||
|
||||
$: {
|
||||
loading = true
|
||||
;(limitedObjects.length > 0 ? nop : setTimeout)(() => {
|
||||
limitedObjects = stateObjects.slice(0, limit)
|
||||
loading = false
|
||||
}, index * 2)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#each limitedObjects as object, i (object._id)}
|
||||
@ -88,19 +103,23 @@
|
||||
{/each}
|
||||
{#if stateObjects.length > limitedObjects.length}
|
||||
<div class="step-tb75">
|
||||
<div class="card-container h-18 flex-row-center flex-between p-4">
|
||||
<span class="p-1">
|
||||
{limitedObjects.length}/{stateObjects.length}
|
||||
</span>
|
||||
<Button
|
||||
size={'small'}
|
||||
icon={IconMoreH}
|
||||
label={ui.string.ShowMore}
|
||||
on:click={() => {
|
||||
limit = limit + 20
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{#if loading}
|
||||
<Spinner />
|
||||
{:else}
|
||||
<div class="card-container h-18 flex-row-center flex-between p-4">
|
||||
<span class="p-1">
|
||||
{limitedObjects.length}/{stateObjects.length}
|
||||
</span>
|
||||
<Button
|
||||
size={'small'}
|
||||
icon={IconMoreH}
|
||||
label={ui.string.ShowMore}
|
||||
on:click={() => {
|
||||
limit = limit + 20
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
@ -392,6 +392,9 @@
|
||||
}
|
||||
|
||||
delayCall(() => {
|
||||
if (divBox === undefined) {
|
||||
return
|
||||
}
|
||||
const tempEls = divBox.querySelectorAll('.categoryHeader')
|
||||
observer = new IntersectionObserver(checkIntersection, { root: null, rootMargin: '0px', threshold: 0.1 })
|
||||
tempEls.forEach((el) => observer.observe(el))
|
||||
|
@ -101,8 +101,12 @@
|
||||
bind:this={imageElement}
|
||||
on:load={(data) => {
|
||||
if (imageElement !== undefined) {
|
||||
accentColor = imageToColor(imageElement)
|
||||
dispatch('accent-color', accentColor)
|
||||
try {
|
||||
accentColor = imageToColor(imageElement)
|
||||
dispatch('accent-color', accentColor)
|
||||
} catch (err) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@ -114,8 +118,12 @@
|
||||
bind:this={imageElement}
|
||||
on:load={(data) => {
|
||||
if (imageElement !== undefined) {
|
||||
accentColor = imageToColor(imageElement)
|
||||
dispatch('accent-color', accentColor)
|
||||
try {
|
||||
accentColor = imageToColor(imageElement)
|
||||
dispatch('accent-color', accentColor)
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
@ -1,13 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { Employee } from '@hcengineering/contact'
|
||||
import { WithLookup } from '@hcengineering/core'
|
||||
import { Ref, WithLookup } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { IconSize } from '@hcengineering/ui'
|
||||
import { PersonLabelTooltip } from '..'
|
||||
import { PersonLabelTooltip, employeeByIdStore } from '..'
|
||||
import PersonPresenter from '../components/PersonPresenter.svelte'
|
||||
import contact from '../plugin'
|
||||
|
||||
export let value: WithLookup<Employee> | null | undefined
|
||||
export let value: Ref<Employee> | WithLookup<Employee> | null | undefined
|
||||
export let tooltipLabels: PersonLabelTooltip | undefined = undefined
|
||||
export let shouldShowAvatar: boolean = true
|
||||
export let shouldShowName: boolean = true
|
||||
@ -21,11 +21,13 @@
|
||||
export let accent: boolean = false
|
||||
export let defaultName: IntlString | undefined = undefined
|
||||
export let element: HTMLElement | undefined = undefined
|
||||
|
||||
$: employeeValue = typeof value === 'string' ? $employeeByIdStore.get(value) : value
|
||||
</script>
|
||||
|
||||
<PersonPresenter
|
||||
bind:element
|
||||
{value}
|
||||
value={employeeValue}
|
||||
{tooltipLabels}
|
||||
onEdit={onEmployeeEdit}
|
||||
{shouldShowAvatar}
|
||||
@ -37,6 +39,6 @@
|
||||
{colorInherit}
|
||||
{accent}
|
||||
{defaultName}
|
||||
statusLabel={value?.active === false && shouldShowName ? contact.string.Inactive : undefined}
|
||||
statusLabel={employeeValue?.active === false && shouldShowName ? contact.string.Inactive : undefined}
|
||||
on:accent-color
|
||||
/>
|
||||
|
@ -68,7 +68,7 @@
|
||||
<div class="tool mr-1 flex-row-center">
|
||||
{#if !dragged}
|
||||
<div class="mr-2">
|
||||
<Component is={notification.component.NotificationPresenter} props={{ value: object }} />
|
||||
<Component showLoading={false} is={notification.component.NotificationPresenter} props={{ value: object }} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@ -76,6 +76,7 @@
|
||||
<div class="tool mr-1 flex-row-center">
|
||||
<div class="step-lr75">
|
||||
<Component
|
||||
showLoading={false}
|
||||
is={contact.component.ChannelsPresenter}
|
||||
props={{ value: channels, object: object.$lookup?.attachedTo, length: 'tiny' }}
|
||||
/>
|
||||
@ -89,7 +90,7 @@
|
||||
<div class="mr-2">
|
||||
<ApplicationPresenter value={object} />
|
||||
</div>
|
||||
<Component is={tracker.component.RelatedIssueSelector} props={{ object }} />
|
||||
<Component showLoading={false} is={tracker.component.RelatedIssueSelector} props={{ object }} />
|
||||
</div>
|
||||
<DueDatePresenter
|
||||
value={object.dueDate}
|
||||
@ -115,7 +116,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
<AssigneePresenter
|
||||
value={object.$lookup?.assignee}
|
||||
value={object.assignee}
|
||||
issueId={object._id}
|
||||
defaultClass={contact.class.Employee}
|
||||
currentSpace={object.space}
|
||||
|
@ -17,14 +17,14 @@
|
||||
import { Class, Doc, Ref, Space } from '@hcengineering/core'
|
||||
import { Task } from '@hcengineering/task'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { UsersPopup } from '@hcengineering/contact-resources'
|
||||
import { UsersPopup, employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import { AttributeModel } from '@hcengineering/view'
|
||||
import { eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
import { getObjectPresenter } from '@hcengineering/view-resources'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { IntlString, getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import task from '../plugin'
|
||||
|
||||
export let value: Employee | null | undefined
|
||||
export let value: Ref<Employee> | Employee | null | undefined
|
||||
export let issueId: Ref<Task>
|
||||
export let defaultClass: Ref<Class<Doc>> | undefined = undefined
|
||||
export let currentSpace: Ref<Space> | undefined = undefined
|
||||
@ -33,13 +33,15 @@
|
||||
export let defaultName: IntlString | undefined = undefined
|
||||
export let placeholderLabel: IntlString | undefined = undefined
|
||||
|
||||
$: employeeValue = typeof value === 'string' ? $employeeByIdStore.get(value) : value
|
||||
|
||||
const client = getClient()
|
||||
|
||||
let presenter: AttributeModel | undefined
|
||||
|
||||
$: if (value || defaultClass) {
|
||||
if (value) {
|
||||
getObjectPresenter(client, value._class, { key: '' }).then((p) => {
|
||||
$: if (employeeValue || defaultClass) {
|
||||
if (employeeValue) {
|
||||
getObjectPresenter(client, employeeValue._class, { key: '' }).then((p) => {
|
||||
presenter = p
|
||||
})
|
||||
} else if (defaultClass) {
|
||||
@ -84,12 +86,12 @@
|
||||
UsersPopup,
|
||||
{
|
||||
_class: contact.class.Employee,
|
||||
selected: value?._id,
|
||||
selected: employeeValue?._id,
|
||||
docQuery: {
|
||||
active: true
|
||||
},
|
||||
allowDeselect: true,
|
||||
placeholder: task.string.AssignThisTask
|
||||
placeholder: placeholderLabel ?? presenter?.label ?? task.string.AssignThisTask
|
||||
},
|
||||
eventToHTMLElement(event),
|
||||
handleAssigneeChanged
|
||||
@ -108,7 +110,7 @@
|
||||
shouldShowName={shouldShowLabel}
|
||||
onEmployeeEdit={handleAssigneeEditorOpened}
|
||||
tooltipLabels={{
|
||||
personLabel: value ? getName(value) : undefined,
|
||||
personLabel: employeeValue ? getEmbeddedLabel(getName(employeeValue)) : undefined,
|
||||
placeholderLabel: placeholderLabel ?? presenter.label
|
||||
}}
|
||||
/>
|
||||
|
@ -14,7 +14,6 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact from '@hcengineering/contact'
|
||||
import {
|
||||
CategoryType,
|
||||
Class,
|
||||
@ -133,7 +132,6 @@
|
||||
...options,
|
||||
lookup: {
|
||||
...options?.lookup,
|
||||
assignee: contact.class.Employee,
|
||||
space: task.class.SpaceWithStates,
|
||||
state: task.class.State,
|
||||
doneState: task.class.DoneState
|
||||
|
@ -13,10 +13,9 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact from '@hcengineering/contact'
|
||||
import { Class, Doc, FindOptions, getObjectValue, Ref, Timestamp } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Issue, Component } from '@hcengineering/tracker'
|
||||
import { Component, Issue } from '@hcengineering/tracker'
|
||||
import { CheckBox, Spinner, Timeline, TimelineRow } from '@hcengineering/ui'
|
||||
import { AttributeModel, BuildModelKey } from '@hcengineering/view'
|
||||
import { buildModel, LoadingProps } from '@hcengineering/view-resources'
|
||||
@ -37,7 +36,6 @@
|
||||
|
||||
const baseOptions: FindOptions<Issue> = {
|
||||
lookup: {
|
||||
assignee: contact.class.Employee,
|
||||
status: tracker.class.IssueStatus
|
||||
}
|
||||
}
|
||||
|
@ -13,10 +13,9 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact from '@hcengineering/contact'
|
||||
import { Class, Doc, FindOptions, getObjectValue, Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Issue, Component } from '@hcengineering/tracker'
|
||||
import { Component, Issue } from '@hcengineering/tracker'
|
||||
import { CheckBox, Spinner, tooltip } from '@hcengineering/ui'
|
||||
import { BuildModelKey } from '@hcengineering/view'
|
||||
import { buildModel, LoadingProps } from '@hcengineering/view-resources'
|
||||
@ -37,7 +36,6 @@
|
||||
|
||||
const baseOptions: FindOptions<Issue> = {
|
||||
lookup: {
|
||||
assignee: contact.class.Employee,
|
||||
status: tracker.class.IssueStatus
|
||||
}
|
||||
}
|
||||
|
@ -14,17 +14,17 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { Employee } from '@hcengineering/contact'
|
||||
import { UsersPopup } from '@hcengineering/contact-resources'
|
||||
import { Class, Doc, Ref } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { UsersPopup } from '@hcengineering/contact-resources'
|
||||
import { Issue, IssueTemplate } from '@hcengineering/tracker'
|
||||
import { eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
import { AttributeModel } from '@hcengineering/view'
|
||||
import { getObjectPresenter } from '@hcengineering/view-resources'
|
||||
import tracker from '../../plugin'
|
||||
|
||||
export let value: Employee | null | undefined
|
||||
export let value: Employee | Ref<Employee> | null | undefined
|
||||
export let object: Issue | IssueTemplate
|
||||
export let defaultClass: Ref<Class<Doc>> | undefined = undefined
|
||||
export let isEditable: boolean = true
|
||||
@ -37,9 +37,11 @@
|
||||
|
||||
$: if (value || defaultClass) {
|
||||
if (value) {
|
||||
getObjectPresenter(client, value._class, { key: '' }).then((p) => {
|
||||
presenter = p
|
||||
})
|
||||
getObjectPresenter(client, typeof value === 'string' ? contact.class.Employee : value._class, { key: '' }).then(
|
||||
(p) => {
|
||||
presenter = p
|
||||
}
|
||||
)
|
||||
} else if (defaultClass) {
|
||||
getObjectPresenter(client, defaultClass, { key: '' }).then((p) => {
|
||||
presenter = p
|
||||
@ -68,7 +70,7 @@
|
||||
UsersPopup,
|
||||
{
|
||||
_class: contact.class.Employee,
|
||||
selected: value?._id,
|
||||
selected: typeof value === 'string' ? value : value?._id,
|
||||
docQuery: {
|
||||
active: true
|
||||
},
|
||||
|
@ -130,7 +130,6 @@
|
||||
}
|
||||
|
||||
const lookup: Lookup<Issue> = {
|
||||
assignee: contact.class.Employee,
|
||||
space: tracker.class.Project,
|
||||
status: tracker.class.IssueStatus,
|
||||
component: tracker.class.Component,
|
||||
|
@ -13,12 +13,11 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact from '@hcengineering/contact'
|
||||
import { Doc, DocumentQuery, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Issue, Project } from '@hcengineering/tracker'
|
||||
import { Label, Spinner } from '@hcengineering/ui'
|
||||
import { Viewlet, ViewOptions } from '@hcengineering/view'
|
||||
import { ViewOptions, Viewlet } from '@hcengineering/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../../plugin'
|
||||
import AddIssueDuo from '../../icons/AddIssueDuo.svelte'
|
||||
@ -41,10 +40,7 @@
|
||||
let projects: Map<Ref<Project>, Project> | undefined
|
||||
|
||||
$: subIssuesQuery.query(tracker.class.Issue, query, async (result) => (subIssues = result), {
|
||||
sort: { rank: SortingOrder.Ascending },
|
||||
lookup: {
|
||||
assignee: contact.class.Employee
|
||||
}
|
||||
sort: { rank: SortingOrder.Ascending }
|
||||
})
|
||||
|
||||
const projectsQuery = createQuery()
|
||||
|
@ -98,7 +98,7 @@ export const defaultPriorities = [
|
||||
|
||||
export const issuesGroupBySorting: Record<IssuesGrouping, SortingQuery<Issue>> = {
|
||||
[IssuesGrouping.Status]: { '$lookup.status.rank': SortingOrder.Ascending },
|
||||
[IssuesGrouping.Assignee]: { '$lookup.assignee.name': SortingOrder.Ascending },
|
||||
[IssuesGrouping.Assignee]: { assignee: SortingOrder.Ascending },
|
||||
[IssuesGrouping.Priority]: { priority: SortingOrder.Ascending },
|
||||
[IssuesGrouping.Component]: { '$lookup.component.label': SortingOrder.Ascending },
|
||||
[IssuesGrouping.Sprint]: { '$lookup.sprint.label': SortingOrder.Ascending },
|
||||
|
Loading…
Reference in New Issue
Block a user