platform/plugins/chunter-resources/src/components/chat-message/ChatMessagePresenter.svelte
Kristina cf8ff1be31
Make inbox messages more compact (#4378)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
2024-01-18 15:52:59 +07:00

209 lines
6.3 KiB
Svelte

<!--
// Copyright © 2023 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { Person, PersonAccount } from '@hcengineering/contact'
import { personByIdStore } from '@hcengineering/contact-resources'
import { Account, Class, Doc, getCurrentAccount, Ref } from '@hcengineering/core'
import { createQuery, getClient, MessageViewer } from '@hcengineering/presentation'
import core from '@hcengineering/core/lib/component'
import { AttachmentDocList } from '@hcengineering/attachment-resources'
import { LinkPresenter } from '@hcengineering/view-resources'
import { Action, Button, IconEdit, ShowMore } from '@hcengineering/ui'
import view from '@hcengineering/view'
import activity, { DisplayActivityMessage } from '@hcengineering/activity'
import { ActivityMessageTemplate } from '@hcengineering/activity-resources'
import chunter, { ChatMessage, ChatMessageViewlet } from '@hcengineering/chunter'
import ChatMessageHeader from './ChatMessageHeader.svelte'
import ChatMessageInput from './ChatMessageInput.svelte'
export let value: ChatMessage | undefined
export let showNotify: boolean = false
export let isHighlighted: boolean = false
export let isSelected: boolean = false
export let shouldScroll: boolean = false
export let embedded: boolean = false
export let withActions: boolean = true
export let showEmbedded = false
export let hideReplies = false
export let skipLabel = false
export let actions: Action[] = []
export let excludedActions: string[] = []
export let withFlatActions: boolean = true
export let onClick: (() => void) | undefined = undefined
export let onReply: (() => void) | undefined = undefined
const client = getClient()
const hierarchy = client.getHierarchy()
const viewletQuery = createQuery()
const userQuery = createQuery()
const currentAccount = getCurrentAccount()
let user: PersonAccount | undefined = undefined
let person: Person | undefined = undefined
let parentMessage: DisplayActivityMessage | undefined = undefined
let parentObject: Doc | undefined
let object: Doc | undefined
let viewlet: ChatMessageViewlet | undefined
$: value &&
viewletQuery.query(
chunter.class.ChatMessageViewlet,
{ objectClass: value.attachedToClass, messageClass: value._class },
(result: ChatMessageViewlet[]) => {
viewlet = result[0]
}
)
$: value &&
userQuery.query(core.class.Account, { _id: value.createdBy }, (res: Account[]) => {
user = res[0] as PersonAccount
})
$: person = user?.person && $personByIdStore.get(user.person)
$: value &&
getParentMessage(value.attachedToClass, value.attachedTo).then((res) => {
parentMessage = res as DisplayActivityMessage
})
$: value &&
client.findOne(value.attachedToClass, { _id: value.attachedTo }).then((result) => {
object = result
})
$: parentMessage &&
client.findOne(parentMessage.attachedToClass, { _id: parentMessage.attachedTo }).then((result) => {
parentObject = result
})
$: links = getLinks(value?.message)
function getLinks (content?: string): HTMLLinkElement[] {
if (!content) {
return []
}
const parser = new DOMParser()
const parent = parser.parseFromString(content, 'text/html').firstChild?.childNodes[1] as HTMLElement
return parseLinks(parent.childNodes)
}
function parseLinks (nodes: NodeListOf<ChildNode>): HTMLLinkElement[] {
const res: HTMLLinkElement[] = []
nodes.forEach((node) => {
if (node.nodeType !== Node.TEXT_NODE) {
if (node.nodeName === 'A') {
res.push(node as HTMLLinkElement)
}
res.push(...parseLinks(node.childNodes))
}
})
return res
}
async function getParentMessage (_class: Ref<Class<Doc>>, _id: Ref<Doc>) {
if (hierarchy.isDerived(_class, activity.class.ActivityMessage)) {
return await client.findOne(_class, { _id })
}
}
async function handleEditAction () {
isEditing = true
}
let isEditing = false
let additionalActions: Action[] = []
$: isOwn = user !== undefined && user._id === currentAccount._id
$: additionalActions = [
...(isOwn
? [
{
label: activity.string.Edit,
icon: IconEdit,
group: 'edit',
action: handleEditAction
}
]
: []),
...actions
]
let refInput: ChatMessageInput
</script>
{#if value}
<ActivityMessageTemplate
message={value}
{viewlet}
{parentMessage}
{person}
{excludedActions}
{showNotify}
{isHighlighted}
{isSelected}
{shouldScroll}
{embedded}
{withActions}
actions={additionalActions}
{showEmbedded}
{hideReplies}
{withFlatActions}
{onClick}
{onReply}
>
<svelte:fragment slot="header">
<ChatMessageHeader {object} {parentObject} message={value} {viewlet} {person} {skipLabel} />
</svelte:fragment>
<svelte:fragment slot="content">
{#if !isEditing}
<ShowMore>
<div class="clear-mins">
<MessageViewer message={value.message} />
<AttachmentDocList {value} />
{#each links as link}
<LinkPresenter {link} />
{/each}
</div>
</ShowMore>
{:else if object}
<ChatMessageInput
bind:this={refInput}
chatMessage={value}
shouldSaveDraft={false}
{object}
on:submit={() => {
isEditing = false
}}
/>
<div class="flex-row-center gap-2 justify-end mt-2">
<Button
label={view.string.Cancel}
on:click={() => {
isEditing = false
}}
/>
<Button label={activity.string.Update} accent on:click={() => refInput.submit()} />
</div>
{/if}
</svelte:fragment>
</ActivityMessageTemplate>
{/if}