mirror of
https://github.com/hcengineering/platform.git
synced 2025-03-19 13:18:56 +00:00
Merge remote-tracking branch 'origin/develop' into staging
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
commit
ad3473acc2
@ -35,6 +35,7 @@
|
|||||||
"@hcengineering/model": "^0.6.11",
|
"@hcengineering/model": "^0.6.11",
|
||||||
"@hcengineering/model-core": "^0.6.0",
|
"@hcengineering/model-core": "^0.6.0",
|
||||||
"@hcengineering/model-preference": "^0.6.0",
|
"@hcengineering/model-preference": "^0.6.0",
|
||||||
|
"@hcengineering/model-presentation": "^0.6.0",
|
||||||
"@hcengineering/model-view": "^0.6.0",
|
"@hcengineering/model-view": "^0.6.0",
|
||||||
"@hcengineering/notification": "^0.6.23",
|
"@hcengineering/notification": "^0.6.23",
|
||||||
"@hcengineering/platform": "^0.6.11",
|
"@hcengineering/platform": "^0.6.11",
|
||||||
|
@ -70,6 +70,7 @@ import preference, { TPreference } from '@hcengineering/model-preference'
|
|||||||
import view from '@hcengineering/model-view'
|
import view from '@hcengineering/model-view'
|
||||||
import type { Asset, IntlString, Resource } from '@hcengineering/platform'
|
import type { Asset, IntlString, Resource } from '@hcengineering/platform'
|
||||||
import { type AnyComponent } from '@hcengineering/ui/src/types'
|
import { type AnyComponent } from '@hcengineering/ui/src/types'
|
||||||
|
import presentation from '@hcengineering/model-presentation'
|
||||||
|
|
||||||
import activity from './plugin'
|
import activity from './plugin'
|
||||||
import { buildActions } from './actions'
|
import { buildActions } from './actions'
|
||||||
@ -375,6 +376,10 @@ export function createModel (builder: Builder): void {
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
builder.mixin(activity.class.Reaction, core.class.Class, presentation.mixin.InstantTransactions, {
|
||||||
|
txClasses: [core.class.TxCreateDoc]
|
||||||
|
})
|
||||||
|
|
||||||
buildActions(builder)
|
buildActions(builder)
|
||||||
buildNotifications(builder)
|
buildNotifications(builder)
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
|||||||
throw new Error('createDoc cannot be called for DOMAIN_MODEL classes with non-model space')
|
throw new Error('createDoc cannot be called for DOMAIN_MODEL classes with non-model space')
|
||||||
}
|
}
|
||||||
const tx = this.txFactory.createTxCreateDoc(_class, space, attributes, id, modifiedOn, modifiedBy)
|
const tx = this.txFactory.createTxCreateDoc(_class, space, attributes, id, modifiedOn, modifiedBy)
|
||||||
await this.client.tx(tx)
|
await this.tx(tx)
|
||||||
return tx.objectId
|
return tx.objectId
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
|||||||
modifiedOn,
|
modifiedOn,
|
||||||
modifiedBy
|
modifiedBy
|
||||||
)
|
)
|
||||||
await this.client.tx(tx)
|
await this.tx(tx)
|
||||||
return tx.tx.objectId as unknown as Ref<P>
|
return tx.tx.objectId as unknown as Ref<P>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
|||||||
modifiedOn,
|
modifiedOn,
|
||||||
modifiedBy
|
modifiedBy
|
||||||
)
|
)
|
||||||
await this.client.tx(tx)
|
await this.tx(tx)
|
||||||
return tx.objectId
|
return tx.objectId
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +170,7 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
|||||||
modifiedOn,
|
modifiedOn,
|
||||||
modifiedBy
|
modifiedBy
|
||||||
)
|
)
|
||||||
await this.client.tx(tx)
|
await this.tx(tx)
|
||||||
return tx.objectId
|
return tx.objectId
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
|||||||
modifiedBy?: Ref<Account>
|
modifiedBy?: Ref<Account>
|
||||||
): Promise<TxResult> {
|
): Promise<TxResult> {
|
||||||
const tx = this.txFactory.createTxUpdateDoc(_class, space, objectId, operations, retrieve, modifiedOn, modifiedBy)
|
const tx = this.txFactory.createTxUpdateDoc(_class, space, objectId, operations, retrieve, modifiedOn, modifiedBy)
|
||||||
return this.client.tx(tx)
|
return this.tx(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeDoc<T extends Doc>(
|
removeDoc<T extends Doc>(
|
||||||
@ -195,7 +195,7 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
|||||||
modifiedBy?: Ref<Account>
|
modifiedBy?: Ref<Account>
|
||||||
): Promise<TxResult> {
|
): Promise<TxResult> {
|
||||||
const tx = this.txFactory.createTxRemoveDoc(_class, space, objectId, modifiedOn, modifiedBy)
|
const tx = this.txFactory.createTxRemoveDoc(_class, space, objectId, modifiedOn, modifiedBy)
|
||||||
return this.client.tx(tx)
|
return this.tx(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
createMixin<D extends Doc, M extends D>(
|
createMixin<D extends Doc, M extends D>(
|
||||||
@ -216,7 +216,7 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
|||||||
modifiedOn,
|
modifiedOn,
|
||||||
modifiedBy
|
modifiedBy
|
||||||
)
|
)
|
||||||
return this.client.tx(tx)
|
return this.tx(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMixin<D extends Doc, M extends D>(
|
updateMixin<D extends Doc, M extends D>(
|
||||||
@ -237,7 +237,7 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
|||||||
modifiedOn,
|
modifiedOn,
|
||||||
modifiedBy
|
modifiedBy
|
||||||
)
|
)
|
||||||
return this.client.tx(tx)
|
return this.tx(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
async update<T extends Doc>(
|
async update<T extends Doc>(
|
||||||
|
@ -111,7 +111,7 @@ export function addNotification (
|
|||||||
title,
|
title,
|
||||||
subTitle,
|
subTitle,
|
||||||
severity,
|
severity,
|
||||||
position: NotificationPosition.TopRight,
|
position: NotificationPosition.BottomLeft,
|
||||||
component,
|
component,
|
||||||
closeTimeout,
|
closeTimeout,
|
||||||
params
|
params
|
||||||
|
@ -211,6 +211,11 @@
|
|||||||
{
|
{
|
||||||
sort: {
|
sort: {
|
||||||
createdOn: SortingOrder.Ascending
|
createdOn: SortingOrder.Ascending
|
||||||
|
},
|
||||||
|
lookup: {
|
||||||
|
_id: {
|
||||||
|
reactions: activity.class.Reaction
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
$: void updateInlineActions(message, excludedActions)
|
$: void updateInlineActions(message, excludedActions)
|
||||||
|
|
||||||
savedMessagesStore.subscribe(() => {
|
savedMessagesStore.subscribe(() => {
|
||||||
void updateInlineActions(message)
|
void updateInlineActions(message, excludedActions)
|
||||||
})
|
})
|
||||||
|
|
||||||
function handleActionMenuOpened (): void {
|
function handleActionMenuOpened (): void {
|
||||||
|
@ -69,6 +69,7 @@
|
|||||||
attribute={attributeModel.attribute}
|
attribute={attributeModel.attribute}
|
||||||
value={values[0]}
|
value={values[0]}
|
||||||
{prevValue}
|
{prevValue}
|
||||||
|
withShowMore={false}
|
||||||
showOnlyDiff
|
showOnlyDiff
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
ev.stopPropagation()
|
ev.stopPropagation()
|
||||||
showPopup(EmojiPopup, {}, ev.target as HTMLElement, async (emoji: string) => {
|
showPopup(EmojiPopup, {}, ev.target as HTMLElement, async (emoji: string) => {
|
||||||
await updateDocReactions(client, reactions, object, emoji)
|
await updateDocReactions(reactions, object, emoji)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -14,40 +14,48 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import activity, { ActivityMessage, Reaction } from '@hcengineering/activity'
|
import activity, { ActivityMessage, Reaction } from '@hcengineering/activity'
|
||||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
import { createQuery } from '@hcengineering/presentation'
|
||||||
|
import { WithLookup } from '@hcengineering/core'
|
||||||
|
|
||||||
import { getSpace, updateDocReactions } from '../../utils'
|
import { getSpace, updateDocReactions } from '../../utils'
|
||||||
import Reactions from './Reactions.svelte'
|
import Reactions from './Reactions.svelte'
|
||||||
|
|
||||||
export let object: ActivityMessage | undefined
|
export let object: WithLookup<ActivityMessage> | undefined
|
||||||
export let readonly = false
|
export let readonly = false
|
||||||
|
|
||||||
const client = getClient()
|
|
||||||
const reactionsQuery = createQuery()
|
const reactionsQuery = createQuery()
|
||||||
|
|
||||||
let reactions: Reaction[] = []
|
let reactions: Reaction[] = []
|
||||||
|
|
||||||
$: hasReactions = object?.reactions && object.reactions > 0
|
$: hasReactions = (object?.reactions ?? 0) > 0
|
||||||
|
$: lookupReactions = object?.$lookup?.reactions as Reaction[] | undefined
|
||||||
|
|
||||||
$: if (object && hasReactions) {
|
$: updateReactions(hasReactions, object, lookupReactions)
|
||||||
reactionsQuery.query(
|
|
||||||
activity.class.Reaction,
|
function updateReactions (hasReactions: boolean, object?: ActivityMessage, lookupReaction?: Reaction[]): void {
|
||||||
{ attachedTo: object._id, space: getSpace(object) },
|
if (lookupReaction !== undefined) {
|
||||||
(res: Reaction[]) => {
|
reactions = lookupReaction
|
||||||
reactions = res
|
} else if (object && hasReactions) {
|
||||||
}
|
reactionsQuery.query(
|
||||||
)
|
activity.class.Reaction,
|
||||||
} else {
|
{ attachedTo: object._id, space: getSpace(object) },
|
||||||
reactionsQuery.unsubscribe()
|
(res: Reaction[]) => {
|
||||||
|
reactions = res
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
reactionsQuery.unsubscribe()
|
||||||
|
reactions = []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClick = (ev: CustomEvent) => {
|
const handleClick = (ev: CustomEvent) => {
|
||||||
if (readonly) return
|
if (readonly) return
|
||||||
void updateDocReactions(client, reactions, object, ev.detail)
|
void updateDocReactions(reactions, object, ev.detail)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if object && hasReactions}
|
{#if object && reactions.length > 0}
|
||||||
<div class="footer flex-col p-inline contrast mt-2 min-h-6">
|
<div class="footer flex-col p-inline contrast mt-2 min-h-6">
|
||||||
<Reactions {reactions} {object} {readonly} on:click={handleClick} />
|
<Reactions {reactions} {object} {readonly} on:click={handleClick} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -66,7 +66,7 @@
|
|||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
showPopup(EmojiPopup, {}, e.target as HTMLElement, (emoji: string) => {
|
showPopup(EmojiPopup, {}, e.target as HTMLElement, (emoji: string) => {
|
||||||
void updateDocReactions(client, reactions, message, emoji)
|
void updateDocReactions(reactions, message, emoji)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,12 +1,5 @@
|
|||||||
import type { ActivityMessage, Reaction } from '@hcengineering/activity'
|
import type { ActivityMessage, Reaction } from '@hcengineering/activity'
|
||||||
import core, {
|
import core, { getCurrentAccount, isOtherHour, type Doc, type Ref, type Space } from '@hcengineering/core'
|
||||||
getCurrentAccount,
|
|
||||||
isOtherHour,
|
|
||||||
type Doc,
|
|
||||||
type Ref,
|
|
||||||
type TxOperations,
|
|
||||||
type Space
|
|
||||||
} from '@hcengineering/core'
|
|
||||||
import { getClient, isSpace } from '@hcengineering/presentation'
|
import { getClient, isSpace } from '@hcengineering/presentation'
|
||||||
import {
|
import {
|
||||||
EmojiPopup,
|
EmojiPopup,
|
||||||
@ -22,18 +15,13 @@ import { get } from 'svelte/store'
|
|||||||
import { savedMessagesStore } from './activity'
|
import { savedMessagesStore } from './activity'
|
||||||
import activity from './plugin'
|
import activity from './plugin'
|
||||||
|
|
||||||
export async function updateDocReactions (
|
export async function updateDocReactions (reactions: Reaction[], object?: Doc, emoji?: string): Promise<void> {
|
||||||
client: TxOperations,
|
|
||||||
reactions: Reaction[],
|
|
||||||
object?: Doc,
|
|
||||||
emoji?: string
|
|
||||||
): Promise<void> {
|
|
||||||
if (emoji === undefined || object === undefined) {
|
if (emoji === undefined || object === undefined) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const client = getClient()
|
||||||
const currentAccount = getCurrentAccount()
|
const currentAccount = getCurrentAccount()
|
||||||
|
|
||||||
const reaction = reactions.find((r) => r.emoji === emoji && r.createBy === currentAccount._id)
|
const reaction = reactions.find((r) => r.emoji === emoji && r.createBy === currentAccount._id)
|
||||||
|
|
||||||
if (reaction == null) {
|
if (reaction == null) {
|
||||||
@ -72,7 +60,7 @@ export async function addReactionAction (
|
|||||||
closePopup()
|
closePopup()
|
||||||
|
|
||||||
showPopup(EmojiPopup, {}, element, (emoji: string) => {
|
showPopup(EmojiPopup, {}, element, (emoji: string) => {
|
||||||
void updateDocReactions(client, reactions, message, emoji)
|
void updateDocReactions(reactions, message, emoji)
|
||||||
params?.onClose?.()
|
params?.onClose?.()
|
||||||
})
|
})
|
||||||
params?.onOpen?.()
|
params?.onOpen?.()
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
type DocumentQuery,
|
type DocumentQuery,
|
||||||
getCurrentAccount,
|
getCurrentAccount,
|
||||||
isOtherDay,
|
isOtherDay,
|
||||||
|
type Lookup,
|
||||||
type Ref,
|
type Ref,
|
||||||
SortingOrder,
|
SortingOrder,
|
||||||
type Space,
|
type Space,
|
||||||
@ -119,7 +120,7 @@ export class ChannelDataProvider implements IChannelDataProvider {
|
|||||||
})
|
})
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
readonly context: DocNotifyContext | undefined,
|
private context: DocNotifyContext | undefined,
|
||||||
readonly space: Ref<Space>,
|
readonly space: Ref<Space>,
|
||||||
chatId: Ref<Doc>,
|
chatId: Ref<Doc>,
|
||||||
_class: Ref<Class<ActivityMessage>>,
|
_class: Ref<Class<ActivityMessage>>,
|
||||||
@ -209,6 +210,13 @@ export class ChannelDataProvider implements IChannelDataProvider {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateNewTimestamp (context?: DocNotifyContext): Promise<void> {
|
||||||
|
this.context = context ?? this.context
|
||||||
|
const firstNewMsgIndex = await this.getFirstNewMsgIndex()
|
||||||
|
const metadata = get(this.metadataStore)
|
||||||
|
this.newTimestampStore.set(firstNewMsgIndex !== undefined ? metadata[firstNewMsgIndex]?.createdOn : undefined)
|
||||||
|
}
|
||||||
|
|
||||||
private async loadInitialMessages (
|
private async loadInitialMessages (
|
||||||
selectedMsg?: Ref<ActivityMessage>,
|
selectedMsg?: Ref<ActivityMessage>,
|
||||||
loadAll = false,
|
loadAll = false,
|
||||||
@ -285,13 +293,21 @@ export class ChannelDataProvider implements IChannelDataProvider {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
sort: { createdOn: SortingOrder.Descending },
|
sort: { createdOn: SortingOrder.Descending },
|
||||||
lookup: {
|
lookup: this.getLookup()
|
||||||
_id: { attachments: attachment.class.Attachment, inlineButtons: chunter.class.InlineButton }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLookup (): Lookup<ActivityMessage> {
|
||||||
|
return {
|
||||||
|
_id: {
|
||||||
|
attachments: attachment.class.Attachment,
|
||||||
|
inlineButtons: chunter.class.InlineButton,
|
||||||
|
reactions: activity.class.Reaction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
isNextLoading (mode: LoadMode): boolean {
|
isNextLoading (mode: LoadMode): boolean {
|
||||||
return mode === 'forward' ? get(this.isForwardLoading) : get(this.isBackwardLoading)
|
return mode === 'forward' ? get(this.isForwardLoading) : get(this.isBackwardLoading)
|
||||||
}
|
}
|
||||||
@ -331,9 +347,7 @@ export class ChannelDataProvider implements IChannelDataProvider {
|
|||||||
{
|
{
|
||||||
limit: limit ?? this.limit,
|
limit: limit ?? this.limit,
|
||||||
sort: { createdOn: isBackward ? SortingOrder.Descending : SortingOrder.Ascending },
|
sort: { createdOn: isBackward ? SortingOrder.Descending : SortingOrder.Ascending },
|
||||||
lookup: {
|
lookup: this.getLookup()
|
||||||
_id: { attachments: attachment.class.Attachment, inlineButtons: chunter.class.InlineButton }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
messageInFocus,
|
messageInFocus,
|
||||||
sortActivityMessages
|
sortActivityMessages
|
||||||
} from '@hcengineering/activity-resources'
|
} from '@hcengineering/activity-resources'
|
||||||
import { Doc, getDay, Ref, Timestamp } from '@hcengineering/core'
|
import { Doc, getCurrentAccount, getDay, Ref, Timestamp } from '@hcengineering/core'
|
||||||
import { DocNotifyContext } from '@hcengineering/notification'
|
import { DocNotifyContext } from '@hcengineering/notification'
|
||||||
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
|
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
|
||||||
import { getResource } from '@hcengineering/platform'
|
import { getResource } from '@hcengineering/platform'
|
||||||
@ -72,6 +72,7 @@
|
|||||||
const minMsgHeightRem = 2
|
const minMsgHeightRem = 2
|
||||||
const loadMoreThreshold = 40
|
const loadMoreThreshold = 40
|
||||||
|
|
||||||
|
const me = getCurrentAccount()
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const inboxClient = InboxNotificationsClientImpl.getClient()
|
const inboxClient = InboxNotificationsClientImpl.getClient()
|
||||||
const contextByDocStore = inboxClient.contextByDoc
|
const contextByDocStore = inboxClient.contextByDoc
|
||||||
@ -131,8 +132,23 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let isPageHidden = false
|
||||||
|
let lastMsgBeforeFreeze: Ref<ActivityMessage> | undefined = undefined
|
||||||
|
|
||||||
|
function handleVisibilityChange (): void {
|
||||||
|
if (document.hidden) {
|
||||||
|
isPageHidden = true
|
||||||
|
lastMsgBeforeFreeze = shouldScrollToNew ? displayMessages[displayMessages.length - 1]?._id : undefined
|
||||||
|
} else {
|
||||||
|
if (isPageHidden) {
|
||||||
|
isPageHidden = false
|
||||||
|
void provider.updateNewTimestamp(notifyContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function isFreeze (): boolean {
|
function isFreeze (): boolean {
|
||||||
return freeze
|
return freeze || isPageHidden
|
||||||
}
|
}
|
||||||
|
|
||||||
$: displayMessages = filterChatMessages(messages, filters, filterResources, doc._class, selectedFilters)
|
$: displayMessages = filterChatMessages(messages, filters, filterResources, doc._class, selectedFilters)
|
||||||
@ -295,6 +311,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function scrollToStartOfNew (): void {
|
||||||
|
if (!scrollElement || !lastMsgBeforeFreeze) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastIndex = displayMessages.findIndex(({ _id }) => _id === lastMsgBeforeFreeze)
|
||||||
|
if (lastIndex === -1) return
|
||||||
|
const firstNewMessage = displayMessages.find(({ createdBy }, index) => index > lastIndex && createdBy !== me._id)
|
||||||
|
|
||||||
|
if (firstNewMessage === undefined) {
|
||||||
|
scrollToBottom()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const messagesElements = scrollContentBox?.getElementsByClassName('activityMessage')
|
||||||
|
const msgElement = messagesElements?.[firstNewMessage._id as any]
|
||||||
|
|
||||||
|
if (!msgElement) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageRect = msgElement.getBoundingClientRect()
|
||||||
|
|
||||||
|
const topOffset = messageRect.top - 150
|
||||||
|
|
||||||
|
if (topOffset < 0) {
|
||||||
|
scroller?.scrollBy(topOffset)
|
||||||
|
} else if (topOffset > 0) {
|
||||||
|
scroller?.scrollBy(topOffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleScroll ({ autoScrolling }: ScrollParams): Promise<void> {
|
async function handleScroll ({ autoScrolling }: ScrollParams): Promise<void> {
|
||||||
saveScrollPosition()
|
saveScrollPosition()
|
||||||
updateDownButtonVisibility($metadataStore, displayMessages, scrollElement)
|
updateDownButtonVisibility($metadataStore, displayMessages, scrollElement)
|
||||||
@ -339,7 +387,7 @@
|
|||||||
return messageRect.top >= containerRect.top && messageRect.bottom - messageRect.height / 2 <= containerRect.bottom
|
return messageRect.top >= containerRect.top && messageRect.bottom - messageRect.height / 2 <= containerRect.bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
const messagesToReadAccumulator: DisplayActivityMessage[] = []
|
const messagesToReadAccumulator: Set<DisplayActivityMessage> = new Set<DisplayActivityMessage>()
|
||||||
let messagesToReadAccumulatorTimer: any
|
let messagesToReadAccumulatorTimer: any
|
||||||
|
|
||||||
function readViewportMessages (): void {
|
function readViewportMessages (): void {
|
||||||
@ -359,13 +407,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (messageInView(msgElement, containerRect)) {
|
if (messageInView(msgElement, containerRect)) {
|
||||||
messagesToReadAccumulator.push(message)
|
messagesToReadAccumulator.add(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clearTimeout(messagesToReadAccumulatorTimer)
|
clearTimeout(messagesToReadAccumulatorTimer)
|
||||||
messagesToReadAccumulatorTimer = setTimeout(() => {
|
messagesToReadAccumulatorTimer = setTimeout(() => {
|
||||||
const messagesToRead = [...messagesToReadAccumulator]
|
const messagesToRead = [...messagesToReadAccumulator]
|
||||||
|
messagesToReadAccumulator.clear()
|
||||||
void readChannelMessages(sortActivityMessages(messagesToRead), notifyContext)
|
void readChannelMessages(sortActivityMessages(messagesToRead), notifyContext)
|
||||||
}, 500)
|
}, 500)
|
||||||
}
|
}
|
||||||
@ -555,8 +604,12 @@
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const prevCount = messagesCount
|
||||||
|
messagesCount = newCount
|
||||||
|
|
||||||
if (isFreeze()) {
|
if (isFreeze()) {
|
||||||
messagesCount = newCount
|
await wait()
|
||||||
|
scrollToStartOfNew()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,15 +618,13 @@
|
|||||||
} else if (dateToJump !== undefined) {
|
} else if (dateToJump !== undefined) {
|
||||||
await wait()
|
await wait()
|
||||||
scrollToDate(dateToJump)
|
scrollToDate(dateToJump)
|
||||||
} else if (shouldScrollToNew && messagesCount > 0 && newCount > messagesCount) {
|
} else if (shouldScrollToNew && prevCount > 0 && newCount > prevCount) {
|
||||||
await wait()
|
await wait()
|
||||||
scrollToNewMessages()
|
scrollToNewMessages()
|
||||||
} else {
|
} else {
|
||||||
await wait()
|
await wait()
|
||||||
readViewportMessages()
|
readViewportMessages()
|
||||||
}
|
}
|
||||||
|
|
||||||
messagesCount = newCount
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$: void handleMessagesUpdated(displayMessages.length)
|
$: void handleMessagesUpdated(displayMessages.length)
|
||||||
@ -610,10 +661,12 @@
|
|||||||
|
|
||||||
afterUpdate(() => {
|
afterUpdate(() => {
|
||||||
if (!scrollElement) return
|
if (!scrollElement) return
|
||||||
const { scrollHeight } = scrollElement
|
const { offsetHeight, scrollHeight, scrollTop } = scrollElement
|
||||||
|
|
||||||
if (!isInitialScrolling && prevScrollHeight < scrollHeight && isScrollAtBottom) {
|
if (!isInitialScrolling && !isFreeze() && prevScrollHeight < scrollHeight && isScrollAtBottom) {
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
|
} else if (isFreeze()) {
|
||||||
|
isScrollAtBottom = scrollHeight <= Math.ceil(scrollTop + offsetHeight)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -641,10 +694,12 @@
|
|||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
chatReadMessagesStore.update(() => new Set())
|
chatReadMessagesStore.update(() => new Set())
|
||||||
|
document.addEventListener('visibilitychange', handleVisibilityChange)
|
||||||
})
|
})
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
unsubscribe()
|
unsubscribe()
|
||||||
|
document.removeEventListener('visibilitychange', handleVisibilityChange)
|
||||||
})
|
})
|
||||||
|
|
||||||
let showScrollDownButton = false
|
let showScrollDownButton = false
|
||||||
@ -715,7 +770,7 @@
|
|||||||
|
|
||||||
const canLoadNextForwardStore = provider.canLoadNextForwardStore
|
const canLoadNextForwardStore = provider.canLoadNextForwardStore
|
||||||
|
|
||||||
$: if (!freeze) {
|
$: if (!freeze && !isPageHidden && isScrollInitialized) {
|
||||||
readViewportMessages()
|
readViewportMessages()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -37,14 +37,14 @@
|
|||||||
$: savedMessages = $savedMessagesStore
|
$: savedMessages = $savedMessagesStore
|
||||||
$: savedAttachments = $savedAttachmentsStore
|
$: savedAttachments = $savedAttachmentsStore
|
||||||
|
|
||||||
async function openAttachment (attach?: Attachment) {
|
async function openAttachment (attach?: Attachment): Promise<void> {
|
||||||
if (attach === undefined) {
|
if (attach === undefined) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const messageId: Ref<ActivityMessage> = attach.attachedTo as Ref<ActivityMessage>
|
const messageId: Ref<ActivityMessage> = attach.attachedTo as Ref<ActivityMessage>
|
||||||
await client.findOne(activity.class.ActivityMessage, { _id: messageId }).then((res) => {
|
await client.findOne(activity.class.ActivityMessage, { _id: messageId }).then((res) => {
|
||||||
if (res !== undefined) {
|
if (res !== undefined) {
|
||||||
openMessageFromSpecial(res)
|
void openMessageFromSpecial(res)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -62,8 +62,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function handleMessageClicked (message?: ActivityMessage) {
|
function handleMessageClicked (message?: ActivityMessage): void {
|
||||||
openMessageFromSpecial(message)
|
void openMessageFromSpecial(message)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -19,4 +19,11 @@
|
|||||||
export let message: ActivityMessage
|
export let message: ActivityMessage
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ActivityMessagePresenter value={message} hideFooter hoverStyles="filledHover" withShowMore={false} skipLabel />
|
<ActivityMessagePresenter
|
||||||
|
value={message}
|
||||||
|
hideFooter
|
||||||
|
hoverStyles="filledHover"
|
||||||
|
withShowMore={false}
|
||||||
|
attachmentImageSize="x-large"
|
||||||
|
skipLabel
|
||||||
|
/>
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
import activity, { ActivityMessage } from '@hcengineering/activity'
|
import activity, { ActivityMessage } from '@hcengineering/activity'
|
||||||
import { PersonAccount } from '@hcengineering/contact'
|
import { PersonAccount } from '@hcengineering/contact'
|
||||||
import { ActivityMessagePresenter } from '@hcengineering/activity-resources'
|
import { ActivityMessagePresenter } from '@hcengineering/activity-resources'
|
||||||
|
import notification from '@hcengineering/notification'
|
||||||
|
import attachment from '@hcengineering/attachment'
|
||||||
|
|
||||||
import chunter from '../../plugin'
|
import chunter from '../../plugin'
|
||||||
import Header from '../Header.svelte'
|
import Header from '../Header.svelte'
|
||||||
@ -32,15 +34,21 @@
|
|||||||
$: threadsQuery.query(
|
$: threadsQuery.query(
|
||||||
activity.class.ActivityMessage,
|
activity.class.ActivityMessage,
|
||||||
{
|
{
|
||||||
replies: { $exists: true }
|
replies: { $exists: true },
|
||||||
|
[`${notification.mixin.Collaborators}.collaborators`]: me._id
|
||||||
},
|
},
|
||||||
(res) => {
|
(res) => {
|
||||||
threads = res.filter(
|
threads = res.filter(({ replies }) => (replies ?? 0) > 0)
|
||||||
({ createdBy, repliedPersons, replies }) =>
|
|
||||||
(replies !== undefined && replies > 0 && createdBy === me._id) || repliedPersons?.includes(me.person)
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
{ sort: { modifiedOn: SortingOrder.Descending } }
|
{
|
||||||
|
sort: { modifiedOn: SortingOrder.Descending },
|
||||||
|
lookup: {
|
||||||
|
_id: {
|
||||||
|
attachments: attachment.class.Attachment,
|
||||||
|
reactions: activity.class.Reaction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -430,11 +430,7 @@ export async function readChannelMessages (
|
|||||||
messages: DisplayActivityMessage[],
|
messages: DisplayActivityMessage[],
|
||||||
context: DocNotifyContext | undefined
|
context: DocNotifyContext | undefined
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (messages.length === 0) {
|
if (messages.length === 0 || context === undefined) {
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context === undefined) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,9 +438,7 @@ export async function readChannelMessages (
|
|||||||
const client = getClient().apply(undefined, 'readViewportMessages')
|
const client = getClient().apply(undefined, 'readViewportMessages')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const readMessages = get(chatReadMessagesStore)
|
const allIds = getAllIds(messages)
|
||||||
const allIds = getAllIds(messages).filter((id) => !readMessages.has(id))
|
|
||||||
|
|
||||||
const notifications = get(inboxClient.activityInboxNotifications)
|
const notifications = get(inboxClient.activityInboxNotifications)
|
||||||
.filter(({ attachedTo, $lookup, isViewed }) => {
|
.filter(({ attachedTo, $lookup, isViewed }) => {
|
||||||
if (isViewed) return false
|
if (isViewed) return false
|
||||||
|
@ -16,7 +16,12 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import activity, { ActivityMessage } from '@hcengineering/activity'
|
import activity, { ActivityMessage } from '@hcengineering/activity'
|
||||||
import { ActivityFilter, ActivityMessagePresenter, canGroupMessages } from '@hcengineering/activity-resources'
|
import {
|
||||||
|
ActivityFilter,
|
||||||
|
ActivityMessagePresenter,
|
||||||
|
canGroupMessages,
|
||||||
|
combineActivityMessages
|
||||||
|
} from '@hcengineering/activity-resources'
|
||||||
import { SortingOrder } from '@hcengineering/core'
|
import { SortingOrder } from '@hcengineering/core'
|
||||||
import { Document } from '@hcengineering/document'
|
import { Document } from '@hcengineering/document'
|
||||||
import { createQuery } from '@hcengineering/presentation'
|
import { createQuery } from '@hcengineering/presentation'
|
||||||
@ -33,7 +38,9 @@
|
|||||||
activity.class.ActivityMessage,
|
activity.class.ActivityMessage,
|
||||||
{ attachedTo: value._id, space: value.space },
|
{ attachedTo: value._id, space: value.space },
|
||||||
(res) => {
|
(res) => {
|
||||||
messages = res
|
void combineActivityMessages(res).then((res) => {
|
||||||
|
messages = res
|
||||||
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sort: {
|
sort: {
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
export let value: Markup | undefined
|
export let value: Markup | undefined
|
||||||
export let prevValue: Markup | undefined = undefined
|
export let prevValue: Markup | undefined = undefined
|
||||||
export let attribute: AnyAttribute | undefined = undefined
|
export let attribute: AnyAttribute | undefined = undefined
|
||||||
|
export let withShowMore = true
|
||||||
|
|
||||||
export let showOnlyDiff: boolean = false
|
export let showOnlyDiff: boolean = false
|
||||||
|
|
||||||
@ -85,8 +86,14 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ShowMore>
|
{#if withShowMore}
|
||||||
|
<ShowMore>
|
||||||
|
{#key [value, prevValue]}
|
||||||
|
<MarkupDiffViewer objectClass={attribute?.attributeOf} {content} {comparedVersion} />
|
||||||
|
{/key}
|
||||||
|
</ShowMore>
|
||||||
|
{:else}
|
||||||
{#key [value, prevValue]}
|
{#key [value, prevValue]}
|
||||||
<MarkupDiffViewer objectClass={attribute?.attributeOf} {content} {comparedVersion} />
|
<MarkupDiffViewer objectClass={attribute?.attributeOf} {content} {comparedVersion} />
|
||||||
{/key}
|
{/key}
|
||||||
</ShowMore>
|
{/if}
|
||||||
|
@ -21,7 +21,8 @@
|
|||||||
location as locationStore,
|
location as locationStore,
|
||||||
Location,
|
Location,
|
||||||
Header,
|
Header,
|
||||||
Breadcrumbs
|
Breadcrumbs,
|
||||||
|
getCurrentLocation
|
||||||
} from '@hcengineering/ui'
|
} from '@hcengineering/ui'
|
||||||
import { onDestroy } from 'svelte'
|
import { onDestroy } from 'svelte'
|
||||||
|
|
||||||
@ -45,13 +46,33 @@
|
|||||||
$: widgetState = widget !== undefined ? $sidebarStore.widgetsState.get(widget._id) : undefined
|
$: widgetState = widget !== undefined ? $sidebarStore.widgetsState.get(widget._id) : undefined
|
||||||
|
|
||||||
$: tabId = widgetState?.tab
|
$: tabId = widgetState?.tab
|
||||||
$: tabs = widgetState?.tabs ?? []
|
$: tabs = getTabs(widget, widgetState)
|
||||||
$: tab = tabId !== undefined ? tabs.find((it) => it.id === tabId) ?? tabs[0] : tabs[0]
|
$: tab = tabId !== undefined ? tabs.find((it) => it.id === tabId) ?? tabs[0] : tabs[0]
|
||||||
|
|
||||||
$: if ($sidebarStore.widget === undefined) {
|
$: if ($sidebarStore.widget === undefined) {
|
||||||
sidebarStore.update((s) => ({ ...s, variant: SidebarVariant.MINI }))
|
sidebarStore.update((s) => ({ ...s, variant: SidebarVariant.MINI }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTabs (widget?: Widget, state?: WidgetState): WidgetTab[] {
|
||||||
|
if (widget === undefined || !state?.tabs) return []
|
||||||
|
const loc = getCurrentLocation()
|
||||||
|
|
||||||
|
const result: WidgetTab[] = []
|
||||||
|
for (const tab of state.tabs) {
|
||||||
|
if (tab.allowedPath !== undefined && !tab.isPinned) {
|
||||||
|
const path = loc.path.join('/')
|
||||||
|
if (!path.startsWith(tab.allowedPath)) {
|
||||||
|
void handleTabClose(tab.id, widget)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push(tab)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
const unsubscribe = locationStore.subscribe((loc: Location) => {
|
const unsubscribe = locationStore.subscribe((loc: Location) => {
|
||||||
if (widget === undefined) return
|
if (widget === undefined) return
|
||||||
|
|
||||||
|
@ -248,7 +248,7 @@ export async function getPersonNotificationTxes (
|
|||||||
...content,
|
...content,
|
||||||
docNotifyContext: context._id,
|
docNotifyContext: context._id,
|
||||||
_id: generateId(),
|
_id: generateId(),
|
||||||
_class: notification.class.CommonInboxNotification,
|
_class: notification.class.MentionInboxNotification,
|
||||||
space: receiverSpace._id,
|
space: receiverSpace._id,
|
||||||
modifiedOn: originTx.modifiedOn,
|
modifiedOn: originTx.modifiedOn,
|
||||||
modifiedBy: sender._id
|
modifiedBy: sender._id
|
||||||
|
@ -78,7 +78,7 @@ import serverNotification, {
|
|||||||
SenderInfo
|
SenderInfo
|
||||||
} from '@hcengineering/server-notification'
|
} from '@hcengineering/server-notification'
|
||||||
import serverView from '@hcengineering/server-view'
|
import serverView from '@hcengineering/server-view'
|
||||||
import { markupToHTML, markupToText, stripTags } from '@hcengineering/text'
|
import { markupToText, stripTags } from '@hcengineering/text'
|
||||||
import { encodeObjectURI } from '@hcengineering/view'
|
import { encodeObjectURI } from '@hcengineering/view'
|
||||||
import { workbenchId } from '@hcengineering/workbench'
|
import { workbenchId } from '@hcengineering/workbench'
|
||||||
import webpush, { WebPushError } from 'web-push'
|
import webpush, { WebPushError } from 'web-push'
|
||||||
@ -240,8 +240,10 @@ export async function getContentByTemplate (
|
|||||||
notificationData !== undefined &&
|
notificationData !== undefined &&
|
||||||
control.hierarchy.isDerived(notificationData._class, notification.class.MentionInboxNotification)
|
control.hierarchy.isDerived(notificationData._class, notification.class.MentionInboxNotification)
|
||||||
) {
|
) {
|
||||||
const text = (notificationData as MentionInboxNotification).messageHtml
|
const messageContent = (notificationData as MentionInboxNotification).messageHtml
|
||||||
params.body = text !== undefined ? markupToHTML(text) : params.body
|
const text = messageContent !== undefined ? markupToText(messageContent) : undefined
|
||||||
|
params.body = text ?? params.body
|
||||||
|
params.message = text ?? params.message
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message !== undefined) {
|
if (message !== undefined) {
|
||||||
|
@ -698,16 +698,16 @@ export class PlatformWorker {
|
|||||||
errors++
|
errors++
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (workspaceInfo?.workspace === undefined) {
|
||||||
|
this.ctx.error('No workspace exists for workspaceId', { workspace })
|
||||||
|
errors++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (workspaceInfo?.disabled === true) {
|
||||||
|
this.ctx.error('Workspace is disabled workspaceId', { workspace })
|
||||||
|
return
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (workspaceInfo?.workspace === undefined) {
|
|
||||||
this.ctx.error('No workspace exists for workspaceId', { workspace })
|
|
||||||
errors++
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (workspaceInfo?.disabled === true) {
|
|
||||||
this.ctx.error('Workspace is disabled workspaceId', { workspace })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const branding = Object.values(this.brandingMap).find((b) => b.key === workspaceInfo?.branding) ?? null
|
const branding = Object.values(this.brandingMap).find((b) => b.key === workspaceInfo?.branding) ?? null
|
||||||
const workerCtx = this.ctx.newChild('worker', { workspace: workspaceInfo.workspace }, {})
|
const workerCtx = this.ctx.newChild('worker', { workspace: workspaceInfo.workspace }, {})
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user