Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UBERF-7489: Some more chat optimizations #5999

Merged
merged 1 commit into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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