diff --git a/models/activity/package.json b/models/activity/package.json index 00d35887ac..71570dd350 100644 --- a/models/activity/package.json +++ b/models/activity/package.json @@ -35,6 +35,7 @@ "@hcengineering/model": "^0.6.11", "@hcengineering/model-core": "^0.6.0", "@hcengineering/model-preference": "^0.6.0", + "@hcengineering/model-presentation": "^0.6.0", "@hcengineering/model-view": "^0.6.0", "@hcengineering/notification": "^0.6.23", "@hcengineering/platform": "^0.6.11", diff --git a/models/activity/src/index.ts b/models/activity/src/index.ts index c14f33b646..6d63a993c0 100644 --- a/models/activity/src/index.ts +++ b/models/activity/src/index.ts @@ -70,6 +70,7 @@ import preference, { TPreference } from '@hcengineering/model-preference' import view from '@hcengineering/model-view' import type { Asset, IntlString, Resource } from '@hcengineering/platform' import { type AnyComponent } from '@hcengineering/ui/src/types' +import presentation from '@hcengineering/model-presentation' import activity from './plugin' 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) buildNotifications(builder) } diff --git a/packages/core/src/operations.ts b/packages/core/src/operations.ts index 5dd2c35e4c..9db10bdae0 100644 --- a/packages/core/src/operations.ts +++ b/packages/core/src/operations.ts @@ -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') } const tx = this.txFactory.createTxCreateDoc(_class, space, attributes, id, modifiedOn, modifiedBy) - await this.client.tx(tx) + await this.tx(tx) return tx.objectId } @@ -122,7 +122,7 @@ export class TxOperations implements Omit<Client, 'notify'> { modifiedOn, modifiedBy ) - await this.client.tx(tx) + await this.tx(tx) return tx.tx.objectId as unknown as Ref<P> } @@ -147,7 +147,7 @@ export class TxOperations implements Omit<Client, 'notify'> { modifiedOn, modifiedBy ) - await this.client.tx(tx) + await this.tx(tx) return tx.objectId } @@ -170,7 +170,7 @@ export class TxOperations implements Omit<Client, 'notify'> { modifiedOn, modifiedBy ) - await this.client.tx(tx) + await this.tx(tx) return tx.objectId } @@ -184,7 +184,7 @@ export class TxOperations implements Omit<Client, 'notify'> { modifiedBy?: Ref<Account> ): Promise<TxResult> { 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>( @@ -195,7 +195,7 @@ export class TxOperations implements Omit<Client, 'notify'> { modifiedBy?: Ref<Account> ): Promise<TxResult> { 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>( @@ -216,7 +216,7 @@ export class TxOperations implements Omit<Client, 'notify'> { modifiedOn, modifiedBy ) - return this.client.tx(tx) + return this.tx(tx) } updateMixin<D extends Doc, M extends D>( @@ -237,7 +237,7 @@ export class TxOperations implements Omit<Client, 'notify'> { modifiedOn, modifiedBy ) - return this.client.tx(tx) + return this.tx(tx) } async update<T extends Doc>( diff --git a/packages/ui/src/utils.ts b/packages/ui/src/utils.ts index fb056b68bc..e769f24194 100644 --- a/packages/ui/src/utils.ts +++ b/packages/ui/src/utils.ts @@ -111,7 +111,7 @@ export function addNotification ( title, subTitle, severity, - position: NotificationPosition.TopRight, + position: NotificationPosition.BottomLeft, component, closeTimeout, params diff --git a/plugins/activity-resources/src/components/Activity.svelte b/plugins/activity-resources/src/components/Activity.svelte index 9fea4555e9..5a2cc7db4f 100644 --- a/plugins/activity-resources/src/components/Activity.svelte +++ b/plugins/activity-resources/src/components/Activity.svelte @@ -211,6 +211,11 @@ { sort: { createdOn: SortingOrder.Ascending + }, + lookup: { + _id: { + reactions: activity.class.Reaction + } } } ) diff --git a/plugins/activity-resources/src/components/ActivityMessageActions.svelte b/plugins/activity-resources/src/components/ActivityMessageActions.svelte index 62129d1bf8..5a28faa06e 100644 --- a/plugins/activity-resources/src/components/ActivityMessageActions.svelte +++ b/plugins/activity-resources/src/components/ActivityMessageActions.svelte @@ -39,7 +39,7 @@ $: void updateInlineActions(message, excludedActions) savedMessagesStore.subscribe(() => { - void updateInlineActions(message) + void updateInlineActions(message, excludedActions) }) function handleActionMenuOpened (): void { diff --git a/plugins/activity-resources/src/components/doc-update-message/attributes/SetAttributesPresenter.svelte b/plugins/activity-resources/src/components/doc-update-message/attributes/SetAttributesPresenter.svelte index 605c857b63..e50a586b7a 100644 --- a/plugins/activity-resources/src/components/doc-update-message/attributes/SetAttributesPresenter.svelte +++ b/plugins/activity-resources/src/components/doc-update-message/attributes/SetAttributesPresenter.svelte @@ -69,6 +69,7 @@ attribute={attributeModel.attribute} value={values[0]} {prevValue} + withShowMore={false} showOnlyDiff /> {/if} diff --git a/plugins/activity-resources/src/components/reactions/Reactions.svelte b/plugins/activity-resources/src/components/reactions/Reactions.svelte index 7df064d594..440cb291ba 100644 --- a/plugins/activity-resources/src/components/reactions/Reactions.svelte +++ b/plugins/activity-resources/src/components/reactions/Reactions.svelte @@ -55,7 +55,7 @@ ev.preventDefault() ev.stopPropagation() showPopup(EmojiPopup, {}, ev.target as HTMLElement, async (emoji: string) => { - await updateDocReactions(client, reactions, object, emoji) + await updateDocReactions(reactions, object, emoji) }) } </script> diff --git a/plugins/activity-resources/src/components/reactions/ReactionsPresenter.svelte b/plugins/activity-resources/src/components/reactions/ReactionsPresenter.svelte index a495545053..86eb49e3a1 100644 --- a/plugins/activity-resources/src/components/reactions/ReactionsPresenter.svelte +++ b/plugins/activity-resources/src/components/reactions/ReactionsPresenter.svelte @@ -14,40 +14,48 @@ --> <script lang="ts"> 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 Reactions from './Reactions.svelte' - export let object: ActivityMessage | undefined + export let object: WithLookup<ActivityMessage> | undefined export let readonly = false - const client = getClient() const reactionsQuery = createQuery() let reactions: Reaction[] = [] - $: hasReactions = object?.reactions && object.reactions > 0 + $: hasReactions = (object?.reactions ?? 0) > 0 + $: lookupReactions = object?.$lookup?.reactions as Reaction[] | undefined - $: if (object && hasReactions) { - reactionsQuery.query( - activity.class.Reaction, - { attachedTo: object._id, space: getSpace(object) }, - (res: Reaction[]) => { - reactions = res - } - ) - } else { - reactionsQuery.unsubscribe() + $: updateReactions(hasReactions, object, lookupReactions) + + function updateReactions (hasReactions: boolean, object?: ActivityMessage, lookupReaction?: Reaction[]): void { + if (lookupReaction !== undefined) { + reactions = lookupReaction + } else if (object && hasReactions) { + reactionsQuery.query( + activity.class.Reaction, + { attachedTo: object._id, space: getSpace(object) }, + (res: Reaction[]) => { + reactions = res + } + ) + } else { + reactionsQuery.unsubscribe() + reactions = [] + } } const handleClick = (ev: CustomEvent) => { if (readonly) return - void updateDocReactions(client, reactions, object, ev.detail) + void updateDocReactions(reactions, object, ev.detail) } </script> -{#if object && hasReactions} +{#if object && reactions.length > 0} <div class="footer flex-col p-inline contrast mt-2 min-h-6"> <Reactions {reactions} {object} {readonly} on:click={handleClick} /> </div> diff --git a/plugins/activity-resources/src/components/reactions/ReactionsPreview.svelte b/plugins/activity-resources/src/components/reactions/ReactionsPreview.svelte index 9e5f5374c0..917e11a97a 100644 --- a/plugins/activity-resources/src/components/reactions/ReactionsPreview.svelte +++ b/plugins/activity-resources/src/components/reactions/ReactionsPreview.svelte @@ -66,7 +66,7 @@ e.stopPropagation() e.preventDefault() showPopup(EmojiPopup, {}, e.target as HTMLElement, (emoji: string) => { - void updateDocReactions(client, reactions, message, emoji) + void updateDocReactions(reactions, message, emoji) }) } </script> diff --git a/plugins/activity-resources/src/utils.ts b/plugins/activity-resources/src/utils.ts index 7a91bb20bb..06df26ee33 100644 --- a/plugins/activity-resources/src/utils.ts +++ b/plugins/activity-resources/src/utils.ts @@ -1,12 +1,5 @@ import type { ActivityMessage, Reaction } from '@hcengineering/activity' -import core, { - getCurrentAccount, - isOtherHour, - type Doc, - type Ref, - type TxOperations, - type Space -} from '@hcengineering/core' +import core, { getCurrentAccount, isOtherHour, type Doc, type Ref, type Space } from '@hcengineering/core' import { getClient, isSpace } from '@hcengineering/presentation' import { EmojiPopup, @@ -22,18 +15,13 @@ import { get } from 'svelte/store' import { savedMessagesStore } from './activity' import activity from './plugin' -export async function updateDocReactions ( - client: TxOperations, - reactions: Reaction[], - object?: Doc, - emoji?: string -): Promise<void> { +export async function updateDocReactions (reactions: Reaction[], object?: Doc, emoji?: string): Promise<void> { if (emoji === undefined || object === undefined) { return } + const client = getClient() const currentAccount = getCurrentAccount() - const reaction = reactions.find((r) => r.emoji === emoji && r.createBy === currentAccount._id) if (reaction == null) { @@ -72,7 +60,7 @@ export async function addReactionAction ( closePopup() showPopup(EmojiPopup, {}, element, (emoji: string) => { - void updateDocReactions(client, reactions, message, emoji) + void updateDocReactions(reactions, message, emoji) params?.onClose?.() }) params?.onOpen?.() diff --git a/plugins/chunter-resources/src/channelDataProvider.ts b/plugins/chunter-resources/src/channelDataProvider.ts index 66244cb07d..c095d0e855 100644 --- a/plugins/chunter-resources/src/channelDataProvider.ts +++ b/plugins/chunter-resources/src/channelDataProvider.ts @@ -20,6 +20,7 @@ import { type DocumentQuery, getCurrentAccount, isOtherDay, + type Lookup, type Ref, SortingOrder, type Space, @@ -119,7 +120,7 @@ export class ChannelDataProvider implements IChannelDataProvider { }) constructor ( - readonly context: DocNotifyContext | undefined, + private context: DocNotifyContext | undefined, readonly space: Ref<Space>, chatId: Ref<Doc>, _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 ( selectedMsg?: Ref<ActivityMessage>, loadAll = false, @@ -285,13 +293,21 @@ export class ChannelDataProvider implements IChannelDataProvider { }, { sort: { createdOn: SortingOrder.Descending }, - lookup: { - _id: { attachments: attachment.class.Attachment, inlineButtons: chunter.class.InlineButton } - } + lookup: this.getLookup() } ) } + getLookup (): Lookup<ActivityMessage> { + return { + _id: { + attachments: attachment.class.Attachment, + inlineButtons: chunter.class.InlineButton, + reactions: activity.class.Reaction + } + } + } + isNextLoading (mode: LoadMode): boolean { return mode === 'forward' ? get(this.isForwardLoading) : get(this.isBackwardLoading) } @@ -331,9 +347,7 @@ export class ChannelDataProvider implements IChannelDataProvider { { limit: limit ?? this.limit, sort: { createdOn: isBackward ? SortingOrder.Descending : SortingOrder.Ascending }, - lookup: { - _id: { attachments: attachment.class.Attachment, inlineButtons: chunter.class.InlineButton } - } + lookup: this.getLookup() } ) diff --git a/plugins/chunter-resources/src/components/ChannelScrollView.svelte b/plugins/chunter-resources/src/components/ChannelScrollView.svelte index 5dffbc7151..d02a25591e 100644 --- a/plugins/chunter-resources/src/components/ChannelScrollView.svelte +++ b/plugins/chunter-resources/src/components/ChannelScrollView.svelte @@ -26,7 +26,7 @@ messageInFocus, sortActivityMessages } 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 { InboxNotificationsClientImpl } from '@hcengineering/notification-resources' import { getResource } from '@hcengineering/platform' @@ -72,6 +72,7 @@ const minMsgHeightRem = 2 const loadMoreThreshold = 40 + const me = getCurrentAccount() const client = getClient() const inboxClient = InboxNotificationsClientImpl.getClient() 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 { - return freeze + return freeze || isPageHidden } $: 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> { saveScrollPosition() updateDownButtonVisibility($metadataStore, displayMessages, scrollElement) @@ -339,7 +387,7 @@ 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 function readViewportMessages (): void { @@ -359,13 +407,14 @@ } if (messageInView(msgElement, containerRect)) { - messagesToReadAccumulator.push(message) + messagesToReadAccumulator.add(message) } } clearTimeout(messagesToReadAccumulatorTimer) messagesToReadAccumulatorTimer = setTimeout(() => { const messagesToRead = [...messagesToReadAccumulator] + messagesToReadAccumulator.clear() void readChannelMessages(sortActivityMessages(messagesToRead), notifyContext) }, 500) } @@ -555,8 +604,12 @@ return } + const prevCount = messagesCount + messagesCount = newCount + if (isFreeze()) { - messagesCount = newCount + await wait() + scrollToStartOfNew() return } @@ -565,15 +618,13 @@ } else if (dateToJump !== undefined) { await wait() scrollToDate(dateToJump) - } else if (shouldScrollToNew && messagesCount > 0 && newCount > messagesCount) { + } else if (shouldScrollToNew && prevCount > 0 && newCount > prevCount) { await wait() scrollToNewMessages() } else { await wait() readViewportMessages() } - - messagesCount = newCount } $: void handleMessagesUpdated(displayMessages.length) @@ -610,10 +661,12 @@ afterUpdate(() => { if (!scrollElement) return - const { scrollHeight } = scrollElement + const { offsetHeight, scrollHeight, scrollTop } = scrollElement - if (!isInitialScrolling && prevScrollHeight < scrollHeight && isScrollAtBottom) { + if (!isInitialScrolling && !isFreeze() && prevScrollHeight < scrollHeight && isScrollAtBottom) { scrollToBottom() + } else if (isFreeze()) { + isScrollAtBottom = scrollHeight <= Math.ceil(scrollTop + offsetHeight) } }) @@ -641,10 +694,12 @@ onMount(() => { chatReadMessagesStore.update(() => new Set()) + document.addEventListener('visibilitychange', handleVisibilityChange) }) onDestroy(() => { unsubscribe() + document.removeEventListener('visibilitychange', handleVisibilityChange) }) let showScrollDownButton = false @@ -715,7 +770,7 @@ const canLoadNextForwardStore = provider.canLoadNextForwardStore - $: if (!freeze) { + $: if (!freeze && !isPageHidden && isScrollInitialized) { readViewportMessages() } </script> diff --git a/plugins/chunter-resources/src/components/chat/specials/SavedMessages.svelte b/plugins/chunter-resources/src/components/chat/specials/SavedMessages.svelte index 54a3d3d5ee..d1855f0b59 100644 --- a/plugins/chunter-resources/src/components/chat/specials/SavedMessages.svelte +++ b/plugins/chunter-resources/src/components/chat/specials/SavedMessages.svelte @@ -37,14 +37,14 @@ $: savedMessages = $savedMessagesStore $: savedAttachments = $savedAttachmentsStore - async function openAttachment (attach?: Attachment) { + async function openAttachment (attach?: Attachment): Promise<void> { if (attach === undefined) { return } const messageId: Ref<ActivityMessage> = attach.attachedTo as Ref<ActivityMessage> await client.findOne(activity.class.ActivityMessage, { _id: messageId }).then((res) => { if (res !== undefined) { - openMessageFromSpecial(res) + void openMessageFromSpecial(res) } }) } @@ -62,8 +62,8 @@ } } } - function handleMessageClicked (message?: ActivityMessage) { - openMessageFromSpecial(message) + function handleMessageClicked (message?: ActivityMessage): void { + void openMessageFromSpecial(message) } </script> diff --git a/plugins/chunter-resources/src/components/threads/ThreadParentPresenter.svelte b/plugins/chunter-resources/src/components/threads/ThreadParentPresenter.svelte index 0f9403890f..c0436d5e95 100644 --- a/plugins/chunter-resources/src/components/threads/ThreadParentPresenter.svelte +++ b/plugins/chunter-resources/src/components/threads/ThreadParentPresenter.svelte @@ -19,4 +19,11 @@ export let message: ActivityMessage </script> -<ActivityMessagePresenter value={message} hideFooter hoverStyles="filledHover" withShowMore={false} skipLabel /> +<ActivityMessagePresenter + value={message} + hideFooter + hoverStyles="filledHover" + withShowMore={false} + attachmentImageSize="x-large" + skipLabel +/> diff --git a/plugins/chunter-resources/src/components/threads/Threads.svelte b/plugins/chunter-resources/src/components/threads/Threads.svelte index 73c954fba3..3e3a0cc68c 100644 --- a/plugins/chunter-resources/src/components/threads/Threads.svelte +++ b/plugins/chunter-resources/src/components/threads/Threads.svelte @@ -19,6 +19,8 @@ import activity, { ActivityMessage } from '@hcengineering/activity' import { PersonAccount } from '@hcengineering/contact' import { ActivityMessagePresenter } from '@hcengineering/activity-resources' + import notification from '@hcengineering/notification' + import attachment from '@hcengineering/attachment' import chunter from '../../plugin' import Header from '../Header.svelte' @@ -32,15 +34,21 @@ $: threadsQuery.query( activity.class.ActivityMessage, { - replies: { $exists: true } + replies: { $exists: true }, + [`${notification.mixin.Collaborators}.collaborators`]: me._id }, (res) => { - threads = res.filter( - ({ createdBy, repliedPersons, replies }) => - (replies !== undefined && replies > 0 && createdBy === me._id) || repliedPersons?.includes(me.person) - ) + threads = res.filter(({ replies }) => (replies ?? 0) > 0) }, - { sort: { modifiedOn: SortingOrder.Descending } } + { + sort: { modifiedOn: SortingOrder.Descending }, + lookup: { + _id: { + attachments: attachment.class.Attachment, + reactions: activity.class.Reaction + } + } + } ) </script> diff --git a/plugins/chunter-resources/src/utils.ts b/plugins/chunter-resources/src/utils.ts index 87b75edcc7..950ea5ec1a 100644 --- a/plugins/chunter-resources/src/utils.ts +++ b/plugins/chunter-resources/src/utils.ts @@ -430,11 +430,7 @@ export async function readChannelMessages ( messages: DisplayActivityMessage[], context: DocNotifyContext | undefined ): Promise<void> { - if (messages.length === 0) { - return - } - - if (context === undefined) { + if (messages.length === 0 || context === undefined) { return } @@ -442,9 +438,7 @@ export async function readChannelMessages ( const client = getClient().apply(undefined, 'readViewportMessages') try { - const readMessages = get(chatReadMessagesStore) - const allIds = getAllIds(messages).filter((id) => !readMessages.has(id)) - + const allIds = getAllIds(messages) const notifications = get(inboxClient.activityInboxNotifications) .filter(({ attachedTo, $lookup, isViewed }) => { if (isViewed) return false diff --git a/plugins/document-resources/src/components/sidebar/Activity.svelte b/plugins/document-resources/src/components/sidebar/Activity.svelte index 2a55727b35..c6a02152f9 100644 --- a/plugins/document-resources/src/components/sidebar/Activity.svelte +++ b/plugins/document-resources/src/components/sidebar/Activity.svelte @@ -16,7 +16,12 @@ --> <script lang="ts"> 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 { Document } from '@hcengineering/document' import { createQuery } from '@hcengineering/presentation' @@ -33,7 +38,9 @@ activity.class.ActivityMessage, { attachedTo: value._id, space: value.space }, (res) => { - messages = res + void combineActivityMessages(res).then((res) => { + messages = res + }) }, { sort: { diff --git a/plugins/view-resources/src/components/MarkupDiffPresenter.svelte b/plugins/view-resources/src/components/MarkupDiffPresenter.svelte index 3a81fdb9fd..f756ec3753 100644 --- a/plugins/view-resources/src/components/MarkupDiffPresenter.svelte +++ b/plugins/view-resources/src/components/MarkupDiffPresenter.svelte @@ -23,6 +23,7 @@ export let value: Markup | undefined export let prevValue: Markup | undefined = undefined export let attribute: AnyAttribute | undefined = undefined + export let withShowMore = true export let showOnlyDiff: boolean = false @@ -85,8 +86,14 @@ } </script> -<ShowMore> +{#if withShowMore} + <ShowMore> + {#key [value, prevValue]} + <MarkupDiffViewer objectClass={attribute?.attributeOf} {content} {comparedVersion} /> + {/key} + </ShowMore> +{:else} {#key [value, prevValue]} <MarkupDiffViewer objectClass={attribute?.attributeOf} {content} {comparedVersion} /> {/key} -</ShowMore> +{/if} diff --git a/plugins/workbench-resources/src/components/sidebar/SidebarExpanded.svelte b/plugins/workbench-resources/src/components/sidebar/SidebarExpanded.svelte index ef3285a94a..7209b04357 100644 --- a/plugins/workbench-resources/src/components/sidebar/SidebarExpanded.svelte +++ b/plugins/workbench-resources/src/components/sidebar/SidebarExpanded.svelte @@ -21,7 +21,8 @@ location as locationStore, Location, Header, - Breadcrumbs + Breadcrumbs, + getCurrentLocation } from '@hcengineering/ui' import { onDestroy } from 'svelte' @@ -45,13 +46,33 @@ $: widgetState = widget !== undefined ? $sidebarStore.widgetsState.get(widget._id) : undefined $: tabId = widgetState?.tab - $: tabs = widgetState?.tabs ?? [] + $: tabs = getTabs(widget, widgetState) $: tab = tabId !== undefined ? tabs.find((it) => it.id === tabId) ?? tabs[0] : tabs[0] $: if ($sidebarStore.widget === undefined) { 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) => { if (widget === undefined) return diff --git a/server-plugins/activity-resources/src/references.ts b/server-plugins/activity-resources/src/references.ts index d2e66a7531..f260299f70 100644 --- a/server-plugins/activity-resources/src/references.ts +++ b/server-plugins/activity-resources/src/references.ts @@ -248,7 +248,7 @@ export async function getPersonNotificationTxes ( ...content, docNotifyContext: context._id, _id: generateId(), - _class: notification.class.CommonInboxNotification, + _class: notification.class.MentionInboxNotification, space: receiverSpace._id, modifiedOn: originTx.modifiedOn, modifiedBy: sender._id diff --git a/server-plugins/notification-resources/src/index.ts b/server-plugins/notification-resources/src/index.ts index 9390692e34..a09e410c19 100644 --- a/server-plugins/notification-resources/src/index.ts +++ b/server-plugins/notification-resources/src/index.ts @@ -78,7 +78,7 @@ import serverNotification, { SenderInfo } from '@hcengineering/server-notification' 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 { workbenchId } from '@hcengineering/workbench' import webpush, { WebPushError } from 'web-push' @@ -240,8 +240,10 @@ export async function getContentByTemplate ( notificationData !== undefined && control.hierarchy.isDerived(notificationData._class, notification.class.MentionInboxNotification) ) { - const text = (notificationData as MentionInboxNotification).messageHtml - params.body = text !== undefined ? markupToHTML(text) : params.body + const messageContent = (notificationData as MentionInboxNotification).messageHtml + const text = messageContent !== undefined ? markupToText(messageContent) : undefined + params.body = text ?? params.body + params.message = text ?? params.message } if (message !== undefined) { diff --git a/services/github/pod-github/src/platform.ts b/services/github/pod-github/src/platform.ts index 626f1237fd..57091ea7d4 100644 --- a/services/github/pod-github/src/platform.ts +++ b/services/github/pod-github/src/platform.ts @@ -698,16 +698,16 @@ export class PlatformWorker { errors++ 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 { - 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 workerCtx = this.ctx.newChild('worker', { workspace: workspaceInfo.workspace }, {})