Chat UI fixes (#6046)

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
Kristina 2024-07-10 19:27:44 +04:00 committed by GitHub
parent bf0991d76a
commit b14cc90039
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 63 additions and 33 deletions

View File

@ -645,8 +645,17 @@ export function createModel (builder: Builder): void {
indexes: [] indexes: []
} }
) )
builder.mixin<Class<DocNotifyContext>, IndexingConfiguration<DocNotifyContext>>( builder.mixin<Class<InboxNotification>, IndexingConfiguration<InboxNotification>>(
notification.class.ActivityInboxNotification, notification.class.InboxNotification,
core.class.Class,
core.mixin.IndexConfiguration,
{
searchDisabled: true,
indexes: []
}
)
builder.mixin<Class<BrowserNotification>, IndexingConfiguration<BrowserNotification>>(
notification.class.BrowserNotification,
core.class.Class, core.class.Class,
core.mixin.IndexConfiguration, core.mixin.IndexConfiguration,
{ {

View File

@ -254,6 +254,8 @@
} }
.text-input { .text-input {
max-height: 18.75rem;
overflow: auto;
min-height: 2.75rem; min-height: 2.75rem;
padding: 0.125rem 0.75rem; padding: 0.125rem 0.75rem;
} }

View File

@ -32,6 +32,7 @@ import { combineActivityMessages } from '@hcengineering/activity-resources'
import chunter from './plugin' import chunter from './plugin'
import { type ChatMessage } from '@hcengineering/chunter' import { type ChatMessage } from '@hcengineering/chunter'
import notification, { type DocNotifyContext, type InboxNotification } from '@hcengineering/notification'
export type LoadMode = 'forward' | 'backward' export type LoadMode = 'forward' | 'backward'
@ -71,7 +72,7 @@ export class ChannelDataProvider implements IChannelDataProvider {
private readonly tailQuery = createQuery(true) private readonly tailQuery = createQuery(true)
private chatId: Ref<Doc> | undefined = undefined private chatId: Ref<Doc> | undefined = undefined
private readonly lastViewedTimestamp: Timestamp | undefined = undefined private readonly context: DocNotifyContext | undefined = undefined
private readonly msgClass: Ref<Class<ActivityMessage>> private readonly msgClass: Ref<Class<ActivityMessage>>
private selectedMsgId: Ref<ActivityMessage> | undefined = undefined private selectedMsgId: Ref<ActivityMessage> | undefined = undefined
private tailStart: Timestamp | undefined = undefined private tailStart: Timestamp | undefined = undefined
@ -101,15 +102,15 @@ export class ChannelDataProvider implements IChannelDataProvider {
constructor ( constructor (
chatId: Ref<Doc>, chatId: Ref<Doc>,
_class: Ref<Class<ActivityMessage>>, _class: Ref<Class<ActivityMessage>>,
lastViewedTimestamp?: Timestamp, context: DocNotifyContext | undefined,
selectedMsgId?: Ref<ActivityMessage>, selectedMsgId?: Ref<ActivityMessage>,
loadAll = false loadAll = false
) { ) {
this.chatId = chatId this.chatId = chatId
this.lastViewedTimestamp = lastViewedTimestamp this.context = context
this.msgClass = _class this.msgClass = _class
this.selectedMsgId = selectedMsgId this.selectedMsgId = selectedMsgId
this.loadData(loadAll) void this.loadData(loadAll)
} }
public destroy (): void { public destroy (): void {
@ -154,7 +155,7 @@ export class ChannelDataProvider implements IChannelDataProvider {
this.selectedMsgId = undefined this.selectedMsgId = undefined
} }
private loadData (loadAll = false): void { private async loadData (loadAll = false): Promise<void> {
if (this.chatId === undefined) { if (this.chatId === undefined) {
return return
} }
@ -182,10 +183,25 @@ export class ChannelDataProvider implements IChannelDataProvider {
return return
} }
const client = getClient()
this.isInitialLoadingStore.set(true) this.isInitialLoadingStore.set(true)
const firstNotification =
this.context !== undefined
? await client.findOne(
notification.class.InboxNotification,
{
_class: {
$in: [notification.class.MentionInboxNotification, notification.class.ActivityInboxNotification]
},
docNotifyContext: this.context._id,
isViewed: false
},
{ sort: { createdOn: SortingOrder.Ascending } }
)
: undefined
const metadata = get(this.metadataStore) const metadata = get(this.metadataStore)
const firstNewMsgIndex = this.getFirstNewMsgIndex(this.lastViewedTimestamp) const firstNewMsgIndex = this.getFirstNewMsgIndex(firstNotification)
if (get(this.newTimestampStore) === undefined) { if (get(this.newTimestampStore) === undefined) {
this.newTimestampStore.set(firstNewMsgIndex !== undefined ? metadata[firstNewMsgIndex]?.createdOn : undefined) this.newTimestampStore.set(firstNewMsgIndex !== undefined ? metadata[firstNewMsgIndex]?.createdOn : undefined)
@ -335,19 +351,33 @@ export class ChannelDataProvider implements IChannelDataProvider {
return firsNewMsgIndex return firsNewMsgIndex
} }
private getFirstNewMsgIndex (lastViewedTimestamp?: Timestamp): number | undefined { private getFirstNewMsgIndex (firstNotification: InboxNotification | undefined): number | undefined {
const metadata = get(this.metadataStore) const metadata = get(this.metadataStore)
if (metadata.length === 0) { if (metadata.length === 0) {
return undefined return undefined
} }
if (lastViewedTimestamp === undefined) { if (this.context === undefined) {
return -1
}
const lastViewedTimestamp = this.context.lastViewedTimestamp
if (lastViewedTimestamp === undefined && firstNotification === undefined) {
return -1 return -1
} }
const me = getCurrentAccount()._id const me = getCurrentAccount()._id
let newTimestamp = 0
if (lastViewedTimestamp !== undefined && firstNotification !== undefined) {
newTimestamp = Math.min(lastViewedTimestamp ?? 0, firstNotification?.createdOn ?? 0)
} else {
newTimestamp = lastViewedTimestamp ?? firstNotification?.createdOn ?? 0
}
return metadata.findIndex((message) => { return metadata.findIndex((message) => {
if (message.createdBy === me) { if (message.createdBy === me) {
return false return false
@ -355,7 +385,7 @@ export class ChannelDataProvider implements IChannelDataProvider {
const createdOn = message.createdOn ?? 0 const createdOn = message.createdOn ?? 0
return lastViewedTimestamp < createdOn return newTimestamp < createdOn
}) })
} }

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import { Class, Doc, getCurrentAccount, Ref, Timestamp } from '@hcengineering/core' import { Class, Doc, getCurrentAccount, Ref } from '@hcengineering/core'
import notification, { DocNotifyContext } from '@hcengineering/notification' import notification, { DocNotifyContext } from '@hcengineering/notification'
import activity, { ActivityMessage, ActivityMessagesFilter } from '@hcengineering/activity' import activity, { ActivityMessage, ActivityMessagesFilter } from '@hcengineering/activity'
import { getClient } from '@hcengineering/presentation' import { getClient } from '@hcengineering/presentation'
@ -61,29 +61,25 @@
$: _class = isDocChannel ? activity.class.ActivityMessage : chunter.class.ChatMessage $: _class = isDocChannel ? activity.class.ActivityMessage : chunter.class.ChatMessage
$: collection = isDocChannel ? 'comments' : 'messages' $: collection = isDocChannel ? 'comments' : 'messages'
$: updateDataProvider(object._id, _class, context?.lastViewedTimestamp, selectedMessageId) $: void updateDataProvider(object._id, _class, selectedMessageId)
async function updateDataProvider ( async function updateDataProvider (
attachedTo: Ref<Doc>, attachedTo: Ref<Doc>,
_class: Ref<Class<ActivityMessage>>, _class: Ref<Class<ActivityMessage>>,
lastViewedTimestamp?: Timestamp,
selectedMessageId?: Ref<ActivityMessage> selectedMessageId?: Ref<ActivityMessage>
): Promise<void> { ): Promise<void> {
if (dataProvider === undefined) { if (dataProvider === undefined) {
// For now loading all messages for documents with activity. Need to correct handle aggregation with pagination. // For now loading all messages for documents with activity. Need to correct handle aggregation with pagination.
// Perhaps we should load all activity messages once, and keep loading in chunks only for ChatMessages then merge them correctly with activity messages // Perhaps we should load all activity messages once, and keep loading in chunks only for ChatMessages then merge them correctly with activity messages
const loadAll = isDocChannel const loadAll = isDocChannel
const timestamp = const ctx =
lastViewedTimestamp ?? context ??
( (await client.findOne(notification.class.DocNotifyContext, {
await client.findOne( attachedTo: object._id,
notification.class.DocNotifyContext, user: getCurrentAccount()._id
{ attachedTo: object._id, user: getCurrentAccount()._id }, }))
{ projection: { lastViewedTimestamp: 1 } }
)
)?.lastViewedTimestamp
dataProvider = new ChannelDataProvider(attachedTo, _class, timestamp, selectedMessageId, loadAll) dataProvider = new ChannelDataProvider(attachedTo, _class, ctx, selectedMessageId, loadAll)
} }
} }
</script> </script>

View File

@ -15,7 +15,6 @@
// //
import activity, { ActivityMessage, DocUpdateMessage } from '@hcengineering/activity' import activity, { ActivityMessage, DocUpdateMessage } from '@hcengineering/activity'
import { Analytics } from '@hcengineering/analytics'
import chunter, { ChatMessage } from '@hcengineering/chunter' import chunter, { ChatMessage } from '@hcengineering/chunter'
import contact, { import contact, {
type AvatarInfo, type AvatarInfo,
@ -467,12 +466,6 @@ async function activityInboxNotificationToText (doc: Data<ActivityInboxNotificat
body = await translate(doc.body, params) body = await translate(doc.body, params)
} }
// TODO: temporary log to understand problem. Remove it later.
if (body === 'chunter:string:MessageNotificationBody') {
console.error('Cannot translate chunter notification: ', { doc, params })
Analytics.handleError(new Error('Cannot translate chunter notification'))
}
return [title, body] return [title, body]
} }
@ -1299,7 +1292,7 @@ async function applyUserTxes (
cache.set(account._id, account) cache.set(account._id, account)
await control.apply(txs) await control.apply(txs)
const m1 = toIdMap(txes) const m1 = toIdMap(txs)
control.operationContext.derived.targets.docNotifyContext = (it) => { control.operationContext.derived.targets.docNotifyContext = (it) => {
if (m1.has(it._id)) { if (m1.has(it._id)) {
return [account.email] return [account.email]

View File

@ -414,7 +414,7 @@ export async function getNotificationContent (
export async function getUsersInfo (ids: Ref<PersonAccount>[], control: TriggerControl): Promise<UserInfo[]> { export async function getUsersInfo (ids: Ref<PersonAccount>[], control: TriggerControl): Promise<UserInfo[]> {
const accounts = await control.modelDb.findAll(contact.class.PersonAccount, { _id: { $in: ids } }) const accounts = await control.modelDb.findAll(contact.class.PersonAccount, { _id: { $in: ids } })
const persons = await control.findAll(contact.class.Person, { _id: { $in: accounts.map(({ person }) => person) } }) const persons = await control.queryFind(contact.class.Person, {})
return accounts.map((account) => ({ return accounts.map((account) => ({
_id: account._id, _id: account._id,