From 26d8099f1d284821335f1e99a3b11abeb9158a70 Mon Sep 17 00:00:00 2001 From: Andrey Sobolev <haiodo@users.noreply.github.com> Date: Thu, 10 Aug 2023 22:12:48 +0700 Subject: [PATCH] UBER-720: Rework list view to multiple requests (#3578) Signed-off-by: Andrey Sobolev <haiodo@gmail.com> --- models/tracker/src/index.ts | 8 +--- .../src/components/PersonRefPresenter.svelte | 1 + .../src/components/issues/Issues.svelte | 17 ++++++- .../src/components/myissues/MyIssues.svelte | 19 +++++++- .../src/components/ViewletSelector.svelte | 4 +- .../src/components/filter/ValueFilter.svelte | 8 +++- .../src/components/list/List.svelte | 28 +++++++++++- .../src/components/list/ListCategories.svelte | 21 ++++++++- .../src/components/list/ListCategory.svelte | 45 ++++++++++++++++--- .../src/components/list/ListHeader.svelte | 7 +-- 10 files changed, 131 insertions(+), 27 deletions(-) diff --git a/models/tracker/src/index.ts b/models/tracker/src/index.ts index 0c951dfd51..5d88c0c832 100644 --- a/models/tracker/src/index.ts +++ b/models/tracker/src/index.ts @@ -541,12 +541,7 @@ export function createModel (builder: Builder): void { displayProps: { key: 'assignee', fixed: 'right' }, props: { kind: 'list', shouldShowName: false, avatarSize: 'x-small' } } - ], - options: { - lookup: { - space: tracker.class.Project - } - } + ] }, tracker.viewlet.IssueList ) @@ -1052,7 +1047,6 @@ export function createModel (builder: Builder): void { icon: tracker.icon.Issues, component: tracker.component.Issues, componentProps: { - baseQuery: { '$lookup.space.archived': false }, space: undefined, title: tracker.string.AllIssues, config: [ diff --git a/plugins/contact-resources/src/components/PersonRefPresenter.svelte b/plugins/contact-resources/src/components/PersonRefPresenter.svelte index 82a43daad6..5f05b77d05 100644 --- a/plugins/contact-resources/src/components/PersonRefPresenter.svelte +++ b/plugins/contact-resources/src/components/PersonRefPresenter.svelte @@ -59,4 +59,5 @@ {disabled} {inline} {accent} + on:accent-color /> diff --git a/plugins/tracker-resources/src/components/issues/Issues.svelte b/plugins/tracker-resources/src/components/issues/Issues.svelte index 471d05fe80..2f900fc11a 100644 --- a/plugins/tracker-resources/src/components/issues/Issues.svelte +++ b/plugins/tracker-resources/src/components/issues/Issues.svelte @@ -60,13 +60,28 @@ } ) + const archivedProjectQuery = createQuery() + let archived: Ref<Project>[] = [] + + archivedProjectQuery.query( + tracker.class.Project, + { archived: true }, + (res) => { + archived = res.map((it) => it._id) + }, + { projection: { _id: 1 } } + ) + $: queries = { all, active, backlog } $: mode = $resolvedLocationStore.query?.mode ?? undefined $: if (mode === undefined || queries[mode] === undefined) { ;[[mode]] = config } $: if (mode !== undefined) { - query = { ...queries[mode], '$lookup.space.archived': false } + query = { ...queries[mode] } + if (query?.space === undefined) { + query = { ...query, space: { $nin: archived } } + } modeSelectorProps = { config, mode, diff --git a/plugins/tracker-resources/src/components/myissues/MyIssues.svelte b/plugins/tracker-resources/src/components/myissues/MyIssues.svelte index 229e01cf04..fedfcf88f6 100644 --- a/plugins/tracker-resources/src/components/myissues/MyIssues.svelte +++ b/plugins/tracker-resources/src/components/myissues/MyIssues.svelte @@ -18,7 +18,7 @@ import { Doc, DocumentQuery, getCurrentAccount, Ref } from '@hcengineering/core' import type { IntlString } from '@hcengineering/platform' import { createQuery } from '@hcengineering/presentation' - import type { Issue } from '@hcengineering/tracker' + import type { Issue, Project } from '@hcengineering/tracker' import { resolvedLocationStore } from '@hcengineering/ui' import { IModeSelector } from '@hcengineering/ui' @@ -50,13 +50,28 @@ { sort: { _id: 1 } } ) + const archivedProjectQuery = createQuery() + let archived: Ref<Project>[] = [] + + archivedProjectQuery.query( + tracker.class.Project, + { archived: true }, + (res) => { + archived = res.map((it) => it._id) + }, + { projection: { _id: 1 } } + ) + $: queries = { assigned, created, subscribed } $: mode = $resolvedLocationStore.query?.mode ?? undefined $: if (mode === undefined || queries[mode] === undefined) { ;[[mode]] = config } $: if (mode !== undefined) { - query = { ...queries[mode], '$lookup.space.archived': false } + query = { ...queries[mode] } + if (query?.space === undefined) { + query = { ...query, space: { $nin: archived } } + } modeSelectorProps = { config, mode, diff --git a/plugins/view-resources/src/components/ViewletSelector.svelte b/plugins/view-resources/src/components/ViewletSelector.svelte index c286ae117e..a3ad056383 100644 --- a/plugins/view-resources/src/components/ViewletSelector.svelte +++ b/plugins/view-resources/src/components/ViewletSelector.svelte @@ -46,7 +46,7 @@ activeViewlet: Record<string, Ref<Viewlet> | null>, key: string ) { - if (viewlets.length === 0) return + if (viewlets == null || viewlets.length === 0) return const newViewlet = viewlets.find((viewlet) => viewlet?._id === activeViewlet[key]) ?? viewlets[0] if (viewlet?._id !== newViewlet?._id) { viewlet = newViewlet @@ -55,7 +55,7 @@ } } - $: viewslist = viewlets.map((views) => { + $: viewslist = viewlets?.map((views) => { return { id: views._id, icon: views.$lookup?.descriptor?.icon, diff --git a/plugins/view-resources/src/components/filter/ValueFilter.svelte b/plugins/view-resources/src/components/filter/ValueFilter.svelte index d20b81e3cf..d481941b12 100644 --- a/plugins/view-resources/src/components/filter/ValueFilter.svelte +++ b/plugins/view-resources/src/components/filter/ValueFilter.svelte @@ -73,11 +73,17 @@ prefix = attr.attributeOf + '.' } const isDerivedFromSpace = hierarchy.isDerived(_class, core.class.Space) + + const archived = + space === undefined || isDerivedFromSpace + ? [] + : (await client.findAll(core.class.Space, { archived: true }, { projection: { _id: 1 } })).map((it) => it._id) + objectsPromise = client.findAll( _class, { ...resultQuery, - ...(space ? { space } : isDerivedFromSpace ? { archived: false } : { '$lookup.space.archived': false }) + ...(space ? { space } : isDerivedFromSpace ? { archived: false } : { space: { $nin: archived } }) }, { sort: { [filter.key.key]: SortingOrder.Ascending }, diff --git a/plugins/view-resources/src/components/list/List.svelte b/plugins/view-resources/src/components/list/List.svelte index 9968c39834..7f8df629aa 100644 --- a/plugins/view-resources/src/components/list/List.svelte +++ b/plugins/view-resources/src/components/list/List.svelte @@ -48,6 +48,7 @@ $: orderBy = viewOptions.orderBy const docsQuery = createQuery() + $: lookup = buildConfigLookup(client.getHierarchy(), _class, config, options?.lookup) $: resultOptions = { ...options, lookup, ...(orderBy !== undefined ? { sort: { [orderBy[0]]: orderBy[1] } } : {}) } @@ -59,17 +60,38 @@ $: if (documents === undefined) { docsQuery.query( _class, - resultQuery, + noLookup(resultQuery), (res) => { docs = res }, - resultOptions + { + ...resultOptions, + projection: { ...resultOptions.projection, _id: 1, _class: 1, ...getProjection(viewOptions.groupBy) } + } ) } else { docsQuery.unsubscribe() docs = documents } + function getProjection (fields: string[]): Record<string, number> { + const res: Record<string, number> = {} + for (const f of fields) { + res[f] = 1 + } + return res + } + + function noLookup (query: DocumentQuery<Doc>): DocumentQuery<Doc> { + const newQuery: DocumentQuery<Doc> = {} + for (const [k, v] of Object.entries(query)) { + if (!k.startsWith('$lookup.')) { + newQuery[k] = v + } + } + return newQuery + } + $: dispatch('content', docs) const dispatch = createEventDispatcher() @@ -153,6 +175,8 @@ select(-2, evt.detail) }} on:collapsed + {resultQuery} + {resultOptions} /> </div> diff --git a/plugins/view-resources/src/components/list/ListCategories.svelte b/plugins/view-resources/src/components/list/ListCategories.svelte index 3fce049331..31e6284e74 100644 --- a/plugins/view-resources/src/components/list/ListCategories.svelte +++ b/plugins/view-resources/src/components/list/ListCategories.svelte @@ -13,7 +13,17 @@ // limitations under the License. --> <script lang="ts"> - import { CategoryType, Class, Doc, DocumentQuery, generateId, Lookup, Ref, Space } from '@hcengineering/core' + import { + CategoryType, + Class, + Doc, + DocumentQuery, + FindOptions, + generateId, + Lookup, + Ref, + Space + } from '@hcengineering/core' import { getResource, IntlString } from '@hcengineering/platform' import { getClient } from '@hcengineering/presentation' import { AnyComponent, AnySvelteComponent } from '@hcengineering/ui' @@ -68,6 +78,9 @@ export let groupPersistKey: string export let compactMode: boolean = false + export let resultQuery: DocumentQuery<Doc> + export let resultOptions: FindOptions<Doc> + $: groupByKey = viewOptions.groupBy[level] ?? noCategory let categories: CategoryType[] = [] $: updateCategories(_class, docs, groupByKey, viewOptions, viewOptionsConfig) @@ -348,13 +361,15 @@ oneCat={viewOptions.groupBy.length === 1} lastCat={i === categories.length - 1} {category} - {items} + itemProj={items} {newObjectProps} {createItemDialog} {createItemDialogProps} {createItemLabel} {viewOptionsConfig} {compactMode} + {resultQuery} + {resultOptions} on:check on:uncheckAll on:row-focus @@ -411,6 +426,8 @@ {initIndex} {viewOptionsConfig} {listDiv} + {resultQuery} + {resultOptions} on:dragItem on:check on:uncheckAll diff --git a/plugins/view-resources/src/components/list/ListCategory.svelte b/plugins/view-resources/src/components/list/ListCategory.svelte index 3f82f9861c..ecf9f15132 100644 --- a/plugins/view-resources/src/components/list/ListCategory.svelte +++ b/plugins/view-resources/src/components/list/ListCategory.svelte @@ -13,9 +13,20 @@ // limitations under the License. --> <script lang="ts"> - import { AggregateValue, Class, Doc, DocumentUpdate, Lookup, PrimitiveType, Ref, Space } from '@hcengineering/core' + import { + AggregateValue, + Class, + Doc, + DocumentQuery, + DocumentUpdate, + FindOptions, + Lookup, + PrimitiveType, + Ref, + Space + } from '@hcengineering/core' import { IntlString } from '@hcengineering/platform' - import { getClient } from '@hcengineering/presentation' + import { createQuery, getClient } from '@hcengineering/presentation' import { DocWithRank, calcRank } from '@hcengineering/task' import { AnyComponent, @@ -42,7 +53,7 @@ export let groupByKey: string export let space: Ref<Space> | undefined export let baseMenuClass: Ref<Class<Doc>> | undefined - export let items: Doc[] + export let itemProj: Doc[] export let createItemDialog: AnyComponent | AnySvelteComponent | undefined export let createItemDialogProps: Record<string, any> | undefined export let createItemLabel: IntlString | undefined @@ -68,15 +79,34 @@ export let index: number export let groupPersistKey: string export let compactMode: boolean = false + export let resultQuery: DocumentQuery<Doc> + export let resultOptions: FindOptions<Doc> $: lastLevel = level + 1 >= viewOptions.groupBy.length + let items: Doc[] = [] + + const docsQuery = createQuery() + const autoFoldLimit = 20 const defaultLimit = 20 const singleCategoryLimit = 50 $: initialLimit = !lastLevel ? undefined : singleCat ? singleCategoryLimit : defaultLimit $: limit = initialLimit + $: if (lastLevel) { + docsQuery.query( + _class, + { ...resultQuery, _id: { $in: itemProj.map((it) => it._id) } }, + (res) => { + items = res + }, + { ...resultOptions, limit: limit ?? 200 } + ) + } else { + docsQuery.unsubscribe() + } + $: categoryCollapseKey = `list_collapsing_${location.pathname}_${groupPersistKey}` $: storedCollapseState = localStorage.getItem(categoryCollapseKey) @@ -92,7 +122,7 @@ function initCollapsed (singleCat: boolean, lastLevel: boolean): void { if (localStorage.getItem(categoryCollapseKey) === null) { - collapsed = !disableHeader && !singleCat && items.length > (lastLevel ? autoFoldLimit : singleCategoryLimit) + collapsed = !disableHeader && !singleCat && itemProj.length > (lastLevel ? autoFoldLimit : singleCategoryLimit) } } @@ -388,8 +418,9 @@ {category} {space} {level} - limited={limited.length} - {items} + limited={lastLevel ? limited.length : itemProj.length} + itemsProj={itemProj} + items={limited} {headerComponent} {createItemDialog} {createItemDialogProps} @@ -423,7 +454,7 @@ {#if !lastLevel} <slot name="category" - docs={items} + docs={itemProj} {_class} {space} {lookup} diff --git a/plugins/view-resources/src/components/list/ListHeader.svelte b/plugins/view-resources/src/components/list/ListHeader.svelte index b6556a694b..c4cfba63d0 100644 --- a/plugins/view-resources/src/components/list/ListHeader.svelte +++ b/plugins/view-resources/src/components/list/ListHeader.svelte @@ -45,6 +45,7 @@ export let space: Ref<Space> | undefined export let limited: number export let items: Doc[] + export let itemsProj: Doc[] export let flat = false export let collapsed = false export let lastCat = false @@ -139,11 +140,11 @@ </span> </span> {/if} - {#if limited < items.length} + {#if limited < itemsProj.length} <div class="antiSection-header__counter flex-row-center mx-2"> <span class="caption-color">{limited}</span> <span class="text-xs mx-0-5">/</span> - {items.length} + {itemsProj.length} </div> <ActionIcon size={'small'} @@ -154,7 +155,7 @@ }} /> {:else} - <span class="antiSection-header__counter ml-2">{items.length}</span> + <span class="antiSection-header__counter ml-2">{itemsProj.length}</span> {/if} <div class="flex-row-center flex-reverse flex-grow mr-2 gap-2 reverse"> {#each extraHeaders ?? [] as extra}