mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 03:14:40 +03:00
UBERF-5527: add context menu for activity and inbox (#5373)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
parent
577c948d29
commit
7405b01d04
@ -21,8 +21,6 @@ import {
|
||||
type ActivityInfoMessage,
|
||||
type ActivityMessage,
|
||||
type ActivityMessageControl,
|
||||
type ActivityMessageExtension,
|
||||
type ActivityMessageExtensionKind,
|
||||
type ActivityMessagePreview,
|
||||
type ActivityMessagesFilter,
|
||||
type ActivityReference,
|
||||
@ -70,7 +68,7 @@ import {
|
||||
} from '@hcengineering/model'
|
||||
import { TAttachedDoc, TClass, TDoc } from '@hcengineering/model-core'
|
||||
import preference, { TPreference } from '@hcengineering/model-preference'
|
||||
import view from '@hcengineering/model-view'
|
||||
import view, { createAction } from '@hcengineering/model-view'
|
||||
import notification from '@hcengineering/notification'
|
||||
import type { Asset, IntlString, Resource } from '@hcengineering/platform'
|
||||
import { type AnyComponent } from '@hcengineering/ui/src/types'
|
||||
@ -210,15 +208,6 @@ export class TDocUpdateMessageViewlet extends TDoc implements DocUpdateMessageVi
|
||||
onlyWithParent?: boolean
|
||||
}
|
||||
|
||||
@Model(activity.class.ActivityMessageExtension, core.class.Doc, DOMAIN_MODEL)
|
||||
export class TActivityMessageExtension extends TDoc implements ActivityMessageExtension {
|
||||
@Prop(TypeRef(activity.class.ActivityMessage), core.string.Class)
|
||||
@Index(IndexKind.Indexed)
|
||||
ofMessage!: Ref<Class<ActivityMessage>>
|
||||
|
||||
components!: { kind: ActivityMessageExtensionKind, component: AnyComponent }[]
|
||||
}
|
||||
|
||||
@Model(activity.class.ActivityExtension, core.class.Doc, DOMAIN_MODEL)
|
||||
export class TActivityExtension extends TDoc implements ActivityExtension {
|
||||
@Prop(TypeRef(core.class.Class), core.string.Class)
|
||||
@ -275,7 +264,6 @@ export function createModel (builder: Builder): void {
|
||||
TTxViewlet,
|
||||
TActivityDoc,
|
||||
TActivityMessagesFilter,
|
||||
TActivityMessageExtension,
|
||||
TActivityMessage,
|
||||
TDocUpdateMessage,
|
||||
TDocUpdateMessageViewlet,
|
||||
@ -426,6 +414,113 @@ export function createModel (builder: Builder): void {
|
||||
{ attachedToClass: 1 }
|
||||
]
|
||||
})
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: activity.actionImpl.AddReaction,
|
||||
label: activity.string.AddReaction,
|
||||
icon: activity.icon.Emoji,
|
||||
input: 'focus',
|
||||
category: activity.category.Activity,
|
||||
target: activity.class.ActivityMessage,
|
||||
inline: true,
|
||||
context: {
|
||||
mode: 'context',
|
||||
group: 'edit'
|
||||
}
|
||||
},
|
||||
activity.ids.AddReactionAction
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: activity.actionImpl.SaveForLater,
|
||||
label: activity.string.SaveForLater,
|
||||
icon: activity.icon.Bookmark,
|
||||
input: 'focus',
|
||||
inline: true,
|
||||
actionProps: {
|
||||
size: 'x-small'
|
||||
},
|
||||
category: activity.category.Activity,
|
||||
target: activity.class.ActivityMessage,
|
||||
visibilityTester: activity.function.CanSaveForLater,
|
||||
context: {
|
||||
mode: 'context',
|
||||
group: 'edit'
|
||||
}
|
||||
},
|
||||
activity.ids.SaveForLaterAction
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: activity.actionImpl.RemoveFromSaved,
|
||||
label: activity.string.RemoveFromLater,
|
||||
icon: activity.icon.BookmarkFilled,
|
||||
input: 'focus',
|
||||
inline: true,
|
||||
actionProps: {
|
||||
iconProps: {
|
||||
fill: 'var(--global-accent-TextColor)'
|
||||
}
|
||||
},
|
||||
category: activity.category.Activity,
|
||||
target: activity.class.ActivityMessage,
|
||||
visibilityTester: activity.function.CanRemoveFromSaved,
|
||||
context: {
|
||||
mode: 'context',
|
||||
group: 'edit'
|
||||
}
|
||||
},
|
||||
activity.ids.RemoveFromLaterAction
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: activity.actionImpl.PinMessage,
|
||||
label: view.string.Pin,
|
||||
icon: view.icon.Pin,
|
||||
input: 'focus',
|
||||
inline: true,
|
||||
category: activity.category.Activity,
|
||||
target: activity.class.ActivityMessage,
|
||||
visibilityTester: activity.function.CanPinMessage,
|
||||
context: {
|
||||
mode: 'context',
|
||||
group: 'edit'
|
||||
}
|
||||
},
|
||||
activity.ids.PinMessageAction
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: activity.actionImpl.UnpinMessage,
|
||||
label: view.string.Unpin,
|
||||
icon: view.icon.Pin,
|
||||
input: 'focus',
|
||||
inline: true,
|
||||
actionProps: {
|
||||
iconProps: {
|
||||
fill: 'var(--global-accent-TextColor)'
|
||||
}
|
||||
},
|
||||
category: activity.category.Activity,
|
||||
target: activity.class.ActivityMessage,
|
||||
visibilityTester: activity.function.CanUnpinMessage,
|
||||
context: {
|
||||
mode: 'context',
|
||||
group: 'edit'
|
||||
}
|
||||
},
|
||||
activity.ids.UnpinMessageAction
|
||||
)
|
||||
}
|
||||
|
||||
export default activity
|
||||
|
@ -16,7 +16,13 @@ import { activityId, type ActivityMessage, type DocUpdateMessageViewlet } from '
|
||||
import activity from '@hcengineering/activity-resources/src/plugin'
|
||||
import { type IntlString, mergeIds, type Resource } from '@hcengineering/platform'
|
||||
import { type Doc, type Ref } from '@hcengineering/core'
|
||||
import { type ActionCategory } from '@hcengineering/view'
|
||||
import type { Location } from '@hcengineering/ui'
|
||||
import {
|
||||
type Action,
|
||||
type ActionCategory,
|
||||
type ViewAction,
|
||||
type ViewActionAvailabilityFunction
|
||||
} from '@hcengineering/view'
|
||||
import { type NotificationGroup, type NotificationType } from '@hcengineering/notification'
|
||||
|
||||
export default mergeIds(activityId, activity, {
|
||||
@ -24,7 +30,10 @@ export default mergeIds(activityId, activity, {
|
||||
Attributes: '' as IntlString,
|
||||
Pinned: '' as IntlString,
|
||||
Emoji: '' as IntlString,
|
||||
Replies: '' as IntlString
|
||||
Replies: '' as IntlString,
|
||||
AddReaction: '' as IntlString,
|
||||
SaveForLater: '' as IntlString,
|
||||
RemoveFromLater: '' as IntlString
|
||||
},
|
||||
filter: {
|
||||
AttributesFilter: '' as Resource<(message: ActivityMessage, _class?: Ref<Doc>) => boolean>,
|
||||
@ -35,9 +44,28 @@ export default mergeIds(activityId, activity, {
|
||||
ids: {
|
||||
ReactionAddedActivityViewlet: '' as Ref<DocUpdateMessageViewlet>,
|
||||
ActivityNotificationGroup: '' as Ref<NotificationGroup>,
|
||||
AddReactionNotification: '' as Ref<NotificationType>
|
||||
AddReactionNotification: '' as Ref<NotificationType>,
|
||||
AddReactionAction: '' as Ref<Action>,
|
||||
SaveForLaterAction: '' as Ref<Action>,
|
||||
RemoveFromLaterAction: '' as Ref<Action>,
|
||||
PinMessageAction: '' as Ref<Action>,
|
||||
UnpinMessageAction: '' as Ref<Action>
|
||||
},
|
||||
function: {
|
||||
GetFragment: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>,
|
||||
CanSaveForLater: '' as Resource<ViewActionAvailabilityFunction>,
|
||||
CanRemoveFromSaved: '' as Resource<ViewActionAvailabilityFunction>,
|
||||
CanPinMessage: '' as Resource<ViewActionAvailabilityFunction>,
|
||||
CanUnpinMessage: '' as Resource<ViewActionAvailabilityFunction>
|
||||
},
|
||||
category: {
|
||||
Activity: '' as Ref<ActionCategory>
|
||||
},
|
||||
actionImpl: {
|
||||
AddReaction: '' as ViewAction,
|
||||
SaveForLater: '' as ViewAction,
|
||||
RemoveFromSaved: '' as ViewAction,
|
||||
PinMessage: '' as ViewAction,
|
||||
UnpinMessage: '' as ViewAction
|
||||
}
|
||||
})
|
||||
|
@ -677,46 +677,6 @@ export function createModel (builder: Builder, options = { addApplication: true
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityMessageExtension, core.space.Model, {
|
||||
ofMessage: chunter.class.ChatMessage,
|
||||
components: [{ kind: 'footer', component: chunter.component.Replies }]
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityMessageExtension, core.space.Model, {
|
||||
ofMessage: activity.class.DocUpdateMessage,
|
||||
components: [{ kind: 'footer', component: chunter.component.Replies }]
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityMessageExtension, core.space.Model, {
|
||||
ofMessage: activity.class.ActivityInfoMessage,
|
||||
components: [{ kind: 'footer', component: chunter.component.Replies }]
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityMessageExtension, core.space.Model, {
|
||||
ofMessage: activity.class.ActivityReference,
|
||||
components: [{ kind: 'footer', component: chunter.component.Replies }]
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityMessageExtension, core.space.Model, {
|
||||
ofMessage: chunter.class.ChatMessage,
|
||||
components: [{ kind: 'action', component: chunter.component.ReplyToThreadAction }]
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityMessageExtension, core.space.Model, {
|
||||
ofMessage: activity.class.DocUpdateMessage,
|
||||
components: [{ kind: 'action', component: chunter.component.ReplyToThreadAction }]
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityMessageExtension, core.space.Model, {
|
||||
ofMessage: activity.class.ActivityInfoMessage,
|
||||
components: [{ kind: 'action', component: chunter.component.ReplyToThreadAction }]
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityMessageExtension, core.space.Model, {
|
||||
ofMessage: activity.class.ActivityReference,
|
||||
components: [{ kind: 'action', component: chunter.component.ReplyToThreadAction }]
|
||||
})
|
||||
|
||||
builder.mixin(chunter.class.Channel, core.class.Class, chunter.mixin.ObjectChatPanel, {
|
||||
ignoreKeys: ['archived', 'collaborators', 'lastMessage', 'pinned', 'topic', 'description']
|
||||
})
|
||||
@ -741,6 +701,25 @@ export function createModel (builder: Builder, options = { addApplication: true
|
||||
builder.createDoc(activity.class.ReplyProvider, core.space.Model, {
|
||||
function: chunter.function.ReplyToThread
|
||||
})
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: chunter.actionImpl.ReplyToThread,
|
||||
label: chunter.string.ReplyToThread,
|
||||
icon: chunter.icon.Thread,
|
||||
input: 'focus',
|
||||
category: chunter.category.Chunter,
|
||||
target: activity.class.ActivityMessage,
|
||||
visibilityTester: chunter.function.CanReplyToThread,
|
||||
inline: true,
|
||||
context: {
|
||||
mode: 'context',
|
||||
group: 'edit'
|
||||
}
|
||||
},
|
||||
chunter.action.ReplyToThreadAction
|
||||
)
|
||||
}
|
||||
|
||||
export default chunter
|
||||
|
@ -40,13 +40,15 @@ export default mergeIds(chunterId, chunter, {
|
||||
ArchiveChannel: '' as Ref<Action>,
|
||||
UnarchiveChannel: '' as Ref<Action>,
|
||||
ConvertToPrivate: '' as Ref<Action>,
|
||||
CopyChatMessageLink: '' as Ref<Action<Doc, any>>
|
||||
CopyChatMessageLink: '' as Ref<Action<Doc, any>>,
|
||||
ReplyToThreadAction: '' as Ref<Action>
|
||||
},
|
||||
actionImpl: {
|
||||
ArchiveChannel: '' as ViewAction,
|
||||
UnarchiveChannel: '' as ViewAction,
|
||||
ConvertDmToPrivateChannel: '' as ViewAction,
|
||||
DeleteChatMessage: '' as ViewAction
|
||||
DeleteChatMessage: '' as ViewAction,
|
||||
ReplyToThread: '' as ViewAction
|
||||
},
|
||||
category: {
|
||||
Chunter: '' as Ref<ActionCategory>
|
||||
@ -100,8 +102,9 @@ export default mergeIds(chunterId, chunter, {
|
||||
CanCopyMessageLink: '' as Resource<(doc?: Doc | Doc[]) => Promise<boolean>>,
|
||||
GetChunterSpaceLinkFragment: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>,
|
||||
GetThreadLink: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>,
|
||||
GetMessageLink: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>,
|
||||
ReplyToThread: '' as Resource<(doc: ActivityMessage) => Promise<void>>
|
||||
ReplyToThread: '' as Resource<(doc: ActivityMessage) => Promise<void>>,
|
||||
CanReplyToThread: '' as Resource<(doc?: Doc | Doc[]) => Promise<boolean>>,
|
||||
GetMessageLink: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>
|
||||
},
|
||||
filter: {
|
||||
ChatMessagesFilter: '' as Resource<(message: ActivityMessage, _class?: Ref<Doc>) => boolean>
|
||||
|
@ -111,6 +111,7 @@ export function closePopup (category?: string): void {
|
||||
} else {
|
||||
for (let i = popups.length - 1; i >= 0; i--) {
|
||||
if (popups[i].options.fixed !== true) {
|
||||
popups[i].onClose?.(undefined)
|
||||
popups.splice(i, 1)
|
||||
break
|
||||
}
|
||||
|
@ -239,6 +239,7 @@ export type IconSize =
|
||||
| 'inline'
|
||||
| 'tiny'
|
||||
| 'card'
|
||||
| 'xx-small'
|
||||
| 'x-small'
|
||||
| 'smaller'
|
||||
| 'small'
|
||||
@ -258,6 +259,7 @@ export function getIconSize2x (size: IconSize): IconSize {
|
||||
switch (size) {
|
||||
case 'inline':
|
||||
case 'tiny':
|
||||
case 'xx-small':
|
||||
case 'x-small':
|
||||
case 'small':
|
||||
case 'card':
|
||||
|
@ -11,4 +11,7 @@
|
||||
<symbol id="bookmark" viewBox="0 0 20 28">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 4.00012C0 1.79098 1.79086 0.00012207 4 0.00012207H16C18.2091 0.00012207 20 1.79098 20 4.00012V27.0001C20 27.3689 19.797 27.7078 19.4719 27.8818C19.1467 28.0558 18.7522 28.0367 18.4453 27.8322L10 22.202L1.5547 27.8322C1.24784 28.0367 0.8533 28.0558 0.528142 27.8818C0.202985 27.7078 0 27.3689 0 27.0001V4.00012ZM4 2.00012C2.89543 2.00012 2 2.89555 2 4.00012V25.1316L9.4453 20.1681C9.7812 19.9441 10.2188 19.9441 10.5547 20.1681L18 25.1316V4.00012C18 2.89555 17.1046 2.00012 16 2.00012H4Z" />
|
||||
</symbol>
|
||||
<symbol id="bookmark-filled" viewBox="0 0 32 32">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6 6.00012C6 3.79098 7.79086 2.00012 10 2.00012H22C24.2091 2.00012 26 3.79098 26 6.00012V29.0001C26 29.3689 25.797 29.7078 25.4719 29.8818C25.1467 30.0558 24.7522 30.0367 24.4453 29.8322L16 24.202L7.5547 29.8322C7.24784 30.0367 6.8533 30.0558 6.52814 29.8818C6.20298 29.7078 6 29.3689 6 29.0001V6.00012Z" />
|
||||
</symbol>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.8 KiB |
@ -40,6 +40,9 @@
|
||||
"Mentions": "Mentions",
|
||||
"MentionedYouIn": "Mentioned you in {title}",
|
||||
"Messages": "Messages",
|
||||
"Thread": "Thread"
|
||||
"Thread": "Thread",
|
||||
"AddReaction": "Add reaction",
|
||||
"SaveForLater": "Save for later",
|
||||
"RemoveFromLater": "Remove from saved"
|
||||
}
|
||||
}
|
@ -40,6 +40,9 @@
|
||||
"Mentions": "Упоминания",
|
||||
"MentionedYouIn": "Упомянул(а) вас в {title}",
|
||||
"Messages": "Cообщения",
|
||||
"Thread": "Обсуждение"
|
||||
"Thread": "Обсуждение",
|
||||
"AddReaction": "Добавить реакцию",
|
||||
"SaveForLater": "Cохранить",
|
||||
"RemoveFromLater": "Удалить из сохраненных"
|
||||
}
|
||||
}
|
@ -20,5 +20,6 @@ const icons = require('../assets/icons.svg') as string // eslint-disable-line
|
||||
loadMetadata(activity.icon, {
|
||||
Activity: `${icons}#activity`,
|
||||
Emoji: `${icons}#emoji`,
|
||||
Bookmark: `${icons}#bookmark`
|
||||
Bookmark: `${icons}#bookmark`,
|
||||
BookmarkFilled: `${icons}#bookmark-filled`
|
||||
})
|
||||
|
@ -14,9 +14,10 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { type AnySvelteComponent, ButtonIcon, IconSize } from '@hcengineering/ui'
|
||||
import { Asset } from '@hcengineering/platform'
|
||||
import { Asset, IntlString } from '@hcengineering/platform'
|
||||
import { ComponentType } from 'svelte'
|
||||
|
||||
export let label: IntlString
|
||||
export let icon: Asset | AnySvelteComponent | ComponentType
|
||||
export let iconProps: any | undefined = undefined
|
||||
export let size: IconSize = 'small'
|
||||
@ -30,4 +31,13 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<ButtonIcon {icon} {iconProps} iconSize={size} size="small" kind="tertiary" pressed={opened} on:click={onClick} />
|
||||
<ButtonIcon
|
||||
{icon}
|
||||
{iconProps}
|
||||
iconSize={size}
|
||||
size="small"
|
||||
kind="tertiary"
|
||||
pressed={opened}
|
||||
on:click={onClick}
|
||||
tooltip={{ label }}
|
||||
/>
|
||||
|
@ -13,60 +13,95 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import activity, { ActivityMessage, ActivityMessageExtension } from '@hcengineering/activity'
|
||||
import activity, { ActivityMessage } from '@hcengineering/activity'
|
||||
import { Action, IconMoreV, showPopup } from '@hcengineering/ui'
|
||||
import { Menu } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { getActions, Menu } from '@hcengineering/view-resources'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import view, { Action as ViewAction } from '@hcengineering/view'
|
||||
|
||||
import ActivityMessageAction from './ActivityMessageAction.svelte'
|
||||
import PinMessageAction from './PinMessageAction.svelte'
|
||||
import SaveMessageAction from './SaveMessageAction.svelte'
|
||||
import ActivityMessageExtensionComponent from './activity-message/ActivityMessageExtension.svelte'
|
||||
import AddReactionAction from './reactions/AddReactionAction.svelte'
|
||||
import { savedMessagesStore } from '../activity'
|
||||
|
||||
export let message: ActivityMessage | undefined
|
||||
export let extensions: ActivityMessageExtension[] = []
|
||||
export let actions: Action[] = []
|
||||
export let withActionMenu = true
|
||||
export let onOpen: () => void
|
||||
export let onClose: () => void
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
let inlineActions: ViewAction[] = []
|
||||
let isActionMenuOpened = false
|
||||
|
||||
$: void updateInlineActions(message)
|
||||
|
||||
savedMessagesStore.subscribe(() => {
|
||||
void updateInlineActions(message)
|
||||
})
|
||||
|
||||
function handleActionMenuOpened (): void {
|
||||
isActionMenuOpened = true
|
||||
dispatch('open')
|
||||
onOpen()
|
||||
}
|
||||
|
||||
function handleActionMenuClosed (): void {
|
||||
isActionMenuOpened = false
|
||||
dispatch('close')
|
||||
onClose()
|
||||
}
|
||||
|
||||
function showMenu (ev: MouseEvent): void {
|
||||
const excludedActions = inlineActions.map(({ _id }) => _id)
|
||||
|
||||
showPopup(
|
||||
Menu,
|
||||
{
|
||||
object: message,
|
||||
actions,
|
||||
baseMenuClass: activity.class.ActivityMessage
|
||||
baseMenuClass: activity.class.ActivityMessage,
|
||||
excludedActions
|
||||
},
|
||||
ev.target as HTMLElement,
|
||||
handleActionMenuClosed
|
||||
)
|
||||
handleActionMenuOpened()
|
||||
}
|
||||
|
||||
async function updateInlineActions (message?: ActivityMessage): Promise<void> {
|
||||
if (message === undefined) {
|
||||
inlineActions = []
|
||||
return
|
||||
}
|
||||
inlineActions = (await getActions(client, message, activity.class.ActivityMessage)).filter(
|
||||
(action) => action.inline
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if message}
|
||||
<div class="root">
|
||||
<AddReactionAction object={message} on:open on:close />
|
||||
<ActivityMessageExtensionComponent kind="action" {extensions} props={{ object: message }} on:close on:open />
|
||||
<PinMessageAction object={message} />
|
||||
<SaveMessageAction object={message} />
|
||||
{#each inlineActions as inline}
|
||||
{#if inline.icon}
|
||||
{#await getResource(inline.action) then action}
|
||||
<ActivityMessageAction
|
||||
label={inline.label}
|
||||
size={inline.actionProps?.size ?? 'small'}
|
||||
icon={inline.icon}
|
||||
iconProps={inline.actionProps?.iconProps}
|
||||
action={(ev) => action(message, ev, { onOpen, onClose })}
|
||||
/>
|
||||
{/await}
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
{#if withActionMenu}
|
||||
<ActivityMessageAction size="small" icon={IconMoreV} opened={isActionMenuOpened} action={showMenu} />
|
||||
<ActivityMessageAction
|
||||
size="small"
|
||||
icon={IconMoreV}
|
||||
opened={isActionMenuOpened}
|
||||
action={showMenu}
|
||||
label={view.string.MoreActions}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -26,9 +26,10 @@
|
||||
import core, { Account, Doc, Ref, Timestamp } from '@hcengineering/core'
|
||||
import { Icon, Label, resizeObserver, TimeSince, tooltip } from '@hcengineering/ui'
|
||||
import { Asset, getEmbeddedLabel, IntlString } from '@hcengineering/platform'
|
||||
import activity, { ActivityMessagePreviewType } from '@hcengineering/activity'
|
||||
import { classIcon, DocNavLink } from '@hcengineering/view-resources'
|
||||
import activity, { ActivityMessage, ActivityMessagePreviewType } from '@hcengineering/activity'
|
||||
import { classIcon, DocNavLink, showMenu } from '@hcengineering/view-resources'
|
||||
|
||||
export let message: ActivityMessage | undefined = undefined
|
||||
export let text: string | undefined = undefined
|
||||
export let intlLabel: IntlString | undefined = undefined
|
||||
export let readonly = false
|
||||
@ -99,6 +100,12 @@
|
||||
width = element.clientWidth
|
||||
}}
|
||||
on:click
|
||||
on:contextmenu={(evt) => {
|
||||
showMenu(evt, { object: message, baseMenuClass: activity.class.ActivityMessage }, () => {
|
||||
isActionsOpened = false
|
||||
})
|
||||
isActionsOpened = true
|
||||
}}
|
||||
>
|
||||
<span class="left overflow-label">
|
||||
{#if type === 'full'}
|
||||
|
@ -1,34 +0,0 @@
|
||||
<!--
|
||||
// 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 { getClient } from '@hcengineering/presentation'
|
||||
import view from '@hcengineering/view'
|
||||
import { ActivityMessage } from '@hcengineering/activity'
|
||||
import ActivityMessageAction from './ActivityMessageAction.svelte'
|
||||
|
||||
export let object: ActivityMessage
|
||||
|
||||
const client = getClient()
|
||||
|
||||
async function toggleMessagePinning (): Promise<void> {
|
||||
await client.update(object, { isPinned: !object.isPinned })
|
||||
}
|
||||
</script>
|
||||
|
||||
<ActivityMessageAction
|
||||
icon={view.icon.Pin}
|
||||
iconProps={{ fill: object.isPinned ? '#3265cb' : 'currentColor' }}
|
||||
action={toggleMessagePinning}
|
||||
/>
|
@ -29,7 +29,6 @@
|
||||
|
||||
export let object: ActivityMessage
|
||||
export let embedded = false
|
||||
export let onReply: (() => void) | undefined = undefined
|
||||
|
||||
const client = getClient()
|
||||
const maxDisplayPersons = 5
|
||||
@ -83,11 +82,6 @@
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
|
||||
if (onReply) {
|
||||
onReply()
|
||||
return
|
||||
}
|
||||
|
||||
if (replyProvider) {
|
||||
const fn = await getResource(replyProvider.function)
|
||||
fn(object)
|
||||
|
@ -1,52 +0,0 @@
|
||||
<!--
|
||||
// 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 { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import activity, { ActivityMessage, SavedMessage } from '@hcengineering/activity'
|
||||
import preference from '@hcengineering/preference'
|
||||
|
||||
import BookmarkBorder from './icons/BookmarkBorder.svelte'
|
||||
import ActivityMessageAction from './ActivityMessageAction.svelte'
|
||||
import Bookmark from './icons/Bookmark.svelte'
|
||||
import { savedMessagesStore } from '../activity'
|
||||
|
||||
export let object: ActivityMessage
|
||||
|
||||
const client = getClient()
|
||||
|
||||
let savedMessage: SavedMessage | undefined = undefined
|
||||
|
||||
savedMessagesStore.subscribe((saved) => {
|
||||
savedMessage = saved.find(({ attachedTo }) => attachedTo === object._id)
|
||||
})
|
||||
|
||||
async function toggleSaveMessage (): Promise<void> {
|
||||
if (savedMessage !== undefined) {
|
||||
await client.remove(savedMessage)
|
||||
savedMessage = undefined
|
||||
} else {
|
||||
await client.createDoc(activity.class.SavedMessage, preference.space.Preference, {
|
||||
attachedTo: object._id
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<ActivityMessageAction
|
||||
icon={savedMessage ? Bookmark : BookmarkBorder}
|
||||
size={savedMessage ? 'small' : 'x-small'}
|
||||
iconProps={{ fill: savedMessage ? 'var(--global-accent-TextColor)' : 'currentColor' }}
|
||||
action={toggleSaveMessage}
|
||||
/>
|
@ -1,30 +0,0 @@
|
||||
<!--
|
||||
// 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 { ActivityMessageExtension, ActivityMessageExtensionKind } from '@hcengineering/activity'
|
||||
import { Component } from '@hcengineering/ui'
|
||||
|
||||
export let kind: ActivityMessageExtensionKind
|
||||
export let extensions: ActivityMessageExtension[] = []
|
||||
export let props: Record<string, any> = {}
|
||||
</script>
|
||||
|
||||
{#each extensions as extension}
|
||||
{#each extension.components as component}
|
||||
{#if component.kind === kind}
|
||||
<Component is={component.component} {props} showLoading={false} on:close on:open />
|
||||
{/if}
|
||||
{/each}
|
||||
{/each}
|
@ -39,7 +39,6 @@
|
||||
export let hideLink = false
|
||||
export let compact = false
|
||||
export let onClick: (() => void) | undefined = undefined
|
||||
export let onReply: (() => void) | undefined = undefined
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
@ -70,8 +69,7 @@
|
||||
videoPreload,
|
||||
hideLink,
|
||||
compact,
|
||||
onClick,
|
||||
onReply
|
||||
onClick
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
@ -13,23 +13,18 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import activity, {
|
||||
ActivityMessageExtension,
|
||||
ActivityMessageViewlet,
|
||||
DisplayActivityMessage
|
||||
} from '@hcengineering/activity'
|
||||
import activity, { ActivityMessageViewlet, DisplayActivityMessage } from '@hcengineering/activity'
|
||||
import { Person } from '@hcengineering/contact'
|
||||
import { Avatar, EmployeePresenter, SystemAvatar } from '@hcengineering/contact-resources'
|
||||
import core from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Action, Label } from '@hcengineering/ui'
|
||||
import { getActions, restrictionStore } from '@hcengineering/view-resources'
|
||||
import { Action, Icon, Label } from '@hcengineering/ui'
|
||||
import { getActions, restrictionStore, showMenu } from '@hcengineering/view-resources'
|
||||
|
||||
import ReactionsPresenter from '../reactions/ReactionsPresenter.svelte'
|
||||
import ActivityMessagePresenter from './ActivityMessagePresenter.svelte'
|
||||
import ActivityMessageActions from '../ActivityMessageActions.svelte'
|
||||
import { isReactionMessage } from '../../activityMessagesUtils'
|
||||
import Bookmark from '../icons/Bookmark.svelte'
|
||||
import { savedMessagesStore } from '../../activity'
|
||||
import MessageTimestamp from '../MessageTimestamp.svelte'
|
||||
import Replies from '../Replies.svelte'
|
||||
@ -53,14 +48,12 @@
|
||||
export let hoverStyles: 'borderedHover' | 'filledHover' = 'borderedHover'
|
||||
export let showDatePreposition = false
|
||||
export let onClick: (() => void) | undefined = undefined
|
||||
export let onReply: (() => void) | undefined = undefined
|
||||
|
||||
const client = getClient()
|
||||
|
||||
let menuActionIds: string[] = []
|
||||
|
||||
let element: HTMLDivElement | undefined = undefined
|
||||
let extensions: ActivityMessageExtension[] = []
|
||||
let isActionsOpened = false
|
||||
|
||||
let isSaved = false
|
||||
@ -85,8 +78,6 @@
|
||||
setTimeout(scrollToMessage, 100)
|
||||
}
|
||||
|
||||
$: extensions = client.getModel().findAllSync(activity.class.ActivityMessageExtension, { ofMessage: message._class })
|
||||
|
||||
function handleActionsOpened (): void {
|
||||
isActionsOpened = true
|
||||
}
|
||||
@ -121,6 +112,12 @@
|
||||
class:borderedHover={hoverStyles === 'borderedHover'}
|
||||
class:filledHover={hoverStyles === 'filledHover'}
|
||||
on:click={onClick}
|
||||
on:contextmenu={(evt) => {
|
||||
showMenu(evt, { object: message, baseMenuClass: activity.class.ActivityMessage }, () => {
|
||||
isActionsOpened = false
|
||||
})
|
||||
isActionsOpened = true
|
||||
}}
|
||||
>
|
||||
{#if showNotify && !embedded}
|
||||
<div class="notify" />
|
||||
@ -136,7 +133,7 @@
|
||||
{/if}
|
||||
{#if isSaved}
|
||||
<div class="saveMarker">
|
||||
<Bookmark size="xx-small" />
|
||||
<Icon icon={activity.icon.BookmarkFilled} size="xx-small" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@ -171,7 +168,7 @@
|
||||
<slot name="content" />
|
||||
|
||||
{#if !hideFooter}
|
||||
<Replies {embedded} object={message} {onReply} />
|
||||
<Replies {embedded} object={message} />
|
||||
{/if}
|
||||
<ReactionsPresenter object={message} {readonly} />
|
||||
{#if parentMessage && showEmbedded}
|
||||
@ -184,11 +181,10 @@
|
||||
<div class="actions" class:opened={isActionsOpened}>
|
||||
<ActivityMessageActions
|
||||
message={isReactionMessage(message) ? parentMessage : message}
|
||||
{extensions}
|
||||
{actions}
|
||||
{withActionMenu}
|
||||
on:open={handleActionsOpened}
|
||||
on:close={handleActionsClosed}
|
||||
onOpen={handleActionsOpened}
|
||||
onClose={handleActionsClosed}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -14,7 +14,6 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import activity, { ActivityMessage, ActivityMessagePreviewType } from '@hcengineering/activity'
|
||||
|
||||
@ -30,17 +29,14 @@
|
||||
export let message: ActivityMessage
|
||||
export let actions: Action[] = []
|
||||
|
||||
const client = getClient()
|
||||
|
||||
let previewElement: BasePreview
|
||||
let isCompact = false
|
||||
|
||||
$: extensions = client.getModel().findAllSync(activity.class.ActivityMessageExtension, { ofMessage: message._class })
|
||||
</script>
|
||||
|
||||
<BasePreview
|
||||
bind:this={previewElement}
|
||||
bind:isCompact
|
||||
{message}
|
||||
{text}
|
||||
{intlLabel}
|
||||
{readonly}
|
||||
@ -58,13 +54,14 @@
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
{#if previewElement}
|
||||
<ActivityMessageActions
|
||||
{message}
|
||||
{extensions}
|
||||
{actions}
|
||||
withActionMenu={false}
|
||||
on:open={previewElement.onActionsOpened}
|
||||
on:close={previewElement.onActionsClosed}
|
||||
onOpen={previewElement.onActionsOpened}
|
||||
onClose={previewElement.onActionsClosed}
|
||||
/>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</BasePreview>
|
||||
|
@ -43,7 +43,6 @@
|
||||
export let hideLink = false
|
||||
export let compact = false
|
||||
export let onClick: (() => void) | undefined = undefined
|
||||
export let onReply: (() => void) | undefined = undefined
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
@ -112,7 +111,6 @@
|
||||
{hoverStyles}
|
||||
showDatePreposition
|
||||
{onClick}
|
||||
{onReply}
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<span class="header">
|
||||
|
@ -51,7 +51,6 @@
|
||||
export let hoverStyles: 'borderedHover' | 'filledHover' = 'borderedHover'
|
||||
export let hideLink = false
|
||||
export let onClick: (() => void) | undefined = undefined
|
||||
export let onReply: (() => void) | undefined = undefined
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
@ -172,7 +171,6 @@
|
||||
{hoverStyles}
|
||||
showDatePreposition={hideLink}
|
||||
{onClick}
|
||||
{onReply}
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<DocUpdateMessageHeader
|
||||
|
@ -1,26 +0,0 @@
|
||||
<!--
|
||||
// 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">
|
||||
export let size: 'tiny' | 'xx-small' | 'x-small' | 'small' | 'medium' | 'large' = 'small'
|
||||
export let fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M6 6.00012C6 3.79098 7.79086 2.00012 10 2.00012H22C24.2091 2.00012 26 3.79098 26 6.00012V29.0001C26 29.3689 25.797 29.7078 25.4719 29.8818C25.1467 30.0558 24.7522 30.0367 24.4453 29.8322L16 24.202L7.5547 29.8322C7.24784 30.0367 6.8533 30.0558 6.52814 29.8818C6.20298 29.7078 6 29.3689 6 29.0001V6.00012Z"
|
||||
/>
|
||||
</svg>
|
@ -1,26 +0,0 @@
|
||||
<!--
|
||||
// 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">
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
export let fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} viewBox="0 0 20 28" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M0 4.00012C0 1.79098 1.79086 0.00012207 4 0.00012207H16C18.2091 0.00012207 20 1.79098 20 4.00012V27.0001C20 27.3689 19.797 27.7078 19.4719 27.8818C19.1467 28.0558 18.7522 28.0367 18.4453 27.8322L10 22.202L1.5547 27.8322C1.24784 28.0367 0.8533 28.0558 0.528142 27.8818C0.202985 27.7078 0 27.3689 0 27.0001V4.00012ZM4 2.00012C2.89543 2.00012 2 2.89555 2 4.00012V25.1316L9.4453 20.1681C9.7812 19.9441 10.2188 19.9441 10.5547 20.1681L18 25.1316V4.00012C18 2.89555 17.1046 2.00012 16 2.00012H4Z"
|
||||
/>
|
||||
</svg>
|
@ -1,52 +0,0 @@
|
||||
<!--
|
||||
// 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 { createEventDispatcher } from 'svelte'
|
||||
import { EmojiPopup, showPopup } from '@hcengineering/ui'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import activity, { ActivityMessage, Reaction } from '@hcengineering/activity'
|
||||
|
||||
import { updateDocReactions } from '../../utils'
|
||||
import ActivityMessageAction from '../ActivityMessageAction.svelte'
|
||||
|
||||
export let object: ActivityMessage | undefined = undefined
|
||||
|
||||
const client = getClient()
|
||||
|
||||
const reactionsQuery = createQuery()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let reactions: Reaction[] = []
|
||||
let isOpened = false
|
||||
|
||||
$: if (object?.reactions && object.reactions > 0) {
|
||||
reactionsQuery.query(activity.class.Reaction, { attachedTo: object._id }, (res?: Reaction[]) => {
|
||||
reactions = res || []
|
||||
})
|
||||
}
|
||||
|
||||
function openEmojiPalette (ev: Event) {
|
||||
dispatch('open')
|
||||
showPopup(EmojiPopup, {}, ev.target as HTMLElement, (emoji: string) => {
|
||||
updateDocReactions(client, reactions, object, emoji)
|
||||
isOpened = false
|
||||
dispatch('close')
|
||||
})
|
||||
|
||||
isOpened = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<ActivityMessageAction icon={activity.icon.Emoji} action={openEmojiPalette} opened={isOpened} />
|
@ -28,6 +28,17 @@ import ActivityInfoMessagePreview from './components/activity-info-message/Activ
|
||||
|
||||
import { attributesFilter, pinnedFilter, allFilter, referencesFilter } from './activityMessagesUtils'
|
||||
import { updateReferences } from './references'
|
||||
import {
|
||||
addReactionAction,
|
||||
canPinMessage,
|
||||
canRemoveFromSaved,
|
||||
saveForLater,
|
||||
unpinMessage,
|
||||
pinMessage,
|
||||
canSaveForLater,
|
||||
canUnpinMessage,
|
||||
removeFromSaved
|
||||
} from './utils'
|
||||
|
||||
export * from './activity'
|
||||
export * from './utils'
|
||||
@ -42,7 +53,6 @@ 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 as ActivityMessageAction } from './components/ActivityMessageAction.svelte'
|
||||
export { default as ActivityMessagesFilterPopup } from './components/FilterPopup.svelte'
|
||||
export { default as ActivityReferencePresenter } from './components/activity-reference/ActivityReferencePresenter.svelte'
|
||||
@ -70,7 +80,20 @@ export default async (): Promise<Resources> => ({
|
||||
AllFilter: allFilter,
|
||||
ReferencesFilter: referencesFilter
|
||||
},
|
||||
function: {
|
||||
CanSaveForLater: canSaveForLater,
|
||||
CanRemoveFromSaved: canRemoveFromSaved,
|
||||
CanPinMessage: canPinMessage,
|
||||
CanUnpinMessage: canUnpinMessage
|
||||
},
|
||||
backreference: {
|
||||
Update: updateReferences
|
||||
},
|
||||
actionImpl: {
|
||||
AddReaction: addReactionAction,
|
||||
SaveForLater: saveForLater,
|
||||
RemoveFromSaved: removeFromSaved,
|
||||
PinMessage: pinMessage,
|
||||
UnpinMessage: unpinMessage
|
||||
}
|
||||
})
|
||||
|
@ -20,18 +20,23 @@ import core, {
|
||||
getCurrentAccount
|
||||
} from '@hcengineering/core'
|
||||
import { type Asset, type IntlString, getResource, translate } from '@hcengineering/platform'
|
||||
import { getAttributePresenterClass } from '@hcengineering/presentation'
|
||||
import { getAttributePresenterClass, getClient } from '@hcengineering/presentation'
|
||||
import {
|
||||
type AnyComponent,
|
||||
type AnySvelteComponent,
|
||||
ErrorPresenter,
|
||||
themeStore,
|
||||
type Location
|
||||
type Location,
|
||||
getEventPositionElement,
|
||||
closePopup,
|
||||
showPopup,
|
||||
EmojiPopup
|
||||
} from '@hcengineering/ui'
|
||||
import view, { type AttributeModel, type BuildModelKey, type BuildModelOptions } from '@hcengineering/view'
|
||||
import { getObjectPresenter } from '@hcengineering/view-resources'
|
||||
import preference from '@hcengineering/preference'
|
||||
|
||||
import { type ActivityKey, activityKey } from './activity'
|
||||
import { type ActivityKey, activityKey, savedMessagesStore } from './activity'
|
||||
import activity from './plugin'
|
||||
|
||||
const valueTypes: ReadonlyArray<Ref<Class<Doc>>> = [
|
||||
@ -427,3 +432,90 @@ export async function updateDocReactions (
|
||||
export function getMessageFromLoc (loc: Location): Ref<ActivityMessage> | undefined {
|
||||
return (loc.query?.message ?? undefined) as Ref<ActivityMessage> | undefined
|
||||
}
|
||||
|
||||
interface ActivityMessageActionParams {
|
||||
onClose?: () => void
|
||||
onOpen?: () => void
|
||||
}
|
||||
|
||||
export async function addReactionAction (
|
||||
message?: ActivityMessage,
|
||||
ev?: MouseEvent,
|
||||
params?: ActivityMessageActionParams
|
||||
): Promise<void> {
|
||||
if (message === undefined || ev === undefined) return
|
||||
|
||||
const client = getClient()
|
||||
const reactions: Reaction[] =
|
||||
(message.reactions ?? 0) > 0
|
||||
? await client.findAll<Reaction>(activity.class.Reaction, { attachedTo: message._id })
|
||||
: []
|
||||
const element = getEventPositionElement(ev)
|
||||
|
||||
closePopup()
|
||||
|
||||
showPopup(EmojiPopup, {}, element, (emoji: string) => {
|
||||
void updateDocReactions(client, reactions, message, emoji)
|
||||
params?.onClose?.()
|
||||
})
|
||||
params?.onOpen?.()
|
||||
}
|
||||
|
||||
export async function saveForLater (message?: ActivityMessage): Promise<void> {
|
||||
if (message === undefined) return
|
||||
closePopup()
|
||||
const client = getClient()
|
||||
|
||||
await client.createDoc(activity.class.SavedMessage, preference.space.Preference, {
|
||||
attachedTo: message._id
|
||||
})
|
||||
}
|
||||
|
||||
export async function removeFromSaved (message?: ActivityMessage): Promise<void> {
|
||||
if (message === undefined) return
|
||||
closePopup()
|
||||
const client = getClient()
|
||||
const saved = get(savedMessagesStore).find((saved) => saved.attachedTo === message._id)
|
||||
|
||||
if (saved !== undefined) {
|
||||
await client.removeDoc(saved._class, saved.space, saved._id)
|
||||
}
|
||||
}
|
||||
|
||||
export async function canSaveForLater (message?: ActivityMessage): Promise<boolean> {
|
||||
if (message === undefined) return false
|
||||
|
||||
const saved = get(savedMessagesStore).find((saved) => saved.attachedTo === message._id)
|
||||
|
||||
return saved === undefined
|
||||
}
|
||||
|
||||
export async function canRemoveFromSaved (message?: ActivityMessage): Promise<boolean> {
|
||||
if (message === undefined) return false
|
||||
|
||||
return !(await canSaveForLater(message))
|
||||
}
|
||||
|
||||
export async function canPinMessage (message?: ActivityMessage): Promise<boolean> {
|
||||
return message !== undefined && message.isPinned !== true
|
||||
}
|
||||
|
||||
export async function canUnpinMessage (message?: ActivityMessage): Promise<boolean> {
|
||||
return message !== undefined && message.isPinned === true
|
||||
}
|
||||
|
||||
export async function pinMessage (message?: ActivityMessage): Promise<void> {
|
||||
if (message === undefined) return
|
||||
closePopup()
|
||||
const client = getClient()
|
||||
|
||||
await client.update(message, { isPinned: true })
|
||||
}
|
||||
|
||||
export async function unpinMessage (message?: ActivityMessage): Promise<void> {
|
||||
if (message === undefined) return
|
||||
closePopup()
|
||||
const client = getClient()
|
||||
|
||||
await client.update(message, { isPinned: false })
|
||||
}
|
||||
|
@ -157,16 +157,6 @@ export interface ActivityInfoMessage extends ActivityMessage {
|
||||
links?: { _class: Ref<Class<Doc>>, _id: Ref<Doc> }[]
|
||||
}
|
||||
|
||||
export type ActivityMessageExtensionKind = 'action' | 'footer'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ActivityMessageExtension extends Doc {
|
||||
ofMessage: Ref<Class<ActivityMessage>>
|
||||
components: { kind: ActivityMessageExtensionKind, component: AnyComponent }[]
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -333,7 +323,6 @@ export default plugin(activityId, {
|
||||
ActivityInfoMessage: '' as Ref<Class<ActivityInfoMessage>>,
|
||||
ActivityMessageControl: '' as Ref<Class<ActivityMessageControl>>,
|
||||
DocUpdateMessageViewlet: '' as Ref<Class<DocUpdateMessageViewlet>>,
|
||||
ActivityMessageExtension: '' as Ref<Class<ActivityMessageExtension>>,
|
||||
ActivityMessagesFilter: '' as Ref<Class<ActivityMessagesFilter>>,
|
||||
ActivityExtension: '' as Ref<Class<ActivityExtension>>,
|
||||
Reaction: '' as Ref<Class<Reaction>>,
|
||||
@ -344,7 +333,8 @@ export default plugin(activityId, {
|
||||
icon: {
|
||||
Activity: '' as Asset,
|
||||
Emoji: '' as Asset,
|
||||
Bookmark: '' as Asset
|
||||
Bookmark: '' as Asset,
|
||||
BookmarkFilled: '' as Asset
|
||||
},
|
||||
string: {
|
||||
Activity: '' as IntlString,
|
||||
|
@ -1,32 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2024 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 { ActivityMessage } from '@hcengineering/activity'
|
||||
import { ActivityMessageAction } from '@hcengineering/activity-resources'
|
||||
|
||||
import chunter from './../plugin'
|
||||
import { replyToThread } from '../index'
|
||||
import { canReplyToThread } from '../utils'
|
||||
|
||||
export let object: ActivityMessage
|
||||
|
||||
function onReply (): void {
|
||||
replyToThread(object)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if canReplyToThread(object)}
|
||||
<ActivityMessageAction size="small" icon={chunter.icon.Thread} action={onReply} />
|
||||
{/if}
|
@ -50,7 +50,6 @@
|
||||
export let hideLink = false
|
||||
export let compact = false
|
||||
export let onClick: (() => void) | undefined = undefined
|
||||
export let onReply: (() => void) | undefined = undefined
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
@ -183,7 +182,6 @@
|
||||
{skipLabel}
|
||||
showDatePreposition={hideLink}
|
||||
{onClick}
|
||||
{onReply}
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<ChatMessageHeader {object} {parentObject} message={value} {viewlet} {person} {skipLabel} {hideLink} />
|
||||
|
@ -40,7 +40,6 @@
|
||||
export let attachmentImageSize: AttachmentImageSize = 'x-large'
|
||||
export let videoPreload = true
|
||||
export let onClick: (() => void) | undefined = undefined
|
||||
export let onReply: (() => void) | undefined = undefined
|
||||
|
||||
const client = getClient()
|
||||
</script>
|
||||
@ -76,6 +75,5 @@
|
||||
{videoPreload}
|
||||
showLinksPreview={false}
|
||||
{onClick}
|
||||
{onReply}
|
||||
/>
|
||||
{/if}
|
||||
|
@ -48,7 +48,6 @@ import ChannelIcon from './components/ChannelIcon.svelte'
|
||||
import ThreadNotificationPresenter from './components/notification/ThreadNotificationPresenter.svelte'
|
||||
import ChatMessageNotificationLabel from './components/notification/ChatMessageNotificationLabel.svelte'
|
||||
import ChatAside from './components/chat/ChatAside.svelte'
|
||||
import ReplyToThreadAction from './components/ReplyToThreadAction.svelte'
|
||||
import ThreadMessagePreview from './components/threads/ThreadMessagePreview.svelte'
|
||||
import ChatMessagePreview from './components/chat-message/ChatMessagePreview.svelte'
|
||||
|
||||
@ -62,7 +61,8 @@ import {
|
||||
getUnreadThreadsCount,
|
||||
canCopyMessageLink,
|
||||
leaveChannelAction,
|
||||
removeChannelAction
|
||||
removeChannelAction,
|
||||
canReplyToThread
|
||||
} from './utils'
|
||||
import {
|
||||
chunterSpaceLinkFragmentProvider,
|
||||
@ -180,7 +180,6 @@ export default async (): Promise<Resources> => ({
|
||||
ChatMessageNotificationLabel,
|
||||
ThreadNotificationPresenter,
|
||||
ChatAside,
|
||||
ReplyToThreadAction,
|
||||
ThreadMessagePreview,
|
||||
ChatMessagePreview
|
||||
},
|
||||
@ -197,8 +196,9 @@ export default async (): Promise<Resources> => ({
|
||||
GetChunterSpaceLinkFragment: chunterSpaceLinkFragmentProvider,
|
||||
GetUnreadThreadsCount: getUnreadThreadsCount,
|
||||
GetThreadLink: getThreadLink,
|
||||
GetMessageLink: getMessageLocation,
|
||||
ReplyToThread: replyToThread
|
||||
ReplyToThread: replyToThread,
|
||||
CanReplyToThread: canReplyToThread,
|
||||
GetMessageLink: getMessageLocation
|
||||
},
|
||||
actionImpl: {
|
||||
ArchiveChannel,
|
||||
@ -206,6 +206,7 @@ export default async (): Promise<Resources> => ({
|
||||
ConvertDmToPrivateChannel,
|
||||
DeleteChatMessage: deleteChatMessage,
|
||||
LeaveChannel: leaveChannelAction,
|
||||
RemoveChannel: removeChannelAction
|
||||
RemoveChannel: removeChannelAction,
|
||||
ReplyToThread: replyToThread
|
||||
}
|
||||
})
|
||||
|
@ -138,8 +138,6 @@ export default plugin(chunterId, {
|
||||
ChatMessagePresenter: '' as AnyComponent,
|
||||
ThreadMessagePresenter: '' as AnyComponent,
|
||||
ChatAside: '' as AnyComponent,
|
||||
Replies: '' as AnyComponent,
|
||||
ReplyToThreadAction: '' as AnyComponent,
|
||||
ChatMessagePreview: '' as AnyComponent,
|
||||
ThreadMessagePreview: '' as AnyComponent
|
||||
},
|
||||
|
@ -119,6 +119,7 @@
|
||||
"ToViewCommands": "to view available commands",
|
||||
"UnArchive": "Unarchive",
|
||||
"Pin": "Pin",
|
||||
"Unpin": "Unpin"
|
||||
"Unpin": "Unpin",
|
||||
"MoreActions": "More actions"
|
||||
}
|
||||
}
|
||||
|
@ -116,6 +116,7 @@
|
||||
"ToViewCommands": "чтобы увидеть команды",
|
||||
"UnArchive": "Разархивировать",
|
||||
"Pin": "Закрепить",
|
||||
"Unpin": "Открепить"
|
||||
"Unpin": "Открепить",
|
||||
"MoreActions": "Больше действий"
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +194,8 @@ const view = plugin(viewId, {
|
||||
Type: '' as IntlString,
|
||||
UnArchive: '' as IntlString,
|
||||
Save: '' as IntlString,
|
||||
PublicView: '' as IntlString
|
||||
PublicView: '' as IntlString,
|
||||
MoreActions: '' as IntlString
|
||||
},
|
||||
icon: {
|
||||
Table: '' as Asset,
|
||||
|
Loading…
Reference in New Issue
Block a user