From f258c6755a5b561f8e75daf2d24fc4d573e6b0a8 Mon Sep 17 00:00:00 2001 From: Kristina Date: Tue, 29 Oct 2024 17:06:16 +0400 Subject: [PATCH] UBERF-8547: Inbox cleanup and other (#7058) Signed-off-by: Kristina Fefelova --- models/notification/src/index.ts | 12 +- models/notification/src/migration.ts | 27 +- models/notification/src/plugin.ts | 7 +- models/workbench/src/migration.ts | 10 + packages/query/src/__tests__/query.test.ts | 35 +- packages/query/src/index.ts | 44 +- .../ui/src/components/ListViewItem.svelte | 4 +- .../components/notifications/Notification.ts | 1 + .../src/components/notifications/actions.ts | 14 +- packages/ui/src/utils.ts | 4 +- .../src/components/Channel.svelte | 2 + .../src/components/ChannelInput.svelte | 3 +- .../src/components/ChannelPanel.svelte | 3 +- .../src/components/ChannelScrollView.svelte | 917 ------------------ .../src/components/ChannelSidebarView.svelte | 1 + .../src/components/ChannelView.svelte | 2 + .../ReverseChannelScrollView.svelte | 13 +- .../components/threads/ThreadContent.svelte | 2 + .../src/components/threads/ThreadView.svelte | 3 +- plugins/chunter-resources/src/navigation.ts | 18 +- plugins/chunter-resources/src/utils.ts | 10 +- plugins/chunter/src/index.ts | 9 +- .../components/EditOrganizationPanel.svelte | 4 +- .../src/components/EditDocPanel.svelte | 4 +- .../src/components/EditDoc.svelte | 4 +- .../src/components/Chats.svelte | 4 +- .../src/components/Main.svelte | 2 +- .../src/components/NewMessage.svelte | 2 +- .../src/components/NewMessages.svelte | 2 +- plugins/notification-resources/package.json | 1 + .../components/BrowserNotificatator.svelte | 71 +- .../components/DocNotifyContextCard.svelte | 19 +- .../src/components/inbox/Inbox.svelte | 46 +- .../inbox/InboxGroupedListView.svelte | 67 +- .../src/inboxNotificationsClient.ts | 20 +- plugins/notification-resources/src/index.ts | 6 +- plugins/notification-resources/src/utils.ts | 32 +- plugins/notification/src/index.ts | 13 +- .../product-version/EditProductVersion.svelte | 4 +- .../src/components/product/EditProduct.svelte | 4 +- .../src/components/EditVacancy.svelte | 4 +- .../src/components/Chat.svelte | 4 +- .../components/issues/edit/EditIssue.svelte | 4 +- .../templates/EditIssueTemplate.svelte | 4 +- .../src/components/EditDoc.svelte | 4 +- .../src/components/Workbench.svelte | 4 +- plugins/workbench-resources/src/index.ts | 5 +- plugins/workbench-resources/src/sidebar.ts | 23 +- plugins/workbench-resources/src/workbench.ts | 4 +- plugins/workbench/src/index.ts | 7 +- .../notification-resources/src/index.ts | 2 - tests/sanity/tests/chat/chat.spec.ts | 2 - tests/sanity/tests/recruiting/talents.spec.ts | 4 +- 53 files changed, 397 insertions(+), 1120 deletions(-) delete mode 100644 plugins/chunter-resources/src/components/ChannelScrollView.svelte diff --git a/models/notification/src/index.ts b/models/notification/src/index.ts index 7a7026d5b4..3c2116987c 100644 --- a/models/notification/src/index.ts +++ b/models/notification/src/index.ts @@ -33,7 +33,8 @@ import { type Space, type Timestamp, type Tx, - type TxCUD + type TxCUD, + DOMAIN_TRANSIENT } from '@hcengineering/core' import { ArrOf, @@ -75,7 +76,6 @@ import { type NotificationProvider, type NotificationProviderDefaults, type NotificationProviderSetting, - type NotificationStatus, type NotificationTemplate, type NotificationType, type NotificationTypeSetting, @@ -92,7 +92,7 @@ export { notificationId, DOMAIN_USER_NOTIFY, DOMAIN_NOTIFICATION, DOMAIN_DOC_NOT export { notificationOperation } from './migration' export { notification as default } -@Model(notification.class.BrowserNotification, core.class.Doc, DOMAIN_USER_NOTIFY) +@Model(notification.class.BrowserNotification, core.class.Doc, DOMAIN_TRANSIENT) export class TBrowserNotification extends TDoc implements BrowserNotification { senderId?: Ref | undefined tag!: Ref> @@ -100,7 +100,6 @@ export class TBrowserNotification extends TDoc implements BrowserNotification { body!: string onClickLocation?: Location | undefined user!: Ref - status!: NotificationStatus messageId?: Ref messageClass?: Ref> objectId!: Ref @@ -368,6 +367,10 @@ export function createModel (builder: Builder): void { TNotificationProviderDefaults ) + builder.mixin(notification.class.BrowserNotification, core.class.Class, core.mixin.TransientConfiguration, { + broadcastOnly: true + }) + builder.createDoc( setting.class.SettingsCategory, core.space.Model, @@ -389,6 +392,7 @@ export function createModel (builder: Builder): void { { label: notification.string.Inbox, icon: notification.icon.Notifications, + locationDataResolver: notification.function.LocationDataResolver, alias: notificationId, hidden: true, locationResolver: notification.resolver.Location, diff --git a/models/notification/src/migration.ts b/models/notification/src/migration.ts index 19405eaf68..19bc06698e 100644 --- a/models/notification/src/migration.ts +++ b/models/notification/src/migration.ts @@ -23,7 +23,6 @@ import { } from '@hcengineering/model' import notification, { notificationId, - NotificationStatus, type BrowserNotification, type DocNotifyContext, type InboxNotification @@ -401,7 +400,7 @@ export const notificationOperation: MigrateOperation = { } }, { - state: 'remove-update-txes-docnotify-ctx', + state: 'remove-update-txes-docnotify-ctx-v2', func: async (client) => { await client.deleteMany(DOMAIN_TX, { _class: core.class.TxUpdateDoc, @@ -410,14 +409,28 @@ export const notificationOperation: MigrateOperation = { $exists: true } }) + await client.deleteMany(DOMAIN_TX, { + _class: core.class.TxUpdateDoc, + objectClass: notification.class.DocNotifyContext, + 'operations.lastUpdateTimestamp': { + $exists: true + } + }) + } + }, + { + state: 'remove-browser-notification-v2', + func: async (client) => { + await client.deleteMany(DOMAIN_USER_NOTIFY, { + _class: notification.class.BrowserNotification + }) + + await client.deleteMany(DOMAIN_TX, { + objectClass: notification.class.BrowserNotification + }) } } ]) - - await client.deleteMany(DOMAIN_USER_NOTIFY, { - _class: notification.class.BrowserNotification, - status: NotificationStatus.Notified - }) }, async upgrade (state: Map>, client: () => Promise): Promise {} } diff --git a/models/notification/src/plugin.ts b/models/notification/src/plugin.ts index 7ff1fc5261..3f81a3eeab 100644 --- a/models/notification/src/plugin.ts +++ b/models/notification/src/plugin.ts @@ -17,9 +17,9 @@ import { type Doc, type Ref } from '@hcengineering/core' import notification, { notificationId } from '@hcengineering/notification' import { type IntlString, type Resource, mergeIds } from '@hcengineering/platform' -import { type AnyComponent } from '@hcengineering/ui/src/types' +import { type AnyComponent, type Location } from '@hcengineering/ui/src/types' import { type Action, type ActionCategory, type ViewAction } from '@hcengineering/view' -import { type Application } from '@hcengineering/workbench' +import { type Application, type LocationData } from '@hcengineering/workbench' import { type DocUpdateMessageViewlet } from '@hcengineering/activity' export default mergeIds(notificationId, notification, { @@ -53,7 +53,8 @@ export default mergeIds(notificationId, notification, { HasDocNotifyContextPinAction: '' as Resource<(doc?: Doc | Doc[]) => Promise>, HasDocNotifyContextUnpinAction: '' as Resource<(doc?: Doc | Doc[]) => Promise>, CanReadNotifyContext: '' as Resource<(doc?: Doc | Doc[]) => Promise>, - CanUnReadNotifyContext: '' as Resource<(doc?: Doc | Doc[]) => Promise> + CanUnReadNotifyContext: '' as Resource<(doc?: Doc | Doc[]) => Promise>, + LocationDataResolver: '' as Resource<(loc: Location) => Promise> }, category: { Notification: '' as Ref diff --git a/models/workbench/src/migration.ts b/models/workbench/src/migration.ts index d4fb8fdaf3..f938e35b9f 100644 --- a/models/workbench/src/migration.ts +++ b/models/workbench/src/migration.ts @@ -20,6 +20,7 @@ import { } from '@hcengineering/model' import { DOMAIN_PREFERENCE } from '@hcengineering/preference' import workbench from '@hcengineering/workbench' +import core, { DOMAIN_TX } from '@hcengineering/core' import { workbenchId } from '.' @@ -33,6 +34,15 @@ export const workbenchOperation: MigrateOperation = { { state: 'remove-wrong-tabs-v1', func: removeTabs + }, + { + state: 'remove-txes-update-tabs-v1', + func: async () => { + await client.deleteMany(DOMAIN_TX, { + objectClass: workbench.class.WorkbenchTab, + _class: core.class.TxUpdateDoc + }) + } } ]) }, diff --git a/packages/query/src/__tests__/query.test.ts b/packages/query/src/__tests__/query.test.ts index b42e4a845c..89a05facfb 100644 --- a/packages/query/src/__tests__/query.test.ts +++ b/packages/query/src/__tests__/query.test.ts @@ -475,7 +475,6 @@ describe('query', () => { message: 'child' } ) - let attempt = 0 const pp = new Promise((resolve) => { liveQuery.query( test.class.TestComment, @@ -483,14 +482,10 @@ describe('query', () => { (result) => { const comment = result[0] if (comment !== undefined) { - if (attempt++ > 0) { - expect((comment.$lookup?.attachedTo as WithLookup)?.$lookup?.space?._id).toEqual( - futureSpace._id - ) - resolve(null) - } else { - expect((comment.$lookup?.attachedTo as WithLookup)?.$lookup?.space).toBeUndefined() - } + expect((comment.$lookup?.attachedTo as WithLookup)?.$lookup?.space?._id).toEqual( + futureSpace._id + ) + resolve(null) } }, { lookup: { attachedTo: [test.class.TestComment, { space: core.class.Space }] } } @@ -521,7 +516,6 @@ describe('query', () => { message: 'test' } ) - let attempt = 0 const childLength = 3 const pp = new Promise((resolve) => { liveQuery.query( @@ -529,10 +523,13 @@ describe('query', () => { { _id: parentComment }, (result) => { const comment = result[0] - if (comment !== undefined) { - expect((comment.$lookup as any)?.comments).toHaveLength(attempt++) + const res = (comment.$lookup as any)?.comments?.length + + if (res !== undefined) { + expect(res).toBeGreaterThanOrEqual(1) + expect(res).toBeLessThanOrEqual(childLength) } - if (attempt === childLength) { + if ((res ?? 0) === childLength) { resolve(null) } }, @@ -628,23 +625,15 @@ describe('query', () => { message: 'child' } ) - let attempt = -1 const pp = new Promise((resolve) => { liveQuery.query( test.class.TestComment, { _id: childComment }, (result) => { - attempt++ const comment = result[0] if (comment !== undefined) { - if (attempt > 0) { - expect((comment.$lookup?.attachedTo as WithLookup)?.$lookup?.space).toBeUndefined() - resolve(null) - } else { - expect( - ((comment.$lookup?.attachedTo as WithLookup)?.$lookup?.space as Doc)?._id - ).toEqual(futureSpace) - } + expect((comment.$lookup?.attachedTo as WithLookup)?.$lookup?.space).toBeUndefined() + resolve(null) } }, { lookup: { attachedTo: [test.class.TestComment, { space: core.class.Space }] } } diff --git a/packages/query/src/index.ts b/packages/query/src/index.ts index 9291058254..c013743957 100644 --- a/packages/query/src/index.ts +++ b/packages/query/src/index.ts @@ -95,6 +95,8 @@ export class LiveQuery implements WithTx, Client { private queryCounter: number = 0 private closed: boolean = false + private readonly queriesToUpdate = new Map() + // A map of _class to documents. private readonly documentRefs = new Map, DocumentRef>>() @@ -773,7 +775,7 @@ export class LiveQuery implements WithTx, Client { if (q.options?.sort !== undefined) { await resultSort(q.result, q.options?.sort, q._class, this.getHierarchy(), this.client.getModel()) } - await this.callback(q) + await this.callback(q, true) } } @@ -846,6 +848,7 @@ export class LiveQuery implements WithTx, Client { } private async refresh (q: Query): Promise { + this.queriesToUpdate.delete(q.id) await q.refresh() } @@ -1040,10 +1043,10 @@ export class LiveQuery implements WithTx, Client { if (q.options?.limit !== undefined && q.result.length > q.options.limit) { if (q.result.pop()?._id !== doc._id || q.options?.total === true) { - await this.callback(q) + await this.callback(q, true) } } else { - await this.callback(q) + await this.callback(q, true) } } } @@ -1051,7 +1054,7 @@ export class LiveQuery implements WithTx, Client { await this.handleDocAddLookup(q, doc) } - private async callback (q: Query): Promise { + private async callback (q: Query, bulkUpdate = false): Promise { if (q.result instanceof Promise) { q.result = await q.result } @@ -1059,9 +1062,15 @@ export class LiveQuery implements WithTx, Client { this.updateDocuments(q, q.result) const result = q.result - Array.from(q.callbacks.values()).forEach((callback) => { - callback(toFindResult(this.clone(result), q.total)) - }) + + if (bulkUpdate) { + this.queriesToUpdate.set(q.id, [q, result]) + } else { + this.queriesToUpdate.delete(q.id) + Array.from(q.callbacks.values()).forEach((callback) => { + callback(toFindResult(this.clone(result), q.total)) + }) + } } private updateDocuments (q: Query, docs: Doc[], clean: boolean = false): void { @@ -1106,7 +1115,7 @@ export class LiveQuery implements WithTx, Client { if (q.options?.sort !== undefined) { await resultSort(q.result, q.options?.sort, q._class, this.getHierarchy(), this.getModel()) } - await this.callback(q) + await this.callback(q, true) } } @@ -1181,7 +1190,7 @@ export class LiveQuery implements WithTx, Client { if (q.options?.total === true) { q.total-- } - await this.callback(q) + await this.callback(q, true) } await this.handleDocRemoveLookup(q, tx) } @@ -1220,7 +1229,7 @@ export class LiveQuery implements WithTx, Client { if (q.options?.sort !== undefined) { await resultSort(q.result, q.options?.sort, q._class, this.getHierarchy(), this.getModel()) } - await this.callback(q) + await this.callback(q, true) } } @@ -1294,6 +1303,17 @@ export class LiveQuery implements WithTx, Client { } result.push(await this._tx(tx, docCache)) } + + if (this.queriesToUpdate.size > 0) { + const copy = new Map(this.queriesToUpdate) + this.queriesToUpdate.clear() + + for (const [q, res] of copy.values()) { + Array.from(q.callbacks.values()).forEach((callback) => { + callback(toFindResult(this.clone(res), q.total)) + }) + } + } return result } @@ -1533,10 +1553,10 @@ export class LiveQuery implements WithTx, Client { return } if (q.result.pop()?._id !== updatedDoc._id) { - await this.callback(q) + await this.callback(q, true) } } else { - await this.callback(q) + await this.callback(q, true) } } } diff --git a/packages/ui/src/components/ListViewItem.svelte b/packages/ui/src/components/ListViewItem.svelte index 730e8f0df7..f7d4fa495f 100644 --- a/packages/ui/src/components/ListViewItem.svelte +++ b/packages/ui/src/components/ListViewItem.svelte @@ -59,11 +59,11 @@ margin: 0; } - &.default.highlighted:hover { + &.default.highlighted:hover:not(.highlighted) { background-color: var(--theme-popup-divider); } - &.lumia.highlighted:hover { + &.lumia.highlighted:hover:not(.highlighted) { background-color: var(--global-ui-highlight-BackgroundColor); } diff --git a/packages/ui/src/components/notifications/Notification.ts b/packages/ui/src/components/notifications/Notification.ts index 0502e99bac..01c72a6a4d 100644 --- a/packages/ui/src/components/notifications/Notification.ts +++ b/packages/ui/src/components/notifications/Notification.ts @@ -5,6 +5,7 @@ import { type AnyComponent, type AnySvelteComponent } from '../../types' export interface Notification { id: string title: string + group?: string component: AnyComponent | AnySvelteComponent subTitle?: string subTitlePostfix?: string diff --git a/packages/ui/src/components/notifications/actions.ts b/packages/ui/src/components/notifications/actions.ts index 1a94ac71af..c50a0e5745 100644 --- a/packages/ui/src/components/notifications/actions.ts +++ b/packages/ui/src/components/notifications/actions.ts @@ -18,11 +18,19 @@ export const addNotification = (notification: Notification, store: Writable - [NotificationPosition.TopRight, NotificationPosition.TopLeft].includes(newNotification.position) + update((notifications: Notification[]) => { + if ( + notification.group != null && + notification.group !== '' && + notifications.some(({ group }) => group === notification.group) + ) { + return notifications.map((n) => (n.group === notification.group ? newNotification : n)) + } + + return [NotificationPosition.TopRight, NotificationPosition.TopLeft].includes(newNotification.position) ? [newNotification, ...notifications] : [...notifications, newNotification] - ) + }) } export const removeNotification = (notificationId: string, { update }: Writable): void => { diff --git a/packages/ui/src/utils.ts b/packages/ui/src/utils.ts index e4e75de87e..60c88455e2 100644 --- a/packages/ui/src/utils.ts +++ b/packages/ui/src/utils.ts @@ -103,7 +103,8 @@ export function addNotification ( subTitle: string, component: AnyComponent | AnySvelteComponent, params?: Record, - severity: NotificationSeverity = NotificationSeverity.Success + severity: NotificationSeverity = NotificationSeverity.Success, + group?: string ): void { const closeTimeout = parseInt(localStorage.getItem('#platform.notification.timeout') ?? '10000') const notification: Notification = { @@ -111,6 +112,7 @@ export function addNotification ( title, subTitle, severity, + group, position: NotificationPosition.BottomLeft, component, closeTimeout, diff --git a/plugins/chunter-resources/src/components/Channel.svelte b/plugins/chunter-resources/src/components/Channel.svelte index 3273498461..f74293defd 100644 --- a/plugins/chunter-resources/src/components/Channel.svelte +++ b/plugins/chunter-resources/src/components/Channel.svelte @@ -30,6 +30,7 @@ export let filters: Ref[] = [] export let isAsideOpened = false export let syncLocation = true + export let autofocus = true export let freeze = false export let selectedMessageId: Ref | undefined = undefined @@ -108,6 +109,7 @@ {collection} provider={dataProvider} {freeze} + {autofocus} loadMoreAllowed={!isDocChannel} /> {/if} diff --git a/plugins/chunter-resources/src/components/ChannelInput.svelte b/plugins/chunter-resources/src/components/ChannelInput.svelte index e4fbfa8503..bd928a1863 100644 --- a/plugins/chunter-resources/src/components/ChannelInput.svelte +++ b/plugins/chunter-resources/src/components/ChannelInput.svelte @@ -29,6 +29,7 @@ export let boundary: HTMLElement | undefined | null = undefined export let collection: string | undefined export let isThread = false + export let autofocus = true const client = getClient() const hierarchy = client.getHierarchy() @@ -68,7 +69,7 @@ {:else} diff --git a/plugins/chunter-resources/src/components/ChannelPanel.svelte b/plugins/chunter-resources/src/components/ChannelPanel.svelte index 1a30291bb1..16cc0a8c52 100644 --- a/plugins/chunter-resources/src/components/ChannelPanel.svelte +++ b/plugins/chunter-resources/src/components/ChannelPanel.svelte @@ -22,6 +22,7 @@ export let _id: Ref export let _class: Ref> + export let autofocus = true const objectQuery = createQuery() const inboxClient = InboxNotificationsClientImpl.getClient() @@ -37,5 +38,5 @@ {#if object} - + {/if} diff --git a/plugins/chunter-resources/src/components/ChannelScrollView.svelte b/plugins/chunter-resources/src/components/ChannelScrollView.svelte deleted file mode 100644 index d606e645e4..0000000000 --- a/plugins/chunter-resources/src/components/ChannelScrollView.svelte +++ /dev/null @@ -1,917 +0,0 @@ - - - -{#if isLoading} - -{:else} -
- {#if startFromBottom} -
- {/if} - {#if !embedded && displayMessages.length > 0 && selectedDate} -
- -
- {/if} - {#if isInitialScrolling} -
- -
- {/if} - - {#if loadMoreAllowed && !embedded} - - {/if} - - - {#if displayMessages.length === 0 && !embedded && !readonly} - - {/if} - {#each displayMessages as message, index (message._id)} - {@const isSelected = message._id === selectedMessageId} - {@const canGroup = canGroupChatMessages(message, displayMessages[index - 1])} - - {#if separatorIndex === index} - - {/if} - - {#if !embedded && message.createdOn && $datesStore.includes(message.createdOn)} - - {/if} - - - {/each} - - {#if !fixedInput} - - {/if} - - {#if loadMoreAllowed && $canLoadNextForwardStore} - - {/if} - - - {#if !embedded && showScrollDownButton} -
- -
- {/if} -
- {#if fixedInput} - - {/if} -{/if} - - diff --git a/plugins/chunter-resources/src/components/ChannelSidebarView.svelte b/plugins/chunter-resources/src/components/ChannelSidebarView.svelte index 675b4966cf..0c71f5cf72 100644 --- a/plugins/chunter-resources/src/components/ChannelSidebarView.svelte +++ b/plugins/chunter-resources/src/components/ChannelSidebarView.svelte @@ -97,6 +97,7 @@ {#if threadId && visible}
{/if} diff --git a/plugins/chunter-resources/src/components/ReverseChannelScrollView.svelte b/plugins/chunter-resources/src/components/ReverseChannelScrollView.svelte index 027478ef15..56e48eed3f 100644 --- a/plugins/chunter-resources/src/components/ReverseChannelScrollView.svelte +++ b/plugins/chunter-resources/src/components/ReverseChannelScrollView.svelte @@ -52,6 +52,7 @@ export let fullHeight = true export let freeze = false export let loadMoreAllowed = true + export let autofocus = true const minMsgHeightRem = 2 const loadMoreThreshold = 200 @@ -400,9 +401,7 @@ scrollToBottom() } - const op = client.apply(undefined, 'chunter.scrollDown') - await inboxClient.readDoc(op, doc._id) - await op.commit() + await inboxClient.readDoc(doc._id) } let forceRead = false @@ -419,9 +418,7 @@ if (unViewed.length === 0) { forceRead = true - const op = client.apply(undefined, 'chunter.forceReadContext', true) - await inboxClient.readDoc(op, object._id) - await op.commit() + await inboxClient.readDoc(object._id) } } @@ -642,7 +639,7 @@ {/if} {#if !fixedInput} - + {/if} {#if !isThread && isLatestMessageButtonVisible} @@ -659,7 +656,7 @@
{#if fixedInput} - + {/if}