mirror of
https://github.com/hcengineering/platform.git
synced 2025-01-23 12:05:36 +00:00
UBERF-5024: add reactions control to inbox (#4414)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
parent
04b3103372
commit
57409feb99
@ -244,7 +244,7 @@ function groupByTime<T extends ActivityMessage> (messages: T[]): T[][] {
|
||||
|
||||
function getDocUpdateMessageKey (message: DocUpdateMessage): string {
|
||||
const personAccountById = get(personAccountByIdStore)
|
||||
const person = personAccountById.get(message.modifiedBy as any)?.person ?? message.modifiedBy
|
||||
const person = personAccountById.get(message.createdBy as any)?.person ?? message.createdBy
|
||||
|
||||
if (message.action === 'update') {
|
||||
return [message._class, message.attachedTo, message.action, person, getAttributeUpdatesKey(message)].join('_')
|
||||
|
@ -86,6 +86,7 @@
|
||||
}
|
||||
const loc = getLocation()
|
||||
loc.path[4] = message._id
|
||||
loc.query = { ...loc.query, thread: message._id }
|
||||
navigate(loc)
|
||||
}
|
||||
</script>
|
||||
|
@ -178,8 +178,9 @@
|
||||
class:opened={isActionMenuOpened || message.isPinned}
|
||||
>
|
||||
{#if withActions}
|
||||
<AddReactionAction object={message} on:open={handleActionMenuOpened} on:close={handleActionMenuClosed} />
|
||||
|
||||
{#if withFlatActions}
|
||||
<AddReactionAction object={message} />
|
||||
<PinMessageAction object={message} />
|
||||
<SaveMessageAction object={message} />
|
||||
|
||||
@ -231,11 +232,9 @@
|
||||
}
|
||||
|
||||
&.embedded {
|
||||
padding: 0;
|
||||
|
||||
.content {
|
||||
padding: 0.75rem 0.75rem 0.75rem 0;
|
||||
}
|
||||
padding: 0.75rem 0 0 0;
|
||||
gap: 0.25rem;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.actions {
|
||||
@ -292,7 +291,7 @@
|
||||
}
|
||||
|
||||
.embeddedMarker {
|
||||
width: 6px;
|
||||
width: 0.25rem;
|
||||
border-radius: 0.5rem;
|
||||
background: var(--secondary-button-default);
|
||||
}
|
||||
|
@ -41,8 +41,10 @@
|
||||
|
||||
function handleReply (): void {
|
||||
const loc = getLocation()
|
||||
|
||||
loc.fragment = notification.docNotifyContext
|
||||
loc.query = { message: notification.attachedTo }
|
||||
loc.query = { thread: parentMessage?._id ?? message._id }
|
||||
|
||||
navigate(loc)
|
||||
}
|
||||
</script>
|
||||
|
@ -38,6 +38,7 @@ export { default as ActivityDocLink } from './components/ActivityDocLink.svelte'
|
||||
export { default as ReactionPresenter } from './components/reactions/ReactionPresenter.svelte'
|
||||
export { default as ActivityMessageNotificationLabel } from './components/activity-message/ActivityMessageNotificationLabel.svelte'
|
||||
export { default as ActivityMessageHeader } from './components/activity-message/ActivityMessageHeader.svelte'
|
||||
export { default as AddReactionAction } from './components/reactions/AddReactionAction.svelte'
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
|
@ -48,7 +48,7 @@
|
||||
|
||||
onDestroy(unsubscribe)
|
||||
|
||||
$: isDocChannel = !hierarchy.isDerived(context.attachedToClass, chunter.class.ChunterSpace)
|
||||
$: isDocChannel = !hierarchy.isDerived(object._class, chunter.class.ChunterSpace)
|
||||
$: messagesClass = isDocChannel ? activity.class.ActivityMessage : chunter.class.ChatMessage
|
||||
$: collection = isDocChannel ? 'comments' : 'messages'
|
||||
</script>
|
||||
|
@ -13,13 +13,13 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, Ref } from '@hcengineering/core'
|
||||
import { DocNotifyContext } from '@hcengineering/notification'
|
||||
import { Class, Ref, WithLookup } from '@hcengineering/core'
|
||||
import { ActivityInboxNotification, DocNotifyContext } from '@hcengineering/notification'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import activity, { ActivityMessage } from '@hcengineering/activity'
|
||||
import { ChunterSpace } from '@hcengineering/chunter'
|
||||
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
|
||||
import { location as locationStore } from '@hcengineering/ui'
|
||||
import { location as locationStore, Location } from '@hcengineering/ui'
|
||||
import { isReactionMessage } from '@hcengineering/activity-resources'
|
||||
|
||||
import ChannelPresenter from './ChannelView.svelte'
|
||||
@ -37,10 +37,13 @@
|
||||
|
||||
let object: ChunterSpace | undefined = undefined
|
||||
let threadId: Ref<ActivityMessage> | undefined = undefined
|
||||
let notification: WithLookup<ActivityInboxNotification> | undefined = undefined
|
||||
|
||||
let selectedMessageId: Ref<ActivityMessage> | undefined = undefined
|
||||
let loc: Location | undefined = undefined
|
||||
|
||||
locationStore.subscribe((newLocation) => {
|
||||
loc = newLocation
|
||||
selectedMessageId = newLocation.query?.message as Ref<ActivityMessage> | undefined
|
||||
})
|
||||
|
||||
@ -48,11 +51,25 @@
|
||||
? $activityInboxNotificationsStore.find(({ attachedTo }) => attachedTo === selectedMessageId)
|
||||
: undefined
|
||||
|
||||
$: threadId =
|
||||
hierarchy.isDerived(context.attachedToClass, activity.class.ActivityMessage) &&
|
||||
!isReactionMessage(notification?.$lookup?.attachedTo)
|
||||
? (context.attachedTo as Ref<ActivityMessage>)
|
||||
: undefined
|
||||
$: updateThreadId(context, notification, loc)
|
||||
|
||||
function updateThreadId (
|
||||
context: DocNotifyContext,
|
||||
notification?: WithLookup<ActivityInboxNotification>,
|
||||
loc?: Location
|
||||
) {
|
||||
threadId = loc?.query?.thread as Ref<ActivityMessage> | undefined
|
||||
|
||||
if (threadId !== undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
threadId =
|
||||
hierarchy.isDerived(context.attachedToClass, activity.class.ActivityMessage) &&
|
||||
!isReactionMessage(notification?.$lookup?.attachedTo)
|
||||
? (context.attachedTo as Ref<ActivityMessage>)
|
||||
: undefined
|
||||
}
|
||||
|
||||
$: objectQuery.query(_class, { _id }, (res) => {
|
||||
object = res[0]
|
||||
|
@ -189,6 +189,7 @@ export async function deleteChatMessage (message: ChatMessage): Promise<void> {
|
||||
export async function replyToThread (message: ActivityMessage): Promise<void> {
|
||||
const loc = getLocation()
|
||||
loc.path[4] = message._id
|
||||
loc.query = { ...loc.query, thread: message._id }
|
||||
navigate(loc)
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { ActionIcon, Component, IconMoreH, Label, showPopup } from '@hcengineering/ui'
|
||||
import notification, {
|
||||
ActivityInboxNotification,
|
||||
ActivityNotificationViewlet,
|
||||
DisplayInboxNotification,
|
||||
DocNotifyContext
|
||||
@ -23,19 +24,24 @@
|
||||
import { getDocTitle, getDocIdentifier, Menu } from '@hcengineering/view-resources'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { WithLookup } from '@hcengineering/core'
|
||||
import { AddReactionAction } from '@hcengineering/activity-resources'
|
||||
|
||||
import InboxNotificationPresenter from './inbox/InboxNotificationPresenter.svelte'
|
||||
import NotifyContextIcon from './NotifyContextIcon.svelte'
|
||||
import NotifyMarker from './NotifyMarker.svelte'
|
||||
|
||||
export let value: DocNotifyContext
|
||||
export let notifications: DisplayInboxNotification[] = []
|
||||
export let notifications: WithLookup<DisplayInboxNotification>[] = []
|
||||
export let viewlets: ActivityNotificationViewlet[] = []
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let idTitle: string | undefined
|
||||
let title: string | undefined
|
||||
|
||||
$: visibleNotification = notifications[0]
|
||||
|
||||
function showMenu (ev: MouseEvent): void {
|
||||
@ -50,15 +56,40 @@
|
||||
chunter.action.OpenChannel
|
||||
]
|
||||
},
|
||||
ev.target as HTMLElement
|
||||
ev.target as HTMLElement,
|
||||
handleActionMenuClosed
|
||||
)
|
||||
handleActionMenuOpened()
|
||||
}
|
||||
|
||||
const presenterMixin = hierarchy.classHierarchyMixin(
|
||||
$: presenterMixin = hierarchy.classHierarchyMixin(
|
||||
value.attachedToClass,
|
||||
notification.mixin.NotificationContextPresenter
|
||||
)
|
||||
$: isCompact = notifications.length === 1
|
||||
|
||||
$: message =
|
||||
visibleNotification._class === notification.class.ActivityInboxNotification
|
||||
? (visibleNotification as WithLookup<ActivityInboxNotification>).$lookup?.attachedTo
|
||||
: undefined
|
||||
|
||||
let isActionMenuOpened = false
|
||||
|
||||
function handleActionMenuOpened (): void {
|
||||
isActionMenuOpened = true
|
||||
}
|
||||
|
||||
function handleActionMenuClosed (): void {
|
||||
isActionMenuOpened = false
|
||||
}
|
||||
|
||||
$: getDocIdentifier(client, value.attachedTo, value.attachedToClass).then((res) => {
|
||||
idTitle = res
|
||||
})
|
||||
|
||||
$: getDocTitle(client, value.attachedTo, value.attachedToClass).then((res) => {
|
||||
title = res
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if visibleNotification}
|
||||
@ -74,13 +105,15 @@
|
||||
>
|
||||
{#if isCompact}
|
||||
<InboxNotificationPresenter value={visibleNotification} {viewlets} showNotify={false} withActions={false} />
|
||||
<div class="actions compact">
|
||||
<ActionIcon icon={IconMoreH} size="small" action={showMenu} />
|
||||
</div>
|
||||
|
||||
<div class="notifyMarker compact">
|
||||
<NotifyMarker count={unreadCount} />
|
||||
</div>
|
||||
<div class="actions clear-mins flex flex-gap-2 items-center" class:opened={isActionMenuOpened}>
|
||||
{#if message}
|
||||
<AddReactionAction object={message} on:open={handleActionMenuOpened} on:close={handleActionMenuClosed} />
|
||||
{/if}
|
||||
<ActionIcon icon={IconMoreH} size="small" action={showMenu} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="header">
|
||||
<!-- <CheckBox-->
|
||||
@ -96,30 +129,23 @@
|
||||
<Component is={presenterMixin.labelPresenter} props={{ notification: visibleNotification, context: value }} />
|
||||
{:else}
|
||||
<div class="labels">
|
||||
{#await getDocIdentifier(client, value.attachedTo, value.attachedToClass) then title}
|
||||
{#if title}
|
||||
{title}
|
||||
{:else}
|
||||
<Label label={hierarchy.getClass(value.attachedToClass).label} />
|
||||
{/if}
|
||||
{/await}
|
||||
{#await getDocTitle(client, value.attachedTo, value.attachedToClass) then title}
|
||||
<div class="title overflow-label" {title}>
|
||||
{title ?? hierarchy.getClass(value.attachedToClass).label}
|
||||
</div>
|
||||
{/await}
|
||||
{#if idTitle}
|
||||
{idTitle}
|
||||
{:else}
|
||||
<Label label={hierarchy.getClass(value.attachedToClass).label} />
|
||||
{/if}
|
||||
<div class="title overflow-label" {title}>
|
||||
{title ?? hierarchy.getClass(value.attachedToClass).label}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="actions">
|
||||
<ActionIcon icon={IconMoreH} size="small" action={showMenu} />
|
||||
</div>
|
||||
|
||||
<div class="notifyMarker">
|
||||
<NotifyMarker count={unreadCount} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions clear-mins flex flex-gap-2 items-center" class:opened={isActionMenuOpened}>
|
||||
<ActionIcon icon={IconMoreH} size="small" action={showMenu} />
|
||||
</div>
|
||||
<div class="notifyMarker">
|
||||
<NotifyMarker count={unreadCount} />
|
||||
</div>
|
||||
<div class="notification">
|
||||
<InboxNotificationPresenter value={visibleNotification} {viewlets} embedded skipLabel />
|
||||
</div>
|
||||
@ -135,7 +161,8 @@
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0 1rem;
|
||||
padding: 0.5rem 1rem;
|
||||
padding-right: 0;
|
||||
margin: 0.5rem 0;
|
||||
|
||||
&.compact {
|
||||
@ -155,6 +182,22 @@
|
||||
font-weight: 500;
|
||||
max-width: 20.5rem;
|
||||
}
|
||||
|
||||
.actions {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
top: 0.75rem;
|
||||
right: 0.75rem;
|
||||
color: var(--theme-halfcontent-color);
|
||||
|
||||
&.opened {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover > .actions {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.labels {
|
||||
@ -163,28 +206,17 @@
|
||||
}
|
||||
|
||||
.notification {
|
||||
margin-top: 1rem;
|
||||
margin-top: 0.25rem;
|
||||
margin-left: 4rem;
|
||||
}
|
||||
|
||||
.notifyMarker {
|
||||
position: absolute;
|
||||
right: 1.875rem;
|
||||
left: 0.25rem;
|
||||
top: 0;
|
||||
|
||||
&.compact {
|
||||
right: 2.875rem;
|
||||
top: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
|
||||
&.compact {
|
||||
right: 1rem;
|
||||
left: 0.25rem;
|
||||
top: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
@ -40,8 +40,8 @@
|
||||
|
||||
const client = getClient()
|
||||
const messagesQuery = createQuery()
|
||||
const notificationsClient = InboxNotificationsClientImpl.getClient()
|
||||
const notificationsStore = notificationsClient.inboxNotifications
|
||||
const inboxClient = InboxNotificationsClientImpl.getClient()
|
||||
const notificationsStore = inboxClient.inboxNotifications
|
||||
|
||||
let messages: ActivityMessage[] = []
|
||||
let viewlet: ActivityNotificationViewlet | undefined = undefined
|
||||
@ -81,6 +81,7 @@
|
||||
|
||||
function updateViewlet (viewlets: ActivityNotificationViewlet[], message?: DisplayActivityMessage) {
|
||||
if (viewlets.length === 0 || message === undefined) {
|
||||
viewlet = undefined
|
||||
return
|
||||
}
|
||||
|
||||
@ -91,6 +92,8 @@
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
viewlet = undefined
|
||||
}
|
||||
|
||||
function handleReply (message?: DisplayActivityMessage): void {
|
||||
@ -99,7 +102,7 @@
|
||||
}
|
||||
const loc = getLocation()
|
||||
loc.fragment = value.docNotifyContext
|
||||
loc.query = { message: message._id }
|
||||
loc.query = { thread: message._id }
|
||||
navigate(loc)
|
||||
}
|
||||
|
||||
|
@ -96,6 +96,10 @@
|
||||
|
||||
$: filteredNotifications = filterNotifications(selectedTabId, displayNotifications, $notifyContextsStore)
|
||||
|
||||
locationStore.subscribe((newLocation) => {
|
||||
syncLocation(newLocation)
|
||||
})
|
||||
|
||||
async function syncLocation (newLocation: Location) {
|
||||
const loc = await resolveLocation(newLocation)
|
||||
|
||||
|
@ -49,16 +49,9 @@ import { InboxNotificationsClientImpl } from './inboxNotificationsClient'
|
||||
* @public
|
||||
*/
|
||||
export async function hasMarkAsUnreadAction (doc: DisplayInboxNotification): Promise<boolean> {
|
||||
const inboxNotificationsClient = InboxNotificationsClientImpl.getClient()
|
||||
const canRead = await hasMarkAsReadAction(doc)
|
||||
|
||||
const combinedIds =
|
||||
doc._class === notification.class.ActivityInboxNotification
|
||||
? (doc as DisplayActivityInboxNotification).combinedIds
|
||||
: [doc._id]
|
||||
|
||||
return get(inboxNotificationsClient.inboxNotifications).some(
|
||||
({ _id, isViewed }) => combinedIds.includes(_id) && isViewed
|
||||
)
|
||||
return !canRead
|
||||
}
|
||||
|
||||
export async function hasMarkAsReadAction (doc: DisplayInboxNotification): Promise<boolean> {
|
||||
@ -353,11 +346,11 @@ async function generateLocation (
|
||||
loc: {
|
||||
path: [appComponent, workspace, inboxId],
|
||||
fragment: contextId,
|
||||
query: { message: message !== undefined ? (messageId as string) : null }
|
||||
query: { ...loc.query, message: message !== undefined ? (messageId as string) : null }
|
||||
},
|
||||
defaultLocation: {
|
||||
path: [appComponent, workspace, inboxId],
|
||||
query: { message: message !== undefined ? (messageId as string) : null }
|
||||
query: { ...loc.query, message: message !== undefined ? (messageId as string) : null }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user