[Part 2]: Sidebar and chat fixes (#6656)

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
Kristina 2024-09-20 19:07:04 +04:00 committed by GitHub
parent 25ab61f488
commit f2c961c5d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 185 additions and 95 deletions

View File

@ -152,6 +152,9 @@ export class TActivityReference extends TActivityMessage implements ActivityRefe
@Prop(TypeMarkup(), activity.string.Message)
@Index(IndexKind.FullText)
message!: string
@Prop(TypeTimestamp(), activity.string.Edit)
editedOn?: Timestamp
}
@Model(activity.class.ActivityInfoMessage, activity.class.ActivityMessage)

View File

@ -22,7 +22,6 @@ import {
TypeMarkup,
TypeRef,
TypeString,
TypeTimestamp,
UX
} from '@hcengineering/model'
import core, { TAttachedDoc, TClass, TDoc, TSpace } from '@hcengineering/model-core'
@ -59,6 +58,7 @@ import type { DocNotifyContext } from '@hcengineering/notification'
import chunter from './plugin'
export const DOMAIN_CHUNTER = 'chunter' as Domain
@Model(chunter.class.ChunterSpace, core.class.Space)
export class TChunterSpace extends TSpace implements ChunterSpace {
@Prop(PropCollection(activity.class.ActivityMessage), chunter.string.Messages)
@ -84,9 +84,6 @@ export class TChatMessage extends TActivityMessage implements ChatMessage {
@Index(IndexKind.FullText)
message!: string
@Prop(TypeTimestamp(), chunter.string.Edit)
editedOn?: Timestamp
@Prop(PropCollection(attachment.class.Attachment), attachment.string.Attachments, {
shortLabel: attachment.string.Files
})

View File

@ -321,6 +321,7 @@
width: 100%;
min-width: 0;
min-height: var(--spacing-6_5);
overflow: hidden;
&.clearPadding > .hulyHeader-row {
padding: 0;

View File

@ -39,6 +39,7 @@
export let noPrint: boolean = false
export let freezeBefore: boolean = false
export let doubleRowWidth = 768
export let closeOnEscape: boolean = true
const dispatch = createEventDispatcher()
@ -61,7 +62,7 @@
})
function _close (ev: KeyboardEvent): void {
if (closeButton && ev.key === 'Escape') {
if (closeButton && ev.key === 'Escape' && closeOnEscape) {
ev.preventDefault()
ev.stopPropagation()
@ -138,7 +139,9 @@
{/if}
{#if closeButton}
{#if type !== 'type-popup'}<div class="hulyHeader-divider no-print" />{/if}
<div class="hulyHotKey-item no-print">Esc</div>
{#if closeOnEscape}
<div class="hulyHotKey-item no-print">Esc</div>
{/if}
<ButtonIcon icon={IconClose} kind={'tertiary'} size={'small'} noPrint on:click={() => dispatch('close')} />
{/if}
</div>
@ -233,7 +236,9 @@
{/if}
{#if closeButton}
{#if type !== 'type-popup'}<div class="hulyHeader-divider no-print" />{/if}
<div class="hulyHotKey-item no-print">Esc</div>
{#if closeOnEscape}
<div class="hulyHotKey-item no-print">Esc</div>
{/if}
<ButtonIcon icon={IconClose} kind={'tertiary'} size={'small'} noPrint on:click={() => dispatch('close')} />
{/if}
{/if}

View File

@ -85,6 +85,7 @@
border-radius: 0.25rem;
cursor: pointer;
overflow: hidden;
color: var(--theme-content-color);
&.primary {
background-color: var(--theme-button-pressed);

View File

@ -126,6 +126,7 @@ export interface TabItem {
}
export interface BreadcrumbItem {
id?: string
icon?: Asset | AnySvelteComponent | ComponentType
iconProps?: any
iconWidth?: string

View File

@ -26,6 +26,7 @@
import { getActions, restrictionStore, showMenu } from '@hcengineering/view-resources'
import { Asset } from '@hcengineering/platform'
import { Action as ViewAction } from '@hcengineering/view'
import notification from '@hcengineering/notification'
import ReactionsPresenter from '../reactions/ReactionsPresenter.svelte'
import ActivityMessagePresenter from './ActivityMessagePresenter.svelte'
@ -228,6 +229,9 @@
<span class="text-sm lower">
<MessageTimestamp date={message.createdOn ?? message.modifiedOn} />
</span>
{#if message.editedOn}
<span class="text-sm lower">(<Label label={notification.string.Edited} />)</span>
{/if}
{#if withActions && inlineActions.length > 0 && !readonly}
<div class="flex-presenter flex-gap-2 ml-2">

View File

@ -46,6 +46,7 @@ export interface ActivityMessage extends AttachedDoc {
replies?: number
reactions?: number
editedOn?: Timestamp
}
export type DisplayActivityMessage = DisplayDocUpdateMessage | ActivityMessage
@ -81,6 +82,7 @@ export interface ActivityInfoMessage extends ActivityMessage {
props?: Record<string, any>
icon?: Asset
iconProps?: Record<string, any>
editedOn?: Timestamp
// A possible set of links to some platform resources.
links?: { _class: Ref<Class<Doc>>, _id: Ref<Doc> }[]

View File

@ -42,6 +42,7 @@
hideExtra={false}
adaptive="autoExtra"
doubleRowWidth={350}
closeOnEscape={false}
on:close
>
<div class="title">

View File

@ -30,6 +30,7 @@
export let filters: Ref<ActivityMessagesFilter>[] = []
export let isAsideOpened = false
export let syncLocation = true
export let freeze = false
const client = getClient()
const hierarchy = client.getHierarchy()
@ -111,5 +112,6 @@
provider={dataProvider}
{isAsideOpened}
loadMoreAllowed={!isDocChannel}
{freeze}
/>
{/if}

View File

@ -36,6 +36,7 @@
export let isAsideShown: boolean = false
export let filters: Ref<ActivityMessagesFilter>[] = []
export let canOpenInSidebar: boolean = false
export let closeOnEscape: boolean = true
const client = getClient()
const hierarchy = client.getHierarchy()
@ -80,6 +81,7 @@
{isAsideShown}
{withSearch}
{canOpenInSidebar}
{closeOnEscape}
on:aside-toggled
on:close
>

View File

@ -61,8 +61,9 @@
export let skipLabels = false
export let loadMoreAllowed = true
export let isAsideOpened = false
export let initialScrollBottom = true
export let fullHeight = true
export let fixedInput = true
export let freeze = false
const doc = object
@ -130,17 +131,21 @@
}
})
function isFreeze (): boolean {
return freeze
}
$: displayMessages = filterChatMessages(messages, filters, filterResources, doc._class, selectedFilters)
const unsubscribe = inboxClient.inboxNotificationsByContext.subscribe(() => {
if (notifyContext !== undefined) {
if (notifyContext !== undefined && !isFreeze()) {
recheckNotifications(notifyContext)
readViewportMessages()
}
})
function scrollToBottom (afterScrollFn?: () => void): void {
if (scroller != null && scrollElement != null) {
if (scroller != null && scrollElement != null && !isFreeze()) {
scroller.scrollBy(scrollElement.scrollHeight)
updateSelectedDate()
afterScrollFn?.()
@ -338,7 +343,7 @@
let messagesToReadAccumulatorTimer: any
function readViewportMessages (): void {
if (!scrollElement || !scrollContentBox) {
if (!scrollElement || !scrollContentBox || isFreeze()) {
return
}
@ -452,22 +457,14 @@
isInitialScrolling = false
} else if (separatorIndex === -1) {
await wait()
if (initialScrollBottom) {
isScrollInitialized = true
shouldWaitAndRead = true
autoscroll = true
shouldScrollToNew = true
isInitialScrolling = false
waitLastMessageRenderAndRead(() => {
autoscroll = false
})
} else {
isScrollInitialized = true
isScrollInitialized = true
shouldWaitAndRead = true
autoscroll = true
shouldScrollToNew = true
isInitialScrolling = false
waitLastMessageRenderAndRead(() => {
autoscroll = false
updateShouldScrollToNew()
isInitialScrolling = false
readViewportMessages()
}
})
} else if (separatorElement) {
await wait()
scrollToSeparator()
@ -519,6 +516,7 @@
function scrollToNewMessages (): void {
if (!scrollElement || !shouldScrollToNew) {
readViewportMessages()
return
}
@ -557,14 +555,22 @@
return
}
if (isFreeze()) {
messagesCount = newCount
return
}
if (scrollToRestore > 0) {
void restoreScroll()
} else if (dateToJump !== undefined) {
await wait()
scrollToDate(dateToJump)
} else if (messagesCount > 0 && newCount > messagesCount) {
} else if (shouldScrollToNew && messagesCount > 0 && newCount > messagesCount) {
await wait()
scrollToNewMessages()
} else {
await wait()
readViewportMessages()
}
messagesCount = newCount
@ -576,7 +582,7 @@
return
}
if (shouldScrollToNew && initialScrollBottom) {
if (shouldScrollToNew) {
scrollToBottom()
}
@ -660,7 +666,7 @@
} else if (element != null) {
const { scrollHeight, scrollTop, offsetHeight } = element
showScrollDownButton = scrollHeight > offsetHeight + scrollTop + 300
showScrollDownButton = scrollHeight > offsetHeight + scrollTop + 50
} else {
showScrollDownButton = false
}
@ -691,7 +697,7 @@
$: void forceReadContext(isScrollAtBottom, notifyContext)
async function forceReadContext (isScrollAtBottom: boolean, context?: DocNotifyContext): Promise<void> {
if (context === undefined || !isScrollAtBottom || forceRead) return
if (context === undefined || !isScrollAtBottom || forceRead || isFreeze()) return
const { lastUpdateTimestamp = 0, lastViewedTimestamp = 0 } = context
if (lastViewedTimestamp >= lastUpdateTimestamp) return
@ -708,6 +714,10 @@
}
const canLoadNextForwardStore = provider.canLoadNextForwardStore
$: if (!freeze) {
readViewportMessages()
}
</script>
{#if isLoading}
@ -777,6 +787,16 @@
/>
{/each}
{#if !fixedInput}
<div class="ref-input flex-col">
<ActivityExtensionComponent
kind="input"
{extensions}
props={{ object, boundary: scrollElement, collection, autofocus: true, withTypingInfo: true }}
/>
</div>
{/if}
{#if loadMoreAllowed && $canLoadNextForwardStore}
<HistoryLoading isLoading={$isLoadingMoreStore} />
{/if}
@ -794,7 +814,7 @@
</div>
{/if}
</div>
{#if object}
{#if fixedInput && object}
<div class="ref-input flex-col">
<ActivityExtensionComponent
kind="input"
@ -841,7 +861,7 @@
display: flex;
justify-content: center;
bottom: -0.75rem;
animation: 1s fadeIn;
animation: 0.5s fadeIn;
animation-fill-mode: forwards;
visibility: hidden;
}

View File

@ -58,9 +58,14 @@
{ limit: 1 }
)
}
let renderChannel = tab.data.thread === undefined
$: if (tab.data.thread === undefined) {
renderChannel = true
}
</script>
{#if object}
{#if object && renderChannel}
<div class="channel" class:invisible={threadId !== undefined} style:height style:width>
<ChannelHeader
_id={object._id}
@ -71,16 +76,17 @@
canOpen={true}
allowClose={true}
canOpenInSidebar={false}
closeOnEscape={false}
on:close
/>
{#key object._id}
<Channel {object} {context} syncLocation={false} />
<Channel {object} {context} syncLocation={false} freeze={threadId !== undefined} />
{/key}
</div>
{/if}
{#if threadId}
<div class="thread" style:height style:width>
<ThreadView _id={threadId} on:close={() => closeThreadInSidebarChannel(widget, tab)} />
<ThreadView _id={threadId} on:channel={() => closeThreadInSidebarChannel(widget, tab)} on:close />
</div>
{/if}

View File

@ -93,7 +93,7 @@
}
</script>
<div class="popupPanel panel">
<div class="popupPanel">
<ChannelHeader
_id={object._id}
_class={object._class}

View File

@ -36,7 +36,7 @@
}
</script>
{#if widget && tab && tab.type === 'channel'}
{#if widget && tab}
<ChannelSidebarView
{widget}
{tab}
@ -46,13 +46,4 @@
handleClose(tab?.id)
}}
/>
{:else if widget && tab && tab.type === 'thread'}
<ThreadSidebarView
{tab}
{height}
{width}
on:close={() => {
handleClose(tab?.id)
}}
/>
{/if}

View File

@ -50,7 +50,7 @@
let count: number = 0
$: objectId = tab.type === 'thread' ? tab.data.thread : tab.data._id
$: objectId = tab.data.thread ?? tab.data._id
$: context = objectId ? $contextByDocStore.get(objectId) : undefined
const unsubscribe = notificationClient.inboxNotificationsByContext.subscribe((res) => {

View File

@ -57,6 +57,7 @@
export let adaptive: HeaderAdaptive = 'default'
export let hideActions: boolean = false
export let canOpenInSidebar: boolean = false
export let closeOnEscape: boolean = true
const client = getClient()
const dispatch = createEventDispatcher()
@ -72,6 +73,7 @@
hideActions={!((canOpen && object) || withAside || $$slots.actions) || hideActions}
hideDescription={!description}
adaptive={adaptive !== 'default' ? adaptive : withFilters ? 'freezeActions' : 'disabled'}
{closeOnEscape}
on:click
on:close
>

View File

@ -33,7 +33,7 @@
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
bind:this={div}
class="border-radius-4 over-underline dateSelectorButton clear-mins"
class="border-radius-4 dateSelectorButton clear-mins"
on:click={() => {
showPopup(DateRangePopup, {}, div, (v) => {
if (v) {
@ -68,13 +68,17 @@
left: 0;
width: 100%;
height: 1px;
background-color: var(--theme-divider-color);
background-color: var(--highlight-select-border);
}
.dateSelectorButton {
cursor: pointer;
padding: 0.25rem 0.5rem;
height: max-content;
background-color: var(--theme-list-row-color);
border: 1px solid var(--theme-divider-color);
color: var(--theme-content-color);
background-color: var(--highlight-select);
border: 1px solid var(--highlight-select-border);
font-weight: 500;
z-index: 10;
}
}

View File

@ -14,20 +14,15 @@
-->
<script lang="ts">
import { Label } from '@hcengineering/ui'
import notification from '@hcengineering/notification'
import { IntlString } from '@hcengineering/platform'
import { Timestamp } from '@hcengineering/core'
export let editedOn: Timestamp | undefined
export let label: IntlString | undefined
</script>
{#if label}
<span class="text-sm lower"> <Label {label} /></span>
{/if}
{#if editedOn}
<span class="text-sm lower"><Label label={notification.string.Edited} /></span>
{/if}
<style lang="scss">
span {

View File

@ -255,7 +255,7 @@
{onClick}
>
<svelte:fragment slot="header">
<ChatMessageHeader editedOn={value.editedOn} label={skipLabel ? undefined : viewlet?.label} />
<ChatMessageHeader label={viewlet?.label} />
</svelte:fragment>
<svelte:fragment slot="content">
{#if !isEditing}

View File

@ -76,7 +76,7 @@
if (direct !== undefined && missingAccounts.length > 0) {
await client.updateDoc(chunter.class.DirectMessage, direct.space, direct._id, {
members: [...direct.members, ...missingAccounts]
$push: { members: { $each: missingAccounts, $position: 0 } }
})
}

View File

@ -24,7 +24,7 @@
{#if tab.data.thread}
<div class="root" style:height style:width>
<ThreadView _id={tab.data.thread} on:close />
<ThreadView _id={tab.data.thread} on:close on:channel />
</div>
{/if}

View File

@ -15,7 +15,7 @@
<script lang="ts">
import { Doc, Ref } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { Breadcrumbs, Label, location as locationStore, Header } from '@hcengineering/ui'
import { Breadcrumbs, Label, location as locationStore, Header, BreadcrumbItem } from '@hcengineering/ui'
import { createEventDispatcher, onDestroy } from 'svelte'
import activity, { ActivityMessage, DisplayActivityMessage } from '@hcengineering/activity'
import { getMessageFromLoc, messageInFocus } from '@hcengineering/activity-resources'
@ -104,7 +104,14 @@
channelName = res
})
function getBreadcrumbsItems (channel?: Doc, message?: DisplayActivityMessage, channelName?: string) {
let breadcrumbs: BreadcrumbItem[] = []
$: breadcrumbs = showHeader ? getBreadcrumbsItems(channel, message, channelName) : []
function getBreadcrumbsItems (
channel?: Doc,
message?: DisplayActivityMessage,
channelName?: string
): BreadcrumbItem[] {
if (message === undefined) {
return []
}
@ -115,6 +122,7 @@
return [
{
id: 'channel',
icon: getObjectIcon(message.attachedToClass),
iconProps: { value: channel },
iconWidth: isPersonAvatar ? 'auto' : undefined,
@ -122,14 +130,24 @@
title: channelName,
label: channelName ? undefined : chunter.string.Channel
},
{ label: chunter.string.Thread }
{ id: 'thread', label: chunter.string.Thread }
]
}
function handleBreadcrumbSelect (event: CustomEvent<number>): void {
const index = event.detail
const breadcrumb = breadcrumbs[index]
if (breadcrumb === undefined) return
if (breadcrumb.id !== 'channel') return
dispatch('channel')
}
</script>
{#if showHeader}
<Header type={'type-aside'} adaptive={'disabled'} on:close>
<Breadcrumbs items={getBreadcrumbsItems(channel, message, channelName)} currentOnly />
<Header type={'type-aside'} adaptive={'disabled'} closeOnEscape={false} on:close>
<Breadcrumbs items={breadcrumbs} on:select={handleBreadcrumbSelect} selected={1} />
</Header>
{/if}
@ -141,8 +159,8 @@
skipLabels
object={message}
provider={dataProvider}
initialScrollBottom={false}
fullHeight={false}
fixedInput={false}
>
<svelte:fragment slot="header">
<div class="mt-3">

View File

@ -61,6 +61,7 @@ import {
getThreadLink,
openChannelInSidebar,
openChannelInSidebarAction,
openThreadInSidebar,
replyToThread
} from './navigation'
import {
@ -199,7 +200,8 @@ export default async (): Promise<Resources> => ({
GetMessageLink: getMessageLocation,
CloseChatWidgetTab: closeChatWidgetTab,
OpenChannelInSidebar: openChannelInSidebar,
CanTranslateMessage: canTranslateMessage
CanTranslateMessage: canTranslateMessage,
OpenThreadInSidebar: openThreadInSidebar
},
actionImpl: {
ArchiveChannel,

View File

@ -6,7 +6,7 @@ import {
type Location,
navigate
} from '@hcengineering/ui'
import { type Ref, type Doc, type Class } from '@hcengineering/core'
import { type Ref, type Doc, type Class, generateId } from '@hcengineering/core'
import activity, { type ActivityMessage } from '@hcengineering/activity'
import {
type Channel,
@ -262,11 +262,11 @@ export async function openChannelInSidebar (
size: isDirect || isPerson ? 'tiny' : 'x-small',
compact: true
},
type: 'channel',
data: {
_id,
_class,
thread
thread,
channelName: name
}
}
@ -288,18 +288,26 @@ export async function openThreadInSidebarChannel (
): Promise<void> {
const newTab: ChatWidgetTab = {
...tab,
name: await translate(chunter.string.ThreadIn, { name: tab.data.channelName }),
data: { ...tab.data, thread: message._id }
}
createWidgetTab(widget, newTab)
}
export async function closeThreadInSidebarChannel (widget: Widget, tab: ChatWidgetTab): Promise<void> {
const thread = tab.allowedPath !== undefined ? tab.data.thread : undefined
const newTab: ChatWidgetTab = {
...tab,
id: tab.id.startsWith('thread_') ? generateId() : tab.id,
name: tab.data.channelName,
allowedPath: undefined,
data: { ...tab.data, thread: undefined }
}
createWidgetTab(widget, newTab)
setTimeout(() => {
removeThreadFromLoc(thread)
}, 100)
}
export async function openThreadInSidebar (_id: Ref<ActivityMessage>, msg?: ActivityMessage, doc?: Doc): Promise<void> {
@ -321,9 +329,7 @@ export async function openThreadInSidebar (_id: Ref<ActivityMessage>, msg?: Acti
const allowedPath = loc.path.join('/')
const currentTAbs = get(sidebarStore).widgetsState.get(widget._id)?.tabs ?? []
const tabsToClose = currentTAbs
.filter((t) => t.isPinned !== true && t.allowedPath === allowedPath && (t as ChatWidgetTab).type === 'thread')
.map((t) => t.id)
const tabsToClose = currentTAbs.filter((t) => t.isPinned !== true && t.allowedPath === allowedPath).map((t) => t.id)
if (tabsToClose.length > 0) {
sidebarStore.update((s) => {
@ -341,26 +347,31 @@ export async function openThreadInSidebar (_id: Ref<ActivityMessage>, msg?: Acti
name: tabName,
icon: chunter.icon.Thread,
allowedPath,
type: 'thread',
data: {
_id: object?._id,
_class: object?._class,
thread: message._id
thread: message._id,
channelName: name
}
}
createWidgetTab(widget, tab, true)
}
export function closeChatWidgetTab (tab?: ChatWidgetTab): void {
if (tab?.type === 'thread') {
const loc = getCurrentLocation()
export function removeThreadFromLoc (thread?: Ref<ActivityMessage>): void {
if (thread === undefined) return
const loc = getCurrentLocation()
if (loc.path[2] === chunterId || loc.path[2] === notificationId) {
if (loc.path[4] === tab.data.thread) {
loc.path[4] = ''
loc.path.length = 4
navigate(loc)
}
if (loc.path[2] === chunterId || loc.path[2] === notificationId) {
if (loc.path[4] === thread) {
loc.path[4] = ''
loc.path.length = 4
navigate(loc)
}
}
}
export function closeChatWidgetTab (tab?: ChatWidgetTab): void {
if (tab?.allowedPath !== undefined) {
removeThreadFromLoc(tab.data.thread)
}
}

View File

@ -434,9 +434,13 @@ export async function readChannelMessages (
return
}
const inboxClient = InboxNotificationsClientImpl.getClient()
if (context === undefined) {
return
}
const inboxClient = InboxNotificationsClientImpl.getClient()
const client = getClient().apply(undefined, 'readViewportMessages')
try {
const readMessages = get(chatReadMessagesStore)
const allIds = getAllIds(messages).filter((id) => !readMessages.has(id))
@ -460,12 +464,6 @@ export async function readChannelMessages (
chatReadMessagesStore.update((store) => new Set([...store, ...allIds]))
await inboxClient.readNotifications(client, [...notifications, ...relatedMentions])
if (context === undefined) {
return
}
const storedTimestampUpdates = get(contextsTimestampStore).get(context._id)
const newTimestamp = messages[messages.length - 1].createdOn ?? 0
const prevTimestamp = Math.max(storedTimestampUpdates ?? 0, context.lastViewedTimestamp ?? 0)
@ -478,6 +476,7 @@ export async function readChannelMessages (
})
await client.update(context, { lastViewedTimestamp: newTimestamp })
}
await inboxClient.readNotifications(client, [...notifications, ...relatedMentions])
} finally {
await client.commit()
}

View File

@ -100,8 +100,7 @@ export interface InlineButton extends AttachedDoc {
}
export interface ChatWidgetTab extends WidgetTab {
type: 'channel' | 'thread'
data: { _id?: Ref<Doc>, _class?: Ref<Class<Doc>>, thread?: Ref<ActivityMessage> }
data: { _id?: Ref<Doc>, _class?: Ref<Class<Doc>>, thread?: Ref<ActivityMessage>, channelName: string }
}
/**
@ -232,6 +231,7 @@ export default plugin(chunterId, {
},
function: {
CanTranslateMessage: '' as Resource<(doc?: Doc | Doc[]) => Promise<boolean>>,
OpenThreadInSidebar: '' as Resource<(_id: Ref<ActivityMessage>, msg?: ActivityMessage, doc?: Doc) => Promise<void>>,
OpenChannelInSidebar: '' as Resource<
(_id: Ref<Doc>, _class: Ref<Doc>, doc?: Doc, thread?: Ref<ActivityMessage>) => Promise<void>
>

View File

@ -37,6 +37,7 @@
import view, { decodeObjectURI } from '@hcengineering/view'
import { parseLinkId } from '@hcengineering/view-resources'
import { get } from 'svelte/store'
import { getResource } from '@hcengineering/platform'
import { InboxNotificationsClientImpl } from '../../inboxNotificationsClient'
import notification from '../../plugin'
@ -179,6 +180,12 @@
}
const selectedMessageId = loc?.loc.query?.message as Ref<ActivityMessage> | undefined
const thread = loc?.loc.path[4] as Ref<ActivityMessage> | undefined
if (thread !== undefined) {
const fn = await getResource(chunter.function.OpenThreadInSidebar)
void fn(thread)
}
if (selectedMessageId !== undefined) {
selectedMessage = get(inboxClient.activityInboxNotifications).find(

View File

@ -588,6 +588,7 @@ export async function selectInboxContext (
const client = getClient()
const hierarchy = client.getHierarchy()
const { objectId, objectClass } = context
const loc = getCurrentLocation()
if (isMentionNotification(notification) && isActivityMessageClass(notification.mentionedInClass)) {
const selectedMsg = notification.mentionedIn as Ref<ActivityMessage>
@ -619,17 +620,25 @@ export async function selectInboxContext (
}
if (isReactionMessage(message)) {
void navigateToInboxDoc(linkProviders, objectId, objectClass, undefined, objectId as Ref<ActivityMessage>)
const thread = loc.path[4] === objectId ? objectId : undefined
void navigateToInboxDoc(
linkProviders,
objectId,
objectClass,
thread as Ref<ActivityMessage>,
objectId as Ref<ActivityMessage>
)
return
}
const selectedMsg = (notification as ActivityInboxNotification)?.attachedTo
const thread = selectedMsg !== objectId ? objectId : loc.path[4] === objectId ? objectId : undefined
void navigateToInboxDoc(
linkProviders,
objectId,
objectClass,
selectedMsg !== undefined ? (objectId as Ref<ActivityMessage>) : undefined,
thread as Ref<ActivityMessage>,
selectedMsg ?? (objectId as Ref<ActivityMessage>)
)
return

View File

@ -100,6 +100,7 @@
hideActions={false}
hideDescription={true}
adaptive="disabled"
closeOnEscape={false}
on:close={() => {
if (widget !== undefined) {
closeWidget(widget._id)
@ -168,5 +169,6 @@
flex-direction: column;
flex: 1;
overflow: hidden;
border-bottom: 1px solid transparent;
}
</style>

View File

@ -101,7 +101,12 @@ export function openWidget (widget: Widget, data?: Record<string, any>, active =
const { widgetsState } = state
const widgetState = widgetsState.get(widget._id)
widgetsState.set(widget._id, { _id: widget._id, data, tab: widgetState?.tab, tabs: widgetState?.tabs ?? [] })
widgetsState.set(widget._id, {
_id: widget._id,
data: data ?? widgetState?.data,
tab: widgetState?.tab,
tabs: widgetState?.tabs ?? []
})
sidebarStore.set({
...state,