Skip to content

Commit

Permalink
UBERF-7489: Some more chat optimizations (#5999)
Browse files Browse the repository at this point in the history
  • Loading branch information
haiodo authored Jul 4, 2024
1 parent 29f3198 commit 630ae21
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 63 deletions.
30 changes: 18 additions & 12 deletions plugins/chunter-resources/src/components/ChannelScrollView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -313,14 +313,16 @@
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
}
const containerRect = scrollElement.getBoundingClientRect()
const messagesToRead: DisplayActivityMessage[] = []
const messagesElements = scrollContentBox?.getElementsByClassName('activityMessage')
for (const message of displayMessages) {
Expand All @@ -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 {
Expand Down
113 changes: 65 additions & 48 deletions plugins/chunter-resources/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,44 +12,45 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
import activity, {
type ActivityMessage,
type ActivityMessagesFilter,
type DisplayActivityMessage,
type DisplayDocUpdateMessage,
type DocUpdateMessage
} from '@hcengineering/activity'
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 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,
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,
type DisplayActivityMessage,
type DisplayDocUpdateMessage,
type DocUpdateMessage
} from '@hcengineering/activity'
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) {
Expand Down Expand Up @@ -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()
Expand All @@ -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
Expand All @@ -394,9 +398,18 @@ export function recheckNotifications (context: DocNotifyContext): void {

return false
})
.map((n) => n._id)

void inboxClient.readNotifications(client, toRead)
.forEach((n) => toRead.add(n._id))

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 (
Expand All @@ -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
}

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 (context === undefined) {
return
}

if (prevTimestamp < newTimestamp) {
context.lastViewedTimestamp = newTimestamp
contextsTimestampStore.update((store) => {
store.set(context._id, newTimestamp)
return store
})
void client.update(context, { lastViewedTimestamp: newTimestamp })
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
})
await client.update(context, { lastViewedTimestamp: newTimestamp })
}
} finally {
await client.commit()
}
}

Expand Down
11 changes: 11 additions & 0 deletions plugins/view-resources/src/components/viewer/ImageViewer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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}
4 changes: 4 additions & 0 deletions server/backup/src/backup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 17 additions & 3 deletions server/front/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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', {
Expand Down

0 comments on commit 630ae21

Please sign in to comment.