TSK-1100 Fix saved views (#2942)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2023-04-11 19:39:23 +06:00 committed by GitHub
parent b8b8e8bd44
commit de11193e12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 229 additions and 115 deletions

View File

@ -1,18 +1,20 @@
<script lang="ts">
import { DocumentQuery, Ref, Space, WithLookup } from '@hcengineering/core'
import { IntlString, translate } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation'
import { createQuery } from '@hcengineering/presentation'
import { Issue } from '@hcengineering/tracker'
import { Button, IconDetails, IconDetailsFilled } from '@hcengineering/ui'
import { Button, IconDetails, IconDetailsFilled, location } from '@hcengineering/ui'
import view, { Viewlet } from '@hcengineering/view'
import {
FilterBar,
getActiveViewletId,
getViewOptions,
setActiveViewletId,
ViewletSettingButton,
activeViewlet,
getViewOptions,
makeViewletKey,
setActiveViewletId,
viewOptionStore
} from '@hcengineering/view-resources'
import { onDestroy } from 'svelte'
import tracker from '../../plugin'
import IssuesContent from './IssuesContent.svelte'
import IssuesHeader from './IssuesHeader.svelte'
@ -33,26 +35,36 @@
$: if (query) updateSearchQuery(search)
let resultQuery: DocumentQuery<Issue> = { ...searchQuery }
const client = getClient()
let viewlets: WithLookup<Viewlet>[] = []
$: update()
async function update (): Promise<void> {
viewlets = await client.findAll(
view.class.Viewlet,
{ attachTo: tracker.class.Issue, variant: { $ne: 'subissue' } },
{
lookup: {
descriptor: view.class.ViewletDescriptor
}
$: update(viewlets, active)
const viewletQuery = createQuery()
viewletQuery.query(
view.class.Viewlet,
{ attachTo: tracker.class.Issue, variant: { $ne: 'subissue' } },
(res) => (viewlets = res),
{
lookup: {
descriptor: view.class.ViewletDescriptor
}
)
const _id = getActiveViewletId()
viewlet = viewlets.find((viewlet) => viewlet._id === _id) || viewlets[0]
}
)
let key = makeViewletKey()
onDestroy(
location.subscribe((loc) => {
key = makeViewletKey(loc)
})
)
$: active = $activeViewlet[key]
async function update (viewlets: WithLookup<Viewlet>[], active: Ref<Viewlet> | null): Promise<void> {
viewlet = viewlets.find((viewlet) => viewlet._id === active) ?? viewlets[0]
setActiveViewletId(viewlet._id)
}
$: if (!label && title) {
translate(title, {}).then((res) => {
label = res

View File

@ -236,7 +236,7 @@
}
</script>
{#if categories.length === 0}}
{#if categories.length === 0}
<Loading />
{:else}
<ActionContext

View File

@ -13,23 +13,25 @@
// limitations under the License.
-->
<script lang="ts">
import { DocumentQuery, WithLookup } from '@hcengineering/core'
import { DocumentQuery, Ref, WithLookup } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation'
import { createQuery } from '@hcengineering/presentation'
import { Sprint } from '@hcengineering/tracker'
import { Button, IconAdd, Label, SearchEdit, showPopup } from '@hcengineering/ui'
import { Button, IconAdd, Label, SearchEdit, location, showPopup } from '@hcengineering/ui'
import view, { Viewlet } from '@hcengineering/view'
import {
FilterBar,
FilterButton,
getActiveViewletId,
ViewletSettingButton,
activeViewlet,
getViewOptions,
viewOptionStore,
makeViewletKey,
setActiveViewletId,
ViewletSettingButton
viewOptionStore
} from '@hcengineering/view-resources'
import { onDestroy } from 'svelte'
import tracker from '../../plugin'
import { getIncludedSprintStatuses, sprintTitleMap, SprintViewMode } from '../../utils'
import { SprintViewMode, getIncludedSprintStatuses, sprintTitleMap } from '../../utils'
import NewSprint from './NewSprint.svelte'
import SprintContent from './SprintContent.svelte'
@ -57,25 +59,31 @@
$: title = sprintTitleMap[mode]
$: includedSprintsQuery = { status: { $in: includedSprintStatuses } }
const client = getClient()
let resultQuery: DocumentQuery<Sprint> = { ...searchQuery }
let viewlets: WithLookup<Viewlet>[] = []
$: update()
$: update(viewlets, active)
async function update (): Promise<void> {
viewlets = await client.findAll(
view.class.Viewlet,
{ attachTo: tracker.class.Sprint },
{
lookup: {
descriptor: view.class.ViewletDescriptor
}
}
)
const _id = getActiveViewletId()
viewlet = viewlets.find((viewlet) => viewlet._id === _id) || viewlets[0]
const viewletQuery = createQuery()
viewletQuery.query(view.class.Viewlet, { attachTo: tracker.class.Sprint }, (res) => (viewlets = res), {
lookup: {
descriptor: view.class.ViewletDescriptor
}
})
let key = makeViewletKey()
onDestroy(
location.subscribe((loc) => {
key = makeViewletKey(loc)
})
)
$: active = $activeViewlet[key]
async function update (viewlets: WithLookup<Viewlet>[], active: Ref<Viewlet> | null): Promise<void> {
viewlet = viewlets.find((viewlet) => viewlet._id === active) ?? viewlets[0]
setActiveViewletId(viewlet._id)
}

View File

@ -1,18 +1,20 @@
<script lang="ts">
import { DocumentQuery, WithLookup, Ref, Space } from '@hcengineering/core'
import { DocumentQuery, Ref, Space, WithLookup } from '@hcengineering/core'
import { IntlString, translate } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation'
import { createQuery } from '@hcengineering/presentation'
import { IssueTemplate } from '@hcengineering/tracker'
import { Button, IconAdd, IconDetails, IconDetailsFilled, showPopup } from '@hcengineering/ui'
import { Button, IconAdd, IconDetails, IconDetailsFilled, location, showPopup } from '@hcengineering/ui'
import view, { Viewlet } from '@hcengineering/view'
import {
FilterBar,
getActiveViewletId,
ViewletSettingButton,
activeViewlet,
getViewOptions,
viewOptionStore,
setActiveViewletId
makeViewletKey,
setActiveViewletId,
viewOptionStore
} from '@hcengineering/view-resources'
import { ViewletSettingButton } from '@hcengineering/view-resources'
import { onDestroy } from 'svelte'
import tracker from '../../plugin'
import IssuesHeader from '../issues/IssuesHeader.svelte'
import CreateIssueTemplate from './CreateIssueTemplate.svelte'
@ -35,26 +37,32 @@
$: if (query) updateSearchQuery(search)
let resultQuery: DocumentQuery<IssueTemplate> = { ...searchQuery }
const client = getClient()
let viewlets: WithLookup<Viewlet>[] = []
$: update()
$: update(viewlets, active)
async function update (): Promise<void> {
viewlets = await client.findAll(
view.class.Viewlet,
{ attachTo: tracker.class.IssueTemplate },
{
lookup: {
descriptor: view.class.ViewletDescriptor
}
}
)
const _id = getActiveViewletId()
viewlet = viewlets.find((viewlet) => viewlet._id === _id) || viewlets[0]
const viewletQuery = createQuery()
viewletQuery.query(view.class.Viewlet, { attachTo: tracker.class.IssueTemplate }, (res) => (viewlets = res), {
lookup: {
descriptor: view.class.ViewletDescriptor
}
})
let key = makeViewletKey()
onDestroy(
location.subscribe((loc) => {
key = makeViewletKey(loc)
})
)
$: active = $activeViewlet[key]
async function update (viewlets: WithLookup<Viewlet>[], active: Ref<Viewlet> | null): Promise<void> {
viewlet = viewlets.find((viewlet) => viewlet._id === active) ?? viewlets[0]
setActiveViewletId(viewlet._id)
}
$: if (!label && title) {
translate(title, {}).then((res) => {
label = res

View File

@ -50,7 +50,7 @@ import {
} from '@hcengineering/ui'
import type { BuildModelOptions, Viewlet, ViewletDescriptor } from '@hcengineering/view'
import view, { AttributeModel, BuildModelKey } from '@hcengineering/view'
import { writable } from 'svelte/store'
import { get, writable } from 'svelte/store'
import plugin from './plugin'
import { noCategory } from './viewOptions'
@ -474,20 +474,27 @@ export function categorizeFields (
return result
}
function makeViewletKey (loc?: Location): string {
export function makeViewletKey (loc?: Location): string {
loc = loc ?? getCurrentLocation()
loc.fragment = undefined
loc.query = undefined
return 'viewlet' + locationToUrl(loc)
}
export const activeViewlet = writable<Record<string, Ref<Viewlet> | null>>({})
export function setActiveViewletId (viewletId: Ref<Viewlet> | null, loc?: Location): void {
const key = makeViewletKey(loc)
const current = get(activeViewlet) ?? {}
if (viewletId !== null && viewletId !== undefined) {
localStorage.setItem(key, viewletId)
current[key] = viewletId
} else {
localStorage.removeItem(key)
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete current[key]
}
activeViewlet.set(current)
}
export function getActiveViewletId (): Ref<Viewlet> | null {

View File

@ -40,8 +40,8 @@ export function isDropdownType (viewOption: ViewOptionModel): viewOption is Drop
return viewOption.type === 'dropdown'
}
function makeViewOptionsKey (viewlet: Viewlet): string {
const prefix = viewlet?._id + (viewlet?.variant !== undefined ? `-${viewlet.variant}` : '')
export function makeViewOptionsKey (viewlet: Ref<Viewlet>, variant?: string): string {
const prefix = viewlet + (variant !== undefined ? `-${variant}` : '')
const loc = getCurrentLocation()
loc.fragment = undefined
loc.query = undefined
@ -49,7 +49,7 @@ function makeViewOptionsKey (viewlet: Viewlet): string {
}
export function setViewOptions (viewlet: Viewlet, options: ViewOptions): void {
const key = makeViewOptionsKey(viewlet)
const key = makeViewOptionsKey(viewlet._id, viewlet.variant)
localStorage.setItem(key, JSON.stringify(options))
setStore(key, options)
}
@ -61,7 +61,7 @@ function setStore (key: string, options: ViewOptions): void {
}
function _getViewOptions (viewlet: Viewlet, viewOptionStore: Map<string, ViewOptions>): ViewOptions | null {
const key = makeViewOptionsKey(viewlet)
const key = makeViewOptionsKey(viewlet._id, viewlet.variant)
const store = viewOptionStore.get(key)
if (store !== undefined) {
return store

View File

@ -2,15 +2,19 @@
import { Doc, Ref } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import setting from '@hcengineering/setting'
import { Action, navigate, getCurrentLocation, location } from '@hcengineering/ui'
import view, { FilteredView } from '@hcengineering/view'
import { Action, Location, location, navigate } from '@hcengineering/ui'
import view, { Filter, FilteredView } from '@hcengineering/view'
import {
TreeItem,
TreeNode,
activeViewlet,
filterStore,
getFilterKey,
makeViewOptionsKey,
makeViewletKey,
setActiveViewletId,
setViewOptions,
TreeItem,
TreeNode
viewOptionStore
} from '@hcengineering/view-resources'
import { Application } from '@hcengineering/workbench'
import { createEventDispatcher } from 'svelte'
@ -61,24 +65,35 @@
$filterStore = JSON.parse(fv.filters)
}
let oldLocation: string = ''
$: loc = $location
const clearSelection = () => {
selectedId = selectedFW = undefined
oldLocation = getCurrentLocation().path.join()
dispatch('select', false)
}
$: fs = $filterStore
$: if (loc && Array.isArray(fs) && fs.length > 0 && Array.isArray(filteredViews)) {
function checkSelected (fs: Filter[], loc: Location, filteredViews: FilteredView[] | undefined) {
const filters = JSON.stringify(fs)
selectedFW = filteredViews.filter((fv) => fv.filters === filters)
if (selectedFW.length > 0 && selectedFW[0].location.path.join() === loc.path.join()) {
selectedId = selectedFW[0]._id
oldLocation = selectedFW[0].location.path.join()
dispatch('select', true)
} else clearSelection()
} else clearSelection()
if (loc && Array.isArray(fs) && fs.length > 0 && Array.isArray(filteredViews)) {
for (const fv of filteredViews) {
if (fv.location.path.join() !== loc.path.join()) continue
if (fv.filters !== filters) continue
const key = makeViewletKey(loc)
if (fv.viewletId !== $activeViewlet[key]) continue
if (fv.viewletId !== null) {
const optionKey = makeViewOptionsKey(fv.viewletId)
const viewOptions = $viewOptionStore.get(optionKey)
if (JSON.stringify(fv.viewOptions) !== JSON.stringify(viewOptions)) continue
}
selectedId = fv._id
dispatch('select', true)
return
}
clearSelection()
} else {
clearSelection()
}
}
$: checkSelected($filterStore, $location, filteredViews)
$: dispatch('shown', filteredViews !== undefined && filteredViews.length > 0)
</script>

View File

@ -20,15 +20,21 @@
import {
AnyComponent,
Button,
deviceOptionsStore as deviceInfo,
IconAdd,
SearchEdit,
showPopup,
TabList
TabList,
deviceOptionsStore as deviceInfo,
location,
showPopup
} from '@hcengineering/ui'
import { Viewlet, ViewOptions } from '@hcengineering/view'
import { getActiveViewletId, setActiveViewletId, ViewletSettingButton } from '@hcengineering/view-resources'
import { createEventDispatcher } from 'svelte'
import { ViewOptions, Viewlet } from '@hcengineering/view'
import {
ViewletSettingButton,
activeViewlet,
makeViewletKey,
setActiveViewletId
} from '@hcengineering/view-resources'
import { createEventDispatcher, onDestroy } from 'svelte'
import Header from './Header.svelte'
export let spaceId: Ref<Space> | undefined
@ -54,19 +60,28 @@
showPopup(createItemDialog as AnyComponent, { space: spaceId }, 'top')
}
$: updateViewlets(viewlets)
$: update(viewlets, active)
$: if (prevSpaceId !== spaceId) {
search = ''
dispatch('search', '')
}
function updateViewlets (viewlets: WithLookup<Viewlet>[]) {
const _id = getActiveViewletId()
const index = viewlets.findIndex((p) => p._id === (viewlet?._id ?? _id))
viewlet = index === -1 ? viewlets[0] : viewlets[index]
let key = makeViewletKey()
onDestroy(
location.subscribe((loc) => {
key = makeViewletKey(loc)
})
)
$: active = $activeViewlet[key]
async function update (viewlets: WithLookup<Viewlet>[], active: Ref<Viewlet> | null): Promise<void> {
viewlet = viewlets.find((viewlet) => viewlet._id === active) ?? viewlets[0]
setActiveViewletId(viewlet._id)
}
$: viewslist = viewlets.map((views) => {
return {
id: views._id,

View File

@ -16,10 +16,17 @@
import core, { Class, Doc, Ref, Space, WithLookup } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation'
import { AnyComponent, Component } from '@hcengineering/ui'
import { AnyComponent, Component, location } from '@hcengineering/ui'
import view, { Viewlet } from '@hcengineering/view'
import { getActiveViewletId, getViewOptions, viewOptionStore } from '@hcengineering/view-resources'
import {
activeViewlet,
getViewOptions,
makeViewletKey,
setActiveViewletId,
viewOptionStore
} from '@hcengineering/view-resources'
import type { ViewConfiguration } from '@hcengineering/workbench'
import { onDestroy } from 'svelte'
import SpaceContent from './SpaceContent.svelte'
import SpaceHeader from './SpaceHeader.svelte'
@ -38,9 +45,22 @@
let viewlets: WithLookup<Viewlet>[] = []
$: update(currentSpace, currentView?.class)
let key = makeViewletKey()
onDestroy(
location.subscribe((loc) => {
key = makeViewletKey(loc)
})
)
async function update (currentSpace?: Ref<Space>, attachTo?: Ref<Class<Doc>>): Promise<void> {
$: active = $activeViewlet[key]
$: update(active, currentSpace, currentView?.class)
async function update (
active: Ref<Viewlet> | null,
currentSpace?: Ref<Space>,
attachTo?: Ref<Class<Doc>>
): Promise<void> {
if (currentSpace === undefined) {
space = undefined
return
@ -62,8 +82,8 @@
}
)
if (header !== undefined) {
const _id = getActiveViewletId()
viewlet = viewlets.find((viewlet) => viewlet._id === _id) || viewlets[0]
viewlet = viewlets.find((viewlet) => viewlet._id === active) ?? viewlets[0]
setActiveViewletId(viewlet._id)
}
_class = attachTo
}

View File

@ -20,25 +20,28 @@
AnyComponent,
Button,
Component,
deviceOptionsStore as deviceInfo,
Icon,
IconAdd,
Label,
Loading,
SearchEdit,
showPopup,
TabList
TabList,
deviceOptionsStore as deviceInfo,
location,
showPopup
} from '@hcengineering/ui'
import view, { Viewlet, ViewletDescriptor, ViewletPreference } from '@hcengineering/view'
import {
FilterBar,
FilterButton,
getActiveViewletId,
getViewOptions,
setActiveViewletId,
ViewletSettingButton,
activeViewlet,
getViewOptions,
makeViewletKey,
setActiveViewletId,
viewOptionStore
} from '@hcengineering/view-resources'
import { onDestroy } from 'svelte'
export let _class: Ref<Class<Doc>>
export let space: Ref<Space> | undefined = undefined
@ -67,9 +70,35 @@
let viewlets: WithLookup<Viewlet>[] = []
$: update(_class, descriptors)
const viewletQuery = createQuery()
$: viewletQuery.query(
view.class.Viewlet,
{ attachTo: _class, variant: { $exists: false } },
(res) => (viewlets = res),
{
lookup: {
descriptor: view.class.ViewletDescriptor
}
}
)
async function update (_class: Ref<Class<Doc>>, descriptors?: Ref<ViewletDescriptor>[]): Promise<void> {
let key = makeViewletKey()
onDestroy(
location.subscribe((loc) => {
key = makeViewletKey(loc)
})
)
$: active = $activeViewlet[key]
$: update(_class, active, descriptors)
async function update (
_class: Ref<Class<Doc>>,
active: Ref<Viewlet> | null,
descriptors?: Ref<ViewletDescriptor>[]
): Promise<void> {
loading = true
viewlets = await client.findAll(
view.class.Viewlet,
@ -84,9 +113,9 @@
}
}
)
const _id = getActiveViewletId()
preference = undefined
viewlet = viewlets.find((viewlet) => viewlet._id === _id) || viewlets[0]
viewlet = viewlets.find((viewlet) => viewlet._id === active) ?? viewlets[0]
setActiveViewletId(viewlet._id)
loading = false
}

View File

@ -55,8 +55,8 @@ test.describe('recruit tests', () => {
const panel = page.locator('.popupPanel')
await panel.locator('[id="gmail\\:string\\:Email"]').scrollIntoViewIfNeeded()
await panel.locator('[id="gmail\\:string\\:Email"]').hover()
await expect(page.locator(`text=${email}`).first()).toBeVisible()
await panel.locator('[id="gmail\\:string\\:Email"]').click()
expect(await page.locator('.cover-channel >> input').inputValue()).toEqual(email)
})
test('create-application', async ({ page }) => {