platform/plugins/chunter-resources/src/components/chat/navigator/ChatNavGroup.svelte
Kristina 636d7044c5
Fix pinned channels double selection (#6758)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
2024-09-29 23:45:23 +07:00

234 lines
7.3 KiB
Svelte

<!--
// Copyright © 2023 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import activity from '@hcengineering/activity'
import core, { Class, Doc, groupByArray, reduceCalls, Ref, Space } from '@hcengineering/core'
import { DocNotifyContext } from '@hcengineering/notification'
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
import { IntlString } from '@hcengineering/platform'
import { createQuery, getClient, LiveQuery } from '@hcengineering/presentation'
import { Action } from '@hcengineering/ui'
import chunter from '../../../plugin'
import { ChatGroup, ChatNavGroupModel } from '../types'
import ChatNavSection from './ChatNavSection.svelte'
export let object: Doc | undefined
export let model: ChatNavGroupModel
interface Section {
id: string
_class?: Ref<Class<Doc>>
label: IntlString
objects: Doc[]
count: number
}
const client = getClient()
const hierarchy = client.getHierarchy()
const inboxClient = InboxNotificationsClientImpl.getClient()
const contextsStore = inboxClient.contexts
const contextByDocStore = inboxClient.contextByDoc
const objectsQueryByClass = new Map<Ref<Class<Doc>>, { query: LiveQuery, limit: number }>()
let contexts: DocNotifyContext[] = []
let objectsByClass = new Map<Ref<Class<Doc>>, { docs: Doc[], total: number }>()
let shouldPushObject = false
let sections: Section[] = []
$: contexts = $contextsStore.filter((it) => {
const { objectClass, isPinned, hidden } = it
if (hidden) return false
if (model.isPinned !== isPinned) return false
if (model._class !== undefined && model._class !== objectClass) return false
if (model.skipClasses !== undefined && model.skipClasses.includes(objectClass)) return false
if (hierarchy.classHierarchyMixin(objectClass, activity.mixin.ActivityDoc) === undefined) return false
return true
})
$: loadObjects(contexts)
$: pushObj = shouldPushObject ? object : undefined
const getPushObj = () => pushObj as Doc
$: void getSections(objectsByClass, model, pushObj, getPushObj, (res) => {
sections = res
})
$: shouldPushObject =
object !== undefined &&
getObjectGroup(object) === model.id &&
(!$contextByDocStore.has(object._id) || isArchived(object))
function isArchived (object: Doc): boolean {
return hierarchy.isDerived(object._class, core.class.Space) ? (object as Space).archived : false
}
function loadObjects (contexts: DocNotifyContext[]): void {
const contextsByClass = groupByArray(contexts, ({ objectClass }) => objectClass)
for (const [_class, ctx] of contextsByClass.entries()) {
const isSpace = hierarchy.isDerived(_class, core.class.Space)
const ids = ctx.map(({ objectId }) => objectId)
const { query, limit } = objectsQueryByClass.get(_class) ?? {
query: createQuery(),
limit: isSpace ? -1 : model.maxSectionItems ?? 5
}
objectsQueryByClass.set(_class, { query, limit: limit ?? model.maxSectionItems ?? 5 })
query.query(
_class,
{
_id: { $in: limit !== -1 ? ids.slice(0, limit) : ids },
...(isSpace ? { space: core.space.Space, archived: false } : {})
},
(res) => {
objectsByClass = objectsByClass.set(_class, { docs: res, total: res.total })
},
{ total: true }
)
}
for (const [classRef, query] of objectsQueryByClass.entries()) {
if (!contextsByClass.has(classRef)) {
query.query.unsubscribe()
objectsQueryByClass.delete(classRef)
objectsByClass.delete(classRef)
}
}
objectsByClass = objectsByClass
}
function getObjectGroup (object: Doc): ChatGroup {
if (hierarchy.isDerived(object._class, chunter.class.Channel)) {
return 'channels'
}
if (hierarchy.isDerived(object._class, chunter.class.DirectMessage)) {
return 'direct'
}
return 'activity'
}
const getSections = reduceCalls(
async (
objectsByClass: Map<Ref<Class<Doc>>, { docs: Doc[], total: number }>,
model: ChatNavGroupModel,
object: { _id: Doc['_id'], _class: Doc['_class'] } | undefined,
getPushObj: () => Doc,
handler: (result: Section[]) => void
): Promise<void> => {
const result: Section[] = []
if (!model.wrap) {
result.push({
id: model.id,
objects: Array.from(Array.from(objectsByClass.values()).map((it) => it.docs)).flat(),
label: model.label ?? chunter.string.Channels,
count: Array.from(Array.from(objectsByClass.values()).map((it) => it.total)).reduceRight((a, b) => a + b, 0)
})
handler(result)
return
}
let isObjectPushed = false
if (
Array.from(Array.from(objectsByClass.values()).map((it) => it.docs))
.flat()
.some((o) => o._id === object?._id)
) {
isObjectPushed = true
}
for (let [_class, { docs: objects, total }] of objectsByClass.entries()) {
const clazz = hierarchy.getClass(_class)
const sectionObjects = [...objects]
if (object !== undefined && _class === object._class && !objects.some(({ _id }) => _id === object._id)) {
isObjectPushed = true
sectionObjects.push(getPushObj())
total++
}
result.push({
id: _class,
_class,
objects: sectionObjects,
label: clazz.pluralLabel ?? clazz.label,
count: total
})
}
if (!isObjectPushed && object !== undefined) {
const clazz = hierarchy.getClass(object._class)
result.push({
id: object._id,
_class: object._class,
objects: [getPushObj()],
label: clazz.pluralLabel ?? clazz.label,
count: 1
})
}
handler(result.sort((s1, s2) => s1.label.localeCompare(s2.label)))
}
)
function getSectionActions (section: Section, contexts: DocNotifyContext[]): Action[] {
if (model.getActionsFn === undefined) {
return []
}
const { _class } = section
if (_class === undefined) {
return model.getActionsFn(contexts)
} else {
return model.getActionsFn(contexts.filter(({ objectClass }) => objectClass === _class))
}
}
</script>
{#each sections as section (section.id)}
<ChatNavSection
id={section.id}
objects={section.objects}
{contexts}
objectId={object?._id}
header={section.label}
actions={getSectionActions(section, contexts)}
sortFn={model.sortFn}
itemsCount={section.count}
on:show-more={() => {
if (section._class !== undefined) {
const query = objectsQueryByClass.get(section._class)
if (query !== undefined) {
query.limit += 50
loadObjects(contexts)
}
}
}}
on:select
/>
{/each}