Skip to content
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
19 changes: 15 additions & 4 deletions models/chunter/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import {
type ObjectChatPanel,
type ThreadMessage,
type ChatInfo,
type ChannelInfo
type ChannelInfo,
type TypingInfo
} from '@hcengineering/chunter'
import presentation from '@hcengineering/model-presentation'
import contact, { type ChannelProvider as SocialChannelProvider, type Person } from '@hcengineering/contact'
Expand All @@ -35,7 +36,8 @@ import {
DOMAIN_MODEL,
type Ref,
type Timestamp,
IndexKind
IndexKind,
DOMAIN_TRANSIENT
} from '@hcengineering/core'
import {
type Builder,
Expand All @@ -56,7 +58,7 @@ import core, { TClass, TDoc, TSpace } from '@hcengineering/model-core'
import notification, { TDocNotifyContext } from '@hcengineering/model-notification'
import view from '@hcengineering/model-view'
import workbench from '@hcengineering/model-workbench'
import type { IntlString } from '@hcengineering/platform'
import { type IntlString } from '@hcengineering/platform'
import { TActivityMessage } from '@hcengineering/model-activity'
import { type DocNotifyContext } from '@hcengineering/notification'

Expand Down Expand Up @@ -155,6 +157,14 @@ export class TChatInfo extends TDoc implements ChatInfo {
timestamp!: Timestamp
}

@Model(chunter.class.TypingInfo, core.class.Doc, DOMAIN_TRANSIENT)
export class TTypingInfo extends TDoc implements TypingInfo {
objectId!: Ref<Doc>
objectClass!: Ref<Class<Doc>>
person!: Ref<Person>
lastTyping!: Timestamp
}

export function createModel (builder: Builder): void {
builder.createModel(
TChunterSpace,
Expand All @@ -165,7 +175,8 @@ export function createModel (builder: Builder): void {
TChatMessageViewlet,
TObjectChatPanel,
TChatInfo,
TChannelInfo
TChannelInfo,
TTypingInfo
)
const spaceClasses = [chunter.class.Channel, chunter.class.DirectMessage]

Expand Down
12 changes: 10 additions & 2 deletions plugins/activity-resources/src/components/Activity.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,11 @@
</div>
{#if isNewestFirst && showCommenInput}
<div class="ref-input newest-first">
<ActivityExtensionComponent kind="input" {extensions} props={{ object, boundary, focusIndex }} />
<ActivityExtensionComponent
kind="input"
{extensions}
props={{ object, boundary, focusIndex, withTypingInfo: true }}
/>
</div>
{/if}
<div
Expand Down Expand Up @@ -288,7 +292,11 @@
</div>
{#if showCommenInput && !isNewestFirst}
<div class="ref-input oldest-first">
<ActivityExtensionComponent kind="input" {extensions} props={{ object, boundary, focusIndex }} />
<ActivityExtensionComponent
kind="input"
{extensions}
props={{ object, boundary, focusIndex, withTypingInfo: true }}
/>
</div>
{/if}

Expand Down
4 changes: 3 additions & 1 deletion plugins/chunter-assets/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@
"CreatedChannelOn": "Created this channel on {date}",
"ChannelMessages": "Channel messages",
"JoinChannel": "Join channel",
"YouJoinedChannel": "You have been joined to channel"
"YouJoinedChannel": "You have been joined to channel",
"AndMore": "and {count} more",
"IsTyping": "{count, plural, =1 {is} other {are}} typing..."
}
}
4 changes: 3 additions & 1 deletion plugins/chunter-assets/lang/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@
"CreatedChannelOn": "Creó este canal el {date}",
"ChannelMessages": "Mensajes del canal",
"JoinChannel": "Unirse",
"YouJoinedChannel": "Te has unido al canal"
"YouJoinedChannel": "Te has unido al canal",
"AndMore": "y {count} más",
"IsTyping": "está escribiendo..."
}
}
4 changes: 3 additions & 1 deletion plugins/chunter-assets/lang/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@
"CreatedChannelOn": "A créé ce canal le {date}",
"ChannelMessages": "Messages du canal",
"JoinChannel": "Rejoindre",
"YouJoinedChannel": "Vous avez rejoint le canal"
"YouJoinedChannel": "Vous avez rejoint le canal",
"AndMore": "et {count} de plus",
"IsTyping": "est en train d'écrire..."
}
}
4 changes: 3 additions & 1 deletion plugins/chunter-assets/lang/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@
"CreatedChannelOn": "Criou este canal em {date}",
"ChannelMessages": "Mensagens do canal",
"JoinChannel": "Participar no canal",
"YouJoinedChannel": "Entrou no canal"
"YouJoinedChannel": "Entrou no canal",
"AndMore": "e mais {count}",
"IsTyping": "está a escrever..."
}
}
4 changes: 3 additions & 1 deletion plugins/chunter-assets/lang/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@
"CreatedChannelOn": "Создал этот канал {date}",
"ChannelMessages": "Сообщения каналов",
"JoinChannel": "Приссоединение к каналу",
"YouJoinedChannel": "Вы присоединились к каналу"
"YouJoinedChannel": "Вы присоединились к каналу",
"AndMore": "и еще {count}",
"IsTyping": "{count, plural, =1 {печатает} other {печатают}}..."
}
}
4 changes: 3 additions & 1 deletion plugins/chunter-assets/lang/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@
"CreatedChannelOn": "于 {date} 创建此频道",
"ChannelMessages": "频道消息",
"JoinChannel": "加入频道",
"YouJoinedChannel": "你已加入频道"
"YouJoinedChannel": "你已加入频道",
"AndMore": "和 {count} 人",
"IsTyping": "正在输入..."
}
}
2 changes: 0 additions & 2 deletions plugins/chunter-resources/src/components/Channel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,6 @@

{#if dataProvider}
<ChannelScrollView
objectId={object._id}
objectClass={object._class}
{object}
skipLabels={!isDocChannel}
selectedFilters={filters}
Expand Down
19 changes: 9 additions & 10 deletions plugins/chunter-resources/src/components/ChannelScrollView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
canGroupMessages,
messageInFocus
} from '@hcengineering/activity-resources'
import { Class, Doc, generateId, getDay, Ref, Timestamp } from '@hcengineering/core'
import { Doc, generateId, getDay, Ref, Timestamp } from '@hcengineering/core'
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
import { getResource } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation'
Expand All @@ -49,9 +49,7 @@
import chunter from '../plugin'

export let provider: ChannelDataProvider
export let object: Doc | undefined
export let objectClass: Ref<Class<Doc>>
export let objectId: Ref<Doc>
export let object: Doc
export let selectedMessageId: Ref<ActivityMessage> | undefined = undefined
export let scrollElement: HTMLDivElement | undefined | null = undefined
export let startFromBottom = false
Expand Down Expand Up @@ -115,9 +113,9 @@
$: messages = $messagesStore
$: isLoading = $isLoadingStore

$: extensions = client.getModel().findAllSync(activity.class.ActivityExtension, { ofClass: objectClass })
$: extensions = client.getModel().findAllSync(activity.class.ActivityExtension, { ofClass: doc._class })

$: notifyContext = $contextByDocStore.get(objectId)
$: notifyContext = $contextByDocStore.get(doc._id)

void client
.getModel()
Expand All @@ -129,7 +127,7 @@
}
})

$: displayMessages = filterChatMessages(messages, filters, filterResources, objectClass, selectedFilters)
$: displayMessages = filterChatMessages(messages, filters, filterResources, doc._class, selectedFilters)

const unsubscribe = inboxClient.inboxNotificationsByContext.subscribe(() => {
if (notifyContext !== undefined) {
Expand Down Expand Up @@ -674,7 +672,7 @@
}

const op = client.apply(generateId(), 'chunter.scrollDown')
await inboxClient.readDoc(op, objectId)
await inboxClient.readDoc(op, doc._id)
await op.commit()
}

Expand All @@ -693,7 +691,7 @@
if (unViewed.length === 0) {
forceRead = true
const op = client.apply(generateId(), 'chunter.forceReadContext')
await inboxClient.readDoc(op, objectId)
await inboxClient.readDoc(op, object._id)
await op.commit()
}
}
Expand Down Expand Up @@ -790,7 +788,7 @@
<ActivityExtensionComponent
kind="input"
{extensions}
props={{ object, boundary: scrollElement, collection, autofocus: true }}
props={{ object, boundary: scrollElement, collection, autofocus: true, withTypingInfo: true }}
/>
</div>
{/if}
Expand All @@ -805,6 +803,7 @@
.ref-input {
flex-shrink: 0;
margin: 1.25rem 1rem 1rem;
margin-bottom: 0;
max-height: 18.75rem;
}

Expand Down
83 changes: 83 additions & 0 deletions plugins/chunter-resources/src/components/ChannelTypingInfo.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<!--
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { onMount } from 'svelte'
import chunter, { TypingInfo } from '@hcengineering/chunter'
import { getName, Person } from '@hcengineering/contact'
import { personByIdStore } from '@hcengineering/contact-resources'
import { getCurrentAccount, IdMap } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import { Label } from '@hcengineering/ui'

export let typingInfo: TypingInfo[] = []

const typingDelay = 2000
const maxTypingPersons = 3
const me = getCurrentAccount()
const hierarchy = getClient().getHierarchy()

let typingPersonsLabel: string = ''
let typingPersonsCount = 0
let moreCount: number = 0

$: updateTypingPersons($personByIdStore, typingInfo)

function updateTypingPersons (personById: IdMap<Person>, typingInfo: TypingInfo[]) {
const now = Date.now()
const personIds = new Set(
typingInfo
.filter((info) => info.person !== me.person && now - info.lastTyping < typingDelay)
.map((info) => info.person)
)
const names = Array.from(personIds)
.map((personId) => personById.get(personId))
.filter((person): person is Person => person !== undefined)
.map((person) => getName(hierarchy, person))
.sort((name1, name2) => name1.localeCompare(name2))

typingPersonsCount = names.length
typingPersonsLabel = names.slice(0, maxTypingPersons).join(', ')
moreCount = Math.max(names.length - maxTypingPersons, 0)
}

onMount(() => {
const interval = setInterval(() => {
updateTypingPersons($personByIdStore, typingInfo)
}, typingDelay)
return () => {
clearInterval(interval)
}
})
</script>

<span class="root h-4 mt-1 mb-1 ml-0-5 overflow-label">
{#if typingPersonsLabel !== ''}
<span class="fs-bold">
{typingPersonsLabel}
</span>
{#if moreCount > 0}
<span class="ml-1"><Label label={chunter.string.AndMore} params={{ count: moreCount }} /></span>
{/if}
<span class="ml-1"><Label label={chunter.string.IsTyping} params={{ count: typingPersonsCount }} /></span>
{/if}
</span>

<style>
.root {
display: flex;
align-items: center;
font-size: 0.75rem;
}
</style>
Loading