TSK-1142 С трудом работает Filtered views (#2984)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2023-04-14 16:29:15 +06:00 committed by GitHub
parent 6a842685f7
commit abcaa2e980
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 131 additions and 131 deletions

View File

@ -291,6 +291,10 @@ export function createModel (builder: Builder): void {
},
notification.action.Unsubscribe
)
builder.mixin(notification.class.DocUpdates, core.class.Class, view.mixin.IgnoreActions, {
actions: [view.action.Delete, view.action.Open]
})
}
export { notificationOperation } from './migration'

View File

@ -13,10 +13,20 @@
// limitations under the License.
//
import core, { AnyAttribute, DOMAIN_TX, Ref, TxCreateDoc, TxCUD, TxProcessor, TxRemoveDoc } from '@hcengineering/core'
import core, {
AnyAttribute,
Class,
Doc,
DOMAIN_TX,
Ref,
TxCreateDoc,
TxCUD,
TxProcessor,
TxRemoveDoc
} from '@hcengineering/core'
import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@hcengineering/model'
import { BuildModelKey, FilteredView, Viewlet, ViewletPreference } from '@hcengineering/view'
import { DOMAIN_PREFERENCE } from '@hcengineering/preference'
import { BuildModelKey, FilteredView, Viewlet, ViewletPreference } from '@hcengineering/view'
import view from './plugin'
async function migrateViewletPreference (client: MigrationClient): Promise<void> {
@ -78,6 +88,26 @@ async function migrateSavedFilters (client: MigrationClient): Promise<void> {
}
}
async function migrateSavedFiltersViewlets (client: MigrationClient): Promise<void> {
const preferences = await client.find<FilteredView>(DOMAIN_PREFERENCE, {
_class: view.class.FilteredView,
viewletId: /^\S{24}$/ as any,
attachedTo: 'tracker' as any
})
for (const pref of preferences) {
await client.update<FilteredView>(
DOMAIN_PREFERENCE,
{
_id: pref._id
},
{
viewletId: 'tracker:viewlet:IssueList' as Ref<Viewlet>,
filterClass: 'tracker:class:Issue' as Ref<Class<Doc>>
}
)
}
}
async function fixViewletPreferenceRemovedAttributes (client: MigrationClient): Promise<void> {
const removeTxes = await client.find<TxRemoveDoc<AnyAttribute>>(DOMAIN_TX, {
_class: core.class.TxRemoveDoc,
@ -148,6 +178,7 @@ export const viewOperation: MigrateOperation = {
async migrate (client: MigrationClient): Promise<void> {
await migrateViewletPreference(client)
await migrateSavedFilters(client)
await migrateSavedFiltersViewlets(client)
await fixViewletPreferenceRemovedAttributes(client)
await fixPreferenceObjectKey(client)
},

View File

@ -171,7 +171,6 @@ export { NotificationPosition } from './components/notifications/NotificationPos
export { NotificationSeverity } from './components/notifications/NotificationSeverity'
export { Notification } from './components/notifications/Notification'
export { default as Wizard } from './components/wizard/Wizard.svelte'
export { default as NavLink } from './components/NavLink.svelte'
export { default as StepsDialog } from './components/StepsDialog.svelte'
export * from './types'

View File

@ -15,7 +15,8 @@
<script lang="ts">
import { Channel, chunterId } from '@hcengineering/chunter'
import { getClient } from '@hcengineering/presentation'
import { Icon, NavLink } from '@hcengineering/ui'
import { Icon } from '@hcengineering/ui'
import { NavLink } from '@hcengineering/view-resources'
export let value: Channel
export let inline: boolean = false

View File

@ -1,7 +1,7 @@
<script lang="ts">
import { chunterId, DirectMessage, Message } from '@hcengineering/chunter'
import { createQuery, getClient, MessageViewer } from '@hcengineering/presentation'
import { NavLink } from '@hcengineering/ui'
import { NavLink } from '@hcengineering/view-resources'
import chunter from '../plugin'
import { getDmName } from '../utils'

View File

@ -15,7 +15,8 @@
<script lang="ts">
import { chunterId, DirectMessage } from '@hcengineering/chunter'
import { getClient } from '@hcengineering/presentation'
import { Icon, NavLink } from '@hcengineering/ui'
import { Icon } from '@hcengineering/ui'
import { NavLink } from '@hcengineering/view-resources'
import { getDmName } from '../utils'

View File

@ -16,19 +16,9 @@
import type { Ref } from '@hcengineering/core'
import { createQuery } from '@hcengineering/presentation'
import { recruitId, Vacancy } from '@hcengineering/recruit'
import {
Button,
Icon,
IconAdd,
Label,
Loading,
NavLink,
resizeObserver,
Scroller,
showPopup
} from '@hcengineering/ui'
import { Button, Icon, IconAdd, Label, Loading, resizeObserver, Scroller, showPopup } from '@hcengineering/ui'
import view, { Viewlet, ViewletPreference } from '@hcengineering/view'
import { getViewOptions, Table, ViewletSettingButton, viewOptionStore } from '@hcengineering/view-resources'
import { getViewOptions, NavLink, Table, ViewletSettingButton, viewOptionStore } from '@hcengineering/view-resources'
import recruit from '../plugin'
import CreateApplication from './CreateApplication.svelte'
import IconApplication from './icons/Application.svelte'

View File

@ -19,8 +19,9 @@
import { ChannelsEditor } from '@hcengineering/contact-resources'
import { Ref, WithLookup } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { recruitId, Vacancy } from '@hcengineering/recruit'
import { Component, Label, NavLink } from '@hcengineering/ui'
import { Vacancy, recruitId } from '@hcengineering/recruit'
import { Component, Label } from '@hcengineering/ui'
import { NavLink } from '@hcengineering/view-resources'
import recruit from '../plugin'
import VacancyIcon from './icons/Vacancy.svelte'

View File

@ -19,21 +19,21 @@
import {
Button,
Chevron,
closeTooltip,
ExpandCollapse,
getCurrentLocation,
IconAdd,
IconArrowRight,
IconScaleFull,
Label,
closeTooltip,
getCurrentLocation,
navigate
} from '@hcengineering/ui'
import view, { Viewlet } from '@hcengineering/view'
import {
createFilter,
filterStore,
getViewOptions,
ViewletSettingButton,
createFilter,
getViewOptions,
setFilters,
viewOptionStore
} from '@hcengineering/view-resources'
import tracker from '../../../plugin'
@ -111,7 +111,7 @@
loc.path[3] = issue.space
loc.path[4] = 'issues'
navigate(loc)
$filterStore = [filter]
setFilters([filter])
}
}}
/>

View File

@ -15,10 +15,9 @@
<script lang="ts">
import { Ref, Space } from '@hcengineering/core'
import { Project } from '@hcengineering/tracker'
import { NavLink } from '@hcengineering/ui'
import { TreeNode, NavLink } from '@hcengineering/view-resources'
import { SpacesNavModel } from '@hcengineering/workbench'
import { SpecialElement } from '@hcengineering/workbench-resources'
import { TreeNode } from '@hcengineering/view-resources'
export let space: Project
export let model: SpacesNavModel

View File

@ -16,10 +16,10 @@
import { Class, Doc, DocumentQuery, Ref } from '@hcengineering/core'
import { getResource } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation'
import { Button, eventToHTMLElement, IconAdd, showPopup } from '@hcengineering/ui'
import { Button, IconAdd, eventToHTMLElement, showPopup } from '@hcengineering/ui'
import { Filter, ViewOptions } from '@hcengineering/view'
import { createEventDispatcher } from 'svelte'
import { filterStore, getFilterKey } from '../../filter'
import { filterStore, removeFilter, updateFilter } from '../../filter'
import view from '../../plugin'
import FilterSave from './FilterSave.svelte'
import FilterSection from './FilterSection.svelte'
@ -37,14 +37,7 @@
function onChange (e: Filter | undefined) {
if (e === undefined) return
const index = $filterStore.findIndex((p) => p.index === e.index)
if (index === -1) {
$filterStore.push(e)
} else {
$filterStore[index].onRemove?.()
$filterStore[index] = e
}
$filterStore = $filterStore
updateFilter(e)
}
function add (e: MouseEvent) {
@ -61,45 +54,10 @@
)
}
$: load(_class)
function remove (i: number) {
$filterStore[i]?.onRemove?.()
$filterStore.splice(i, 1)
$filterStore = $filterStore
}
$: saveFilters($filterStore)
function saveFilters (filters: Filter[]) {
const key = getFilterKey(_class)
if (filters.length > 0) {
localStorage.setItem(key, JSON.stringify(filters))
} else {
localStorage.removeItem(key)
}
}
async function saveFilteredView () {
showPopup(FilterSave, { viewOptions, _class })
}
let loading = false
function load (_class: Ref<Class<Doc>>) {
loading = true
const oldFilters = $filterStore
const key = getFilterKey(_class)
const saved = localStorage.getItem(key)
if (saved !== null) {
$filterStore = JSON.parse(saved)
} else {
$filterStore = []
}
loading = false
oldFilters.forEach((p) => p.onRemove?.())
}
async function makeQuery (query: DocumentQuery<Doc>, filters: Filter[]): Promise<void> {
const newQuery = hierarchy.clone(query)
for (let i = 0; i < filters.length; i++) {
@ -172,20 +130,17 @@
{#if visible && $filterStore && $filterStore.length > 0}
<div class="filterbar-container">
<div class="filters">
{#if !loading}
{#each $filterStore as filter, i}
<FilterSection
{filter}
on:change={() => {
makeQuery(query, $filterStore)
saveFilters($filterStore)
}}
on:remove={() => {
remove(i)
}}
/>
{/each}
{/if}
{#each $filterStore as filter, i}
<FilterSection
{filter}
on:change={() => {
makeQuery(query, $filterStore)
}}
on:remove={() => {
removeFilter(i)
}}
/>
{/each}
<div class="add-filter">
<Button size={'small'} icon={IconAdd} kind={'transparent'} on:click={add} />
</div>

View File

@ -15,9 +15,9 @@
<script lang="ts">
import { Class, Doc, Ref, Space } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import { Button, eventToHTMLElement, IconAdd, IconClose, Icon, showPopup, Label } from '@hcengineering/ui'
import { Button, Icon, IconAdd, IconClose, Label, eventToHTMLElement, showPopup } from '@hcengineering/ui'
import { Filter } from '@hcengineering/view'
import { filterStore } from '../../filter'
import { filterStore, setFilters } from '../../filter'
import view from '../../plugin'
import FilterTypePopup from './FilterTypePopup.svelte'
@ -28,7 +28,7 @@
const hierarchy = client.getHierarchy()
function onChange (e: Filter | undefined) {
if (e !== undefined) $filterStore = [e]
if (e !== undefined) setFilters([e])
}
function add (e: MouseEvent) {
@ -62,7 +62,7 @@
borderStyle={'dashed'}
on:click={(ev) => {
if ($filterStore.length === 0) add(ev)
else $filterStore = []
else setFilters([])
}}
>
<svelte:fragment slot="content">

View File

@ -13,8 +13,8 @@
// limitations under the License.
-->
<script lang="ts">
import { location, locationToUrl, navigate } from '../location'
import { Location } from '../types'
import { Location, location, locationToUrl, navigate } from '@hcengineering/ui'
import { setFilters } from '../../filter'
export let app: string | undefined = undefined
export let space: string | undefined = undefined
@ -52,6 +52,7 @@
function clickHandler (e: MouseEvent) {
if (e.metaKey || e.ctrlKey) return
e.preventDefault()
setFilters([])
navigate(loc)
}
</script>

View File

@ -10,10 +10,10 @@ import core, {
Ref
} from '@hcengineering/core'
import { getResource } from '@hcengineering/platform'
import { createQuery, getClient, LiveQuery } from '@hcengineering/presentation'
import { LiveQuery, createQuery, getClient } from '@hcengineering/presentation'
import { AnyComponent, getCurrentLocation, locationToUrl } from '@hcengineering/ui'
import { Filter, FilterMode, KeyFilter } from '@hcengineering/view'
import { writable } from 'svelte/store'
import { get, writable } from 'svelte/store'
import view from './plugin'
/**
@ -21,6 +21,30 @@ import view from './plugin'
*/
export const filterStore = writable<Filter[]>([])
export function setFilters (filters: Filter[]): void {
const old = get(filterStore)
old.forEach((p) => p.onRemove?.())
filterStore.set(filters)
}
export function removeFilter (i: number): void {
const old = get(filterStore)
old[i]?.onRemove?.()
old.splice(i, 1)
filterStore.set(old)
}
export function updateFilter (filter: Filter): void {
const old = get(filterStore)
const index = old.findIndex((p) => p.index === filter.index)
if (index === -1) {
old.push(filter)
} else {
old[index] = filter
}
filterStore.set(old)
}
export async function objectInResult (filter: Filter): Promise<ObjQueryType<any>> {
return { $in: filter.value }
}

View File

@ -60,6 +60,8 @@ import ObjectPresenter from './components/ObjectPresenter.svelte'
import RolePresenter from './components/RolePresenter.svelte'
import SpacePresenter from './components/SpacePresenter.svelte'
import SpaceRefPresenter from './components/SpaceRefPresenter.svelte'
import StatusPresenter from './components/status/StatusPresenter.svelte'
import StatusRefPresenter from './components/status/StatusRefPresenter.svelte'
import StringEditor from './components/StringEditor.svelte'
import StringPresenter from './components/StringPresenter.svelte'
import Table from './components/Table.svelte'
@ -68,8 +70,6 @@ import TimestampPresenter from './components/TimestampPresenter.svelte'
import UpDownNavigator from './components/UpDownNavigator.svelte'
import ValueSelector from './components/ValueSelector.svelte'
import ViewletSettingButton from './components/ViewletSettingButton.svelte'
import StatusPresenter from './components/status/StatusPresenter.svelte'
import StatusRefPresenter from './components/status/StatusRefPresenter.svelte'
import {
afterResult,
@ -83,8 +83,8 @@ import {
} from './filter'
import { IndexedDocumentPreview } from '@hcengineering/presentation'
import { showEmptyGroups } from './viewOptions'
import { statusSort } from './utils'
import { showEmptyGroups } from './viewOptions'
export { getActions, invokeAction } from './actions'
export { default as ActionContext } from './components/ActionContext.svelte'
export { default as ActionHandler } from './components/ActionHandler.svelte'
@ -96,6 +96,7 @@ export { default as List } from './components/list/List.svelte'
export { default as MarkupPresenter } from './components/MarkupPresenter.svelte'
export { default as MarkupPreviewPopup } from './components/MarkupPreviewPopup.svelte'
export { default as ContextMenu } from './components/Menu.svelte'
export { default as NavLink } from './components/navigator/NavLink.svelte'
export { default as ObjectBox } from './components/ObjectBox.svelte'
export { default as ObjectPresenter } from './components/ObjectPresenter.svelte'
export { default as ObjectSearchBox } from './components/ObjectSearchBox.svelte'

View File

@ -15,7 +15,8 @@
<script lang="ts">
import type { Ref } from '@hcengineering/core'
import { createQuery } from '@hcengineering/presentation'
import { NavLink, Scroller } from '@hcengineering/ui'
import { Scroller } from '@hcengineering/ui'
import { NavLink } from '@hcengineering/view-resources'
import type { Application } from '@hcengineering/workbench'
import workbench from '@hcengineering/workbench'
import { hideApplication, showApplication } from '../utils'

View File

@ -18,7 +18,8 @@
import preference, { SpacePreference } from '@hcengineering/preference'
import { createQuery, getClient } from '@hcengineering/presentation'
import setting from '@hcengineering/setting'
import { NavLink, Scroller, showPopup, Icon, Label } from '@hcengineering/ui'
import { Icon, Label, Scroller, showPopup } from '@hcengineering/ui'
import { NavLink } from '@hcengineering/view-resources'
import type { Application, NavigatorModel, SpecialNavModel } from '@hcengineering/workbench'
import workbench from '../plugin'
import { getSpecialSpaceClass } from '../utils'

View File

@ -3,16 +3,16 @@
import { createQuery, getClient } from '@hcengineering/presentation'
import setting from '@hcengineering/setting'
import { Action, Location, location, navigate } from '@hcengineering/ui'
import view, { Filter, FilteredView } from '@hcengineering/view'
import view, { Filter, FilteredView, ViewOptions, Viewlet } from '@hcengineering/view'
import {
TreeItem,
TreeNode,
activeViewlet,
filterStore,
getFilterKey,
makeViewOptionsKey,
getViewOptions,
makeViewletKey,
setActiveViewletId,
setFilters,
setViewOptions,
viewOptionStore
} from '@hcengineering/view-resources'
@ -47,7 +47,10 @@
}
let selectedId: Ref<FilteredView> | undefined = undefined
async function load (fv: FilteredView): Promise<void> {
navigate(fv.location)
setFilters(JSON.parse(fv.filters))
if (fv.viewletId !== undefined && fv.viewletId !== null) {
const viewlet = await client.findOne(view.class.Viewlet, { _id: fv.viewletId })
setActiveViewletId(fv.viewletId, fv.location)
@ -55,13 +58,6 @@
setViewOptions(viewlet, fv.viewOptions)
}
}
if (fv.filterClass !== undefined) {
const key = getFilterKey(fv.filterClass)
const filters = JSON.parse(fv.filters)
localStorage.setItem(key, JSON.stringify(filters))
}
navigate(fv.location)
$filterStore = JSON.parse(fv.filters)
}
const clearSelection = () => {
@ -69,7 +65,12 @@
dispatch('select', false)
}
function checkSelected (fs: Filter[], loc: Location, filteredViews: FilteredView[] | undefined) {
function checkSelected (
fs: Filter[],
loc: Location,
filteredViews: FilteredView[] | undefined,
viewOptionStore: Map<string, ViewOptions>
) {
const filters = JSON.stringify(fs)
if (loc && Array.isArray(fs) && fs.length > 0 && Array.isArray(filteredViews)) {
for (const fv of filteredViews) {
@ -78,8 +79,7 @@
const key = makeViewletKey(loc)
if (fv.viewletId !== $activeViewlet[key]) continue
if (fv.viewletId !== null) {
const optionKey = makeViewOptionsKey(fv.viewletId)
const viewOptions = $viewOptionStore.get(optionKey)
const viewOptions = getViewOptions({ _id: fv.viewletId } as Viewlet, viewOptionStore)
if (JSON.stringify(fv.viewOptions) !== JSON.stringify(viewOptions)) continue
}
selectedId = fv._id
@ -92,7 +92,7 @@
}
}
$: checkSelected($filterStore, $location, filteredViews)
$: checkSelected($filterStore, $location, filteredViews, $viewOptionStore)
$: dispatch('shown', filteredViews !== undefined && filteredViews.length > 0)
</script>

View File

@ -29,7 +29,6 @@
DatePickerPopup,
Label,
Location,
NavLink,
PanelInstance,
Popup,
PopupAlignment,
@ -50,7 +49,7 @@
showPopup
} from '@hcengineering/ui'
import view from '@hcengineering/view'
import { ActionContext, ActionHandler, migrateViewOpttions } from '@hcengineering/view-resources'
import { ActionContext, ActionHandler, NavLink, migrateViewOpttions } from '@hcengineering/view-resources'
import type { Application, NavigatorModel, SpecialNavModel, ViewConfiguration } from '@hcengineering/workbench'
import { getContext, onDestroy, onMount, tick } from 'svelte'
import { get } from 'svelte/store'

View File

@ -20,21 +20,13 @@
import { getResource } from '@hcengineering/platform'
import preference from '@hcengineering/preference'
import { getClient } from '@hcengineering/presentation'
import { Action, IconAdd, IconEdit, IconSearch, getCurrentLocation, navigate, showPopup } from '@hcengineering/ui'
import {
Action,
getCurrentLocation,
IconAdd,
IconEdit,
IconSearch,
navigate,
NavLink,
showPopup
} from '@hcengineering/ui'
import {
getActions as getContributedActions,
getObjectPresenter,
TreeItem,
TreeNode
TreeNode,
getActions as getContributedActions,
getObjectPresenter
} from '@hcengineering/view-resources'
import { SpacesNavModel } from '@hcengineering/workbench'
import { createEventDispatcher } from 'svelte'

View File

@ -17,12 +17,12 @@
import core from '@hcengineering/core'
import notification, { LastView } from '@hcengineering/notification'
import { NotificationClientImpl } from '@hcengineering/notification-resources'
import { getResource, IntlString } from '@hcengineering/platform'
import { IntlString, getResource } from '@hcengineering/platform'
import preference from '@hcengineering/preference'
import { getClient } from '@hcengineering/presentation'
import { Action, IconEdit, NavLink } from '@hcengineering/ui'
import { Action, IconEdit } from '@hcengineering/ui'
import view from '@hcengineering/view'
import { getActions as getContributedActions, TreeItem, TreeNode } from '@hcengineering/view-resources'
import { NavLink, TreeItem, TreeNode, getActions as getContributedActions } from '@hcengineering/view-resources'
import { classIcon, getSpaceName } from '../../utils'
export let label: IntlString