Fix thread messages pin (#6129)

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
Kristina 2024-07-25 10:40:06 +04:00 committed by GitHub
parent 27c626857a
commit 3b9318e34a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 104 additions and 37 deletions

View File

@ -92,7 +92,7 @@
skipLabels={!isDocChannel} skipLabels={!isDocChannel}
selectedFilters={filters} selectedFilters={filters}
startFromBottom startFromBottom
{selectedMessageId} bind:selectedMessageId
{collection} {collection}
provider={dataProvider} provider={dataProvider}
{isAsideOpened} {isAsideOpened}

View File

@ -78,7 +78,7 @@
on:aside-toggled on:aside-toggled
on:close on:close
> >
<PinnedMessages {_id} {_class} /> <PinnedMessages {_id} {_class} on:select />
</Header> </Header>
</div> </div>

View File

@ -55,7 +55,7 @@
export let scrollElement: HTMLDivElement | undefined = undefined export let scrollElement: HTMLDivElement | undefined = undefined
export let startFromBottom = false export let startFromBottom = false
export let selectedFilters: Ref<ActivityMessagesFilter>[] = [] export let selectedFilters: Ref<ActivityMessagesFilter>[] = []
export let withDates: boolean = true export let embedded = false
export let collection: string | undefined = undefined export let collection: string | undefined = undefined
export let showEmbedded = false export let showEmbedded = false
export let skipLabels = false export let skipLabels = false
@ -355,7 +355,7 @@
} }
function updateSelectedDate (): void { function updateSelectedDate (): void {
if (!withDates) { if (embedded) {
return return
} }
@ -468,13 +468,13 @@
if (isLoading || !isScrollInitialized || isInitialScrolling) { if (isLoading || !isScrollInitialized || isInitialScrolling) {
return return
} }
const msg = messages.find(({ _id }) => _id === selectedMessageId) const msg = $metadataStore.find(({ _id }) => _id === selectedMessageId)
if (msg !== undefined) { if (msg !== undefined) {
const isReload = provider.jumpToMessage(msg) const isReload = provider.jumpToMessage(msg)
if (isReload) { if (isReload) {
reinitializeScroll() reinitializeScroll()
} }
} else { } else if (selectedMessageId === undefined) {
provider.jumpToEnd() provider.jumpToEnd()
reinitializeScroll() reinitializeScroll()
} }
@ -677,7 +677,7 @@
{#if startFromBottom} {#if startFromBottom}
<div class="grower" /> <div class="grower" />
{/if} {/if}
{#if withDates && displayMessages.length > 0 && selectedDate} {#if !embedded && displayMessages.length > 0 && selectedDate}
<div class="selectedDate"> <div class="selectedDate">
<JumpToDateSelector {selectedDate} fixed on:jumpToDate={jumpToDate} /> <JumpToDateSelector {selectedDate} fixed on:jumpToDate={jumpToDate} />
</div> </div>
@ -702,7 +702,7 @@
{/if} {/if}
<slot name="header" /> <slot name="header" />
{#if displayMessages.length === 0 && !hierarchy.isDerived(objectClass, activity.class.ActivityMessage)} {#if displayMessages.length === 0 && !embedded}
<BlankView <BlankView
icon={chunter.icon.Thread} icon={chunter.icon.Thread}
header={chunter.string.NoMessagesInChannel} header={chunter.string.NoMessagesInChannel}
@ -717,7 +717,7 @@
<ActivityMessagesSeparator bind:element={separatorElement} label={activity.string.New} /> <ActivityMessagesSeparator bind:element={separatorElement} label={activity.string.New} />
{/if} {/if}
{#if withDates && message.createdOn && $datesStore.includes(message.createdOn)} {#if !embedded && message.createdOn && $datesStore.includes(message.createdOn)}
<JumpToDateSelector selectedDate={message.createdOn} on:jumpToDate={jumpToDate} /> <JumpToDateSelector selectedDate={message.createdOn} on:jumpToDate={jumpToDate} />
{/if} {/if}
@ -741,7 +741,7 @@
{/if} {/if}
</Scroller> </Scroller>
{#if showScrollDownButton} {#if !embedded && showScrollDownButton}
<div class="down-button absolute"> <div class="down-button absolute">
<ModernButton <ModernButton
label={chunter.string.LatestMessages} label={chunter.string.LatestMessages}

View File

@ -16,23 +16,27 @@
import core, { Doc, getCurrentAccount, Ref, Space } from '@hcengineering/core' import core, { Doc, getCurrentAccount, Ref, Space } from '@hcengineering/core'
import { import {
defineSeparators, defineSeparators,
getCurrentLocation,
Label, Label,
location as locationStore, location as locationStore,
ModernButton, ModernButton,
navigate,
panelSeparators, panelSeparators,
Separator Separator
} from '@hcengineering/ui' } from '@hcengineering/ui'
import { DocNotifyContext } from '@hcengineering/notification' import { DocNotifyContext } from '@hcengineering/notification'
import { ActivityMessagesFilter } from '@hcengineering/activity' import { ActivityMessage, ActivityMessagesFilter } from '@hcengineering/activity'
import { getClient } from '@hcengineering/presentation' import { getClient } from '@hcengineering/presentation'
import { Channel } from '@hcengineering/chunter' import { Channel } from '@hcengineering/chunter'
import view from '@hcengineering/view' import view from '@hcengineering/view'
import { messageInFocus } from '@hcengineering/activity-resources'
import ChannelComponent from './Channel.svelte' import ChannelComponent from './Channel.svelte'
import ChannelHeader from './ChannelHeader.svelte' import ChannelHeader from './ChannelHeader.svelte'
import DocAside from './chat/DocAside.svelte' import DocAside from './chat/DocAside.svelte'
import chunter from '../plugin' import chunter from '../plugin'
import ChannelAside from './chat/ChannelAside.svelte' import ChannelAside from './chat/ChannelAside.svelte'
import { isThreadMessage } from '../utils'
export let object: Doc export let object: Doc
export let context: DocNotifyContext | undefined export let context: DocNotifyContext | undefined
@ -76,6 +80,18 @@
} }
defineSeparators('aside', panelSeparators) defineSeparators('aside', panelSeparators)
async function handleMessageSelect (event: CustomEvent<ActivityMessage>): Promise<void> {
const message = event.detail
if (isThreadMessage(message)) {
const location = getCurrentLocation()
location.path[4] = message.attachedTo
navigate(location)
}
messageInFocus.set(message._id)
}
</script> </script>
<div class="popupPanel panel" class:embedded> <div class="popupPanel panel" class:embedded>
@ -89,6 +105,7 @@
canOpen={isDocChat} canOpen={isDocChat}
{isAsideShown} {isAsideShown}
on:close on:close
on:select={handleMessageSelect}
on:aside-toggled={() => { on:aside-toggled={() => {
isAsideShown = !isAsideShown isAsideShown = !isAsideShown
}} }}

View File

@ -19,32 +19,53 @@
import activity, { ActivityMessage } from '@hcengineering/activity' import activity, { ActivityMessage } from '@hcengineering/activity'
import { Class, Doc, Ref } from '@hcengineering/core' import { Class, Doc, Ref } from '@hcengineering/core'
import view from '@hcengineering/view' import view from '@hcengineering/view'
import { ThreadMessage } from '@hcengineering/chunter'
import { createEventDispatcher } from 'svelte'
import chunter from '../plugin' import chunter from '../plugin'
export let _class: Ref<Class<Doc>> export let _class: Ref<Class<Doc>>
export let _id: Ref<Doc> export let _id: Ref<Doc>
const dispatch = createEventDispatcher()
const pinnedQuery = createQuery() const pinnedQuery = createQuery()
const pinnedThreadsQuery = createQuery()
let pinnedMessagesCount = 0 let pinnedMessagesCount = 0
let pinnedThreadsCount = 0
$: pinnedQuery.query( $: pinnedQuery.query(
activity.class.ActivityMessage, activity.class.ActivityMessage,
{ attachedTo: _id, isPinned: true }, { attachedTo: _id, isPinned: true },
(res: ActivityMessage[]) => { (res: ActivityMessage[]) => {
pinnedMessagesCount = res.length pinnedMessagesCount = res.length
} },
{ projection: { _id: 1, attachedTo: 1, isPinned: 1 } }
) )
$: pinnedThreadsQuery.query(
chunter.class.ThreadMessage,
{ objectId: _id, isPinned: true },
(res: ThreadMessage[]) => {
pinnedThreadsCount = res.length
},
{ projection: { _id: 1, objectId: 1, isPinned: 1 } }
)
function openMessagesPopup (ev: MouseEvent) { function openMessagesPopup (ev: MouseEvent) {
showPopup(PinnedMessagesPopup, { attachedTo: _id, attachedToClass: _class }, eventToHTMLElement(ev)) showPopup(PinnedMessagesPopup, { attachedTo: _id, attachedToClass: _class }, eventToHTMLElement(ev), (result) => {
if (result == null) return
dispatch('select', result)
})
} }
$: count = pinnedMessagesCount + pinnedThreadsCount
</script> </script>
{#if pinnedMessagesCount > 0} {#if count > 0}
<div class="antiHSpacer x2" /> <div class="antiHSpacer x2" />
<ModernButton size={'extra-small'} on:click={openMessagesPopup}> <ModernButton size={'extra-small'} on:click={openMessagesPopup}>
<Icon icon={view.icon.Pin} size={'x-small'} /> <Icon icon={view.icon.Pin} size={'x-small'} />
<span class="text-sm"><Label label={chunter.string.PinnedCount} params={{ count: pinnedMessagesCount }} /></span> <span class="text-sm"><Label label={chunter.string.PinnedCount} params={{ count }} /></span>
</ModernButton> </ModernButton>
{/if} {/if}

View File

@ -13,12 +13,13 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import { Class, Doc, Ref } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation' import { createQuery, getClient } from '@hcengineering/presentation'
import activity, { ActivityMessage, DisplayActivityMessage } from '@hcengineering/activity' import activity, { ActivityMessage, DisplayActivityMessage } from '@hcengineering/activity'
import { ActivityMessagePresenter } from '@hcengineering/activity-resources' import { ActivityMessagePresenter, sortActivityMessages } from '@hcengineering/activity-resources'
import { ActionIcon, IconClose } from '@hcengineering/ui' import { ActionIcon, IconClose } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import { ThreadMessage } from '@hcengineering/chunter'
import { Class, Doc, Ref, SortingOrder } from '@hcengineering/core'
import chunter from '../plugin' import chunter from '../plugin'
@ -26,40 +27,54 @@
export let attachedToClass: Ref<Class<Doc>> export let attachedToClass: Ref<Class<Doc>>
const client = getClient() const client = getClient()
const hierarchy = client.getHierarchy()
const messagesQuery = createQuery()
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const pinnedQuery = createQuery()
const pinnedThreadsQuery = createQuery()
let pinnedMessages: DisplayActivityMessage[] = [] let pinnedMessages: ActivityMessage[] = []
let pinnedThreads: ThreadMessage[] = []
$: messagesQuery.query(activity.class.ActivityMessage, { attachedTo, isPinned: true }, (res: ActivityMessage[]) => { $: pinnedQuery.query(activity.class.ActivityMessage, { attachedTo, isPinned: true }, (res: ActivityMessage[]) => {
pinnedMessages = res as DisplayActivityMessage[] pinnedMessages = res
if (pinnedMessages.length === 0) {
dispatch('close')
}
}) })
async function unPinMessaage (message: ActivityMessage): Promise<void> { $: pinnedThreadsQuery.query(
chunter.class.ThreadMessage,
{ objectId: attachedTo, isPinned: true },
(res: ThreadMessage[]) => {
pinnedThreads = res
}
)
$: if (pinnedMessages.length === 0 && pinnedThreads.length === 0) {
dispatch('close', undefined)
}
async function unpinMessage (message: ActivityMessage): Promise<void> {
await client.update(message, { isPinned: false }) await client.update(message, { isPinned: false })
} }
$: displayMessages = sortActivityMessages(pinnedMessages.concat(pinnedThreads), SortingOrder.Descending)
</script> </script>
<div class="antiPopup vScroll popup"> <div class="antiPopup vScroll popup">
{#each pinnedMessages as message} {#each displayMessages as message}
<div class="message relative"> <div class="message relative">
<ActivityMessagePresenter <ActivityMessagePresenter
value={message} value={message}
withActions={false} withActions={false}
hoverable={false} hoverable={false}
skipLabel={!hierarchy.isDerived(attachedToClass, chunter.class.ChunterSpace)} skipLabel={true}
onClick={() => {
dispatch('close', message)
}}
/> />
<div class="actions"> <div class="actions">
<ActionIcon <ActionIcon
size="small" size="small"
icon={IconClose} icon={IconClose}
action={() => { action={() => {
unPinMessaage(message) unpinMessage(message)
}} }}
/> />
</div> </div>

View File

@ -16,9 +16,9 @@
import { Doc, Ref } from '@hcengineering/core' import { Doc, Ref } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation' import { createQuery, getClient } from '@hcengineering/presentation'
import { Breadcrumbs, IconClose, Label, location as locationStore } from '@hcengineering/ui' import { Breadcrumbs, IconClose, Label, location as locationStore } from '@hcengineering/ui'
import { createEventDispatcher, onMount } from 'svelte' import { createEventDispatcher, onDestroy } from 'svelte'
import activity, { ActivityMessage, DisplayActivityMessage } from '@hcengineering/activity' import activity, { ActivityMessage, DisplayActivityMessage } from '@hcengineering/activity'
import { getMessageFromLoc } from '@hcengineering/activity-resources' import { getMessageFromLoc, messageInFocus } from '@hcengineering/activity-resources'
import contact from '@hcengineering/contact' import contact from '@hcengineering/contact'
import chunter from '../../plugin' import chunter from '../../plugin'
@ -45,8 +45,23 @@
let channelName: string | undefined = undefined let channelName: string | undefined = undefined
let dataProvider: ChannelDataProvider | undefined = undefined let dataProvider: ChannelDataProvider | undefined = undefined
locationStore.subscribe((newLocation) => { const unsubscribe = messageInFocus.subscribe((id) => {
selectedMessageId = getMessageFromLoc(newLocation) if (id !== undefined && id !== selectedMessageId) {
selectedMessageId = id
}
messageInFocus.set(undefined)
})
const unsubscribeLocation = locationStore.subscribe((newLocation) => {
const id = getMessageFromLoc(newLocation)
selectedMessageId = id
messageInFocus.set(id)
})
onDestroy(() => {
unsubscribe()
unsubscribeLocation()
}) })
$: messageQuery.query(activity.class.ActivityMessage, { _id }, (result: ActivityMessage[]) => { $: messageQuery.query(activity.class.ActivityMessage, { _id }, (result: ActivityMessage[]) => {
@ -111,14 +126,13 @@
<div class="container"> <div class="container">
{#if message && dataProvider !== undefined} {#if message && dataProvider !== undefined}
<ChannelScrollView <ChannelScrollView
{selectedMessageId} bind:selectedMessageId
withDates={false} embedded
skipLabels skipLabels
object={message} object={message}
objectId={message._id} objectId={message._id}
objectClass={message._class} objectClass={message._class}
provider={dataProvider} provider={dataProvider}
loadMoreAllowed={false}
> >
<svelte:fragment slot="header"> <svelte:fragment slot="header">
<div class="mt-3"> <div class="mt-3">