Fix channels hide and return autohide (#6529)

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
Kristina 2024-09-11 19:35:18 +04:00 committed by GitHub
parent 4e44f73314
commit 682e4b3011
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 129 additions and 196 deletions

View File

@ -23,8 +23,7 @@ import {
type ChunterSpace, type ChunterSpace,
type ObjectChatPanel, type ObjectChatPanel,
type ThreadMessage, type ThreadMessage,
type ChatInfo, type ChatSyncInfo,
type ChannelInfo,
type InlineButton, type InlineButton,
type TypingInfo, type TypingInfo,
type InlineButtonAction type InlineButtonAction
@ -52,12 +51,10 @@ import {
TypeRef, TypeRef,
TypeString, TypeString,
TypeTimestamp, TypeTimestamp,
UX, UX
Hidden
} from '@hcengineering/model' } from '@hcengineering/model'
import attachment from '@hcengineering/model-attachment' import attachment from '@hcengineering/model-attachment'
import core, { TAttachedDoc, TClass, TDoc, TSpace } from '@hcengineering/model-core' import core, { TAttachedDoc, TClass, TDoc, TSpace } from '@hcengineering/model-core'
import notification, { TDocNotifyContext } from '@hcengineering/model-notification'
import view from '@hcengineering/model-view' import view from '@hcengineering/model-view'
import workbench from '@hcengineering/model-workbench' import workbench from '@hcengineering/model-workbench'
import { type IntlString, type Resource } from '@hcengineering/platform' import { type IntlString, type Resource } from '@hcengineering/platform'
@ -152,14 +149,8 @@ export class TObjectChatPanel extends TClass implements ObjectChatPanel {
ignoreKeys!: string[] ignoreKeys!: string[]
} }
@Mixin(chunter.mixin.ChannelInfo, notification.class.DocNotifyContext) @Model(chunter.class.ChatSyncInfo, core.class.Doc, DOMAIN_CHUNTER)
export class TChannelInfo extends TDocNotifyContext implements ChannelInfo { export class TChatSyncInfo extends TDoc implements ChatSyncInfo {
@Hidden()
hidden!: boolean
}
@Model(chunter.class.ChatInfo, core.class.Doc, DOMAIN_CHUNTER)
export class TChatInfo extends TDoc implements ChatInfo {
user!: Ref<Person> user!: Ref<Person>
hidden!: Ref<DocNotifyContext>[] hidden!: Ref<DocNotifyContext>[]
timestamp!: Timestamp timestamp!: Timestamp
@ -190,8 +181,7 @@ export function createModel (builder: Builder): void {
TThreadMessage, TThreadMessage,
TChatMessageViewlet, TChatMessageViewlet,
TObjectChatPanel, TObjectChatPanel,
TChatInfo, TChatSyncInfo,
TChannelInfo,
TInlineButton, TInlineButton,
TTypingInfo TTypingInfo
) )

View File

@ -67,6 +67,7 @@ export async function createDocNotifyContexts (
objectId, objectId,
objectClass, objectClass,
objectSpace, objectSpace,
hidden: false,
isPinned: false isPinned: false
}) })
} }
@ -332,6 +333,19 @@ export const chunterOperation: MigrateOperation = {
await removeWrongActivity(client) await removeWrongActivity(client)
} }
}, },
{
state: 'remove-chat-info-v1',
func: async (client) => {
await client.deleteMany(DOMAIN_CHUNTER, { _class: 'chunter:class:ChatInfo' as Ref<Class<Doc>> })
await client.deleteMany(DOMAIN_TX, { objectClass: 'chunter:class:ChatInfo' })
await client.update(
DOMAIN_DOC_NOTIFY,
{ 'chunter:mixin:ChannelInfo': { $exists: true } },
{ $unset: { 'chunter:mixin:ChannelInfo': true } }
)
await client.deleteMany(DOMAIN_TX, { mixin: 'chunter:mixin:ChannelInfo' })
}
},
{ {
state: 'remove-duplicated-directs-v1', state: 'remove-duplicated-directs-v1',
func: async (client) => { func: async (client) => {

View File

@ -218,6 +218,9 @@ export class TDocNotifyContext extends TDoc implements DocNotifyContext {
@Prop(TypeBoolean(), notification.string.Pinned) @Prop(TypeBoolean(), notification.string.Pinned)
isPinned!: boolean isPinned!: boolean
@Prop(TypeBoolean(), view.string.Hide)
hidden!: boolean
tx?: Ref<TxCUD<Doc>> tx?: Ref<TxCUD<Doc>>
} }

View File

@ -383,6 +383,16 @@ export const notificationOperation: MigrateOperation = {
{ {
state: 'migrate-duplicated-contexts-v1', state: 'migrate-duplicated-contexts-v1',
func: migrateDuplicateContexts func: migrateDuplicateContexts
},
{
state: 'set-default-hidden',
func: async () => {
await client.update(
DOMAIN_DOC_NOTIFY,
{ _class: notification.class.DocNotifyContext, hidden: { $exists: false } },
{ hidden: false }
)
}
} }
]) ])

View File

@ -75,14 +75,6 @@ export function createModel (builder: Builder): void {
} }
) )
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
trigger: serverChunter.trigger.OnContextUpdate,
txMatch: {
_class: core.class.TxUpdateDoc,
objectClass: notification.class.DocNotifyContext
}
})
builder.createDoc(serverCore.class.Trigger, core.space.Model, { builder.createDoc(serverCore.class.Trigger, core.space.Model, {
trigger: serverChunter.trigger.OnChatMessageRemoved, trigger: serverChunter.trigger.OnChatMessageRemoved,
txMatch: { txMatch: {

View File

@ -97,9 +97,12 @@
}) })
if (context !== undefined) { if (context !== undefined) {
if (context.hidden) {
await client.updateDoc(context._class, context.space, context._id, { hidden: false })
}
dispatch('close') dispatch('close')
openChannel(dmId, chunter.class.DirectMessage) openChannel(dmId, chunter.class.DirectMessage)
return return
} }
@ -110,6 +113,7 @@
objectId: dmId, objectId: dmId,
objectClass: chunter.class.DirectMessage, objectClass: chunter.class.DirectMessage,
objectSpace: core.space.Space, objectSpace: core.space.Space,
hidden: false,
isPinned: false isPinned: false
}) })

View File

@ -50,7 +50,9 @@
let sections: Section[] = [] let sections: Section[] = []
$: contexts = $contextsStore.filter(({ objectClass, isPinned }) => { $: contexts = $contextsStore.filter((it) => {
const { objectClass, isPinned, hidden } = it
if (hidden) return false
if (model.isPinned !== isPinned) return false if (model.isPinned !== isPinned) return false
if (model._class !== undefined && model._class !== objectClass) return false if (model._class !== undefined && model._class !== objectClass) return false
if (model.skipClasses !== undefined && model.skipClasses.includes(objectClass)) return false if (model.skipClasses !== undefined && model.skipClasses.includes(objectClass)) return false

View File

@ -357,7 +357,7 @@ function archiveActivityChannels (contexts: DocNotifyContext[]): void {
label: chunter.string.ArchiveActivityConfirmationTitle, label: chunter.string.ArchiveActivityConfirmationTitle,
message: chunter.string.ArchiveActivityConfirmationMessage, message: chunter.string.ArchiveActivityConfirmationMessage,
action: async () => { action: async () => {
await removeActivityChannels(contexts) await hideActivityChannels(contexts)
} }
}, },
'top' 'top'
@ -385,19 +385,12 @@ export function loadSavedAttachments (): void {
} }
} }
export async function removeActivityChannels (contexts: DocNotifyContext[]): Promise<void> { export async function hideActivityChannels (contexts: DocNotifyContext[]): Promise<void> {
const ops = getClient().apply(undefined, 'removeActivityChannels') const ops = getClient().apply(undefined, 'hideActivityChannels')
try { try {
for (const context of contexts) { for (const context of contexts) {
await ops.createMixin(context._id, context._class, context.space, chunter.mixin.ChannelInfo, { hidden: true }) await ops.update(context, { hidden: true })
}
const hidden = contexts.map(({ _id }) => _id)
const account = getCurrentAccount() as PersonAccount
const chatInfo = await ops.findOne(chunter.class.ChatInfo, { user: account.person })
if (chatInfo !== undefined) {
await ops.update(chatInfo, { hidden: chatInfo.hidden.concat(hidden) })
} }
} finally { } finally {
await ops.commit() await ops.commit()
@ -412,7 +405,7 @@ export async function readActivityChannels (contexts: DocNotifyContext[]): Promi
try { try {
for (const context of contexts) { for (const context of contexts) {
const notifications = notificationsByContext.get(context._id) ?? [] const notifications = notificationsByContext.get(context._id) ?? []
await client.archiveNotifications( await client.readNotifications(
ops, ops,
notifications notifications
.filter(({ _class }) => _class === notification.class.ActivityInboxNotification) .filter(({ _class }) => _class === notification.class.ActivityInboxNotification)

View File

@ -513,21 +513,12 @@ export async function removeChannelAction (context?: DocNotifyContext, _?: Event
if (hierarchy.isDerived(objectClass, chunter.class.Channel)) { if (hierarchy.isDerived(objectClass, chunter.class.Channel)) {
const channel = await client.findOne(chunter.class.Channel, { _id: objectId as Ref<Channel>, space: objectSpace }) const channel = await client.findOne(chunter.class.Channel, { _id: objectId as Ref<Channel>, space: objectSpace })
await leaveChannel(channel, getCurrentAccount()._id) await leaveChannel(channel, getCurrentAccount()._id)
await client.remove(context)
} else { } else {
const object = await client.findOne(objectClass, { _id: objectId, space: objectSpace }) const object = await client.findOne(objectClass, { _id: objectId, space: objectSpace })
// const account = getCurrentAccount() as PersonAccount await client.update(context, { hidden: true })
// await client.createMixin(context._id, context._class, context.space, chunter.mixin.ChannelInfo, { hidden: true })
//
// const chatInfo = await client.findOne(chunter.class.ChatInfo, { user: account.person })
//
// if (chatInfo !== undefined) {
// await client.update(chatInfo, { hidden: chatInfo.hidden.concat([context._id]) })
// }
await resetChunterLocIfEqual(objectId, objectClass, object) await resetChunterLocIfEqual(objectId, objectClass, object)
} }
await client.remove(context)
} }
export function isThreadMessage (message: ActivityMessage): message is ThreadMessage { export function isThreadMessage (message: ActivityMessage): message is ThreadMessage {

View File

@ -15,7 +15,7 @@
import { ActivityMessage, ActivityMessageViewlet } from '@hcengineering/activity' import { ActivityMessage, ActivityMessageViewlet } from '@hcengineering/activity'
import type { AttachedDoc, Class, Doc, Markup, Mixin, Ref, Space, Timestamp } from '@hcengineering/core' import type { AttachedDoc, Class, Doc, Markup, Mixin, Ref, Space, Timestamp } from '@hcengineering/core'
import { DocNotifyContext, NotificationType } from '@hcengineering/notification' import { NotificationType } from '@hcengineering/notification'
import type { Asset, Plugin, Resource } from '@hcengineering/platform' import type { Asset, Plugin, Resource } from '@hcengineering/platform'
import { IntlString, plugin } from '@hcengineering/platform' import { IntlString, plugin } from '@hcengineering/platform'
import { AnyComponent } from '@hcengineering/ui' import { AnyComponent } from '@hcengineering/ui'
@ -77,9 +77,8 @@ export interface ChatMessageViewlet extends ActivityMessageViewlet {
label?: IntlString label?: IntlString
} }
export interface ChatInfo extends Doc { export interface ChatSyncInfo extends Doc {
user: Ref<Person> user: Ref<Person>
hidden: Ref<DocNotifyContext>[]
timestamp: Timestamp timestamp: Timestamp
} }
@ -90,10 +89,6 @@ export interface TypingInfo extends Doc {
lastTyping: Timestamp lastTyping: Timestamp
} }
export interface ChannelInfo extends DocNotifyContext {
hidden: boolean
}
export type InlineButtonAction = (button: InlineButton, message: Ref<ChatMessage>, channel: Ref<Doc>) => Promise<void> export type InlineButtonAction = (button: InlineButton, message: Ref<ChatMessage>, channel: Ref<Doc>) => Promise<void>
export interface InlineButton extends AttachedDoc { export interface InlineButton extends AttachedDoc {
@ -146,13 +141,12 @@ export default plugin(chunterId, {
DirectMessage: '' as Ref<Class<DirectMessage>>, DirectMessage: '' as Ref<Class<DirectMessage>>,
ChatMessage: '' as Ref<Class<ChatMessage>>, ChatMessage: '' as Ref<Class<ChatMessage>>,
ChatMessageViewlet: '' as Ref<Class<ChatMessageViewlet>>, ChatMessageViewlet: '' as Ref<Class<ChatMessageViewlet>>,
ChatInfo: '' as Ref<Class<ChatInfo>>, ChatSyncInfo: '' as Ref<Class<ChatSyncInfo>>,
InlineButton: '' as Ref<Class<InlineButton>>, InlineButton: '' as Ref<Class<InlineButton>>,
TypingInfo: '' as Ref<Class<TypingInfo>> TypingInfo: '' as Ref<Class<TypingInfo>>
}, },
mixin: { mixin: {
ObjectChatPanel: '' as Ref<Mixin<ObjectChatPanel>>, ObjectChatPanel: '' as Ref<Mixin<ObjectChatPanel>>
ChannelInfo: '' as Ref<Mixin<ChannelInfo>>
}, },
string: { string: {
Reactions: '' as IntlString, Reactions: '' as IntlString,

View File

@ -285,6 +285,7 @@ export interface DocNotifyContext extends Doc<PersonSpace> {
objectSpace: Ref<Space> objectSpace: Ref<Space>
isPinned: boolean isPinned: boolean
hidden: boolean
lastViewedTimestamp?: Timestamp lastViewedTimestamp?: Timestamp
lastUpdateTimestamp?: Timestamp lastUpdateTimestamp?: Timestamp

View File

@ -14,15 +14,8 @@
// //
import activity, { ActivityMessage, ActivityReference } from '@hcengineering/activity' import activity, { ActivityMessage, ActivityReference } from '@hcengineering/activity'
import chunter, { import chunter, { Channel, ChatMessage, chunterId, ChunterSpace, ThreadMessage } from '@hcengineering/chunter'
Channel, import contact, { Person, PersonAccount } from '@hcengineering/contact'
ChannelInfo,
ChatMessage,
chunterId,
ChunterSpace,
ThreadMessage
} from '@hcengineering/chunter'
import { Person, PersonAccount } from '@hcengineering/contact'
import core, { import core, {
Account, Account,
AttachedDoc, AttachedDoc,
@ -39,7 +32,6 @@ import core, {
TxCollectionCUD, TxCollectionCUD,
TxCreateDoc, TxCreateDoc,
TxCUD, TxCUD,
TxMixin,
TxProcessor, TxProcessor,
TxRemoveDoc, TxRemoveDoc,
TxUpdateDoc, TxUpdateDoc,
@ -391,94 +383,55 @@ function combineAttributes (attributes: any[], key: string, operator: string, ar
).filter((v) => v != null) ).filter((v) => v != null)
} }
async function hideOldDirects ( function getDirectsToHide (directs: DocNotifyContext[], date: Timestamp): DocNotifyContext[] {
directs: DocNotifyContext[],
control: TriggerControl,
date: Timestamp
): Promise<TxMixin<DocNotifyContext, ChannelInfo>[]> {
const visibleDirects = directs.filter((context) => {
const hasMixin = control.hierarchy.hasMixin(context, chunter.mixin.ChannelInfo)
if (!hasMixin) return true
const info = control.hierarchy.as(context, chunter.mixin.ChannelInfo)
return !info.hidden
})
const minVisibleDirects = 10 const minVisibleDirects = 10
if (visibleDirects.length <= minVisibleDirects) return [] if (directs.length <= minVisibleDirects) return []
const canHide = visibleDirects.length - minVisibleDirects const hideCount = directs.length - minVisibleDirects
let toHide: DocNotifyContext[] = [] const toHide: DocNotifyContext[] = []
for (const context of directs) { for (const context of directs) {
const { lastUpdateTimestamp = 0, lastViewedTimestamp = 0 } = context const { lastUpdateTimestamp = 0, lastViewedTimestamp = 0 } = context
if (lastViewedTimestamp === 0) continue
if (lastUpdateTimestamp > lastViewedTimestamp) continue if (lastUpdateTimestamp > lastViewedTimestamp) continue
if (date - lastUpdateTimestamp < hideChannelDelay) continue if (date - lastUpdateTimestamp > hideChannelDelay) {
toHide.push(context)
toHide.push(context) }
} }
if (toHide.length > canHide) { toHide.sort((a, b) => (a.lastUpdateTimestamp ?? 0) - (b.lastUpdateTimestamp ?? 0))
toHide = toHide.splice(0, toHide.length - canHide)
}
return await hideOldChannels(toHide, control) return toHide.slice(0, hideCount)
} }
async function hideOldActivityChannels ( function getActivityToHide (contexts: DocNotifyContext[], date: Timestamp): DocNotifyContext[] {
contexts: DocNotifyContext[],
control: TriggerControl,
date: Timestamp
): Promise<TxMixin<DocNotifyContext, ChannelInfo>[]> {
if (contexts.length === 0) return [] if (contexts.length === 0) return []
const { hierarchy } = control
const toHide: DocNotifyContext[] = [] const toHide: DocNotifyContext[] = []
for (const context of contexts) { for (const context of contexts) {
const { lastUpdateTimestamp = 0, lastViewedTimestamp = 0 } = context const { lastUpdateTimestamp = 0, lastViewedTimestamp = 0 } = context
if (lastViewedTimestamp === 0) continue
if (lastUpdateTimestamp > lastViewedTimestamp) continue if (lastUpdateTimestamp > lastViewedTimestamp) continue
if (date - lastUpdateTimestamp < hideChannelDelay) continue if (date - lastUpdateTimestamp > hideChannelDelay) {
toHide.push(context)
const params = hierarchy.as(context, chunter.mixin.ChannelInfo) }
if (params.hidden) continue
toHide.push(context)
} }
return await hideOldChannels(toHide, control) return toHide
} }
async function hideOldChannels ( export async function syncChat (control: TriggerControl, status: UserStatus, date: Timestamp): Promise<void> {
contexts: DocNotifyContext[],
control: TriggerControl
): Promise<TxMixin<DocNotifyContext, ChannelInfo>[]> {
const res: TxMixin<DocNotifyContext, ChannelInfo>[] = []
for (const context of contexts) {
const tx = control.txFactory.createTxMixin(context._id, context._class, context.space, chunter.mixin.ChannelInfo, {
hidden: true
})
res.push(tx)
}
return res
}
export async function updateChatInfo (control: TriggerControl, status: UserStatus, date: Timestamp): Promise<void> {
const account = getPersonAccountById(status.user as Ref<PersonAccount>, control) const account = getPersonAccountById(status.user as Ref<PersonAccount>, control)
if (account === undefined) return if (account === undefined) return
const update = (await control.findAll(control.ctx, chunter.class.ChatInfo, { user: account.person })).shift() const syncInfo = (await control.findAll(control.ctx, chunter.class.ChatSyncInfo, { user: account.person })).shift()
const shouldUpdate = update === undefined || date - update.timestamp > updateChatInfoDelay const shouldSync = syncInfo === undefined || date - syncInfo.timestamp > updateChatInfoDelay
if (!shouldSync) return
if (!shouldUpdate) return
const contexts = await control.findAll(control.ctx, notification.class.DocNotifyContext, { const contexts = await control.findAll(control.ctx, notification.class.DocNotifyContext, {
user: account._id, user: account._id,
hidden: false,
isPinned: false isPinned: false
}) })
@ -492,83 +445,63 @@ export async function updateChatInfo (control: TriggerControl, status: UserStatu
) )
const activityContexts = contexts.filter( const activityContexts = contexts.filter(
({ objectClass }) => ({ objectClass }) =>
!hierarchy.isDerived(objectClass, chunter.class.DirectMessage) && !hierarchy.isDerived(objectClass, chunter.class.ChunterSpace) &&
!hierarchy.isDerived(objectClass, chunter.class.Channel) &&
!hierarchy.isDerived(objectClass, activity.class.ActivityMessage) !hierarchy.isDerived(objectClass, activity.class.ActivityMessage)
) )
const directTxes = await hideOldDirects(directContexts, control, date) const directsToHide = getDirectsToHide(directContexts, date)
const activityTxes = await hideOldActivityChannels(activityContexts, control, date) const activityToHide = getActivityToHide(activityContexts, date)
const mixinTxes = directTxes.concat(activityTxes) const contextsToHide = directsToHide.concat(activityToHide)
const hidden: Ref<DocNotifyContext>[] = mixinTxes.map((tx) => tx.objectId)
res.push(...mixinTxes) for (const context of contextsToHide) {
if (update === undefined) {
res.push( res.push(
control.txFactory.createTxCreateDoc(chunter.class.ChatInfo, core.space.Workspace, { control.txFactory.createTxUpdateDoc(context._class, context.space, context._id, {
user: account.person, hidden: true
hidden,
timestamp: date
}) })
) )
}
if (syncInfo === undefined) {
const personSpace = (
await control.findAll(control.ctx, contact.class.PersonSpace, { person: account.person })
).shift()
if (personSpace !== undefined) {
res.push(
control.txFactory.createTxCreateDoc(chunter.class.ChatSyncInfo, personSpace._id, {
user: account.person,
timestamp: date
})
)
}
} else { } else {
res.push( res.push(
control.txFactory.createTxUpdateDoc(update._class, update.space, update._id, { control.txFactory.createTxUpdateDoc(syncInfo._class, syncInfo.space, syncInfo._id, {
hidden: Array.from(new Set(update.hidden.concat(hidden))),
timestamp: date timestamp: date
}) })
) )
} }
const txIds = res.map((tx) => tx._id) await control.apply(control.ctx, res, true)
await control.apply(control.ctx, res)
control.ctx.contextData.broadcast.targets.docNotifyContext = (it) => {
if (txIds.includes(it._id)) {
return [account.email]
}
}
} }
async function OnUserStatus (originTx: TxCUD<UserStatus>, control: TriggerControl): Promise<Tx[]> { async function OnUserStatus (originTx: TxCUD<UserStatus>, control: TriggerControl): Promise<Tx[]> {
// const tx = TxProcessor.extractTx(originTx) as TxCUD<UserStatus> const tx = TxProcessor.extractTx(originTx) as TxCUD<UserStatus>
// if (tx.objectClass !== core.class.UserStatus) return [] if (tx.objectClass !== core.class.UserStatus) return []
// if (tx._class === core.class.TxCreateDoc) { if (tx._class === core.class.TxCreateDoc) {
// const createTx = tx as TxCreateDoc<UserStatus> const createTx = tx as TxCreateDoc<UserStatus>
// const { online } = createTx.attributes const { online } = createTx.attributes
// if (online) { if (online) {
// const status = TxProcessor.createDoc2Doc(createTx) const status = TxProcessor.createDoc2Doc(createTx)
// await updateChatInfo(control, status, originTx.modifiedOn) await syncChat(control, status, originTx.modifiedOn)
// } }
// } else if (tx._class === core.class.TxUpdateDoc) { } else if (tx._class === core.class.TxUpdateDoc) {
// const updateTx = tx as TxUpdateDoc<UserStatus> const updateTx = tx as TxUpdateDoc<UserStatus>
// const { online } = updateTx.operations const { online } = updateTx.operations
// if (online === true) { if (online === true) {
// const status = (await control.findAll(core.class.UserStatus, { _id: updateTx.objectId }))[0] const status = (await control.findAll(control.ctx, core.class.UserStatus, { _id: updateTx.objectId }))[0]
// await updateChatInfo(control, status, originTx.modifiedOn) await syncChat(control, status, originTx.modifiedOn)
// } }
// } }
return []
}
async function OnContextUpdate (tx: TxUpdateDoc<DocNotifyContext>, control: TriggerControl): Promise<Tx[]> {
const hasUpdate = 'lastUpdateTimestamp' in tx.operations && tx.operations.lastUpdateTimestamp !== undefined
if (!hasUpdate) return []
// const update = (await control.findAll(notification.class.DocNotifyContext, { _id: tx.objectId }, { limit: 1 })).shift()
// if (update !== undefined) {
// const as = control.hierarchy.as(update, chunter.mixin.ChannelInfo)
// if (as.hidden) {
// return [
// control.txFactory.createTxMixin(tx.objectId, tx.objectClass, tx.objectSpace, chunter.mixin.ChannelInfo, {
// hidden: false
// })
// ]
// }
// }
return [] return []
} }
@ -589,8 +522,7 @@ export default async () => ({
ChunterTrigger, ChunterTrigger,
OnChatMessageRemoved, OnChatMessageRemoved,
ChatNotificationsHandler, ChatNotificationsHandler,
OnUserStatus, OnUserStatus
OnContextUpdate
}, },
function: { function: {
CommentRemove, CommentRemove,

View File

@ -31,8 +31,7 @@ export default plugin(serverChunterId, {
ChunterTrigger: '' as Resource<TriggerFunc>, ChunterTrigger: '' as Resource<TriggerFunc>,
OnChatMessageRemoved: '' as Resource<TriggerFunc>, OnChatMessageRemoved: '' as Resource<TriggerFunc>,
ChatNotificationsHandler: '' as Resource<TriggerFunc>, ChatNotificationsHandler: '' as Resource<TriggerFunc>,
OnUserStatus: '' as Resource<TriggerFunc>, OnUserStatus: '' as Resource<TriggerFunc>
OnContextUpdate: '' as Resource<TriggerFunc>
}, },
function: { function: {
CommentRemove: '' as Resource<ObjectDDParticipantFunc>, CommentRemove: '' as Resource<ObjectDDParticipantFunc>,

View File

@ -716,6 +716,7 @@ async function createNotifyContext (
objectClass, objectClass,
objectSpace, objectSpace,
isPinned: false, isPinned: false,
hidden: false,
tx: tx?._id, tx: tx?._id,
lastUpdateTimestamp: updateTimestamp, lastUpdateTimestamp: updateTimestamp,
lastViewedTimestamp: sender === receiver._id ? updateTimestamp : undefined lastViewedTimestamp: sender === receiver._id ? updateTimestamp : undefined
@ -828,10 +829,11 @@ async function updateContextsTimestamp (
const res: Tx[] = [] const res: Tx[] = []
for (const context of contexts) { for (const context of contexts) {
const account = getPersonAccountById(context.user, control) // accounts.find(({ _id }) => _id === context.user) const account = getPersonAccountById(context.user, control)
const isViewed = const isViewed =
context.lastViewedTimestamp !== undefined && (context.lastUpdateTimestamp ?? 0) <= context.lastViewedTimestamp context.lastViewedTimestamp !== undefined && (context.lastUpdateTimestamp ?? 0) <= context.lastViewedTimestamp
const updateTx = control.txFactory.createTxUpdateDoc(context._class, context.space, context._id, { const updateTx = control.txFactory.createTxUpdateDoc(context._class, context.space, context._id, {
hidden: false,
lastUpdateTimestamp: timestamp, lastUpdateTimestamp: timestamp,
...(isViewed && modifiedBy === context.user ...(isViewed && modifiedBy === context.user
? { ? {
@ -1680,7 +1682,11 @@ async function updateCollaborators (
const info = toReceiverInfo(hierarchy, addedUser) const info = toReceiverInfo(hierarchy, addedUser)
if (info === undefined) continue if (info === undefined) continue
const context = getDocNotifyContext(control, contexts, objectId, info._id) const context = getDocNotifyContext(control, contexts, objectId, info._id)
if (context !== undefined) continue if (context !== undefined) {
if (context.hidden) {
res.push(control.txFactory.createTxUpdateDoc(context._class, context.space, context._id, { hidden: false }))
}
}
await createNotifyContext(ctx, control, objectId, objectClass, objectSpace, info, tx.modifiedBy, undefined, tx) await createNotifyContext(ctx, control, objectId, objectClass, objectSpace, info, tx.modifiedBy, undefined, tx)
} }

View File

@ -74,7 +74,8 @@ export async function getDirect (
objectId: dmId, objectId: dmId,
objectClass: chunter.class.DirectMessage, objectClass: chunter.class.DirectMessage,
objectSpace: core.space.Space, objectSpace: core.space.Space,
isPinned: false isPinned: false,
hidden: false
}) })
return dmId return dmId

View File

@ -17,7 +17,8 @@ export async function createNotification (
objectClass: forDoc._class, objectClass: forDoc._class,
objectSpace: forDoc.space, objectSpace: forDoc.space,
user: data.user, user: data.user,
isPinned: false isPinned: false,
hidden: false
}) })
docNotifyContext = await client.findOne(notification.class.DocNotifyContext, { _id: docNotifyContextId }) docNotifyContext = await client.findOne(notification.class.DocNotifyContext, { _id: docNotifyContextId })
} }