diff --git a/models/activity/src/index.ts b/models/activity/src/index.ts index a242723ec8..778d177abb 100644 --- a/models/activity/src/index.ts +++ b/models/activity/src/index.ts @@ -290,7 +290,7 @@ export function createModel (builder: Builder): void { { objectClass: activity.class.Reaction, action: 'create', - component: activity.component.ReactionAddedMessage, + component: activity.component.ReactionPresenter, label: activity.string.Reacted, onlyWithParent: true, hideIfRemoved: true @@ -317,6 +317,14 @@ export function createModel (builder: Builder): void { fields: ['createdBy', 'repliedPersons'] }) + builder.mixin(activity.class.ActivityMessage, core.class.Class, view.mixin.ObjectPanel, { + component: view.component.AttachedDocPanel + }) + + builder.mixin(activity.class.ActivityMessage, core.class.Class, notification.mixin.NotificationContextPresenter, { + labelPresenter: activity.component.ActivityMessageNotificationLabel + }) + builder.createDoc( notification.class.NotificationType, core.space.Model, @@ -333,6 +341,14 @@ export function createModel (builder: Builder): void { }, activity.ids.AddReactionNotification ) + + builder.createDoc(notification.class.ActivityNotificationViewlet, core.space.Model, { + messageMatch: { + _class: activity.class.DocUpdateMessage, + objectClass: activity.class.Reaction + }, + presenter: activity.component.ReactionNotificationPresenter + }) } export default activity diff --git a/models/activity/src/plugin.ts b/models/activity/src/plugin.ts index d2566c8e9f..9fa2ccf565 100644 --- a/models/activity/src/plugin.ts +++ b/models/activity/src/plugin.ts @@ -25,7 +25,6 @@ export default mergeIds(activityId, activity, { Attributes: '' as IntlString, Pinned: '' as IntlString, Emoji: '' as IntlString, - Reacted: '' as IntlString, Replies: '' as IntlString }, filter: { diff --git a/models/chunter/src/index.ts b/models/chunter/src/index.ts index 3b14b8a2bc..89fe74a3b8 100644 --- a/models/chunter/src/index.ts +++ b/models/chunter/src/index.ts @@ -235,6 +235,14 @@ export function createModel (builder: Builder, options = { addApplication: true ) const spaceClasses = [chunter.class.Channel, chunter.class.DirectMessage] + builder.mixin(chunter.class.DirectMessage, core.class.Class, view.mixin.ObjectIcon, { + component: chunter.component.DirectIcon + }) + + builder.mixin(chunter.class.Channel, core.class.Class, view.mixin.ObjectIcon, { + component: chunter.component.ChannelIcon + }) + spaceClasses.forEach((spaceClass) => { builder.mixin(spaceClass, core.class.Class, activity.mixin.ActivityDoc, {}) @@ -253,7 +261,7 @@ export function createModel (builder: Builder, options = { addApplication: true }) builder.mixin(spaceClass, core.class.Class, view.mixin.ObjectPanel, { - component: chunter.component.EditChannel + component: chunter.component.ChannelPanel }) }) @@ -262,7 +270,11 @@ export function createModel (builder: Builder, options = { addApplication: true }) builder.mixin(chunter.class.DirectMessage, core.class.Class, view.mixin.ObjectTitle, { - titleProvider: chunter.function.DirectMessageTitleProvider + titleProvider: chunter.function.DirectTitleProvider + }) + + builder.mixin(chunter.class.Channel, core.class.Class, view.mixin.ObjectTitle, { + titleProvider: chunter.function.ChannelTitleProvider }) builder.mixin(chunter.class.DirectMessage, core.class.Class, notification.mixin.ClassCollaborators, { @@ -273,14 +285,6 @@ export function createModel (builder: Builder, options = { addApplication: true fields: ['members'] }) - builder.mixin(chunter.class.Channel, core.class.Class, view.mixin.ObjectPanel, { - component: chunter.component.ChannelViewPanel - }) - - builder.mixin(chunter.class.DirectMessage, core.class.Class, view.mixin.ObjectPanel, { - component: chunter.component.ChannelViewPanel - }) - builder.mixin(chunter.class.DirectMessage, core.class.Class, view.mixin.ObjectPresenter, { presenter: chunter.component.DmPresenter }) @@ -309,6 +313,17 @@ export function createModel (builder: Builder, options = { addApplication: true presenter: chunter.component.ChannelPresenter }) + builder.mixin(chunter.class.ChatMessage, core.class.Class, notification.mixin.NotificationContextPresenter, { + labelPresenter: chunter.component.ChatMessageNotificationLabel + }) + + builder.createDoc(notification.class.ActivityNotificationViewlet, core.space.Model, { + messageMatch: { + _class: chunter.class.ThreadMessage + }, + presenter: chunter.component.ThreadNotificationPresenter + }) + builder.mixin(chunter.class.DirectMessage, core.class.Class, view.mixin.SpaceHeader, { header: chunter.component.DmHeader }) @@ -391,8 +406,7 @@ export function createModel (builder: Builder, options = { addApplication: true alias: chunterId, hidden: false, component: chunter.component.Chat, - aside: chunter.component.ThreadView, - shouldNotify: chunter.function.ShouldNotify + aside: chunter.component.ThreadView }, chunter.app.Chunter ) diff --git a/models/chunter/src/plugin.ts b/models/chunter/src/plugin.ts index a7e5cfa6f1..9041a376b8 100644 --- a/models/chunter/src/plugin.ts +++ b/models/chunter/src/plugin.ts @@ -17,7 +17,7 @@ import type { ActivityMessage, DocUpdateMessageViewlet, TxViewlet } from '@hceng import { chunterId, type Channel } from '@hcengineering/chunter' import chunter from '@hcengineering/chunter-resources/src/plugin' import { type Client, type Doc, type Ref } from '@hcengineering/core' -import { type DocNotifyContext, type NotificationGroup } from '@hcengineering/notification' +import { type NotificationGroup } from '@hcengineering/notification' import type { IntlString, Resource } from '@hcengineering/platform' import { mergeIds } from '@hcengineering/platform' import type { AnyComponent, Location } from '@hcengineering/ui/src/types' @@ -32,7 +32,9 @@ export default mergeIds(chunterId, chunter, { BacklinkContent: '' as AnyComponent, BacklinkReference: '' as AnyComponent, ChannelsPanel: '' as AnyComponent, - Chat: '' as AnyComponent + Chat: '' as AnyComponent, + ChatMessageNotificationLabel: '' as AnyComponent, + ThreadNotificationPresenter: '' as AnyComponent }, action: { MarkCommentUnread: '' as Ref, @@ -40,8 +42,7 @@ export default mergeIds(chunterId, chunter, { ArchiveChannel: '' as Ref, UnarchiveChannel: '' as Ref, ConvertToPrivate: '' as Ref, - CopyChatMessageLink: '' as Ref>, - OpenChannel: '' as Ref + CopyChatMessageLink: '' as Ref> }, actionImpl: { ArchiveChannel: '' as ViewAction, @@ -104,7 +105,6 @@ export default mergeIds(chunterId, chunter, { function: { GetLink: '' as Resource<(doc: Doc, props: Record) => Promise>, GetFragment: '' as Resource<(doc: Doc, props: Record) => Promise>, - ShouldNotify: '' as Resource<(docNotifyContexts: DocNotifyContext[]) => Promise>, DmIdentifierProvider: '' as Resource<(client: Client, ref: Ref, doc?: T) => Promise>, CanDeleteMessage: '' as Resource<(doc?: Doc | Doc[]) => Promise>, GetChunterSpaceLinkFragment: '' as Resource<(doc: Doc, props: Record) => Promise> diff --git a/models/contact/src/index.ts b/models/contact/src/index.ts index 2b55024d43..e875c27748 100644 --- a/models/contact/src/index.ts +++ b/models/contact/src/index.ts @@ -245,6 +245,10 @@ export function createModel (builder: Builder): void { builder.mixin(contact.class.Channel, core.class.Class, activity.mixin.ActivityDoc, {}) + builder.mixin(contact.class.Person, core.class.Class, view.mixin.ObjectIcon, { + component: contact.component.PersonIcon + }) + builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: contact.class.Contact, components: { input: chunter.component.ChatMessageInput } diff --git a/models/notification/src/index.ts b/models/notification/src/index.ts index 3142c6b0cf..03e0622ca4 100644 --- a/models/notification/src/index.ts +++ b/models/notification/src/index.ts @@ -23,6 +23,7 @@ import { type Collection, type Data, type Doc, + type DocumentQuery, type Domain, DOMAIN_MODEL, Hierarchy, @@ -68,13 +69,16 @@ import { notificationId, type NotificationObjectPresenter, type ActivityInboxNotification, - type CommonInboxNotification + type CommonInboxNotification, + type NotificationContextPresenter, + type ActivityNotificationViewlet } from '@hcengineering/notification' import { type Asset, type IntlString } from '@hcengineering/platform' import setting from '@hcengineering/setting' import { type AnyComponent } from '@hcengineering/ui/src/types' import notification from './plugin' +import { defineViewlets } from './viewlets' export { notificationId } from '@hcengineering/notification' export { notificationOperation } from './migration' @@ -159,6 +163,11 @@ export class TNotificationPreview extends TClass implements NotificationPreview presenter!: AnyComponent } +@Mixin(notification.mixin.NotificationContextPresenter, core.class.Class) +export class TNotificationContextPresenter extends TClass implements NotificationContextPresenter { + labelPresenter?: AnyComponent +} + @Model(notification.class.DocUpdates, core.class.Doc, DOMAIN_NOTIFICATION) export class TDocUpdates extends TDoc implements DocUpdates { @Index(IndexKind.Indexed) @@ -247,6 +256,13 @@ export class TCommonInboxNotification extends TInboxNotification implements Comm iconProps!: Record } +@Model(notification.class.ActivityNotificationViewlet, core.class.Doc, DOMAIN_MODEL) +export class TActivityNotificationViewlet extends TDoc implements ActivityNotificationViewlet { + messageMatch!: DocumentQuery + + presenter!: AnyComponent +} + export function createModel (builder: Builder): void { builder.createModel( TNotification, @@ -263,7 +279,9 @@ export function createModel (builder: Builder): void { TDocNotifyContext, TInboxNotification, TActivityInboxNotification, - TCommonInboxNotification + TCommonInboxNotification, + TNotificationContextPresenter, + TActivityNotificationViewlet ) // Temporarily disabled, we should think about it @@ -315,36 +333,11 @@ export function createModel (builder: Builder): void { core.space.Model, { label: notification.string.Inbox, - icon: notification.icon.Notifications, + icon: notification.icon.Inbox, alias: inboxId, hidden: true, locationResolver: notification.resolver.Location, - navigatorModel: { - aside: notification.component.InboxAside, - spaces: [], - specials: [ - { - id: 'all', - component: notification.component.Inbox, - icon: activity.icon.Activity, - label: activity.string.AllActivity, - componentProps: { - type: 'all', - label: activity.string.AllActivity - } - }, - { - id: 'reactions', - component: notification.component.Inbox, - icon: activity.icon.Emoji, - label: activity.string.Reactions, - componentProps: { - _class: activity.class.Reaction, - label: activity.string.Reactions - } - } - ] - } + component: notification.component.Inbox }, notification.app.Inbox ) @@ -487,17 +480,61 @@ export function createModel (builder: Builder): void { createAction( builder, { - action: notification.actionImpl.HideDocNotifyContext, - label: view.string.Archive, + action: notification.actionImpl.ReadNotifyContext, + label: notification.string.MarkAsRead, + icon: notification.icon.Notifications, + input: 'focus', + visibilityTester: notification.function.CanReadNotifyContext, + category: notification.category.Notification, + target: notification.class.DocNotifyContext, + context: { mode: 'context', application: notification.app.Notification, group: 'edit' } + }, + notification.action.ReadNotifyContext + ) + + createAction( + builder, + { + action: notification.actionImpl.UnReadNotifyContext, + label: notification.string.MarkAsUnread, + icon: notification.icon.Notifications, + input: 'focus', + visibilityTester: notification.function.CanUnReadNotifyContext, + category: notification.category.Notification, + target: notification.class.DocNotifyContext, + context: { mode: 'context', application: notification.app.Notification, group: 'edit' } + }, + notification.action.UnReadNotifyContext + ) + + createAction( + builder, + { + action: notification.actionImpl.DeleteContextNotifications, + label: notification.string.Archive, icon: view.icon.Archive, input: 'focus', - category: view.category.General, + category: notification.category.Notification, + target: notification.class.DocNotifyContext, + context: { mode: 'context', application: notification.app.Notification, group: 'edit' } + }, + notification.action.DeleteContextNotifications + ) + + createAction( + builder, + { + action: notification.actionImpl.HideDocNotifyContext, + label: notification.string.DontTrack, + icon: notification.icon.DontTrack, + input: 'focus', + category: notification.category.Notification, target: notification.class.DocNotifyContext, context: { mode: ['browser', 'context'], group: 'remove' }, - visibilityTester: notification.function.IsDocNotifyContextVisible + visibilityTester: notification.function.IsDocNotifyContextTracked }, notification.action.HideDocNotifyContext ) @@ -561,6 +598,8 @@ export function createModel (builder: Builder): void { builder.mixin(notification.class.CommonInboxNotification, core.class.Class, view.mixin.ObjectPresenter, { presenter: notification.component.CommonInboxNotificationPresenter }) + + defineViewlets(builder) } export function generateClassNotificationTypes ( diff --git a/models/notification/src/plugin.ts b/models/notification/src/plugin.ts index 0a63725891..81e737fefe 100644 --- a/models/notification/src/plugin.ts +++ b/models/notification/src/plugin.ts @@ -48,7 +48,6 @@ export default mergeIds(notificationId, notification, { }, component: { NotificationSettings: '' as AnyComponent, - InboxAside: '' as AnyComponent, ActivityInboxNotificationPresenter: '' as AnyComponent, CommonInboxNotificationPresenter: '' as AnyComponent }, @@ -56,7 +55,9 @@ export default mergeIds(notificationId, notification, { HasMarkAsUnreadAction: '' as Resource<(doc?: Doc | Doc[]) => Promise>, HasMarkAsReadAction: '' as Resource<(doc?: Doc | Doc[]) => Promise>, HasDocNotifyContextPinAction: '' as Resource<(doc?: Doc | Doc[]) => Promise>, - HasDocNotifyContextUnpinAction: '' as Resource<(doc?: Doc | Doc[]) => Promise> + HasDocNotifyContextUnpinAction: '' as Resource<(doc?: Doc | Doc[]) => Promise>, + CanReadNotifyContext: '' as Resource<(doc?: Doc | Doc[]) => Promise>, + CanUnReadNotifyContext: '' as Resource<(doc?: Doc | Doc[]) => Promise> }, category: { Notification: '' as Ref @@ -73,6 +74,9 @@ export default mergeIds(notificationId, notification, { UnpinDocNotifyContext: '' as ViewAction, PinDocNotifyContext: '' as ViewAction, HideDocNotifyContext: '' as ViewAction, - UnHideDocNotifyContext: '' as ViewAction + UnHideDocNotifyContext: '' as ViewAction, + UnReadNotifyContext: '' as ViewAction, + ReadNotifyContext: '' as ViewAction, + DeleteContextNotifications: '' as ViewAction } }) diff --git a/models/notification/src/viewlets.ts b/models/notification/src/viewlets.ts new file mode 100644 index 0000000000..2cedd01da3 --- /dev/null +++ b/models/notification/src/viewlets.ts @@ -0,0 +1,64 @@ +// +// 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. +// +import type { Builder } from '@hcengineering/model' +import view from '@hcengineering/model-view' +import core from '@hcengineering/model-core' +import notification from '@hcengineering/notification' + +export function defineViewlets (builder: Builder): void { + builder.createDoc( + view.class.ViewletDescriptor, + core.space.Model, + { + label: notification.string.GroupedList, + icon: view.icon.Card, + component: notification.component.InboxGroupedListView + }, + notification.viewlet.GroupedList + ) + + builder.createDoc( + view.class.ViewletDescriptor, + core.space.Model, + { + label: notification.string.FlatList, + icon: view.icon.List, + component: notification.component.InboxFlatListView + }, + notification.viewlet.FlatList + ) + + builder.createDoc( + view.class.Viewlet, + core.space.Model, + { + attachTo: notification.class.DocNotifyContext, + descriptor: notification.viewlet.GroupedList, + config: [] + }, + notification.viewlet.InboxGroupedList + ) + + builder.createDoc( + view.class.Viewlet, + core.space.Model, + { + attachTo: notification.class.DocNotifyContext, + descriptor: notification.viewlet.FlatList, + config: [] + }, + notification.viewlet.InboxFlatList + ) +} diff --git a/models/tracker/src/index.ts b/models/tracker/src/index.ts index 28e120ff0e..1e3a1fb6cc 100644 --- a/models/tracker/src/index.ts +++ b/models/tracker/src/index.ts @@ -162,7 +162,7 @@ function defineFilters (builder: Builder): void { }) builder.mixin(tracker.class.Issue, core.class.Class, view.mixin.ObjectIdentifier, { - provider: tracker.function.IssueTitleProvider + provider: tracker.function.IssueIdentifierProvider }) builder.mixin(tracker.class.Issue, core.class.Class, chunter.mixin.ObjectChatPanel, { diff --git a/models/view/src/index.ts b/models/view/src/index.ts index 9e1b0edf09..1914840092 100644 --- a/models/view/src/index.ts +++ b/models/view/src/index.ts @@ -85,7 +85,8 @@ import { type Viewlet, type ViewletDescriptor, type ViewletPreference, - type ObjectIdentifier + type ObjectIdentifier, + type ObjectIcon } from '@hcengineering/view' import view from './plugin' @@ -293,6 +294,11 @@ export class TAggregation extends TClass implements Aggregation { createAggregationManager!: CreateAggregationManagerFunc } +@Mixin(view.mixin.ObjectIcon, core.class.Class) +export class TObjectIcon extends TClass implements ObjectIcon { + component!: AnyComponent +} + @Model(view.class.ViewletPreference, preference.class.Preference) export class TViewletPreference extends TPreference implements ViewletPreference { declare attachedTo: Ref @@ -444,7 +450,8 @@ export function createModel (builder: Builder): void { TAllValuesFunc, TAggregation, TGroupping, - TObjectIdentifier + TObjectIdentifier, + TObjectIcon ) classPresenter( diff --git a/plugins/activity-assets/assets/icons.svg b/plugins/activity-assets/assets/icons.svg index c6ea788e24..57bc2628e3 100644 --- a/plugins/activity-assets/assets/icons.svg +++ b/plugins/activity-assets/assets/icons.svg @@ -1,9 +1,8 @@ - - - + + {/if} - + {title} diff --git a/plugins/activity-resources/src/components/Replies.svelte b/plugins/activity-resources/src/components/Replies.svelte index db6bb26a93..fd18572109 100644 --- a/plugins/activity-resources/src/components/Replies.svelte +++ b/plugins/activity-resources/src/components/Replies.svelte @@ -45,9 +45,9 @@ let displayPersons: Person[] = [] $: docNotifyContextByDocStore = inboxClient?.docNotifyContextByDoc - $: inboxNotificationsByContextStore = inboxClient?.inboxNotificationsByContext + $: notificationsByContextStore = inboxClient?.inboxNotificationsByContext - $: hasNew = hasNewReplies(message, $docNotifyContextByDocStore, $inboxNotificationsByContextStore) + $: hasNew = hasNewReplies(message, $docNotifyContextByDocStore, $notificationsByContextStore) $: updateQuery(persons, $personByIdStore) function hasNewReplies ( diff --git a/plugins/activity-resources/src/components/activity-message/ActivityInfoMessagePresenter.svelte b/plugins/activity-resources/src/components/activity-message/ActivityInfoMessagePresenter.svelte index 8f67ad9928..e746cd565c 100644 --- a/plugins/activity-resources/src/components/activity-message/ActivityInfoMessagePresenter.svelte +++ b/plugins/activity-resources/src/components/activity-message/ActivityInfoMessagePresenter.svelte @@ -22,12 +22,12 @@ personAccountByIdStore, personByIdStore } from '@hcengineering/contact-resources' - - import ActivityMessageTemplate from './ActivityMessageTemplate.svelte' - + import { Action } from '@hcengineering/ui' import { Ref } from '@hcengineering/core' import { translate } from '@hcengineering/platform' import { MessageViewer } from '@hcengineering/presentation' + + import ActivityMessageTemplate from './ActivityMessageTemplate.svelte' import ActivityMessageHeader from './ActivityMessageHeader.svelte' export let value: ActivityInfoMessage @@ -37,6 +37,8 @@ export let shouldScroll: boolean = false export let embedded: boolean = false export let withActions: boolean = true + export let excludedActions: string[] = [] + export let actions: Action[] = [] export let onClick: (() => void) | undefined = undefined $: personAccount = $personAccountByIdStore.get((value.createdBy ?? value.modifiedBy) as Ref) @@ -66,6 +68,8 @@ {shouldScroll} {embedded} {withActions} + {actions} + {excludedActions} viewlet={undefined} {onClick} > diff --git a/plugins/activity-resources/src/components/activity-message/ActivityMessageNotificationLabel.svelte b/plugins/activity-resources/src/components/activity-message/ActivityMessageNotificationLabel.svelte new file mode 100644 index 0000000000..0fca4c7c1f --- /dev/null +++ b/plugins/activity-resources/src/components/activity-message/ActivityMessageNotificationLabel.svelte @@ -0,0 +1,118 @@ + + + +{#if object} + {#if reaction} +
+
+
+ +
+ {#if personAccount?.person} + + {:else} +
+
+ {/if} + +
+
+ + +
+
+ {:else} +
+
+ {/if} +{/if} + + diff --git a/plugins/activity-resources/src/components/activity-message/ActivityMessagePresenter.svelte b/plugins/activity-resources/src/components/activity-message/ActivityMessagePresenter.svelte index c3e4addcf1..f7c055e982 100644 --- a/plugins/activity-resources/src/components/activity-message/ActivityMessagePresenter.svelte +++ b/plugins/activity-resources/src/components/activity-message/ActivityMessagePresenter.svelte @@ -30,7 +30,7 @@ export let hideReplies = false export let skipLabel = false export let actions: Action[] = [] - + export let excludedActions: string[] = [] export let onClick: (() => void) | undefined = undefined export let onReply: (() => void) | undefined = undefined @@ -55,6 +55,7 @@ showEmbedded, hideReplies, actions, + excludedActions, onClick, onReply }} diff --git a/plugins/activity-resources/src/components/activity-message/ActivityMessageTemplate.svelte b/plugins/activity-resources/src/components/activity-message/ActivityMessageTemplate.svelte index a92d7b9c24..cbde7f0b2c 100644 --- a/plugins/activity-resources/src/components/activity-message/ActivityMessageTemplate.svelte +++ b/plugins/activity-resources/src/components/activity-message/ActivityMessageTemplate.svelte @@ -34,9 +34,9 @@ import SaveMessageAction from '../SaveMessageAction.svelte' export let message: DisplayActivityMessage - export let parentMessage: DisplayActivityMessage | undefined + export let parentMessage: DisplayActivityMessage | undefined = undefined - export let viewlet: ActivityMessageViewlet | undefined + export let viewlet: ActivityMessageViewlet | undefined = undefined export let person: Person | undefined = undefined export let actions: Action[] = [] export let excludedActions: string[] = [] @@ -211,6 +211,7 @@ overflow: hidden; border: 1px solid transparent; border-radius: 0.25rem; + width: calc(100% - 2rem); &.clickable { cursor: pointer; @@ -225,7 +226,6 @@ } &.embedded { - background: var(--theme-navpanel-icons-divider); padding: 0; .content { @@ -288,6 +288,7 @@ .embeddedMarker { width: 6px; - background: var(--theme-link-color); + border-radius: 0.5rem; + background: var(--secondary-button-default); } diff --git a/plugins/activity-resources/src/components/doc-update-message/DocUpdateMessagePresenter.svelte b/plugins/activity-resources/src/components/doc-update-message/DocUpdateMessagePresenter.svelte index 05864c460e..06cecd723f 100644 --- a/plugins/activity-resources/src/components/doc-update-message/DocUpdateMessagePresenter.svelte +++ b/plugins/activity-resources/src/components/doc-update-message/DocUpdateMessagePresenter.svelte @@ -46,6 +46,7 @@ export let showEmbedded = false export let hideReplies = false export let actions: Action[] = [] + export let excludedActions: string[] = [] export let onClick: (() => void) | undefined = undefined export let onReply: (() => void) | undefined = undefined @@ -150,6 +151,7 @@ {isSelected} {shouldScroll} {embedded} + {excludedActions} {withActions} {viewlet} {showEmbedded} diff --git a/plugins/activity-resources/src/components/reactions/ReactionNotificationPresenter.svelte b/plugins/activity-resources/src/components/reactions/ReactionNotificationPresenter.svelte new file mode 100644 index 0000000000..32ca122236 --- /dev/null +++ b/plugins/activity-resources/src/components/reactions/ReactionNotificationPresenter.svelte @@ -0,0 +1,49 @@ + + + +{#if embedded && parentMessage} + +{:else if !embedded && message} + +{/if} diff --git a/plugins/activity-resources/src/components/reactions/ReactionAddedMessage.svelte b/plugins/activity-resources/src/components/reactions/ReactionPresenter.svelte similarity index 100% rename from plugins/activity-resources/src/components/reactions/ReactionAddedMessage.svelte rename to plugins/activity-resources/src/components/reactions/ReactionPresenter.svelte diff --git a/plugins/activity-resources/src/index.ts b/plugins/activity-resources/src/index.ts index 4b297333c0..1af62fc07b 100644 --- a/plugins/activity-resources/src/index.ts +++ b/plugins/activity-resources/src/index.ts @@ -19,7 +19,9 @@ import Activity from './components/Activity.svelte' import ActivityMessagePresenter from './components/activity-message/ActivityMessagePresenter.svelte' import DocUpdateMessagePresenter from './components/doc-update-message/DocUpdateMessagePresenter.svelte' import ActivityInfoMessagePresenter from './components/activity-message/ActivityInfoMessagePresenter.svelte' -import ReactionAddedMessage from './components/reactions/ReactionAddedMessage.svelte' +import ReactionPresenter from './components/reactions/ReactionPresenter.svelte' +import ReactionNotificationPresenter from './components/reactions/ReactionNotificationPresenter.svelte' +import ActivityMessageNotificationLabel from './components/activity-message/ActivityMessageNotificationLabel.svelte' import { getMessageFragment, attributesFilter, pinnedFilter, allFilter } from './activityMessagesUtils' @@ -32,16 +34,19 @@ export { default as ActivityMessageTemplate } from './components/activity-messag export { default as ActivityMessagePresenter } from './components/activity-message/ActivityMessagePresenter.svelte' export { default as ActivityExtension } from './components/ActivityExtension.svelte' export { default as ActivityScrolledView } from './components/ActivityScrolledView.svelte' -export { default as ActivityMessageHeader } from './components/activity-message/ActivityMessageHeader.svelte' 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 async (): Promise => ({ component: { Activity, ActivityMessagePresenter, DocUpdateMessagePresenter, - ReactionAddedMessage, - ActivityInfoMessagePresenter + ReactionPresenter, + ActivityInfoMessagePresenter, + ReactionNotificationPresenter, + ActivityMessageNotificationLabel }, filter: { AttributesFilter: attributesFilter, diff --git a/plugins/activity/src/index.ts b/plugins/activity/src/index.ts index 706aba1db3..90bf578709 100644 --- a/plugins/activity/src/index.ts +++ b/plugins/activity/src/index.ts @@ -333,14 +333,18 @@ export default plugin(activityId, { AllActivity: '' as IntlString, Reactions: '' as IntlString, LastReply: '' as IntlString, - RepliesCount: '' as IntlString + RepliesCount: '' as IntlString, + Reacted: '' as IntlString, + Message: '' as IntlString }, component: { Activity: '' as AnyComponent, ActivityMessagePresenter: '' as AnyComponent, DocUpdateMessagePresenter: '' as AnyComponent, ActivityInfoMessagePresenter: '' as AnyComponent, - ReactionAddedMessage: '' as AnyComponent + ReactionPresenter: '' as AnyComponent, + ReactionNotificationPresenter: '' as AnyComponent, + ActivityMessageNotificationLabel: '' as AnyComponent }, ids: { AllFilter: '' as Ref diff --git a/plugins/chunter-assets/lang/en.json b/plugins/chunter-assets/lang/en.json index 1bfcbcc245..47959c0de0 100644 --- a/plugins/chunter-assets/lang/en.json +++ b/plugins/chunter-assets/lang/en.json @@ -83,7 +83,8 @@ "NewestFirst": "Newest first", "ReplyToThread": "Reply to thread", "SentMessage": "Sent message", - "Direct": "direct", - "RepliedToThread": "Replied to thread" + "Direct": "Direct", + "RepliedToThread": "Replied to thread", + "RepliedTo": "replied to: {message}" } } \ No newline at end of file diff --git a/plugins/chunter-assets/lang/ru.json b/plugins/chunter-assets/lang/ru.json index 11cdb15c65..3f020063c1 100644 --- a/plugins/chunter-assets/lang/ru.json +++ b/plugins/chunter-assets/lang/ru.json @@ -83,7 +83,8 @@ "NewestFirst": "Сначала новые", "ReplyToThread": "Ответить в канале", "SentMessage": "Отправил(а) сообщение", - "Direct": "личные сообщения", - "RepliedToThread": "Ответил(а) в канале" + "Direct": "Личные сообщения", + "RepliedToThread": "Ответил(а) в канале", + "RepliedTo": "Ответил(а) на: {message}" } } \ No newline at end of file diff --git a/plugins/chunter-resources/src/components/Channel.svelte b/plugins/chunter-resources/src/components/Channel.svelte index 2349d76605..1875d83908 100644 --- a/plugins/chunter-resources/src/components/Channel.svelte +++ b/plugins/chunter-resources/src/components/Channel.svelte @@ -19,22 +19,26 @@ import { onDestroy } from 'svelte' import activity, { ActivityMessage, ActivityMessagesFilter } from '@hcengineering/activity' import { ActivityScrolledView } from '@hcengineering/activity-resources' + import { getClient } from '@hcengineering/presentation' import chunter from '../plugin' - export let notifyContext: DocNotifyContext + export let context: DocNotifyContext export let object: Doc export let filterId: Ref = activity.ids.AllFilter + const client = getClient() + const hierarchy = client.getHierarchy() + let selectedMessageId: Ref | undefined = undefined const unsubscribe = locationStore.subscribe((newLocation) => { - selectedMessageId = newLocation.fragment as Ref + selectedMessageId = newLocation.query?.message as Ref | undefined }) onDestroy(unsubscribe) - $: isDocChannel = ![chunter.class.DirectMessage, chunter.class.Channel].includes(notifyContext.attachedToClass) + $: isDocChannel = !hierarchy.isDerived(context.attachedToClass, chunter.class.ChunterSpace) $: messagesClass = isDocChannel ? activity.class.ActivityMessage : chunter.class.ChatMessage $: collection = isDocChannel ? 'comments' : 'messages' @@ -47,5 +51,5 @@ startFromBottom {selectedMessageId} {collection} - lastViewedTimestamp={notifyContext.lastViewedTimestamp} + lastViewedTimestamp={context.lastViewedTimestamp} /> diff --git a/plugins/chunter-resources/src/components/ChannelHeader.svelte b/plugins/chunter-resources/src/components/ChannelHeader.svelte index 18157c8e36..3ffe19ed24 100644 --- a/plugins/chunter-resources/src/components/ChannelHeader.svelte +++ b/plugins/chunter-resources/src/components/ChannelHeader.svelte @@ -23,6 +23,7 @@ import { getChannelIcon } from '../utils' export let object: Doc + export let allowClose = false const client = getClient() const hierarchy = client.getHierarchy() @@ -45,6 +46,14 @@ label={title} intlLabel={title ? undefined : chunter.string.Channel} description={topic} + {allowClose} + on:close /> {/await} + + diff --git a/plugins/chunter-resources/src/components/ChannelIcon.svelte b/plugins/chunter-resources/src/components/ChannelIcon.svelte new file mode 100644 index 0000000000..68fd8e9106 --- /dev/null +++ b/plugins/chunter-resources/src/components/ChannelIcon.svelte @@ -0,0 +1,28 @@ + + + +{#if value.private} + +{:else} + +{/if} diff --git a/plugins/chunter-resources/src/components/ChannelPanel.svelte b/plugins/chunter-resources/src/components/ChannelPanel.svelte new file mode 100644 index 0000000000..25901c7efc --- /dev/null +++ b/plugins/chunter-resources/src/components/ChannelPanel.svelte @@ -0,0 +1,51 @@ + + + +{#if threadId} + +{:else if object} +
+ +
+{/if} diff --git a/plugins/chunter-resources/src/components/ChannelView.svelte b/plugins/chunter-resources/src/components/ChannelView.svelte index 5b879c9576..b352b7e12e 100644 --- a/plugins/chunter-resources/src/components/ChannelView.svelte +++ b/plugins/chunter-resources/src/components/ChannelView.svelte @@ -16,16 +16,17 @@ import { Ref, Doc } from '@hcengineering/core' import { getLocation, navigate } from '@hcengineering/ui' import { DocNotifyContext } from '@hcengineering/notification' - import activity, { ActivityMessagesFilter } from '@hcengineering/activity' + import activity, { ActivityMessage, ActivityMessagesFilter } from '@hcengineering/activity' import { ChatMessage } from '@hcengineering/chunter' import Channel from './Channel.svelte' import PinnedMessages from './PinnedMessages.svelte' import ChannelHeader from './ChannelHeader.svelte' - export let notifyContext: DocNotifyContext + export let context: DocNotifyContext export let object: Doc export let filterId: Ref = activity.ids.AllFilter + export let allowClose = false function openThread (_id: Ref) { const loc = getLocation() @@ -34,10 +35,10 @@ } - - + + { diff --git a/plugins/chunter-resources/src/components/ChannelViewPanel.svelte b/plugins/chunter-resources/src/components/ChannelViewPanel.svelte deleted file mode 100644 index 8289f01d7f..0000000000 --- a/plugins/chunter-resources/src/components/ChannelViewPanel.svelte +++ /dev/null @@ -1,44 +0,0 @@ - - - -{#if notifyContext && object} -
- -
-{/if} diff --git a/plugins/chunter-resources/src/components/DirectIcon.svelte b/plugins/chunter-resources/src/components/DirectIcon.svelte new file mode 100644 index 0000000000..f32a916b0e --- /dev/null +++ b/plugins/chunter-resources/src/components/DirectIcon.svelte @@ -0,0 +1,95 @@ + + + +{#if persons.length === 0} + +{/if} + +{#if persons.length === 1} + +{/if} + +{#if persons.length > 1 && size === 'medium'} +
+ {#each persons.slice(0, visiblePersons - 1) as person} + + {/each} + {#if persons.length > visiblePersons} +
+ +{persons.length - visiblePersons + 1} +
+ {/if} + {#if persons.length < visiblePersons} + {#each Array(visiblePersons - persons.length) as _} +
+ {/each} + {/if} +
+{/if} + +{#if persons.length > 1 && size !== 'medium'} + _id)} {size} limit={visiblePersons} /> +{/if} + + diff --git a/plugins/chunter-resources/src/components/DmPresenter.svelte b/plugins/chunter-resources/src/components/DmPresenter.svelte index 51e5780cf9..f818d148a8 100644 --- a/plugins/chunter-resources/src/components/DmPresenter.svelte +++ b/plugins/chunter-resources/src/components/DmPresenter.svelte @@ -18,7 +18,7 @@ import { NavLink } from '@hcengineering/view-resources' import { getDmName } from '../utils' - import DmIconPresenter from './DmIconPresenter.svelte' + import DirectIcon from './DirectIcon.svelte' export let value: DirectMessage export let disabled = false @@ -30,7 +30,7 @@ {#await getDmName(client, value) then name}
- + {name}
diff --git a/plugins/chunter-resources/src/components/Header.svelte b/plugins/chunter-resources/src/components/Header.svelte index 23c2cdd610..6eaf74173f 100644 --- a/plugins/chunter-resources/src/components/Header.svelte +++ b/plugins/chunter-resources/src/components/Header.svelte @@ -14,7 +14,9 @@ --> -
+
+ {#if allowClose} +
- - -
- -
- - {/if} -
- {#if selectedEmployee !== undefined && component === undefined} - select(e.detail)} - on:dm={(e) => { - openDM(e.detail) - }} - on:close={() => { - selectedEmployee = undefined - }} - /> - {:else if component && _id && _class} - select(undefined)} /> - {/if} -
-
diff --git a/plugins/notification-resources/src/components/NotificationView.svelte b/plugins/notification-resources/src/components/NotificationView.svelte deleted file mode 100644 index 37059b4133..0000000000 --- a/plugins/notification-resources/src/components/NotificationView.svelte +++ /dev/null @@ -1,121 +0,0 @@ - - - - - -{#if doc} -
- {#if newTxes > 0 && !selected}
{/if} -
-
- {#if presenter} - - {/if} - {#if newTxes > 0 && !selected} -
{newTxes}
- {/if} -
- {#if preview && object && notificationPreviewPresenter !== undefined} -
- -
- {/if} - {#if !preview || notificationPreviewPresenter === undefined} -
-
- {#if tx} - - {/if} -
-
- -
-
- {/if} -
-
-{/if} diff --git a/plugins/notification-resources/src/components/NotifyContextIcon.svelte b/plugins/notification-resources/src/components/NotifyContextIcon.svelte new file mode 100644 index 0000000000..7cfbbfa510 --- /dev/null +++ b/plugins/notification-resources/src/components/NotifyContextIcon.svelte @@ -0,0 +1,56 @@ + + + +
+ {#if iconMixin && object} + + {:else} + + {/if} +
+ + diff --git a/plugins/notification-resources/src/components/NotifyMarker.svelte b/plugins/notification-resources/src/components/NotifyMarker.svelte new file mode 100644 index 0000000000..9d8bdf572e --- /dev/null +++ b/plugins/notification-resources/src/components/NotifyMarker.svelte @@ -0,0 +1,44 @@ + + + +{#if count > 0} +
+ {#if count > maxNumber} + {maxNumber}+ + {:else} + {count} + {/if} +
+{/if} + + diff --git a/plugins/notification-resources/src/components/TxView.svelte b/plugins/notification-resources/src/components/TxView.svelte deleted file mode 100644 index b58ffc46e1..0000000000 --- a/plugins/notification-resources/src/components/TxView.svelte +++ /dev/null @@ -1,258 +0,0 @@ - - - -{#if (viewlet !== undefined && !((viewlet?.hideOnRemove ?? false) && ptx?.removed)) || model.length > 0} -
- {#if withAvatar} -
- -
- {:else} -
- {#if iconComponent} - - {:else if viewlet} - - {:else if viewlet === undefined && model.length > 0} - - {:else} - - {/if} -
- {/if} -
-
- {#if viewlet && viewlet?.editable} - {#if viewlet.label} - - {/if} - {#if ptx?.updated} - - {/if} - {:else if viewlet && viewlet.label} - - - {/if} - {#if viewlet && viewlet.labelComponent} - - {/if} - - {#if viewlet === undefined && model.length > 0 && ptx?.updateTx} - {#each model as m} - {#await getValue(client, m, ptx) then value} - {#if value.added.length} - - - - {#each value.added as cvalue} - {#if value.isObjectAdded} - - {:else} - - {/if} - {/each} - {:else if value.removed.length} - - - - {#each value.removed as cvalue} - {#if value.isObjectRemoved} - - {:else} - - {/if} - {/each} - {:else if value.set === null || value.set === undefined || value.set === ''} - - - {:else} - - - - - {#if !hasMessageType} - - {#if value.isObjectSet} - - {:else} - - {/if} - - {/if} - {/if} - {/await} - {/each} - {:else if viewlet === undefined && model.length > 0 && ptx?.mixinTx} - {#each model as m} - {#await getValue(client, m, ptx) then value} - {#if value.set === null || value.set === ''} - - - {:else} - - - - - {#if !hasMessageType} -
- {#if value.isObjectSet} - - {:else} - - {/if} -
- {/if} - {/if} - {/await} - {/each} - {:else if viewlet && viewlet.display === 'inline' && viewlet.component} - {#if typeof viewlet.component === 'string'} - - {:else} - - {/if} - {/if} -
-
-
-{/if} - - diff --git a/plugins/notification-resources/src/components/inbox/ActivityInboxNotificationPresenter.svelte b/plugins/notification-resources/src/components/inbox/ActivityInboxNotificationPresenter.svelte index 7cd8100f58..50b59b1921 100644 --- a/plugins/notification-resources/src/components/inbox/ActivityInboxNotificationPresenter.svelte +++ b/plugins/notification-resources/src/components/inbox/ActivityInboxNotificationPresenter.svelte @@ -14,22 +14,28 @@ --> {#if displayMessage !== undefined} - { - handleReply(displayMessage) - }} - onClick={() => { - handleMessageClicked(displayMessage) - }} - /> +
+ {#if !embedded} +
+ { + if (onCheck) { + onCheck(event.detail) + } + }} + /> +
+ {/if} + {#if viewlet} + + {:else} + { + handleReply(displayMessage) + }} + {onClick} + /> + {/if} +
{/if} + + diff --git a/plugins/notification-resources/src/components/inbox/CommonInboxNotificationPresenter.svelte b/plugins/notification-resources/src/components/inbox/CommonInboxNotificationPresenter.svelte index 68acc78784..f76c3bcd94 100644 --- a/plugins/notification-resources/src/components/inbox/CommonInboxNotificationPresenter.svelte +++ b/plugins/notification-resources/src/components/inbox/CommonInboxNotificationPresenter.svelte @@ -26,7 +26,7 @@ import { translate } from '@hcengineering/platform' import { createQuery, getClient, MessageViewer } from '@hcengineering/presentation' import notification, { CommonInboxNotification } from '@hcengineering/notification' - import { ActionIcon, IconMoreH, Label, showPopup } from '@hcengineering/ui' + import { ActionIcon, CheckBox, IconMoreH, Label, showPopup } from '@hcengineering/ui' import { getDocLinkTitle, Menu } from '@hcengineering/view-resources' import { ActivityDocLink } from '@hcengineering/activity-resources' import activity from '@hcengineering/activity' @@ -35,6 +35,9 @@ import { InboxNotificationsClientImpl } from '../../inboxNotificationsClient' export let value: CommonInboxNotification + export let embedded = false + export let onClick: (() => void) | undefined = undefined + export let onCheck: ((isChecked: boolean) => void) | undefined = undefined const objectQuery = createQuery() const client = getClient() @@ -89,52 +92,74 @@ } -
- {#if !value.isViewed} -
+
+ {#if !embedded} + { + if (onCheck) { + onCheck(event.detail) + } + }} + /> {/if} - {#if value.icon} - - {:else if person} - - {:else} - - {/if} -
-
- {#if person} - + + +
+ {#if !embedded} + {#if !value.isViewed} +
+ {/if} + + {#if value.icon} + + {:else if person} + {:else} -
-