UBERF-7489: Some more chat optimizations (#5999)

This commit is contained in:
Andrey Sobolev 2024-07-04 08:48:11 +07:00 committed by GitHub
parent 29f3198b69
commit 630ae21a66
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 117 additions and 65 deletions

View File

@ -13,36 +13,36 @@
// limitations under the License.
-->
<script lang="ts">
import { Class, Doc, getDay, Ref, Timestamp } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import activity, {
ActivityExtension,
ActivityMessage,
ActivityMessagesFilter,
DisplayActivityMessage
} from '@hcengineering/activity'
import { Loading, Scroller, ScrollParams } from '@hcengineering/ui'
import {
ActivityExtension as ActivityExtensionComponent,
ActivityMessagePresenter,
canGroupMessages
} from '@hcengineering/activity-resources'
import { Class, Doc, getDay, Ref, Timestamp } from '@hcengineering/core'
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
import { get } from 'svelte/store'
import { tick, beforeUpdate, afterUpdate, onMount, onDestroy } from 'svelte'
import { getResource } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation'
import { Loading, Scroller, ScrollParams } from '@hcengineering/ui'
import { afterUpdate, beforeUpdate, onDestroy, onMount, tick } from 'svelte'
import { get } from 'svelte/store'
import ActivityMessagesSeparator from './ChannelMessagesSeparator.svelte'
import { ChannelDataProvider, MessageMetadata } from '../channelDataProvider'
import {
chatReadMessagesStore,
filterChatMessages,
getClosestDate,
readChannelMessages,
chatReadMessagesStore,
recheckNotifications
} from '../utils'
import HistoryLoading from './LoadingHistory.svelte'
import { ChannelDataProvider, MessageMetadata } from '../channelDataProvider'
import ActivityMessagesSeparator from './ChannelMessagesSeparator.svelte'
import JumpToDateSelector from './JumpToDateSelector.svelte'
import HistoryLoading from './LoadingHistory.svelte'
export let provider: ChannelDataProvider
export let object: Doc | undefined
@ -313,6 +313,9 @@
return messageRect.top >= containerRect.top && messageRect.bottom - messageRect.height / 2 <= containerRect.bottom
}
const messagesToReadAccumulator: DisplayActivityMessage[] = []
let messagesToReadAccumulatorTimer: any
function readViewportMessages (): void {
if (!scrollElement || !scrollContentBox) {
return
@ -320,7 +323,6 @@
const containerRect = scrollElement.getBoundingClientRect()
const messagesToRead: DisplayActivityMessage[] = []
const messagesElements = scrollContentBox?.getElementsByClassName('activityMessage')
for (const message of displayMessages) {
@ -331,11 +333,15 @@
}
if (messageInView(msgElement, containerRect)) {
messagesToRead.push(message)
messagesToReadAccumulator.push(message)
}
}
void readChannelMessages(messagesToRead, notifyContext)
clearTimeout(messagesToReadAccumulatorTimer)
messagesToReadAccumulatorTimer = setTimeout(() => {
const messagesToRead = [...messagesToReadAccumulator]
void readChannelMessages(messagesToRead, notifyContext)
}, 500)
}
function updateSelectedDate (): void {

View File

@ -12,24 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
import { type Channel, type ChatMessage, type DirectMessage, type ThreadMessage } from '@hcengineering/chunter'
import contact, { type Employee, getName, type Person, type PersonAccount } from '@hcengineering/contact'
import { employeeByIdStore, PersonIcon } from '@hcengineering/contact-resources'
import {
type Account,
type Class,
type Client,
type Doc,
getCurrentAccount,
type IdMap,
type Ref,
type Space,
type Timestamp
} from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import { type AnySvelteComponent } from '@hcengineering/ui'
import { type Asset, translate } from '@hcengineering/platform'
import { classIcon, getDocLinkTitle, getDocTitle } from '@hcengineering/view-resources'
import activity, {
type ActivityMessage,
type ActivityMessagesFilter,
@ -37,19 +19,38 @@ import activity, {
type DisplayDocUpdateMessage,
type DocUpdateMessage
} from '@hcengineering/activity'
import { type Channel, type ChatMessage, type DirectMessage, type ThreadMessage } from '@hcengineering/chunter'
import contact, { getName, type Employee, type Person, type PersonAccount } from '@hcengineering/contact'
import { PersonIcon, employeeByIdStore } from '@hcengineering/contact-resources'
import {
generateId,
getCurrentAccount,
type Account,
type Class,
type Client,
type Doc,
type IdMap,
type Ref,
type Space,
type Timestamp
} from '@hcengineering/core'
import notification, { type DocNotifyContext, type InboxNotification } from '@hcengineering/notification'
import {
archiveContextNotifications,
InboxNotificationsClientImpl,
archiveContextNotifications,
isActivityNotification,
isMentionNotification
} from '@hcengineering/notification-resources'
import notification, { type DocNotifyContext } from '@hcengineering/notification'
import { get, type Unsubscriber, writable } from 'svelte/store'
import { translate, type Asset } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation'
import { type AnySvelteComponent } from '@hcengineering/ui'
import { classIcon, getDocLinkTitle, getDocTitle } from '@hcengineering/view-resources'
import { get, writable, type Unsubscriber } from 'svelte/store'
import chunter from './plugin'
import DirectIcon from './components/DirectIcon.svelte'
import ChannelIcon from './components/ChannelIcon.svelte'
import DirectIcon from './components/DirectIcon.svelte'
import { resetChunterLocIfEqual } from './navigation'
import chunter from './plugin'
export async function getDmName (client: Client, space?: Space): Promise<string> {
if (space === undefined) {
@ -366,6 +367,9 @@ function getAllIds (messages: DisplayActivityMessage[]): Array<Ref<ActivityMessa
.flat()
}
let toReadTimer: any
const toRead = new Set<Ref<InboxNotification>>()
export function recheckNotifications (context: DocNotifyContext): void {
const client = getClient()
const inboxClient = InboxNotificationsClientImpl.getClient()
@ -378,7 +382,7 @@ export function recheckNotifications (context: DocNotifyContext): void {
const notifications = get(inboxClient.inboxNotificationsByContext).get(context._id) ?? []
const toRead = notifications
notifications
.filter((it) => {
if (it.isViewed) {
return false
@ -394,9 +398,18 @@ export function recheckNotifications (context: DocNotifyContext): void {
return false
})
.map((n) => n._id)
.forEach((n) => toRead.add(n._id))
void inboxClient.readNotifications(client, toRead)
clearTimeout(toReadTimer)
toReadTimer = setTimeout(() => {
const toReadData = Array.from(toRead)
toRead.clear()
void (async () => {
const _client = client.apply(generateId())
await inboxClient.readNotifications(_client, toReadData)
await _client.commit()
})()
}, 500)
}
export async function readChannelMessages (
@ -408,38 +421,42 @@ export async function readChannelMessages (
}
const inboxClient = InboxNotificationsClientImpl.getClient()
const client = getClient()
const readMessages = get(chatReadMessagesStore)
const allIds = getAllIds(messages).filter((id) => !readMessages.has(id))
const client = getClient().apply(generateId())
try {
const readMessages = get(chatReadMessagesStore)
const allIds = getAllIds(messages).filter((id) => !readMessages.has(id))
const notifications = get(inboxClient.activityInboxNotifications)
.filter(({ _id, attachedTo }) => allIds.includes(attachedTo))
.map((n) => n._id)
const notifications = get(inboxClient.activityInboxNotifications)
.filter(({ _id, attachedTo }) => allIds.includes(attachedTo))
.map((n) => n._id)
const relatedMentions = get(inboxClient.otherInboxNotifications)
.filter((n) => !n.isViewed && isMentionNotification(n) && allIds.includes(n.mentionedIn as Ref<ActivityMessage>))
.map((n) => n._id)
const relatedMentions = get(inboxClient.otherInboxNotifications)
.filter((n) => !n.isViewed && isMentionNotification(n) && allIds.includes(n.mentionedIn as Ref<ActivityMessage>))
.map((n) => n._id)
chatReadMessagesStore.update((store) => new Set([...store, ...allIds]))
chatReadMessagesStore.update((store) => new Set([...store, ...allIds]))
void inboxClient.readNotifications(client, [...notifications, ...relatedMentions])
await inboxClient.readNotifications(client, [...notifications, ...relatedMentions])
if (context === undefined) {
return
}
if (context === undefined) {
return
}
const storedTimestampUpdates = get(contextsTimestampStore).get(context._id)
const newTimestamp = messages[messages.length - 1].createdOn ?? 0
const prevTimestamp = Math.max(storedTimestampUpdates ?? 0, context.lastViewedTimestamp ?? 0)
const storedTimestampUpdates = get(contextsTimestampStore).get(context._id)
const newTimestamp = messages[messages.length - 1].createdOn ?? 0
const prevTimestamp = Math.max(storedTimestampUpdates ?? 0, context.lastViewedTimestamp ?? 0)
if (prevTimestamp < newTimestamp) {
context.lastViewedTimestamp = newTimestamp
contextsTimestampStore.update((store) => {
store.set(context._id, newTimestamp)
return store
})
void client.update(context, { lastViewedTimestamp: newTimestamp })
if (prevTimestamp < newTimestamp) {
context.lastViewedTimestamp = newTimestamp
contextsTimestampStore.update((store) => {
store.set(context._id, newTimestamp)
return store
})
await client.update(context, { lastViewedTimestamp: newTimestamp })
}
} finally {
await client.commit()
}
}

View File

@ -15,6 +15,7 @@
<script lang="ts">
import { type Blob, type Ref } from '@hcengineering/core'
import { getBlobRef, type BlobMetadata } from '@hcengineering/presentation'
import { Loading } from '@hcengineering/ui'
export let value: Blob | Ref<Blob>
export let name: string
@ -26,15 +27,25 @@
$: height = metadata?.originalHeight
? `min(${metadata.originalHeight / metadata?.pixelRatio ?? 1}px, ${fit ? '100%' : '80vh'})`
: '100%'
let loading = true
</script>
{#await p then blobRef}
{#if loading}
<div class="flex justify-center">
<Loading />
</div>
{/if}
<img
on:load={(evt) => {
loading = false
}}
class="object-contain mx-auto"
style:max-width={width}
style:max-height={height}
src={blobRef.src}
srcset={blobRef.srcset}
alt={name}
style:height={loading ? '0' : ''}
/>
{/await}

View File

@ -1198,6 +1198,10 @@ export async function restore (
async function sendChunk (doc: Doc | undefined, len: number): Promise<void> {
if (doc !== undefined) {
docsToAdd.delete(doc._id)
if (opt.recheck === true) {
// We need to clear %hash% in case our is wrong.
delete (doc as any)['%hash%']
}
docs.push(doc)
}
sendSize = sendSize + len

View File

@ -15,7 +15,7 @@
//
import { Analytics } from '@hcengineering/analytics'
import { MeasureContext, Blob as PlatformBlob, WorkspaceId, metricsAggregate } from '@hcengineering/core'
import { MeasureContext, Blob as PlatformBlob, WorkspaceId, metricsAggregate, type Ref } from '@hcengineering/core'
import { Token, decodeToken } from '@hcengineering/server-token'
import { StorageAdapter, removeAllObjects } from '@hcengineering/storage'
import bp from 'body-parser'
@ -798,8 +798,22 @@ async function getGeneratePreview (
pipeline.destroy()
// Add support of avif as well.
await config.storageAdapter.put(ctx, payload.workspace, sizeId, dataBuff, contentType, dataBuff.length)
return (await config.storageAdapter.stat(ctx, payload.workspace, sizeId)) ?? blob
const upload = await config.storageAdapter.put(
ctx,
payload.workspace,
sizeId,
dataBuff,
contentType,
dataBuff.length
)
return {
...blob,
_id: sizeId as Ref<PlatformBlob>,
size: dataBuff.length,
contentType,
etag: upload.etag,
storageId: sizeId
}
} catch (err: any) {
Analytics.handleError(err)
ctx.error('failed to resize image', {