mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-23 03:22:19 +03:00
UBERF-6829: group messages of the same type and user (#5569)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
parent
07b200a558
commit
c0348bf313
@ -20,6 +20,12 @@ export function getDay (time: Timestamp): Timestamp {
|
|||||||
return date.getTime()
|
return date.getTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getHour (time: Timestamp): Timestamp {
|
||||||
|
const date: Date = new Date(time)
|
||||||
|
date.setMinutes(0, 0, 0)
|
||||||
|
return date.getTime()
|
||||||
|
}
|
||||||
|
|
||||||
export function getDisplayTime (time: number): string {
|
export function getDisplayTime (time: number): string {
|
||||||
let options: Intl.DateTimeFormatOptions = { hour: 'numeric', minute: 'numeric' }
|
let options: Intl.DateTimeFormatOptions = { hour: 'numeric', minute: 'numeric' }
|
||||||
if (!isToday(time)) {
|
if (!isToday(time)) {
|
||||||
@ -37,6 +43,10 @@ export function isOtherDay (time1: Timestamp, time2: Timestamp): boolean {
|
|||||||
return getDay(time1) !== getDay(time2)
|
return getDay(time1) !== getDay(time2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isOtherHour (time1: Timestamp, time2: Timestamp): boolean {
|
||||||
|
return getHour(time1) !== getHour(time2)
|
||||||
|
}
|
||||||
|
|
||||||
function isToday (time: number): boolean {
|
function isToday (time: number): boolean {
|
||||||
const current = new Date()
|
const current = new Date()
|
||||||
const target = new Date(time)
|
const target = new Date(time)
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
import ActivityExtensionComponent from './ActivityExtension.svelte'
|
import ActivityExtensionComponent from './ActivityExtension.svelte'
|
||||||
import ActivityFilter from './ActivityFilter.svelte'
|
import ActivityFilter from './ActivityFilter.svelte'
|
||||||
import { combineActivityMessages } from '../activityMessagesUtils'
|
import { combineActivityMessages } from '../activityMessagesUtils'
|
||||||
|
import { canGroupMessages } from '../utils'
|
||||||
|
|
||||||
export let object: Doc
|
export let object: Doc
|
||||||
export let showCommenInput: boolean = true
|
export let showCommenInput: boolean = true
|
||||||
@ -95,14 +96,16 @@
|
|||||||
<div class="p-activity select-text" id={activity.string.Activity} class:newest-first={isNewestFirst}>
|
<div class="p-activity select-text" id={activity.string.Activity} class:newest-first={isNewestFirst}>
|
||||||
{#if filteredMessages.length}
|
{#if filteredMessages.length}
|
||||||
<Grid column={1} rowGap={0}>
|
<Grid column={1} rowGap={0}>
|
||||||
{#each filteredMessages as message}
|
{#each filteredMessages as message, index}
|
||||||
|
{@const canGroup = canGroupMessages(message, filteredMessages[index - 1])}
|
||||||
<Lazy>
|
<Lazy>
|
||||||
<Component
|
<Component
|
||||||
is={activity.component.ActivityMessagePresenter}
|
is={activity.component.ActivityMessagePresenter}
|
||||||
props={{
|
props={{
|
||||||
value: message,
|
value: message,
|
||||||
hideLink: true,
|
hideLink: true,
|
||||||
boundary
|
boundary,
|
||||||
|
type: canGroup ? 'short' : 'default'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Lazy>
|
</Lazy>
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||||
|
|
||||||
export let date: Timestamp
|
export let date: Timestamp
|
||||||
|
export let shortTime = false
|
||||||
|
|
||||||
$: fullDate = new Date(date).toLocaleString('default', {
|
$: fullDate = new Date(date).toLocaleString('default', {
|
||||||
minute: '2-digit',
|
minute: '2-digit',
|
||||||
@ -27,8 +28,14 @@
|
|||||||
month: 'short',
|
month: 'short',
|
||||||
year: 'numeric'
|
year: 'numeric'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function getShortTime (date: Timestamp): string {
|
||||||
|
const options: Intl.DateTimeFormatOptions = { hour: 'numeric', minute: 'numeric' }
|
||||||
|
|
||||||
|
return new Date(date).toLocaleTimeString('default', options).split(' ')[0]
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<span class="text-sm" use:tooltip={{ label: getEmbeddedLabel(fullDate) }}>
|
<span class="text-sm" use:tooltip={{ label: getEmbeddedLabel(fullDate) }}>
|
||||||
{getDisplayTime(date)}
|
{shortTime ? getShortTime(date) : getDisplayTime(date)}
|
||||||
</span>
|
</span>
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { DisplayActivityMessage } from '@hcengineering/activity'
|
import { DisplayActivityMessage, ActivityMessageViewType } from '@hcengineering/activity'
|
||||||
import view from '@hcengineering/view'
|
import view from '@hcengineering/view'
|
||||||
import { getClient } from '@hcengineering/presentation'
|
import { getClient } from '@hcengineering/presentation'
|
||||||
import { Action, Component } from '@hcengineering/ui'
|
import { Action, Component } from '@hcengineering/ui'
|
||||||
@ -34,6 +34,7 @@
|
|||||||
export let hoverStyles: 'borderedHover' | 'filledHover' = 'borderedHover'
|
export let hoverStyles: 'borderedHover' | 'filledHover' = 'borderedHover'
|
||||||
export let withShowMore: boolean = true
|
export let withShowMore: boolean = true
|
||||||
export let attachmentImageSize: 'x-large' | undefined = undefined
|
export let attachmentImageSize: 'x-large' | undefined = undefined
|
||||||
|
export let type: ActivityMessageViewType = 'default'
|
||||||
export let showLinksPreview = true
|
export let showLinksPreview = true
|
||||||
export let videoPreload = true
|
export let videoPreload = true
|
||||||
export let hideLink = false
|
export let hideLink = false
|
||||||
@ -68,6 +69,7 @@
|
|||||||
showLinksPreview,
|
showLinksPreview,
|
||||||
videoPreload,
|
videoPreload,
|
||||||
hideLink,
|
hideLink,
|
||||||
|
type,
|
||||||
compact,
|
compact,
|
||||||
onClick
|
onClick
|
||||||
}}
|
}}
|
||||||
|
@ -13,7 +13,11 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import activity, { ActivityMessageViewlet, DisplayActivityMessage } from '@hcengineering/activity'
|
import activity, {
|
||||||
|
ActivityMessageViewlet,
|
||||||
|
DisplayActivityMessage,
|
||||||
|
ActivityMessageViewType
|
||||||
|
} from '@hcengineering/activity'
|
||||||
import { Person } from '@hcengineering/contact'
|
import { Person } from '@hcengineering/contact'
|
||||||
import { Avatar, EmployeePresenter, SystemAvatar } from '@hcengineering/contact-resources'
|
import { Avatar, EmployeePresenter, SystemAvatar } from '@hcengineering/contact-resources'
|
||||||
import core from '@hcengineering/core'
|
import core from '@hcengineering/core'
|
||||||
@ -47,6 +51,7 @@
|
|||||||
export let hoverable = true
|
export let hoverable = true
|
||||||
export let hoverStyles: 'borderedHover' | 'filledHover' = 'borderedHover'
|
export let hoverStyles: 'borderedHover' | 'filledHover' = 'borderedHover'
|
||||||
export let showDatePreposition = false
|
export let showDatePreposition = false
|
||||||
|
export let type: ActivityMessageViewType = 'default'
|
||||||
export let onClick: (() => void) | undefined = undefined
|
export let onClick: (() => void) | undefined = undefined
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
@ -93,6 +98,12 @@
|
|||||||
|
|
||||||
let readonly: boolean = false
|
let readonly: boolean = false
|
||||||
$: readonly = $restrictionStore.disableComments
|
$: readonly = $restrictionStore.disableComments
|
||||||
|
|
||||||
|
function canDisplayShort (type: ActivityMessageViewType, isSaved: boolean): boolean {
|
||||||
|
return type === 'short' && !isSaved && (message.replies ?? 0) === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
$: isShort = canDisplayShort(type, isSaved)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if !isHidden}
|
{#if !isHidden}
|
||||||
@ -119,10 +130,16 @@
|
|||||||
isActionsOpened = true
|
isActionsOpened = true
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{#if showNotify && !embedded}
|
{#if showNotify && !embedded && !isShort}
|
||||||
<div class="notify" />
|
<div class="notify" />
|
||||||
{/if}
|
{/if}
|
||||||
{#if !embedded}
|
{#if embedded}
|
||||||
|
<div class="embeddedMarker" />
|
||||||
|
{:else if isShort}
|
||||||
|
<span class="text-sm lower time">
|
||||||
|
<MessageTimestamp date={message.createdOn ?? message.modifiedOn} shortTime />
|
||||||
|
</span>
|
||||||
|
{:else}
|
||||||
<div class="min-w-6 mt-1 relative">
|
<div class="min-w-6 mt-1 relative">
|
||||||
{#if $$slots.icon}
|
{#if $$slots.icon}
|
||||||
<slot name="icon" />
|
<slot name="icon" />
|
||||||
@ -137,10 +154,9 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
|
||||||
<div class="embeddedMarker" />
|
|
||||||
{/if}
|
{/if}
|
||||||
<div class="flex-col ml-2 w-full clear-mins message-content">
|
<div class="flex-col ml-2 w-full clear-mins message-content">
|
||||||
|
{#if !isShort}
|
||||||
<div class="header clear-mins">
|
<div class="header clear-mins">
|
||||||
{#if person}
|
{#if person}
|
||||||
<EmployeePresenter value={person} shouldShowAvatar={false} compact />
|
<EmployeePresenter value={person} shouldShowAvatar={false} compact />
|
||||||
@ -164,6 +180,7 @@
|
|||||||
<MessageTimestamp date={message.createdOn ?? message.modifiedOn} />
|
<MessageTimestamp date={message.createdOn ?? message.modifiedOn} />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<slot name="content" />
|
<slot name="content" />
|
||||||
|
|
||||||
@ -203,7 +220,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
padding: 0.75rem 0.75rem 0.75rem 1rem;
|
padding: 0.5rem 0.75rem 0.5rem 1rem;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
//overflow: hidden;
|
//overflow: hidden;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
@ -243,6 +260,18 @@
|
|||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover > .time {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
display: flex;
|
||||||
|
justify-content: end;
|
||||||
|
width: 2.5rem;
|
||||||
|
visibility: hidden;
|
||||||
|
margin-top: 0.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
&.actionsOpened {
|
&.actionsOpened {
|
||||||
&.borderedHover {
|
&.borderedHover {
|
||||||
border: 1px solid var(--global-ui-BackgroundColor);
|
border: 1px solid var(--global-ui-BackgroundColor);
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import activity, {
|
import activity, {
|
||||||
ActivityMessage,
|
ActivityMessage,
|
||||||
|
ActivityMessageViewType,
|
||||||
DisplayActivityMessage,
|
DisplayActivityMessage,
|
||||||
DisplayDocUpdateMessage,
|
DisplayDocUpdateMessage,
|
||||||
DocUpdateMessage,
|
DocUpdateMessage,
|
||||||
@ -35,6 +36,7 @@
|
|||||||
import DocUpdateMessageHeader from './DocUpdateMessageHeader.svelte'
|
import DocUpdateMessageHeader from './DocUpdateMessageHeader.svelte'
|
||||||
|
|
||||||
import { getAttributeModel, getCollectionAttribute } from '../../activityMessagesUtils'
|
import { getAttributeModel, getCollectionAttribute } from '../../activityMessagesUtils'
|
||||||
|
import { getIsTextType } from '../../utils'
|
||||||
|
|
||||||
export let value: DisplayDocUpdateMessage
|
export let value: DisplayDocUpdateMessage
|
||||||
export let showNotify: boolean = false
|
export let showNotify: boolean = false
|
||||||
@ -50,6 +52,7 @@
|
|||||||
export let hoverable = true
|
export let hoverable = true
|
||||||
export let hoverStyles: 'borderedHover' | 'filledHover' = 'borderedHover'
|
export let hoverStyles: 'borderedHover' | 'filledHover' = 'borderedHover'
|
||||||
export let hideLink = false
|
export let hideLink = false
|
||||||
|
export let type: ActivityMessageViewType = 'default'
|
||||||
export let onClick: (() => void) | undefined = undefined
|
export let onClick: (() => void) | undefined = undefined
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
@ -169,6 +172,7 @@
|
|||||||
{skipLabel}
|
{skipLabel}
|
||||||
{hoverable}
|
{hoverable}
|
||||||
{hoverStyles}
|
{hoverStyles}
|
||||||
|
type={viewlet?.label || getIsTextType(attributeModel) ? 'default' : type}
|
||||||
showDatePreposition={hideLink}
|
showDatePreposition={hideLink}
|
||||||
{onClick}
|
{onClick}
|
||||||
>
|
>
|
||||||
|
@ -17,7 +17,8 @@ import core, {
|
|||||||
TxProcessor,
|
TxProcessor,
|
||||||
type TxUpdateDoc,
|
type TxUpdateDoc,
|
||||||
matchQuery,
|
matchQuery,
|
||||||
getCurrentAccount
|
getCurrentAccount,
|
||||||
|
isOtherHour
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import { type Asset, type IntlString, getResource, translate } from '@hcengineering/platform'
|
import { type Asset, type IntlString, getResource, translate } from '@hcengineering/platform'
|
||||||
import { getAttributePresenterClass, getClient } from '@hcengineering/presentation'
|
import { getAttributePresenterClass, getClient } from '@hcengineering/presentation'
|
||||||
@ -520,9 +521,36 @@ export async function unpinMessage (message?: ActivityMessage): Promise<void> {
|
|||||||
await client.update(message, { isPinned: false })
|
await client.update(message, { isPinned: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getIsTextType (attributeModel: AttributeModel): boolean {
|
export function getIsTextType (attributeModel?: AttributeModel): boolean {
|
||||||
|
if (attributeModel === undefined) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
attributeModel.attribute?.type?._class === core.class.TypeMarkup ||
|
attributeModel.attribute?.type?._class === core.class.TypeMarkup ||
|
||||||
attributeModel.attribute?.type?._class === core.class.TypeCollaborativeMarkup
|
attributeModel.attribute?.type?._class === core.class.TypeCollaborativeMarkup
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const groupMessagesThresholdMs = 15 * 60 * 1000
|
||||||
|
|
||||||
|
type MessageData = Pick<ActivityMessage, '_class' | 'createdBy' | 'createdOn' | 'modifiedOn'>
|
||||||
|
|
||||||
|
export function canGroupMessages (message: MessageData, prevMessage?: MessageData): boolean {
|
||||||
|
if (prevMessage === undefined) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.createdBy !== prevMessage.createdBy || message._class !== prevMessage._class) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const time1 = message.createdOn ?? message.modifiedOn
|
||||||
|
const time2 = prevMessage.createdOn ?? prevMessage.modifiedOn
|
||||||
|
|
||||||
|
if (isOtherHour(time1, time2)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return time1 - time2 < groupMessagesThresholdMs
|
||||||
|
}
|
||||||
|
@ -313,6 +313,7 @@ export interface UserMentionInfo extends AttachedDoc {
|
|||||||
export interface IgnoreActivity extends Class<Doc> {}
|
export interface IgnoreActivity extends Class<Doc> {}
|
||||||
|
|
||||||
export type ActivityMessagePreviewType = 'full' | 'content-only'
|
export type ActivityMessagePreviewType = 'full' | 'content-only'
|
||||||
|
export type ActivityMessageViewType = 'default' | 'short'
|
||||||
|
|
||||||
export default plugin(activityId, {
|
export default plugin(activityId, {
|
||||||
mixin: {
|
mixin: {
|
||||||
|
@ -34,9 +34,11 @@ import chunter from './plugin'
|
|||||||
|
|
||||||
export type LoadMode = 'forward' | 'backward'
|
export type LoadMode = 'forward' | 'backward'
|
||||||
|
|
||||||
interface MessageMetadata {
|
export interface MessageMetadata {
|
||||||
_id: Ref<ActivityMessage>
|
_id: Ref<ActivityMessage>
|
||||||
|
_class: Ref<Class<ActivityMessage>>
|
||||||
createdOn?: Timestamp
|
createdOn?: Timestamp
|
||||||
|
modifiedOn: Timestamp
|
||||||
createdBy?: Ref<Account>
|
createdBy?: Ref<Account>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +56,7 @@ interface IChannelDataProvider {
|
|||||||
messagesStore: Readable<ActivityMessage[]>
|
messagesStore: Readable<ActivityMessage[]>
|
||||||
newTimestampStore: Readable<Timestamp | undefined>
|
newTimestampStore: Readable<Timestamp | undefined>
|
||||||
datesStore: Readable<Timestamp[]>
|
datesStore: Readable<Timestamp[]>
|
||||||
|
metadataStore: Readable<MessageMetadata[]>
|
||||||
|
|
||||||
loadMore: (mode: LoadMode, loadAfter: Timestamp) => Promise<void>
|
loadMore: (mode: LoadMode, loadAfter: Timestamp) => Promise<void>
|
||||||
canLoadMore: (mode: LoadMode, loadAfter: Timestamp) => boolean
|
canLoadMore: (mode: LoadMode, loadAfter: Timestamp) => boolean
|
||||||
@ -72,7 +75,7 @@ export class ChannelDataProvider implements IChannelDataProvider {
|
|||||||
private selectedMsgId: Ref<ActivityMessage> | undefined = undefined
|
private selectedMsgId: Ref<ActivityMessage> | undefined = undefined
|
||||||
private tailStart: Timestamp | undefined = undefined
|
private tailStart: Timestamp | undefined = undefined
|
||||||
|
|
||||||
private readonly metadataStore = writable<MessageMetadata[]>([])
|
public readonly metadataStore = writable<MessageMetadata[]>([])
|
||||||
private readonly tailStore = writable<ActivityMessage[]>([])
|
private readonly tailStore = writable<ActivityMessage[]>([])
|
||||||
private readonly chunksStore = writable<Chunk[]>([])
|
private readonly chunksStore = writable<Chunk[]>([])
|
||||||
|
|
||||||
@ -166,7 +169,7 @@ export class ChannelDataProvider implements IChannelDataProvider {
|
|||||||
void this.loadInitialMessages(undefined, loadAll)
|
void this.loadInitialMessages(undefined, loadAll)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
projection: { _id: 1, createdOn: 1, createdBy: 1, attachedTo: 1 },
|
projection: { _id: 1, _class: 1, createdOn: 1, createdBy: 1, attachedTo: 1, modifiedOn: 1 },
|
||||||
sort: { createdOn: SortingOrder.Ascending }
|
sort: { createdOn: SortingOrder.Ascending }
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -24,7 +24,8 @@
|
|||||||
import { Loading, Scroller, ScrollParams } from '@hcengineering/ui'
|
import { Loading, Scroller, ScrollParams } from '@hcengineering/ui'
|
||||||
import {
|
import {
|
||||||
ActivityExtension as ActivityExtensionComponent,
|
ActivityExtension as ActivityExtensionComponent,
|
||||||
ActivityMessagePresenter
|
ActivityMessagePresenter,
|
||||||
|
canGroupMessages
|
||||||
} from '@hcengineering/activity-resources'
|
} from '@hcengineering/activity-resources'
|
||||||
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
|
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
|
||||||
import { get } from 'svelte/store'
|
import { get } from 'svelte/store'
|
||||||
@ -34,7 +35,7 @@
|
|||||||
import ActivityMessagesSeparator from './ChannelMessagesSeparator.svelte'
|
import ActivityMessagesSeparator from './ChannelMessagesSeparator.svelte'
|
||||||
import { filterChatMessages, getClosestDate, readChannelMessages } from '../utils'
|
import { filterChatMessages, getClosestDate, readChannelMessages } from '../utils'
|
||||||
import HistoryLoading from './LoadingHistory.svelte'
|
import HistoryLoading from './LoadingHistory.svelte'
|
||||||
import { ChannelDataProvider } from '../channelDataProvider'
|
import { ChannelDataProvider, MessageMetadata } from '../channelDataProvider'
|
||||||
import JumpToDateSelector from './JumpToDateSelector.svelte'
|
import JumpToDateSelector from './JumpToDateSelector.svelte'
|
||||||
|
|
||||||
export let provider: ChannelDataProvider
|
export let provider: ChannelDataProvider
|
||||||
@ -71,6 +72,7 @@
|
|||||||
const isLoadingMoreStore = provider.isLoadingMoreStore
|
const isLoadingMoreStore = provider.isLoadingMoreStore
|
||||||
const newTimestampStore = provider.newTimestampStore
|
const newTimestampStore = provider.newTimestampStore
|
||||||
const datesStore = provider.datesStore
|
const datesStore = provider.datesStore
|
||||||
|
const metadataStore = provider.metadataStore
|
||||||
|
|
||||||
let messages: ActivityMessage[] = []
|
let messages: ActivityMessage[] = []
|
||||||
let displayMessages: DisplayActivityMessage[] = []
|
let displayMessages: DisplayActivityMessage[] = []
|
||||||
@ -553,6 +555,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$: void compensateAside(isAsideOpened)
|
$: void compensateAside(isAsideOpened)
|
||||||
|
|
||||||
|
function canGroupChatMessages (message: ActivityMessage, prevMessage?: ActivityMessage) {
|
||||||
|
let prevMetadata: MessageMetadata | undefined = undefined
|
||||||
|
|
||||||
|
if (prevMessage === undefined) {
|
||||||
|
const metadata = $metadataStore
|
||||||
|
prevMetadata = metadata.find((_, index) => metadata[index + 1]?._id === message._id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return canGroupMessages(message, prevMessage ?? prevMetadata)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if isLoading}
|
{#if isLoading}
|
||||||
@ -589,6 +602,7 @@
|
|||||||
|
|
||||||
{#each displayMessages as message, index (message._id)}
|
{#each displayMessages as message, index (message._id)}
|
||||||
{@const isSelected = message._id === selectedMessageId}
|
{@const isSelected = message._id === selectedMessageId}
|
||||||
|
{@const canGroup = canGroupChatMessages(message, displayMessages[index - 1])}
|
||||||
|
|
||||||
{#if separatorIndex === index}
|
{#if separatorIndex === index}
|
||||||
<ActivityMessagesSeparator bind:element={separatorElement} label={activity.string.New} />
|
<ActivityMessagesSeparator bind:element={separatorElement} label={activity.string.New} />
|
||||||
@ -609,6 +623,7 @@
|
|||||||
withShowMore={false}
|
withShowMore={false}
|
||||||
attachmentImageSize="x-large"
|
attachmentImageSize="x-large"
|
||||||
showLinksPreview={false}
|
showLinksPreview={false}
|
||||||
|
type={canGroup ? 'short' : 'default'}
|
||||||
hideLink
|
hideLink
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -633,7 +648,6 @@
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.msg {
|
.msg {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
min-height: 4.375rem;
|
|
||||||
height: auto;
|
height: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
import chunter, { ChatMessage } from '@hcengineering/chunter'
|
import chunter, { ChatMessage } from '@hcengineering/chunter'
|
||||||
import { closeTooltip, Label, Lazy, Spinner, resizeObserver, MiniToggle } from '@hcengineering/ui'
|
import { closeTooltip, Label, Lazy, Spinner, resizeObserver, MiniToggle } from '@hcengineering/ui'
|
||||||
import { ObjectPresenter, DocNavLink } from '@hcengineering/view-resources'
|
import { ObjectPresenter, DocNavLink } from '@hcengineering/view-resources'
|
||||||
|
import { canGroupMessages } from '@hcengineering/activity-resources'
|
||||||
|
|
||||||
import ChatMessageInput from './ChatMessageInput.svelte'
|
import ChatMessageInput from './ChatMessageInput.svelte'
|
||||||
import ChatMessagePresenter from './ChatMessagePresenter.svelte'
|
import ChatMessagePresenter from './ChatMessagePresenter.svelte'
|
||||||
@ -83,10 +84,11 @@
|
|||||||
<Spinner />
|
<Spinner />
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
{#each messages as message}
|
{#each messages as message, index}
|
||||||
|
{@const canGroup = canGroupMessages(message, messages[index - 1])}
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<Lazy>
|
<Lazy>
|
||||||
<ChatMessagePresenter value={message} hideLink />
|
<ChatMessagePresenter value={message} hideLink type={canGroup ? 'short' : 'default'} />
|
||||||
</Lazy>
|
</Lazy>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
@ -131,10 +133,6 @@
|
|||||||
.item {
|
.item {
|
||||||
max-width: 30rem;
|
max-width: 30rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item + .item {
|
|
||||||
margin-top: 0.75rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
import { getDocLinkTitle, LinkPresenter } from '@hcengineering/view-resources'
|
import { getDocLinkTitle, LinkPresenter } from '@hcengineering/view-resources'
|
||||||
import { Action, Button, IconEdit, ShowMore } from '@hcengineering/ui'
|
import { Action, Button, IconEdit, ShowMore } from '@hcengineering/ui'
|
||||||
import view from '@hcengineering/view'
|
import view from '@hcengineering/view'
|
||||||
import activity, { DisplayActivityMessage } from '@hcengineering/activity'
|
import activity, { ActivityMessageViewType, DisplayActivityMessage } from '@hcengineering/activity'
|
||||||
import { ActivityDocLink, ActivityMessageTemplate } from '@hcengineering/activity-resources'
|
import { ActivityDocLink, ActivityMessageTemplate } from '@hcengineering/activity-resources'
|
||||||
import chunter, { ChatMessage, ChatMessageViewlet } from '@hcengineering/chunter'
|
import chunter, { ChatMessage, ChatMessageViewlet } from '@hcengineering/chunter'
|
||||||
import { Attachment } from '@hcengineering/attachment'
|
import { Attachment } from '@hcengineering/attachment'
|
||||||
@ -49,6 +49,7 @@
|
|||||||
export let videoPreload = true
|
export let videoPreload = true
|
||||||
export let hideLink = false
|
export let hideLink = false
|
||||||
export let compact = false
|
export let compact = false
|
||||||
|
export let type: ActivityMessageViewType = 'default'
|
||||||
export let onClick: (() => void) | undefined = undefined
|
export let onClick: (() => void) | undefined = undefined
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
@ -181,6 +182,7 @@
|
|||||||
{hoverStyles}
|
{hoverStyles}
|
||||||
{skipLabel}
|
{skipLabel}
|
||||||
showDatePreposition={hideLink}
|
showDatePreposition={hideLink}
|
||||||
|
{type}
|
||||||
{onClick}
|
{onClick}
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="header">
|
<svelte:fragment slot="header">
|
||||||
|
@ -121,7 +121,7 @@
|
|||||||
loadMoreAllowed={false}
|
loadMoreAllowed={false}
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="header">
|
<svelte:fragment slot="header">
|
||||||
<div class="mt-3 mr-6 ml-6">
|
<div class="mt-3">
|
||||||
<ThreadParentMessage {message} />
|
<ThreadParentMessage {message} />
|
||||||
</div>
|
</div>
|
||||||
<div class="separator">
|
<div class="separator">
|
||||||
|
Loading…
Reference in New Issue
Block a user