Chunter: Copy link to message (#2078)

Signed-off-by: Denis Bunakalya <denis.bunakalya@xored.com>
This commit is contained in:
Denis Bunakalya 2022-06-17 10:20:43 +03:00 committed by GitHub
parent e0853ecf99
commit 9d772b786f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 108 additions and 12 deletions

View File

@ -16,6 +16,8 @@ let currentLocation: string | undefined
location.subscribe((loc) => { location.subscribe((loc) => {
if (loc.fragment !== currentLocation && loc.fragment !== undefined && loc.fragment.trim().length > 0) { if (loc.fragment !== currentLocation && loc.fragment !== undefined && loc.fragment.trim().length > 0) {
const props = decodeURIComponent(loc.fragment).split('|') const props = decodeURIComponent(loc.fragment).split('|')
if (props.length >= 3) {
showPanel( showPanel(
props[0] as AnyComponent, props[0] as AnyComponent,
props[1], props[1],
@ -23,6 +25,7 @@ location.subscribe((loc) => {
(props[3] ?? undefined) as PopupAlignment, (props[3] ?? undefined) as PopupAlignment,
(props[4] ?? undefined) as AnyComponent (props[4] ?? undefined) as AnyComponent
) )
}
} else if ( } else if (
(loc.fragment === undefined || (loc.fragment !== undefined && loc.fragment.trim().length === 0)) && (loc.fragment === undefined || (loc.fragment !== undefined && loc.fragment.trim().length === 0)) &&
currentLocation !== undefined currentLocation !== undefined

View File

@ -65,6 +65,7 @@
"ThreadMessage": "Thread message", "ThreadMessage": "Thread message",
"ChunterBrowser": "Search", "ChunterBrowser": "Search",
"Messages": "Messages", "Messages": "Messages",
"NoResults": "No results" "NoResults": "No results",
"CopyLink": "Copy link"
} }
} }

View File

@ -64,6 +64,7 @@
"ThreadMessage": "Сообщение в обсуждении", "ThreadMessage": "Сообщение в обсуждении",
"ChunterBrowser": "Поиск", "ChunterBrowser": "Поиск",
"Messages": "Сообщения", "Messages": "Сообщения",
"NoResults": "Нет результатов" "NoResults": "Нет результатов",
"CopyLink": "Копировать ссылку"
} }
} }

View File

@ -19,6 +19,7 @@
import core, { Doc, Ref, Space, Timestamp, WithLookup } from '@anticrm/core' import core, { Doc, Ref, Space, Timestamp, WithLookup } from '@anticrm/core'
import { NotificationClientImpl } from '@anticrm/notification-resources' import { NotificationClientImpl } from '@anticrm/notification-resources'
import { createQuery } from '@anticrm/presentation' import { createQuery } from '@anticrm/presentation'
import { getCurrentLocation, navigate } from '@anticrm/ui'
import { afterUpdate, beforeUpdate } from 'svelte' import { afterUpdate, beforeUpdate } from 'svelte'
import chunter from '../plugin' import chunter from '../plugin'
import { getDay } from '../utils' import { getDay } from '../utils'
@ -34,12 +35,27 @@
let div: HTMLDivElement | undefined let div: HTMLDivElement | undefined
let autoscroll: boolean = false let autoscroll: boolean = false
let messageIdForScroll = ''
let isMessageHighlighted = false
beforeUpdate(() => { beforeUpdate(() => {
autoscroll = div !== undefined && div.offsetHeight + div.scrollTop > div.scrollHeight - 20 autoscroll = div !== undefined && div.offsetHeight + div.scrollTop > div.scrollHeight - 20
}) })
afterUpdate(() => { afterUpdate(() => {
if (messageIdForScroll && !isMessageHighlighted) {
const messageElement = document.getElementById(messageIdForScroll)
messageElement?.scrollIntoView()
isMessageHighlighted = true
setTimeout(() => {
messageIdForScroll = ''
isMessageHighlighted = false
}, 2000)
return
}
if (div && (autoscroll || isScrollForced)) { if (div && (autoscroll || isScrollForced)) {
div.scrollTo(0, div.scrollHeight) div.scrollTo(0, div.scrollHeight)
isScrollForced = false isScrollForced = false
@ -85,6 +101,15 @@
messages = res messages = res
newMessagesPos = newMessagesStart(messages) newMessagesPos = newMessagesStart(messages)
notificationClient.updateLastView(space, chunter.class.ChunterSpace) notificationClient.updateLastView(space, chunter.class.ChunterSpace)
const location = getCurrentLocation()
const messageId = location.fragment
if (messageId && location.path.length === 3) {
messageIdForScroll = messageId
location.fragment = undefined
navigate(location)
}
}, },
{ {
lookup: { lookup: {
@ -205,6 +230,7 @@
<JumpToDateSelector selectedDate={message.createOn} on:jumpToDate={handleJumpToDate} /> <JumpToDateSelector selectedDate={message.createOn} on:jumpToDate={handleJumpToDate} />
{/if} {/if}
<MessageComponent <MessageComponent
isHighlighted={messageIdForScroll === message._id && isMessageHighlighted}
{message} {message}
{employees} {employees}
on:openThread on:openThread

View File

@ -22,7 +22,17 @@
import { NotificationClientImpl } from '@anticrm/notification-resources' import { NotificationClientImpl } from '@anticrm/notification-resources'
import { getResource } from '@anticrm/platform' import { getResource } from '@anticrm/platform'
import { Avatar, getClient, MessageViewer } from '@anticrm/presentation' import { Avatar, getClient, MessageViewer } from '@anticrm/presentation'
import ui, { ActionIcon, IconMoreH, Menu, showPopup, Label, Tooltip, Button } from '@anticrm/ui' import ui, {
ActionIcon,
IconMoreH,
Menu,
showPopup,
Label,
Tooltip,
Button,
getCurrentLocation,
locationToUrl
} from '@anticrm/ui'
import { Action } from '@anticrm/view' import { Action } from '@anticrm/view'
import { getActions, LinkPresenter } from '@anticrm/view-resources' import { getActions, LinkPresenter } from '@anticrm/view-resources'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
@ -42,6 +52,7 @@
export let thread: boolean = false export let thread: boolean = false
export let isPinned: boolean = false export let isPinned: boolean = false
export let isSaved: boolean = false export let isSaved: boolean = false
export let isHighlighted = false
let refInput: AttachmentRefInput let refInput: AttachmentRefInput
@ -95,6 +106,24 @@
} }
} }
const copyLinkAction = {
label: chunter.string.CopyLink,
action: async () => {
const location = getCurrentLocation()
location.fragment = message._id
location.path[2] = message.space
if (message.attachedToClass === chunter.class.Message) {
location.path.length = 4
location.path[3] = message.attachedTo
} else {
location.path.length = 3
}
await navigator.clipboard.writeText(`${window.location.origin}${locationToUrl(location)}`)
}
}
let menuShowed = false let menuShowed = false
const showMenu = async (ev: Event): Promise<void> => { const showMenu = async (ev: Event): Promise<void> => {
@ -115,6 +144,7 @@
await impl(message, evt) await impl(message, evt)
} }
})), })),
copyLinkAction,
...(getCurrentAccount()._id === message.createBy ? [editAction, deleteAction] : []) ...(getCurrentAccount()._id === message.createBy ? [editAction, deleteAction] : [])
] ]
}, },
@ -176,7 +206,7 @@
} }
</script> </script>
<div class="container"> <div class="container" class:highlighted={isHighlighted} id={message._id}>
<div class="avatar"><Avatar size={'medium'} avatar={employee?.avatar} /></div> <div class="avatar"><Avatar size={'medium'} avatar={employee?.avatar} /></div>
<div class="message"> <div class="message">
<div class="header"> <div class="header">
@ -253,11 +283,20 @@
</div> </div>
<style lang="scss"> <style lang="scss">
@keyframes highlight {
50% {
background-color: var(--warning-color);
}
}
.container { .container {
position: relative; position: relative;
display: flex; display: flex;
padding: 0.5rem 2rem; padding: 0.5rem 2rem;
&.highlighted {
animation: highlight 2000ms ease-in-out;
}
.avatar { .avatar {
min-width: 2.25rem; min-width: 2.25rem;
} }

View File

@ -40,12 +40,27 @@
let div: HTMLDivElement | undefined let div: HTMLDivElement | undefined
let autoscroll: boolean = false let autoscroll: boolean = false
let isScrollForced = false let isScrollForced = false
let messageIdForScroll = ''
let isMessageHighlighted = false
beforeUpdate(() => { beforeUpdate(() => {
autoscroll = div !== undefined && div.offsetHeight + div.scrollTop > div.scrollHeight - 20 autoscroll = div !== undefined && div.offsetHeight + div.scrollTop > div.scrollHeight - 20
}) })
afterUpdate(() => { afterUpdate(() => {
if (messageIdForScroll && !isMessageHighlighted) {
const messageElement = document.getElementById(messageIdForScroll)
messageElement?.scrollIntoView()
isMessageHighlighted = true
setTimeout(() => {
messageIdForScroll = ''
isMessageHighlighted = false
}, 2000)
return
}
if (div && (autoscroll || isScrollForced)) { if (div && (autoscroll || isScrollForced)) {
div.scrollTo(0, div.scrollHeight) div.scrollTo(0, div.scrollHeight)
isScrollForced = false isScrollForced = false
@ -97,6 +112,15 @@
comments = res comments = res
newMessagesPos = newMessagesStart(comments, $lastViews) newMessagesPos = newMessagesStart(comments, $lastViews)
notificationClient.updateLastView(id, chunter.class.Message) notificationClient.updateLastView(id, chunter.class.Message)
const location = getCurrentLocation()
const messageId = location.fragment
if (messageId && location.path.length === 4) {
messageIdForScroll = messageId
location.fragment = undefined
navigate(location)
}
}, },
{ {
lookup lookup
@ -211,6 +235,7 @@
<ChannelSeparator title={chunter.string.New} line reverse isNew /> <ChannelSeparator title={chunter.string.New} line reverse isNew />
{/if} {/if}
<MsgView <MsgView
isHighlighted={messageIdForScroll === comment._id && isMessageHighlighted}
message={comment} message={comment}
{employees} {employees}
thread thread

View File

@ -85,6 +85,7 @@ export default mergeIds(chunterId, chunter, {
MessagesBrowser: '' as IntlString, MessagesBrowser: '' as IntlString,
ChunterBrowser: '' as IntlString, ChunterBrowser: '' as IntlString,
Messages: '' as IntlString, Messages: '' as IntlString,
NoResults: '' as IntlString NoResults: '' as IntlString,
CopyLink: '' as IntlString
} }
}) })