mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-30 13:35:04 +02:00
feat:allow to customize typing (#10111)
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
@@ -14,37 +14,73 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import { getName, getCurrentEmployee, Person } from '@hcengineering/contact'
|
||||
import { type Doc, type PersonId, getCurrentAccount } from '@hcengineering/core'
|
||||
import { getName, getPersonRefsBySocialIds } from '@hcengineering/contact'
|
||||
import { getPersonsByPersonRefs } from '@hcengineering/contact-resources'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
import { typing } from '@hcengineering/presence-resources'
|
||||
import { Doc, type Ref } from '@hcengineering/core'
|
||||
|
||||
const maxTypingPersons = 3
|
||||
const me = getCurrentEmployee()
|
||||
const hierarchy = getClient().getHierarchy()
|
||||
import { type TypingInfo, typing } from '@hcengineering/presence-resources'
|
||||
|
||||
export let object: Doc
|
||||
|
||||
let typingInfo = new Map<string, Ref<Person>>()
|
||||
let typingPersonsLabel: string = ''
|
||||
let typingPersonsCount = 0
|
||||
let moreCount: number = 0
|
||||
const maxTypingPersons = 3
|
||||
const acc = getCurrentAccount()
|
||||
const client = getClient()
|
||||
const hierarchy = getClient().getHierarchy()
|
||||
|
||||
interface TypingGroup {
|
||||
status: IntlString
|
||||
names: string
|
||||
count: number
|
||||
moreCount: number
|
||||
}
|
||||
|
||||
let typingInfo = new Map<string, TypingInfo>()
|
||||
let typingGroups: TypingGroup[] = []
|
||||
|
||||
$: void updateTypingPersons(typingInfo)
|
||||
|
||||
async function updateTypingPersons (typingInfo: Map<string, Ref<Person>>): Promise<void> {
|
||||
const persons = await getPersonsByPersonRefs(Array.from(typingInfo.values()))
|
||||
const names = Array.from(persons.values())
|
||||
.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)
|
||||
async function updateTypingPersons (typingInfo: Map<string, TypingInfo>): Promise<void> {
|
||||
if (typingInfo.size === 0) {
|
||||
typingGroups = []
|
||||
return
|
||||
}
|
||||
|
||||
const groupedByStatus = new Map<IntlString, PersonId[]>()
|
||||
for (const info of typingInfo.values()) {
|
||||
const status = info.status ?? chunter.string.IsTyping
|
||||
const existing = groupedByStatus.get(status) ?? []
|
||||
existing.push(info.socialId)
|
||||
groupedByStatus.set(status, existing)
|
||||
}
|
||||
|
||||
const groups: TypingGroup[] = []
|
||||
|
||||
for (const [status, personIds] of groupedByStatus.entries()) {
|
||||
const personRefs = await getPersonRefsBySocialIds(client, personIds)
|
||||
const persons = await getPersonsByPersonRefs(Object.values(personRefs))
|
||||
const names = Array.from(persons.values())
|
||||
.map((person) => getName(hierarchy, person))
|
||||
.sort((name1, name2) => name1.localeCompare(name2))
|
||||
|
||||
const displayNames = names.slice(0, maxTypingPersons).join(', ')
|
||||
const moreCount = Math.max(names.length - maxTypingPersons, 0)
|
||||
|
||||
groups.push({
|
||||
status,
|
||||
names: displayNames,
|
||||
count: names.length,
|
||||
moreCount
|
||||
})
|
||||
}
|
||||
|
||||
groups.sort((a, b) => a.status.localeCompare(b.status))
|
||||
|
||||
typingGroups = groups
|
||||
}
|
||||
|
||||
function handleTyping (typing: Map<string, Ref<Person>>): void {
|
||||
function handleTyping (typing: Map<string, TypingInfo>): void {
|
||||
typingInfo = typing
|
||||
}
|
||||
</script>
|
||||
@@ -52,20 +88,18 @@
|
||||
<span
|
||||
class="root h-4 mt-1 mb-1 ml-0-5 overflow-label"
|
||||
use:typing={{
|
||||
personId: me,
|
||||
socialId: acc.primarySocialId,
|
||||
objectId: object._id,
|
||||
onTyping: handleTyping
|
||||
}}
|
||||
>
|
||||
{#if typingPersonsLabel !== ''}
|
||||
<span class="fs-bold">
|
||||
{typingPersonsLabel}
|
||||
</span>
|
||||
{#if moreCount > 0}
|
||||
<span class="ml-1"><Label label={chunter.string.AndMore} params={{ count: moreCount }} /></span>
|
||||
{#each typingGroups as group, index}
|
||||
<span class="fs-bold" class:ml-1={index > 0}>{group.names}</span>
|
||||
{#if group.moreCount > 0}
|
||||
<span class="ml-1"><Label label={chunter.string.AndMore} params={{ count: group.moreCount }} /></span>
|
||||
{/if}
|
||||
<span class="ml-1"><Label label={chunter.string.IsTyping} params={{ count: typingPersonsCount }} /></span>
|
||||
{/if}
|
||||
<span class="ml-1"><Label label={group.status} params={{ count: group.count }} /></span>
|
||||
{/each}
|
||||
</span>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -17,14 +17,13 @@
|
||||
import { Analytics } from '@hcengineering/analytics'
|
||||
import { AttachmentRefInput } from '@hcengineering/attachment-resources'
|
||||
import chunter, { ChatMessage, ChunterEvents, ThreadMessage } from '@hcengineering/chunter'
|
||||
import { Class, Doc, generateId, Ref, type CommitResult } from '@hcengineering/core'
|
||||
import { Class, Doc, generateId, getCurrentAccount, Ref, type CommitResult } from '@hcengineering/core'
|
||||
import { createQuery, DraftController, draftsStore, getClient } from '@hcengineering/presentation'
|
||||
import { EmptyMarkup, isEmptyMarkup } from '@hcengineering/text'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { getObjectId } from '@hcengineering/view-resources'
|
||||
import { ThrottledCaller } from '@hcengineering/ui'
|
||||
import { getSpace, editingMessageStore } from '@hcengineering/activity-resources'
|
||||
import { getCurrentEmployee } from '@hcengineering/contact'
|
||||
|
||||
import { getChannelSpace } from '../../utils'
|
||||
import ChannelTypingInfo from '../ChannelTypingInfo.svelte'
|
||||
@@ -99,19 +98,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
const me = getCurrentEmployee()
|
||||
const acc = getCurrentAccount()
|
||||
const throttle = new ThrottledCaller(500)
|
||||
|
||||
async function deleteTypingInfo (): Promise<void> {
|
||||
if (!withTypingInfo) return
|
||||
void clearTyping(me, object._id)
|
||||
void clearTyping(acc.primarySocialId, object._id)
|
||||
}
|
||||
|
||||
async function updateTypingInfo (): Promise<void> {
|
||||
if (!withTypingInfo) return
|
||||
|
||||
throttle.call(() => {
|
||||
void setTyping(me, object._id)
|
||||
void setTyping(acc.primarySocialId, object._id)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"YesterdayAt": "Včera v {time}",
|
||||
"AndMore": "a {count} dalších",
|
||||
"IsTyping": "{count, plural, =1 {píše} other {píší}}",
|
||||
"IsThinking": "{count, plural, =1 {přemýšlí} other {přemýšlejí}}",
|
||||
"Loading": "Načítání...",
|
||||
"MessageIn": "Zpráva #{title}",
|
||||
"ThreadWasDeleted": "Tato vlákno byla smazána.",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"YesterdayAt": "Gestern um {time}",
|
||||
"AndMore": "und {count} weitere",
|
||||
"IsTyping": "{count, plural, =1 {schreibt} other {schreiben}}",
|
||||
"IsThinking": "{count, plural, =1 {denkt nach} other {denken nach}}",
|
||||
"Loading": "Laden...",
|
||||
"MessageIn": "Nachricht #{title}",
|
||||
"ThreadWasDeleted": "Dieser Thread wurde gelöscht.",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"YesterdayAt": "Yesterday at {time}",
|
||||
"AndMore": "and {count} more",
|
||||
"IsTyping": "{count, plural, =1 {is typing} other {are typing}}...",
|
||||
"IsThinking": "{count, plural, =1 {is thinking} other {are thinking}}...",
|
||||
"Loading": "Loading...",
|
||||
"MessageIn": "Message #{title}",
|
||||
"ThreadWasRemoved": "This thread was removed.",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"YesterdayAt": "Ayer a las {time}",
|
||||
"AndMore": "y {count} más",
|
||||
"IsTyping": "{count, plural, =1 {está escribiendo} other {están escribiendo}}",
|
||||
"IsThinking": "{count, plural, =1 {está pensando} other {están pensando}}",
|
||||
"Loading": "Cargando...",
|
||||
"MessageIn": "Mensaje #{title}",
|
||||
"ThreadWasDeleted": "Este hilo fue eliminado.",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"YesterdayAt": "Hier à {time}",
|
||||
"AndMore": "et {count} autres",
|
||||
"IsTyping": "{count, plural, =1 {est en train d'écrire} other {écrivent}}",
|
||||
"IsThinking": "{count, plural, =1 {est en train de réfléchir} other {réfléchissent}}",
|
||||
"Loading": "Chargement...",
|
||||
"MessageIn": "Message #{title}",
|
||||
"ThreadWasDeleted": "Ce fil de discussion a été supprimé.",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"YesterdayAt": "Ieri alle {time}",
|
||||
"AndMore": "e {count} altri",
|
||||
"IsTyping": "{count, plural, =1 {sta scrivendo} other {stanno scrivendo}}",
|
||||
"IsThinking": "{count, plural, =1 {sta pensando} other {stanno pensando}}",
|
||||
"Loading": "Caricamento...",
|
||||
"MessageIn": "Messaggio #{title}",
|
||||
"ThreadWasDeleted": "Questo thread è stato eliminato.",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"YesterdayAt": "昨日・{time}",
|
||||
"AndMore": "および{count}件の他",
|
||||
"IsTyping": "{count, plural, =1 {入力中} other {入力中}}",
|
||||
"IsThinking": "{count, plural, =1 {考え中} other {考え中}}",
|
||||
"Loading": "読み込み中...",
|
||||
"MessageIn": "メッセージ #{title}",
|
||||
"ThreadWasDeleted": "このスレッドは削除されました。",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"YesterdayAt": "Ontem às {time}",
|
||||
"AndMore": "e mais {count}",
|
||||
"IsTyping": "{count, plural, =1 {está digitando} other {estão digitando}}",
|
||||
"IsThinking": "{count, plural, =1 {está pensando} other {estão pensando}}",
|
||||
"Loading": "Carregando...",
|
||||
"MessageIn": "Mensagem #{title}",
|
||||
"ThreadWasDeleted": "Este thread foi excluído.",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"YesterdayAt": "Вчера в {time}",
|
||||
"AndMore": "и еще {count}",
|
||||
"IsTyping": "{count, plural, =1 {печатает} other {печатают}}...",
|
||||
"IsThinking": "{count, plural, =1 {думает} other {думают}}...",
|
||||
"Loading": "Загрузка...",
|
||||
"MessageIn": "Сообщение #{title}",
|
||||
"ThreadWasRemoved": "Этот поток был удален.",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"YesterdayAt": "Dün - {time}",
|
||||
"AndMore": "ve {count} daha",
|
||||
"IsTyping": "{count, plural, =1 {yazıyor} other {yazıyorlar}}...",
|
||||
"IsThinking": "{count, plural, =1 {düşünüyor} other {düşünüyorlar}}...",
|
||||
"Loading": "Yükleniyor...",
|
||||
"MessageIn": "#{title} içinde mesaj",
|
||||
"ThreadWasRemoved": "Bu başlık kaldırıldı.",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"YesterdayAt": "昨天 于 {time}",
|
||||
"AndMore": "和其他 {count} 条",
|
||||
"IsTyping": "{count, plural, =1 {正在输入} other {正在输入}}",
|
||||
"IsThinking": "{count, plural, =1 {正在思考} other {正在思考}}",
|
||||
"Loading": "加载中...",
|
||||
"MessageIn": "消息 #{title}",
|
||||
"ThreadWasDeleted": "此线程已被删除。",
|
||||
|
||||
@@ -1,50 +1,88 @@
|
||||
<!-- Copyright © 2025 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. -->
|
||||
<!--
|
||||
// Copyright © 2025 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 { getName, getCurrentEmployee, Person } from '@hcengineering/contact'
|
||||
import { type PersonId, getCurrentAccount } from '@hcengineering/core'
|
||||
import { getName, getPersonRefsBySocialIds } from '@hcengineering/contact'
|
||||
import { getPersonsByPersonRefs } from '@hcengineering/contact-resources'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
import { typing } from '@hcengineering/presence-resources'
|
||||
import { type TypingInfo, typing } from '@hcengineering/presence-resources'
|
||||
import { CardID } from '@hcengineering/communication-types'
|
||||
|
||||
import communication from '../plugin'
|
||||
|
||||
export let cardId: CardID
|
||||
|
||||
const maxTypingPersons = 3
|
||||
const me = getCurrentEmployee()
|
||||
const acc = getCurrentAccount()
|
||||
const client = getClient()
|
||||
const hierarchy = getClient().getHierarchy()
|
||||
|
||||
let typingInfo = new Map<string, Ref<Person>>()
|
||||
let typingPersonsLabel: string = ''
|
||||
let typingPersonsCount = 0
|
||||
let moreCount: number = 0
|
||||
interface TypingGroup {
|
||||
status: IntlString
|
||||
names: string
|
||||
count: number
|
||||
moreCount: number
|
||||
}
|
||||
|
||||
let typingInfo = new Map<string, TypingInfo>()
|
||||
let typingGroups: TypingGroup[] = []
|
||||
|
||||
$: void updateTypingPersons(typingInfo)
|
||||
|
||||
async function updateTypingPersons (typingInfo: Map<string, Ref<Person>>): Promise<void> {
|
||||
const persons = await getPersonsByPersonRefs(Array.from(typingInfo.values()))
|
||||
const names = Array.from(persons.values())
|
||||
.map((person) => getName(hierarchy, person))
|
||||
.sort((name1, name2) => name1.localeCompare(name2))
|
||||
async function updateTypingPersons (typingInfo: Map<string, TypingInfo>): Promise<void> {
|
||||
if (typingInfo.size === 0) {
|
||||
typingGroups = []
|
||||
return
|
||||
}
|
||||
|
||||
typingPersonsCount = names.length
|
||||
typingPersonsLabel = names.slice(0, maxTypingPersons).join(', ')
|
||||
moreCount = Math.max(names.length - maxTypingPersons, 0)
|
||||
const groupedByStatus = new Map<IntlString, PersonId[]>()
|
||||
for (const info of typingInfo.values()) {
|
||||
const status = info.status ?? communication.string.IsTyping
|
||||
const existing = groupedByStatus.get(status) ?? []
|
||||
existing.push(info.socialId)
|
||||
groupedByStatus.set(status, existing)
|
||||
}
|
||||
|
||||
const groups: TypingGroup[] = []
|
||||
|
||||
for (const [status, personIds] of groupedByStatus.entries()) {
|
||||
const personRefs = await getPersonRefsBySocialIds(client, personIds)
|
||||
const persons = await getPersonsByPersonRefs(Object.values(personRefs))
|
||||
const names = Array.from(persons.values())
|
||||
.map((person) => getName(hierarchy, person))
|
||||
.sort((name1, name2) => name1.localeCompare(name2))
|
||||
|
||||
const displayNames = names.slice(0, maxTypingPersons).join(', ')
|
||||
const moreCount = Math.max(names.length - maxTypingPersons, 0)
|
||||
|
||||
groups.push({
|
||||
status,
|
||||
names: displayNames,
|
||||
count: names.length,
|
||||
moreCount
|
||||
})
|
||||
}
|
||||
|
||||
groups.sort((a, b) => a.status.localeCompare(b.status))
|
||||
|
||||
typingGroups = groups
|
||||
}
|
||||
|
||||
function handleTyping (typing: Map<string, Ref<Person>>): void {
|
||||
function handleTyping (typing: Map<string, TypingInfo>): void {
|
||||
typingInfo = typing
|
||||
}
|
||||
</script>
|
||||
@@ -52,20 +90,18 @@
|
||||
<span
|
||||
class="root h-4 mt-1 mb-1 ml-0-5 overflow-label"
|
||||
use:typing={{
|
||||
personId: me,
|
||||
socialId: acc.primarySocialId,
|
||||
objectId: cardId,
|
||||
onTyping: handleTyping
|
||||
}}
|
||||
>
|
||||
{#if typingPersonsLabel !== ''}
|
||||
<span class="fs-bold">
|
||||
{typingPersonsLabel}
|
||||
</span>
|
||||
{#if moreCount > 0}
|
||||
<span class="ml-1"><Label label={communication.string.AndMore} params={{ count: moreCount }} /></span>
|
||||
{#each typingGroups as group, index}
|
||||
<span class="fs-bold" class:ml-1={index > 0}>{group.names}</span>
|
||||
{#if group.moreCount > 0}
|
||||
<span class="ml-1"><Label label={communication.string.AndMore} params={{ count: group.moreCount }} /></span>
|
||||
{/if}
|
||||
<span class="ml-1"><Label label={communication.string.IsTyping} params={{ count: typingPersonsCount }} /></span>
|
||||
{/if}
|
||||
<span class="ml-1"><Label label={group.status} params={{ count: group.count }} /></span>
|
||||
{/each}
|
||||
</span>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -27,8 +27,7 @@
|
||||
Message,
|
||||
MessageID
|
||||
} from '@hcengineering/communication-types'
|
||||
import { getCurrentEmployee } from '@hcengineering/contact'
|
||||
import { generateId, Markup, RateLimiter, Ref } from '@hcengineering/core'
|
||||
import { generateId, getCurrentAccount, Markup, RateLimiter, Ref } from '@hcengineering/core'
|
||||
import { getResource, setPlatformStatus, unknownError } from '@hcengineering/platform'
|
||||
import { clearTyping, setTyping } from '@hcengineering/presence-resources'
|
||||
import {
|
||||
@@ -69,7 +68,7 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
const communicationClient = getCommunicationClient()
|
||||
const client = getClient()
|
||||
const me = getCurrentEmployee()
|
||||
const acc = getCurrentAccount()
|
||||
|
||||
const maxLinkPreviewCount = 4
|
||||
const previewUrls = new Map<string, boolean>()
|
||||
@@ -140,7 +139,7 @@
|
||||
await editMessage(message, markdown, blobsToLoad, linksToLoad, appletsToLoad)
|
||||
}
|
||||
|
||||
void clearTyping(me, card._id)
|
||||
void clearTyping(acc.primarySocialId, card._id)
|
||||
}
|
||||
|
||||
async function attachApplets (messageId: MessageID, appletDrafts: AppletDraft[]): Promise<void> {
|
||||
@@ -462,7 +461,7 @@
|
||||
if (message !== undefined) return
|
||||
if (!isEmptyMarkup(draft.content)) {
|
||||
throttle.call(() => {
|
||||
void setTyping(me, card._id)
|
||||
void setTyping(acc.primarySocialId, card._id)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ export default mergeIds(communicationId, communication, {
|
||||
YesterdayAt: '' as IntlString,
|
||||
AndMore: '' as IntlString,
|
||||
IsTyping: '' as IntlString,
|
||||
IsThinking: '' as IntlString,
|
||||
Loading: '' as IntlString,
|
||||
MessageIn: '' as IntlString,
|
||||
ThreadWasRemoved: '' as IntlString,
|
||||
|
||||
@@ -11,11 +11,10 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License
|
||||
|
||||
import { type Employee, type Person } from '@hcengineering/contact'
|
||||
import { type UnsubscribeCallback, type Callback } from '@hcengineering/hulypulse-client'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
import { type IntlString, getMetadata } from '@hcengineering/platform'
|
||||
import presentation, { createPulseClient } from '@hcengineering/presentation'
|
||||
import { type Doc, type Ref } from '@hcengineering/core'
|
||||
import { type Doc, type Ref, type PersonId } from '@hcengineering/core'
|
||||
|
||||
const typingDelaySeconds = 2
|
||||
|
||||
@@ -24,36 +23,37 @@ function getWorkspace (): string {
|
||||
}
|
||||
|
||||
export interface TypingInfo {
|
||||
personId: Ref<Person>
|
||||
socialId: PersonId
|
||||
objectId: Ref<Doc>
|
||||
status?: IntlString
|
||||
}
|
||||
|
||||
export interface TypingActionParams {
|
||||
personId: Ref<Employee>
|
||||
socialId: PersonId
|
||||
objectId: Ref<Doc>
|
||||
onTyping: (presence: Map<string, Ref<Person>>) => void
|
||||
onTyping: (presence: Map<string, TypingInfo>) => void
|
||||
}
|
||||
|
||||
export function typing (node: HTMLElement, params: TypingActionParams): any {
|
||||
let unsubscribe: Promise<UnsubscribeCallback> | undefined
|
||||
let presence = new Map<string, Ref<Person>>()
|
||||
let typing = new Map<string, TypingInfo>()
|
||||
|
||||
let personId = params.personId
|
||||
let socialId = params.socialId
|
||||
let objectId = params.objectId
|
||||
let onTyping = params.onTyping
|
||||
|
||||
function handleTypingInfo (key: string, value: TypingInfo | undefined): void {
|
||||
if (value?.personId === personId) {
|
||||
if (value?.socialId === socialId) {
|
||||
return
|
||||
}
|
||||
|
||||
if (value === undefined) {
|
||||
presence.delete(key)
|
||||
typing.delete(key)
|
||||
} else {
|
||||
presence.set(key, value.personId)
|
||||
typing.set(key, value)
|
||||
}
|
||||
|
||||
onTyping(presence)
|
||||
onTyping(typing)
|
||||
}
|
||||
|
||||
unsubscribe = subscribeTyping(params.objectId, handleTypingInfo)
|
||||
@@ -61,7 +61,7 @@ export function typing (node: HTMLElement, params: TypingActionParams): any {
|
||||
return {
|
||||
update: (params: TypingActionParams) => {
|
||||
if (objectId !== params.objectId) {
|
||||
personId = params.personId
|
||||
socialId = params.socialId
|
||||
objectId = params.objectId
|
||||
onTyping = params.onTyping
|
||||
|
||||
@@ -69,10 +69,10 @@ export function typing (node: HTMLElement, params: TypingActionParams): any {
|
||||
void unsub()
|
||||
})
|
||||
|
||||
presence = new Map<string, Ref<Person>>()
|
||||
typing = new Map<string, TypingInfo>()
|
||||
unsubscribe = subscribeTyping(params.objectId, handleTypingInfo)
|
||||
|
||||
onTyping(presence)
|
||||
onTyping(typing)
|
||||
}
|
||||
},
|
||||
destroy: () => {
|
||||
@@ -100,27 +100,27 @@ export async function subscribeTyping (
|
||||
return async () => false
|
||||
}
|
||||
|
||||
export async function setTyping (personId: Ref<Employee>, objectId: Ref<Doc>): Promise<void> {
|
||||
export async function setTyping (socialId: PersonId, objectId: Ref<Doc>, status?: IntlString): Promise<void> {
|
||||
const client = await createPulseClient()
|
||||
|
||||
if (client !== undefined) {
|
||||
const workspace = getWorkspace()
|
||||
const typingInfo: TypingInfo = { personId, objectId }
|
||||
const typingInfo: TypingInfo = { socialId, objectId, status }
|
||||
try {
|
||||
await client.put(`${workspace}/typing/${objectId}/${personId}`, typingInfo, typingDelaySeconds)
|
||||
await client.put(`${workspace}/typing/${objectId}/${socialId}`, typingInfo, typingDelaySeconds)
|
||||
} catch (error) {
|
||||
console.warn('failed to put typing info:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function clearTyping (me: string, objectId: string): Promise<void> {
|
||||
export async function clearTyping (socialId: PersonId, objectId: string): Promise<void> {
|
||||
const client = await createPulseClient()
|
||||
|
||||
if (client !== undefined) {
|
||||
const workspace = getWorkspace()
|
||||
try {
|
||||
await client.delete(`${workspace}/typing/${objectId}/${me}`)
|
||||
await client.delete(`${workspace}/typing/${objectId}/${socialId}`)
|
||||
} catch (error) {
|
||||
console.warn('failed to delete typing info:', error)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user