platform/plugins/activity-resources/src/components/Activity.svelte
Denis Bykhov 8a670155db
Activity load fix (#3303)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
2023-05-31 19:23:57 +07:00

175 lines
5.0 KiB
Svelte

<!--
// Copyright © 2022 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, { DisplayTx, TxViewlet } from '@hcengineering/activity'
import chunter from '@hcengineering/chunter'
import core, { Class, Doc, Ref, SortingOrder } from '@hcengineering/core'
import notification, { DocUpdateTx, DocUpdates, Writable } from '@hcengineering/notification'
import { getResource } from '@hcengineering/platform'
import { createQuery, getClient } from '@hcengineering/presentation'
import { Component, Grid, Label, Spinner } from '@hcengineering/ui'
import { ActivityKey, activityKey, newActivity } from '../activity'
import { filterCollectionTxes } from '../utils'
import ActivityFilter from './ActivityFilter.svelte'
import TxView from './TxView.svelte'
export let object: Doc
export let showCommenInput: boolean = true
export let transparent: boolean = false
export let shouldScroll: boolean = false
export let focusIndex: number = -1
getResource(notification.function.GetNotificationClient).then((res) => {
updatesStore = res().docUpdatesStore
})
let updatesStore: Writable<Map<Ref<Doc>, DocUpdates>> | undefined
$: updates = $updatesStore?.get(object._id)
$: newTxes = updates?.txes ?? []
let txes: DisplayTx[] = []
const client = getClient()
const attrs = client.getHierarchy().getAllAttributes(object._class)
const activityQuery = newActivity(client, attrs)
let viewlets: Map<ActivityKey, TxViewlet>
let allViewlets: TxViewlet[] = []
let editableMap: Map<Ref<Class<Doc>>, boolean> | undefined = undefined
const descriptors = createQuery()
$: descriptors.query(activity.class.TxViewlet, {}, (result) => {
allViewlets = result
editableMap = new Map(
allViewlets
.filter((tx) => tx.txClass === core.class.TxCreateDoc)
.map((it) => [it.objectClass, it.editable ?? false])
)
})
$: viewlets = new Map(allViewlets.map((r) => [activityKey(r.objectClass, r.txClass), r]))
let loading = false
function updateTxes (
objectId: Ref<Doc>,
objectClass: Ref<Class<Doc>>,
editableMap: Map<Ref<Class<Doc>>, boolean> | undefined
): void {
loading = true
const res = activityQuery.update(
objectId,
objectClass,
(_id, result) => {
if (_id === objectId) {
txes = filterCollectionTxes(result)
if (txes.length > 0) {
loading = false
}
}
},
SortingOrder.Ascending,
editableMap ?? new Map()
)
if (!res) {
loading = false
}
}
$: updateTxes(object._id, object._class, editableMap)
let filtered: DisplayTx[] = []
let newTxIndexes: number[] = []
$: newTxIndexes = getNewTxes(filtered, newTxes)
function getNewTxes (filtered: DisplayTx[], newTxes: DocUpdateTx[]): number[] {
const res: number[] = []
for (let i = 0; i < filtered.length; i++) {
if (isNew(filtered[i], newTxes)) {
res.push(i)
}
}
return res
}
function isNew (tx: DisplayTx | undefined, newTxes: DocUpdateTx[]): boolean {
if (tx === undefined) return false
const index = newTxes.findIndex((p) => p._id === tx.originTx._id && p.isNew)
return index !== -1
}
$: scrollIndex = shouldScroll ? newTxIndexes[0] ?? filtered.length - 1 : -1
</script>
<div class="antiSection-header high mt-9" class:invisible={transparent}>
<span class="antiSection-header__title flex-row-center">
<Label label={activity.string.Activity} />
{#if loading}
<div class="ml-1">
<Spinner size={'small'} />
</div>
{/if}
</span>
<ActivityFilter
{txes}
{object}
on:update={(e) => {
filtered = e.detail
}}
/>
</div>
<div class="p-activity select-text" id={activity.string.Activity}>
{#if filtered}
<Grid column={1} rowGap={0.75}>
{#each filtered as tx, i}
<TxView
{tx}
{viewlets}
isNew={newTxIndexes.includes(i)}
isNextNew={newTxIndexes.includes(i + 1)}
shouldScroll={i === scrollIndex}
/>
{/each}
</Grid>
{/if}
</div>
{#if showCommenInput}
<div class="ref-input">
<Component is={chunter.component.CommentInput} props={{ object, focusIndex }} />
</div>
{/if}
<style lang="scss">
.ref-input {
flex-shrink: 0;
margin-top: 1.75rem;
padding-bottom: 2.5rem;
}
.p-activity {
margin-top: 1.75rem;
}
.invisible {
display: none;
}
:global(.grid .msgactivity-container.showIcon:last-child::after) {
content: none;
} // Remove the line in the last Activity message
</style>