UBERF-5476: Fix archive in inbox (#4618)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2024-02-14 23:42:16 +07:00 committed by GitHub
parent beab7928e0
commit 715193957b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 175 additions and 114 deletions

View File

@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import { Class, Doc, getCurrentAccount, isOtherDay, Ref, Timestamp } from '@hcengineering/core'
import { Class, Doc, generateId, getCurrentAccount, isOtherDay, Ref, Timestamp } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import activity, {
ActivityExtension,
@ -239,8 +239,10 @@
return [message._id, ...(combined ?? [])]
})
.flat()
inboxClient.readMessages(allIds)
const ops = getClient().apply(generateId())
inboxClient.readMessages(ops, allIds).then(() => {
void ops.commit()
})
if (notifyContext === undefined) {
return

View File

@ -62,7 +62,7 @@
{ attachedTo: channelId },
(res) => {
plainMessages = res
inboxClient.readDoc(channelId)
inboxClient.readDoc(getClient(), channelId)
},
{ sort: { sendOn: SortingOrder.Descending } }
)
@ -84,7 +84,7 @@
messages: convertMessages(object, channel, selectedMessages, $personAccountByIdStore, $employeeByIdStore)
}
)
await inboxClient.readDoc(channel._id)
await inboxClient.readDoc(getClient(), channel._id)
clear()
}

View File

@ -47,7 +47,7 @@
let integrations: Integration[] = []
let selectedIntegration: Integration | undefined = undefined
inboxClient.forceReadDoc(channel._id, channel._class)
inboxClient.forceReadDoc(getClient(), channel._id, channel._class)
const dispatch = createEventDispatcher()

View File

@ -77,7 +77,7 @@
},
objectId
)
await inboxClient.forceReadDoc(channel._id, channel._class)
await inboxClient.forceReadDoc(getClient(), channel._id, channel._class)
objectId = generateId()
dispatch('close')
}

View File

@ -91,7 +91,7 @@
.map((m) => m.trim())
.filter((m) => m.length)
})
await inboxClient.forceReadDoc(channel._id, channel._class)
await inboxClient.forceReadDoc(getClient(), channel._id, channel._class)
for (const attachment of attachments) {
await client.addCollection(
attachmentP.class.Attachment,

View File

@ -213,13 +213,21 @@
const contextNotifications = $notificationsByContextStore.get(selectedContext._id) ?? []
await inboxClient.readNotifications(
contextNotifications
.filter(({ _class, isViewed }) =>
isChunterChannel ? _class === notification.class.CommonInboxNotification : !isViewed
)
.map(({ _id }) => _id)
)
const doneOp = await getClient().measure('readNotifications')
const ops = getClient().apply(selectedContext._id)
try {
await inboxClient.readNotifications(
ops,
contextNotifications
.filter(({ _class, isViewed }) =>
isChunterChannel ? _class === notification.class.CommonInboxNotification : !isViewed
)
.map(({ _id }) => _id)
)
} finally {
await ops.commit()
await doneOp()
}
}
function filterNotifications (

View File

@ -78,7 +78,7 @@
const context = $notifyContextsStore.find(({ _id }) => _id === displayData[listSelection]?.[0])
deleteContextNotifications(context)
void deleteContextNotifications(context)
}
if (key.code === 'Enter') {
key.preventDefault()

View File

@ -13,7 +13,15 @@
// limitations under the License.
//
import activity, { type ActivityMessage } from '@hcengineering/activity'
import { SortingOrder, getCurrentAccount, type Class, type Doc, type Ref, type WithLookup } from '@hcengineering/core'
import {
SortingOrder,
getCurrentAccount,
type Class,
type Doc,
type Ref,
type TxOperations,
type WithLookup
} from '@hcengineering/core'
import notification, {
type ActivityInboxNotification,
type Collaborators,
@ -21,7 +29,7 @@ import notification, {
type InboxNotification,
type InboxNotificationsClient
} from '@hcengineering/notification'
import { createQuery, getClient } from '@hcengineering/presentation'
import { createQuery } from '@hcengineering/presentation'
import { derived, get, writable } from 'svelte/store'
export const inboxMessagesStore = writable<ActivityMessage[]>([])
@ -132,30 +140,28 @@ export class InboxNotificationsClientImpl implements InboxNotificationsClient {
return InboxNotificationsClientImpl._instance
}
async readDoc (_id: Ref<Doc>): Promise<void> {
const client = getClient()
async readDoc (client: TxOperations, _id: Ref<Doc>): Promise<void> {
const docNotifyContext = this._docNotifyContextByDoc.get(_id)
if (docNotifyContext === undefined) {
return
}
const inboxNotifications = get(this.inboxNotifications).filter(
const inboxNotifications = (get(this.inboxNotifications) ?? []).filter(
(notification) => notification.docNotifyContext === docNotifyContext._id && !notification.isViewed
)
await Promise.all([
...inboxNotifications.map(async (notification) => await client.update(notification, { isViewed: true })),
client.update(docNotifyContext, { lastViewedTimestamp: Date.now() })
])
for (const notification of inboxNotifications) {
await client.update(notification, { isViewed: true })
}
await client.update(docNotifyContext, { lastViewedTimestamp: Date.now() })
}
async forceReadDoc (_id: Ref<Doc>, _class: Ref<Class<Doc>>): Promise<void> {
const client = getClient()
async forceReadDoc (client: TxOperations, _id: Ref<Doc>, _class: Ref<Class<Doc>>): Promise<void> {
const context = this._docNotifyContextByDoc.get(_id)
if (context !== undefined) {
await this.readDoc(_id)
await this.readDoc(client, _id)
return
}
@ -200,40 +206,38 @@ export class InboxNotificationsClientImpl implements InboxNotificationsClient {
})
}
async readMessages (ids: Array<Ref<ActivityMessage>>): Promise<void> {
const client = getClient()
async readMessages (client: TxOperations, ids: Array<Ref<ActivityMessage>>): Promise<void> {
const notificationsToRead = await client.findAll(notification.class.ActivityInboxNotification, {
user: getCurrentAccount()._id,
attachedTo: { $in: ids },
isViewed: { $ne: true }
})
await Promise.all(
notificationsToRead.map(async (notification) => await client.update(notification, { isViewed: true }))
)
for (const notification of notificationsToRead) {
await client.update(notification, { isViewed: true })
}
}
async readNotifications (ids: Array<Ref<InboxNotification>>): Promise<void> {
const client = getClient()
const notificationsToRead = get(this.inboxNotifications).filter(({ _id }) => ids.includes(_id))
async readNotifications (client: TxOperations, ids: Array<Ref<InboxNotification>>): Promise<void> {
const notificationsToRead = (get(this.inboxNotifications) ?? []).filter(({ _id }) => ids.includes(_id))
await Promise.all(
notificationsToRead.map(async (notification) => await client.update(notification, { isViewed: true }))
)
for (const notification of notificationsToRead) {
await client.update(notification, { isViewed: true })
}
}
async unreadNotifications (ids: Array<Ref<InboxNotification>>): Promise<void> {
const client = getClient()
const notificationsToUnread = get(this.inboxNotifications).filter(({ _id }) => ids.includes(_id))
async unreadNotifications (client: TxOperations, ids: Array<Ref<InboxNotification>>): Promise<void> {
const notificationsToUnread = (get(this.inboxNotifications) ?? []).filter(({ _id }) => ids.includes(_id))
await Promise.all(
notificationsToUnread.map(async (notification) => await client.update(notification, { isViewed: false }))
)
for (const notification of notificationsToUnread) {
await client.update(notification, { isViewed: false })
}
}
async deleteNotifications (ids: Array<Ref<InboxNotification>>): Promise<void> {
const client = getClient()
const inboxNotifications = get(this.inboxNotifications).filter(({ _id }) => ids.includes(_id))
await Promise.all(inboxNotifications.map(async (notification) => await client.remove(notification)))
async deleteNotifications (client: TxOperations, ids: Array<Ref<InboxNotification>>): Promise<void> {
const inboxNotifications = (get(this.inboxNotifications) ?? []).filter(({ _id }) => ids.includes(_id))
for (const notification of inboxNotifications) {
await client.remove(notification)
}
}
}

View File

@ -13,37 +13,37 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
import { get } from 'svelte/store'
import {
type Class,
type Doc,
type DocumentUpdate,
getCurrentAccount,
type Ref,
SortingOrder,
type TxOperations,
type WithLookup
} from '@hcengineering/core'
import notification, {
type ActivityInboxNotification,
type Collaborators,
type DisplayActivityInboxNotification,
type DisplayInboxNotification,
type DocNotifyContext,
inboxId,
type InboxNotification
} from '@hcengineering/notification'
import { getClient } from '@hcengineering/presentation'
import { getLocation, navigate, type Location, type ResolvedLocation } from '@hcengineering/ui'
import activity, {
type ActivityMessage,
type DisplayDocUpdateMessage,
type DocUpdateMessage
} from '@hcengineering/activity'
import { activityMessagesComparator, combineActivityMessages } from '@hcengineering/activity-resources'
import {
SortingOrder,
getCurrentAccount,
type Class,
type Doc,
type DocumentUpdate,
type Ref,
type TxOperations,
type WithLookup
} from '@hcengineering/core'
import notification, {
inboxId,
type ActivityInboxNotification,
type Collaborators,
type DisplayActivityInboxNotification,
type DisplayInboxNotification,
type DocNotifyContext,
type InboxNotification
} from '@hcengineering/notification'
import { getClient } from '@hcengineering/presentation'
import { getLocation, navigate, type Location, type ResolvedLocation } from '@hcengineering/ui'
import { get } from 'svelte/store'
import { type InboxNotificationsFilter } from './types'
import { InboxNotificationsClientImpl } from './inboxNotificationsClient'
import { type InboxNotificationsFilter } from './types'
/**
* @public
@ -77,13 +77,20 @@ export async function markAsReadInboxNotification (doc: DisplayInboxNotification
const notificationsClient = InboxNotificationsClientImpl.getClient()
const isActivityNotification = doc._class === notification.class.ActivityInboxNotification
const ids = isActivityNotification ? (doc as DisplayActivityInboxNotification).combinedIds : [doc._id]
const ids = (isActivityNotification ? (doc as DisplayActivityInboxNotification).combinedIds : [doc._id]) ?? []
if (isActivityNotification) {
await updateLastViewedTimestampOnRead(doc as WithLookup<ActivityInboxNotification>, ids)
}
await notificationsClient.readNotifications(ids)
const doneOp = await getClient().measure('markAsRead')
const ops = getClient().apply(doc._id)
try {
await notificationsClient.readNotifications(ops, ids)
} finally {
await ops.commit()
await doneOp()
}
}
async function updateLastViewedTimestampOnRead (
@ -158,7 +165,14 @@ export async function markAsUnreadInboxNotification (doc: DisplayInboxNotificati
await updateLastViewedOnUnread(doc as WithLookup<ActivityInboxNotification>)
}
await inboxNotificationsClient.unreadNotifications(ids)
const doneOp = await getClient().measure('unreadNotifications')
const ops = getClient().apply(doc._id)
try {
await inboxNotificationsClient.unreadNotifications(ops, ids)
} finally {
await ops.commit()
await doneOp()
}
}
export async function deleteInboxNotification (doc: DisplayInboxNotification): Promise<void> {
@ -171,7 +185,14 @@ export async function deleteInboxNotification (doc: DisplayInboxNotification): P
await updateLastViewedTimestampOnRead(doc as WithLookup<ActivityInboxNotification>, ids)
}
await inboxNotificationsClient.deleteNotifications(ids)
const doneOp = await getClient().measure('deleteNotifications')
const ops = getClient().apply(doc._id)
try {
await inboxNotificationsClient.deleteNotifications(ops, ids)
} finally {
await ops.commit()
await doneOp()
}
}
export async function hasDocNotifyContextPinAction (docNotifyContext: DocNotifyContext): Promise<boolean> {
@ -235,19 +256,27 @@ export async function canUnReadNotifyContext (doc: DocNotifyContext): Promise<bo
* @public
*/
export async function readNotifyContext (doc: DocNotifyContext): Promise<void> {
const client = getClient()
const inboxClient = InboxNotificationsClientImpl.getClient()
const inboxNotifications = get(inboxClient.inboxNotificationsByContext).get(doc._id) ?? []
await inboxClient.readNotifications(inboxNotifications.map(({ _id }) => _id))
await client.update(doc, { lastViewedTimestamp: Date.now() })
const doneOp = await getClient().measure('readNotifyContext')
const ops = getClient().apply(doc._id)
try {
await inboxClient.readNotifications(
ops,
inboxNotifications.map(({ _id }) => _id)
)
await ops.update(doc, { lastViewedTimestamp: Date.now() })
} finally {
await ops.commit()
await doneOp()
}
}
/**
* @public
*/
export async function unReadNotifyContext (doc: DocNotifyContext): Promise<void> {
const client = getClient()
const inboxClient = InboxNotificationsClientImpl.getClient()
const inboxNotifications = get(inboxClient.inboxNotificationsByContext).get(doc._id) ?? []
const notificationToUnread = inboxNotifications[0]
@ -256,17 +285,25 @@ export async function unReadNotifyContext (doc: DocNotifyContext): Promise<void>
return
}
await inboxClient.unreadNotifications([notificationToUnread._id])
const doneOp = await getClient().measure('unReadNotifyContext')
const ops = getClient().apply(doc._id)
if (notificationToUnread._class === notification.class.ActivityInboxNotification) {
const activityNotification = notificationToUnread as WithLookup<ActivityInboxNotification>
const createdOn = activityNotification?.$lookup?.attachedTo?.createdOn
try {
await inboxClient.unreadNotifications(ops, [notificationToUnread._id])
if (createdOn === undefined || createdOn === 0) {
return
if (notificationToUnread._class === notification.class.ActivityInboxNotification) {
const activityNotification = notificationToUnread as WithLookup<ActivityInboxNotification>
const createdOn = activityNotification?.$lookup?.attachedTo?.createdOn
if (createdOn === undefined || createdOn === 0) {
return
}
await ops.diffUpdate(doc, { lastViewedTimestamp: createdOn - 1 })
}
await client.diffUpdate(doc, { lastViewedTimestamp: createdOn - 1 })
} finally {
await ops.commit()
await doneOp()
}
}
@ -278,12 +315,21 @@ export async function deleteContextNotifications (doc?: DocNotifyContext): Promi
return
}
const client = getClient()
const inboxClient = InboxNotificationsClientImpl.getClient()
const inboxNotifications = get(inboxClient.inboxNotificationsByContext).get(doc._id) ?? []
await inboxClient.deleteNotifications(inboxNotifications.map(({ _id }) => _id))
await client.update(doc, { lastViewedTimestamp: Date.now() })
const doneOp = await getClient().measure('deleteContextNotifications')
const ops = getClient().apply(doc._id)
try {
await inboxClient.deleteNotifications(
ops,
inboxNotifications.map(({ _id }) => _id)
)
await ops.update(doc, { lastViewedTimestamp: Date.now() })
} finally {
await ops.commit()
await doneOp()
}
}
enum OpWithMe {

View File

@ -13,6 +13,7 @@
// limitations under the License.
//
import { ActivityMessage } from '@hcengineering/activity'
import {
Account,
AnyAttribute,
@ -25,16 +26,16 @@ import {
Space,
Timestamp,
Tx,
TxCUD
TxCUD,
TxOperations
} from '@hcengineering/core'
import type { Asset, IntlString, Plugin, Resource } from '@hcengineering/platform'
import { plugin } from '@hcengineering/platform'
import { Preference } from '@hcengineering/preference'
import { IntegrationType } from '@hcengineering/setting'
import { AnyComponent, Location, ResolvedLocation } from '@hcengineering/ui'
import { Readable, Writable } from './types'
import { Preference } from '@hcengineering/preference'
import { Action, Viewlet, ViewletDescriptor } from '@hcengineering/view'
import { ActivityMessage } from '@hcengineering/activity'
import { Readable, Writable } from './types'
export * from './types'
@ -275,12 +276,12 @@ export interface InboxNotificationsClient {
inboxNotifications: Readable<InboxNotification[]>
activityInboxNotifications: Writable<ActivityInboxNotification[]>
inboxNotificationsByContext: Readable<Map<Ref<DocNotifyContext>, InboxNotification[]>>
readDoc: (_id: Ref<Doc>) => Promise<void>
forceReadDoc: (_id: Ref<Doc>, _class: Ref<Class<Doc>>) => Promise<void>
readMessages: (ids: Ref<ActivityMessage>[]) => Promise<void>
readNotifications: (ids: Array<Ref<InboxNotification>>) => Promise<void>
unreadNotifications: (ids: Array<Ref<InboxNotification>>) => Promise<void>
deleteNotifications: (ids: Array<Ref<InboxNotification>>) => Promise<void>
readDoc: (client: TxOperations, _id: Ref<Doc>) => Promise<void>
forceReadDoc: (client: TxOperations, _id: Ref<Doc>, _class: Ref<Class<Doc>>) => Promise<void>
readMessages: (client: TxOperations, ids: Ref<ActivityMessage>[]) => Promise<void>
readNotifications: (client: TxOperations, ids: Array<Ref<InboxNotification>>) => Promise<void>
unreadNotifications: (client: TxOperations, ids: Array<Ref<InboxNotification>>) => Promise<void>
deleteNotifications: (client: TxOperations, ids: Array<Ref<InboxNotification>>) => Promise<void>
}
/**

View File

@ -44,7 +44,7 @@
const inboxClient = getResource(notification.function.GetInboxNotificationsClient).then((res) => res())
onDestroy(async () => {
void inboxClient.then((client) => client.readDoc(_id))
void inboxClient.then((client) => client.readDoc(getClient(), _id))
})
const client = getClient()
@ -57,7 +57,7 @@
const prev = lastId
lastId = _id
if (prev !== undefined) {
void inboxClient.then((client) => client.readDoc(prev))
void inboxClient.then((client) => client.readDoc(getClient(), prev))
}
query.query(recruit.class.Vacancy, { _id }, (result) => {
object = result[0] as Required<Vacancy>

View File

@ -78,12 +78,12 @@
if (lastId !== _id) {
const prev = lastId
lastId = _id
void inboxClient.then((client) => client.readDoc(prev))
void inboxClient.then((client) => client.readDoc(getClient(), prev))
}
}
onDestroy(async () => {
void inboxClient.then((client) => client.readDoc(_id))
void inboxClient.then((client) => client.readDoc(getClient(), _id))
})
$: _id !== undefined &&

View File

@ -54,12 +54,12 @@
if (lastId !== _id) {
const prev = lastId
lastId = _id
void inboxClient.then((client) => client.readDoc(prev))
void inboxClient.then((client) => client.readDoc(getClient(), prev))
}
}
onDestroy(async () => {
void inboxClient.then((client) => client.readDoc(_id))
void inboxClient.then((client) => client.readDoc(getClient(), _id))
})
$: _id !== undefined &&

View File

@ -45,24 +45,24 @@
let lastId: Ref<Doc> = _id
let object: Doc
const client = getClient()
const hierarchy = client.getHierarchy()
const pClient = getClient()
const hierarchy = pClient.getHierarchy()
const inboxClient = getResource(notification.function.GetInboxNotificationsClient).then((res) => res())
$: read(_id)
function read (_id: Ref<Doc>) {
function read (_id: Ref<Doc>): void {
if (lastId !== _id) {
const prev = lastId
lastId = _id
inboxClient.then(async (client) => {
await client.readDoc(prev)
void inboxClient.then(async (client) => {
await client.readDoc(pClient, prev)
})
}
}
onDestroy(async () => {
await inboxClient.then(async (client) => {
await client.readDoc(_id)
await client.readDoc(pClient, _id)
})
})
@ -195,7 +195,7 @@
let rawTitle: string = ''
$: if (object !== undefined) {
getDocLabel(client, object).then((t) => {
getDocLabel(pClient, object).then((t) => {
if (t) {
rawTitle = t
}