mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 03:14:40 +03:00
TSK-1263 Inbox configurator (#3067)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
aa218ab898
commit
98d71a25df
@ -125,15 +125,35 @@ export function createModel (builder: Builder): void {
|
||||
calendar.viewlet.CalendarEvent
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationGroup,
|
||||
core.space.Model,
|
||||
{
|
||||
label: calendar.string.Calendar,
|
||||
icon: calendar.icon.Calendar
|
||||
},
|
||||
calendar.ids.CalendarNotificationGroup
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationType,
|
||||
core.space.Model,
|
||||
{
|
||||
hidden: false,
|
||||
generated: false,
|
||||
label: calendar.string.Reminder,
|
||||
textTemplate: 'Reminder: {doc}',
|
||||
htmlTemplate: 'Reminder: {doc}',
|
||||
subjectTemplate: 'Reminder: {doc}'
|
||||
group: calendar.ids.CalendarNotificationGroup,
|
||||
txClasses: [],
|
||||
objectClass: calendar.mixin.Reminder,
|
||||
templates: {
|
||||
textTemplate: 'Reminder: {doc}',
|
||||
htmlTemplate: 'Reminder: {doc}',
|
||||
subjectTemplate: 'Reminder: {doc}'
|
||||
},
|
||||
providers: {
|
||||
[notification.providers.PlatformNotification]: true,
|
||||
[notification.providers.EmailNotification]: false
|
||||
}
|
||||
},
|
||||
calendar.ids.ReminderNotification
|
||||
)
|
||||
|
@ -17,6 +17,7 @@ import { TxViewlet } from '@hcengineering/activity'
|
||||
import { calendarId } from '@hcengineering/calendar'
|
||||
import calendar from '@hcengineering/calendar-resources/src/plugin'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { NotificationGroup } from '@hcengineering/notification'
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
import { mergeIds } from '@hcengineering/platform'
|
||||
import { AnyComponent } from '@hcengineering/ui'
|
||||
@ -54,6 +55,7 @@ export default mergeIds(calendarId, calendar, {
|
||||
CalendarEvent: '' as Ref<Viewlet>
|
||||
},
|
||||
ids: {
|
||||
ReminderViewlet: '' as Ref<TxViewlet>
|
||||
ReminderViewlet: '' as Ref<TxViewlet>,
|
||||
CalendarNotificationGroup: '' as Ref<NotificationGroup>
|
||||
}
|
||||
})
|
||||
|
@ -38,6 +38,7 @@
|
||||
"@hcengineering/model-view": "^0.6.0",
|
||||
"@hcengineering/model-workbench": "^0.6.1",
|
||||
"@hcengineering/model-notification": "^0.6.0",
|
||||
"@hcengineering/notification": "^0.6.10",
|
||||
"@hcengineering/activity": "^0.6.0",
|
||||
"@hcengineering/workbench": "^0.6.4",
|
||||
"@hcengineering/model-preference": "^0.6.0"
|
||||
|
@ -88,6 +88,7 @@ export class TChunterMessage extends TAttachedDoc implements ChunterMessage {
|
||||
attachments?: number
|
||||
|
||||
@Prop(TypeRef(core.class.Account), chunter.string.CreateBy)
|
||||
@ReadOnly()
|
||||
createBy!: Ref<Account>
|
||||
|
||||
@Prop(TypeTimestamp(), chunter.string.Create)
|
||||
@ -584,7 +585,7 @@ export function createModel (builder: Builder, options = { addApplication: true
|
||||
icon: chunter.icon.Chunter,
|
||||
txClass: core.class.TxCreateDoc,
|
||||
component: chunter.activity.TxMessageCreate,
|
||||
label: notification.string.DMNotification,
|
||||
label: chunter.string.DMNotification,
|
||||
display: 'content',
|
||||
editable: true,
|
||||
hideOnRemove: true
|
||||
@ -641,6 +642,96 @@ export function createModel (builder: Builder, options = { addApplication: true
|
||||
filters: []
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationGroup,
|
||||
core.space.Model,
|
||||
{
|
||||
label: chunter.string.ApplicationLabelChunter,
|
||||
icon: chunter.icon.Chunter
|
||||
},
|
||||
chunter.ids.ChunterNotificationGroup
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationType,
|
||||
core.space.Model,
|
||||
{
|
||||
label: chunter.string.MentionNotification,
|
||||
generated: false,
|
||||
hidden: false,
|
||||
txClasses: [core.class.TxCreateDoc],
|
||||
objectClass: chunter.class.Backlink,
|
||||
group: chunter.ids.ChunterNotificationGroup,
|
||||
providers: {
|
||||
[notification.providers.EmailNotification]: true,
|
||||
[notification.providers.PlatformNotification]: true
|
||||
},
|
||||
templates: {
|
||||
textTemplate: '{sender} mentioned you in {doc} {data}',
|
||||
htmlTemplate: '<p><b>{sender}</b> mentioned you in {doc}</p> {data}',
|
||||
subjectTemplate: 'You were mentioned in {doc}'
|
||||
}
|
||||
},
|
||||
chunter.ids.MentionNotification
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationType,
|
||||
core.space.Model,
|
||||
{
|
||||
label: chunter.string.DM,
|
||||
generated: false,
|
||||
hidden: false,
|
||||
txClasses: [core.class.TxCreateDoc],
|
||||
objectClass: chunter.class.Message,
|
||||
providers: {
|
||||
[notification.providers.EmailNotification]: false,
|
||||
[notification.providers.PlatformNotification]: true
|
||||
},
|
||||
group: chunter.ids.ChunterNotificationGroup,
|
||||
templates: {
|
||||
textTemplate: '{sender} has send you a message: {doc} {data}',
|
||||
htmlTemplate: '<p><b>{sender}</b> has send you a message {doc}</p> {data}',
|
||||
subjectTemplate: 'You have new direct message in {doc}'
|
||||
}
|
||||
},
|
||||
chunter.ids.DMNotification
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationType,
|
||||
core.space.Model,
|
||||
{
|
||||
label: chunter.string.Message,
|
||||
generated: false,
|
||||
hidden: false,
|
||||
txClasses: [core.class.TxCreateDoc],
|
||||
objectClass: chunter.class.Message,
|
||||
providers: {
|
||||
[notification.providers.PlatformNotification]: true
|
||||
},
|
||||
group: chunter.ids.ChunterNotificationGroup
|
||||
},
|
||||
chunter.ids.ThreadNotification
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationType,
|
||||
core.space.Model,
|
||||
{
|
||||
label: chunter.string.ThreadMessage,
|
||||
generated: false,
|
||||
hidden: false,
|
||||
txClasses: [core.class.TxCreateDoc],
|
||||
objectClass: chunter.class.ThreadMessage,
|
||||
providers: {
|
||||
[notification.providers.PlatformNotification]: true
|
||||
},
|
||||
group: chunter.ids.ChunterNotificationGroup
|
||||
},
|
||||
chunter.ids.ChannelNotification
|
||||
)
|
||||
|
||||
createAction(builder, {
|
||||
...viewTemplates.open,
|
||||
target: chunter.class.Channel,
|
||||
|
@ -16,7 +16,8 @@
|
||||
import type { DisplayTx, TxViewlet } from '@hcengineering/activity'
|
||||
import { Channel, chunterId } from '@hcengineering/chunter'
|
||||
import chunter from '@hcengineering/chunter-resources/src/plugin'
|
||||
import type { Ref, Space, Doc } from '@hcengineering/core'
|
||||
import type { Doc, Ref, Space } from '@hcengineering/core'
|
||||
import { NotificationGroup } from '@hcengineering/notification'
|
||||
import type { IntlString, Resource } from '@hcengineering/platform'
|
||||
import { mergeIds } from '@hcengineering/platform'
|
||||
import type { AnyComponent, Location } from '@hcengineering/ui'
|
||||
@ -68,6 +69,7 @@ export default mergeIds(chunterId, chunter, {
|
||||
Edit: '' as IntlString,
|
||||
MarkUnread: '' as IntlString,
|
||||
LastMessage: '' as IntlString,
|
||||
MentionNotification: '' as IntlString,
|
||||
PinnedMessages: '' as IntlString,
|
||||
SavedMessages: '' as IntlString,
|
||||
ThreadMessage: '' as IntlString,
|
||||
@ -75,6 +77,8 @@ export default mergeIds(chunterId, chunter, {
|
||||
Emoji: '' as IntlString,
|
||||
FilterComments: '' as IntlString,
|
||||
FilterBacklinks: '' as IntlString,
|
||||
DM: '' as IntlString,
|
||||
DMNotification: '' as IntlString,
|
||||
ConfigLabel: '' as IntlString,
|
||||
ConfigDescription: '' as IntlString
|
||||
},
|
||||
@ -86,7 +90,8 @@ export default mergeIds(chunterId, chunter, {
|
||||
TxBacklinkCreate: '' as Ref<TxViewlet>,
|
||||
TxCommentRemove: '' as Ref<TxViewlet>,
|
||||
TxBacklinkRemove: '' as Ref<TxViewlet>,
|
||||
TxMessageCreate: '' as Ref<TxViewlet>
|
||||
TxMessageCreate: '' as Ref<TxViewlet>,
|
||||
ChunterNotificationGroup: '' as Ref<NotificationGroup>
|
||||
},
|
||||
activity: {
|
||||
TxCommentCreate: '' as AnyComponent,
|
||||
|
@ -434,15 +434,36 @@ export function createModel (builder: Builder): void {
|
||||
presenter: hr.component.RequestPresenter
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationGroup,
|
||||
core.space.Model,
|
||||
{
|
||||
label: hr.string.HRApplication,
|
||||
icon: hr.icon.HR
|
||||
},
|
||||
hr.ids.HRNotificationGroup
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationType,
|
||||
core.space.Model,
|
||||
{
|
||||
hidden: true,
|
||||
label: hr.string.Request,
|
||||
textTemplate: 'New request: {doc}',
|
||||
htmlTemplate: 'New request: {doc}',
|
||||
subjectTemplate: 'New request'
|
||||
hidden: false,
|
||||
generated: false,
|
||||
label: hr.string.RequestCreated,
|
||||
group: hr.ids.HRNotificationGroup,
|
||||
// will be created with different trigger
|
||||
txClasses: [],
|
||||
objectClass: hr.class.Request,
|
||||
providers: {
|
||||
[notification.providers.EmailNotification]: true,
|
||||
[notification.providers.PlatformNotification]: true
|
||||
},
|
||||
templates: {
|
||||
textTemplate: 'New request: {doc}',
|
||||
htmlTemplate: 'New request: {doc}',
|
||||
subjectTemplate: 'New request'
|
||||
}
|
||||
},
|
||||
hr.ids.CreateRequestNotification
|
||||
)
|
||||
@ -451,11 +472,22 @@ export function createModel (builder: Builder): void {
|
||||
notification.class.NotificationType,
|
||||
core.space.Model,
|
||||
{
|
||||
hidden: true,
|
||||
label: hr.string.Request,
|
||||
textTemplate: 'Request updated: {doc}',
|
||||
htmlTemplate: 'Request updated: {doc}',
|
||||
subjectTemplate: 'Request updated'
|
||||
hidden: false,
|
||||
generated: false,
|
||||
group: hr.ids.HRNotificationGroup,
|
||||
label: hr.string.RequestUpdated,
|
||||
// will be created with different trigger
|
||||
txClasses: [],
|
||||
objectClass: hr.class.Request,
|
||||
providers: {
|
||||
[notification.providers.EmailNotification]: true,
|
||||
[notification.providers.PlatformNotification]: true
|
||||
},
|
||||
templates: {
|
||||
textTemplate: 'Request updated: {doc}',
|
||||
htmlTemplate: 'Request updated: {doc}',
|
||||
subjectTemplate: 'Request updated'
|
||||
}
|
||||
},
|
||||
hr.ids.UpdateRequestNotification
|
||||
)
|
||||
@ -464,11 +496,22 @@ export function createModel (builder: Builder): void {
|
||||
notification.class.NotificationType,
|
||||
core.space.Model,
|
||||
{
|
||||
hidden: true,
|
||||
label: hr.string.Request,
|
||||
textTemplate: 'Request removed: {doc}',
|
||||
htmlTemplate: 'Request removed: {doc}',
|
||||
subjectTemplate: 'Request removed'
|
||||
hidden: false,
|
||||
group: hr.ids.HRNotificationGroup,
|
||||
generated: false,
|
||||
label: hr.string.RequestRemoved,
|
||||
// will be created with different trigger
|
||||
txClasses: [],
|
||||
objectClass: hr.class.Request,
|
||||
providers: {
|
||||
[notification.providers.EmailNotification]: true,
|
||||
[notification.providers.PlatformNotification]: true
|
||||
},
|
||||
templates: {
|
||||
textTemplate: 'Request removed: {doc}',
|
||||
htmlTemplate: 'Request removed: {doc}',
|
||||
subjectTemplate: 'Request removed'
|
||||
}
|
||||
},
|
||||
hr.ids.RemoveRequestNotification
|
||||
)
|
||||
@ -477,11 +520,22 @@ export function createModel (builder: Builder): void {
|
||||
notification.class.NotificationType,
|
||||
core.space.Model,
|
||||
{
|
||||
hidden: true,
|
||||
hidden: false,
|
||||
generated: false,
|
||||
group: hr.ids.HRNotificationGroup,
|
||||
label: hr.string.PublicHoliday,
|
||||
textTemplate: 'New public holiday: {doc}',
|
||||
htmlTemplate: 'New public holiday: {doc}',
|
||||
subjectTemplate: 'New public holiday'
|
||||
// will be created with different trigger
|
||||
txClasses: [],
|
||||
objectClass: hr.class.PublicHoliday,
|
||||
providers: {
|
||||
[notification.providers.EmailNotification]: true,
|
||||
[notification.providers.PlatformNotification]: true
|
||||
},
|
||||
templates: {
|
||||
textTemplate: 'New public holiday: {doc}',
|
||||
htmlTemplate: 'New public holiday: {doc}',
|
||||
subjectTemplate: 'New public holiday'
|
||||
}
|
||||
},
|
||||
hr.ids.CreatePublicHolidayNotification
|
||||
)
|
||||
|
@ -16,6 +16,7 @@
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { hrId } from '@hcengineering/hr'
|
||||
import hr from '@hcengineering/hr-resources/src/plugin'
|
||||
import { NotificationGroup } from '@hcengineering/notification'
|
||||
import { IntlString, mergeIds } from '@hcengineering/platform'
|
||||
import { AnyComponent } from '@hcengineering/ui'
|
||||
import { Action, ActionCategory, ViewAction } from '@hcengineering/view'
|
||||
@ -34,6 +35,9 @@ export default mergeIds(hrId, hr, {
|
||||
Overtime2: '' as IntlString,
|
||||
Subscribers: '' as IntlString,
|
||||
PublicHoliday: '' as IntlString,
|
||||
RequestCreated: '' as IntlString,
|
||||
RequestUpdated: '' as IntlString,
|
||||
RequestRemoved: '' as IntlString,
|
||||
ConfigLabel: '' as IntlString,
|
||||
ConfigDescription: '' as IntlString
|
||||
},
|
||||
@ -60,5 +64,8 @@ export default mergeIds(hrId, hr, {
|
||||
},
|
||||
actionImpl: {
|
||||
EditRequestType: '' as ViewAction
|
||||
},
|
||||
ids: {
|
||||
HRNotificationGroup: '' as Ref<NotificationGroup>
|
||||
}
|
||||
})
|
||||
|
@ -41,6 +41,8 @@
|
||||
"@hcengineering/setting": "^0.6.5",
|
||||
"@hcengineering/view": "^0.6.4",
|
||||
"@hcengineering/task": "^0.6.6",
|
||||
"@hcengineering/model-notification": "^0.6.0",
|
||||
"@hcengineering/notification": "^0.6.10",
|
||||
"@hcengineering/model-task": "^0.6.0",
|
||||
"@hcengineering/workbench": "^0.6.4"
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import {
|
||||
Mixin,
|
||||
Model,
|
||||
Prop,
|
||||
ReadOnly,
|
||||
TypeMarkup,
|
||||
TypeRef,
|
||||
TypeString,
|
||||
@ -38,6 +39,8 @@ import view, { createAction, actionTemplates as viewTemplates } from '@hcenginee
|
||||
import workbench from '@hcengineering/model-workbench'
|
||||
import setting from '@hcengineering/setting'
|
||||
import { ViewOptionsModel } from '@hcengineering/view'
|
||||
import { generateClassNotificationTypes } from '@hcengineering/model-notification'
|
||||
import notification from '@hcengineering/notification'
|
||||
import lead from './plugin'
|
||||
|
||||
export { leadId } from '@hcengineering/lead'
|
||||
@ -62,6 +65,7 @@ export class TFunnel extends TSpaceWithStates implements Funnel {
|
||||
@UX(lead.string.Lead, lead.icon.Lead, undefined, 'title')
|
||||
export class TLead extends TTask implements Lead {
|
||||
@Prop(TypeRef(contact.class.Contact), lead.string.Customer)
|
||||
@ReadOnly()
|
||||
declare attachedTo: Ref<Customer>
|
||||
|
||||
@Prop(TypeString(), lead.string.Title)
|
||||
@ -287,6 +291,49 @@ export function createModel (builder: Builder): void {
|
||||
}
|
||||
}
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationGroup,
|
||||
core.space.Model,
|
||||
{
|
||||
label: lead.string.Lead,
|
||||
icon: lead.icon.Lead,
|
||||
objectClass: lead.class.Lead
|
||||
},
|
||||
lead.ids.LeadNotificationGroup
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationType,
|
||||
core.space.Model,
|
||||
{
|
||||
hidden: false,
|
||||
generated: false,
|
||||
label: task.string.AssignedToMe,
|
||||
group: lead.ids.LeadNotificationGroup,
|
||||
field: 'assignee',
|
||||
txClasses: [core.class.TxCreateDoc, core.class.TxUpdateDoc],
|
||||
objectClass: lead.class.Lead,
|
||||
templates: {
|
||||
textTemplate: '{doc} was assigned to you by {sender}',
|
||||
htmlTemplate: '<p>{doc} was assigned to you by {sender}</p>',
|
||||
subjectTemplate: '{doc} was assigned to you'
|
||||
},
|
||||
providers: {
|
||||
[notification.providers.PlatformNotification]: true,
|
||||
[notification.providers.EmailNotification]: true
|
||||
}
|
||||
},
|
||||
lead.ids.AssigneeNotification
|
||||
)
|
||||
|
||||
generateClassNotificationTypes(
|
||||
builder,
|
||||
lead.class.Lead,
|
||||
lead.ids.LeadNotificationGroup,
|
||||
[],
|
||||
['comments', 'state', 'doneState']
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
import type { Ref, Space } from '@hcengineering/core'
|
||||
import { leadId } from '@hcengineering/lead'
|
||||
import { NotificationGroup, NotificationType } from '@hcengineering/notification'
|
||||
import lead from '@hcengineering/lead-resources/src/plugin'
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
import { mergeIds } from '@hcengineering/platform'
|
||||
@ -63,5 +64,9 @@ export default mergeIds(leadId, lead, {
|
||||
},
|
||||
action: {
|
||||
CreateGlobalLead: '' as Ref<Action>
|
||||
},
|
||||
ids: {
|
||||
LeadNotificationGroup: '' as Ref<NotificationGroup>,
|
||||
AssigneeNotification: '' as Ref<NotificationType>
|
||||
}
|
||||
})
|
||||
|
@ -30,6 +30,7 @@
|
||||
"@hcengineering/ui": "^0.6.6",
|
||||
"@hcengineering/platform": "^0.6.8",
|
||||
"@hcengineering/model-core": "^0.6.0",
|
||||
"@hcengineering/model-preference": "^0.6.0",
|
||||
"@hcengineering/model-view": "^0.6.0",
|
||||
"@hcengineering/view": "^0.6.4",
|
||||
"@hcengineering/workbench": "^0.6.4",
|
||||
|
@ -14,25 +14,42 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Account, Class, Doc, Domain, DOMAIN_MODEL, IndexKind, Ref, Timestamp, TxCUD } from '@hcengineering/core'
|
||||
import {
|
||||
Account,
|
||||
AttachedDoc,
|
||||
Class,
|
||||
Collection,
|
||||
Data,
|
||||
Doc,
|
||||
Domain,
|
||||
DOMAIN_MODEL,
|
||||
Hierarchy,
|
||||
IndexKind,
|
||||
Ref,
|
||||
Timestamp,
|
||||
Tx,
|
||||
TxCUD
|
||||
} from '@hcengineering/core'
|
||||
import { ArrOf, Builder, Index, Mixin, Model, Prop, TypeRef, TypeString, UX } from '@hcengineering/model'
|
||||
import core, { TAttachedDoc, TClass, TDoc } from '@hcengineering/model-core'
|
||||
import preference, { TPreference } from '@hcengineering/model-preference'
|
||||
import view, { createAction } from '@hcengineering/model-view'
|
||||
import {
|
||||
AnotherUserNotifications,
|
||||
DocUpdates,
|
||||
EmailNotification,
|
||||
LastView,
|
||||
Notification,
|
||||
NotificationGroup,
|
||||
notificationId,
|
||||
NotificationObjectPresenter,
|
||||
NotificationProvider,
|
||||
NotificationSetting,
|
||||
NotificationStatus,
|
||||
NotificationTemplate,
|
||||
NotificationType,
|
||||
SpaceLastEdit
|
||||
} from '@hcengineering/notification'
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
import type { Asset, IntlString } from '@hcengineering/platform'
|
||||
import setting from '@hcengineering/setting'
|
||||
import { AnyComponent } from '@hcengineering/ui'
|
||||
import workbench from '@hcengineering/workbench'
|
||||
@ -89,11 +106,22 @@ export class TEmaiNotification extends TDoc implements EmailNotification {
|
||||
|
||||
@Model(notification.class.NotificationType, core.class.Doc, DOMAIN_MODEL)
|
||||
export class TNotificationType extends TDoc implements NotificationType {
|
||||
generated!: boolean
|
||||
label!: IntlString
|
||||
textTemplate!: string
|
||||
htmlTemplate!: string
|
||||
subjectTemplate!: string
|
||||
group!: Ref<NotificationGroup>
|
||||
txClasses!: Ref<Class<Tx>>[]
|
||||
providers!: Record<Ref<NotificationProvider>, boolean>
|
||||
objectClass!: Ref<Class<Doc>>
|
||||
hidden!: boolean
|
||||
templates?: NotificationTemplate
|
||||
}
|
||||
|
||||
@Model(notification.class.NotificationGroup, core.class.Doc, DOMAIN_MODEL)
|
||||
export class TNotificationGroup extends TDoc implements NotificationGroup {
|
||||
label!: IntlString
|
||||
icon!: Asset
|
||||
// using for autogenerated settings
|
||||
objectClass?: Ref<Class<Doc>>
|
||||
}
|
||||
|
||||
@Model(notification.class.NotificationProvider, core.class.Doc, DOMAIN_MODEL)
|
||||
@ -102,10 +130,10 @@ export class TNotificationProvider extends TDoc implements NotificationProvider
|
||||
default!: boolean
|
||||
}
|
||||
|
||||
@Model(notification.class.NotificationSetting, core.class.Doc, DOMAIN_NOTIFICATION)
|
||||
export class TNotificationSetting extends TDoc implements NotificationSetting {
|
||||
@Model(notification.class.NotificationSetting, preference.class.Preference)
|
||||
export class TNotificationSetting extends TPreference implements NotificationSetting {
|
||||
attachedTo!: Ref<TNotificationProvider>
|
||||
type!: Ref<TNotificationType>
|
||||
provider!: Ref<TNotificationProvider>
|
||||
enabled!: boolean
|
||||
}
|
||||
|
||||
@ -114,11 +142,6 @@ export class TSpaceLastEdit extends TClass implements SpaceLastEdit {
|
||||
lastEditField!: string
|
||||
}
|
||||
|
||||
@Mixin(notification.mixin.AnotherUserNotifications, core.class.Class)
|
||||
export class TAnotherUserNotifications extends TClass implements AnotherUserNotifications {
|
||||
fields!: string[]
|
||||
}
|
||||
|
||||
@Mixin(notification.mixin.ClassCollaborators, core.class.Class)
|
||||
export class TClassCollaborators extends TClass {
|
||||
fields!: string[]
|
||||
@ -165,8 +188,8 @@ export function createModel (builder: Builder): void {
|
||||
TNotificationType,
|
||||
TNotificationProvider,
|
||||
TNotificationSetting,
|
||||
TNotificationGroup,
|
||||
TSpaceLastEdit,
|
||||
TAnotherUserNotifications,
|
||||
TClassCollaborators,
|
||||
TTrackedDoc,
|
||||
TCollaborators,
|
||||
@ -174,32 +197,6 @@ export function createModel (builder: Builder): void {
|
||||
TNotificationObjectPresenter
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationType,
|
||||
core.space.Model,
|
||||
{
|
||||
label: notification.string.MentionNotification,
|
||||
hidden: false,
|
||||
textTemplate: '{sender} mentioned you in {doc} {data}',
|
||||
htmlTemplate: '<p><b>{sender}</b> mentioned you in {doc}</p> {data}',
|
||||
subjectTemplate: 'You were mentioned in {doc}'
|
||||
},
|
||||
notification.ids.MentionNotification
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationType,
|
||||
core.space.Model,
|
||||
{
|
||||
label: notification.string.DM,
|
||||
hidden: false,
|
||||
textTemplate: '{sender} has send you a message: {doc} {data}',
|
||||
htmlTemplate: '<p><b>{sender}</b> has send you a message {doc}</p> {data}',
|
||||
subjectTemplate: 'You have new DM message in {doc}'
|
||||
},
|
||||
notification.ids.DMNotification
|
||||
)
|
||||
|
||||
// Temporarily disabled, we should think about it
|
||||
// builder.createDoc(
|
||||
// notification.class.NotificationProvider,
|
||||
@ -215,10 +212,18 @@ export function createModel (builder: Builder): void {
|
||||
notification.class.NotificationProvider,
|
||||
core.space.Model,
|
||||
{
|
||||
label: notification.string.EmailNotification,
|
||||
default: true
|
||||
label: notification.string.Inbox
|
||||
},
|
||||
notification.ids.EmailNotification
|
||||
notification.providers.PlatformNotification
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationProvider,
|
||||
core.space.Model,
|
||||
{
|
||||
label: notification.string.EmailNotification
|
||||
},
|
||||
notification.providers.EmailNotification
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
@ -300,3 +305,49 @@ export function createModel (builder: Builder): void {
|
||||
actions: [view.action.Delete, view.action.Open]
|
||||
})
|
||||
}
|
||||
|
||||
export function generateClassNotificationTypes (
|
||||
builder: Builder,
|
||||
_class: Ref<Class<Doc>>,
|
||||
group: Ref<NotificationGroup>,
|
||||
ignoreKeys: string[] = [],
|
||||
defaultEnabled: string[] = []
|
||||
): void {
|
||||
const txes = builder.getTxes()
|
||||
const hierarchy = new Hierarchy()
|
||||
for (const tx of txes) {
|
||||
hierarchy.tx(tx)
|
||||
}
|
||||
const attributes = hierarchy.getAllAttributes(
|
||||
_class,
|
||||
hierarchy.isDerived(_class, core.class.AttachedDoc) ? core.class.AttachedDoc : core.class.Doc
|
||||
)
|
||||
const filtered = Array.from(attributes.values()).filter((p) => p.hidden !== true && p.readonly !== true)
|
||||
for (const attribute of filtered) {
|
||||
if (ignoreKeys.includes(attribute.name)) continue
|
||||
const isCollection: boolean = core.class.Collection === attribute.type._class
|
||||
const objectClass = !isCollection ? _class : (attribute.type as Collection<AttachedDoc>).of
|
||||
const txClasses = !isCollection
|
||||
? hierarchy.isMixin(attribute.attributeOf)
|
||||
? [core.class.TxMixin]
|
||||
: [core.class.TxUpdateDoc]
|
||||
: [core.class.TxCreateDoc, core.class.TxRemoveDoc]
|
||||
const data: Data<NotificationType> = {
|
||||
attribute: attribute._id,
|
||||
group,
|
||||
generated: true,
|
||||
objectClass,
|
||||
txClasses,
|
||||
hidden: false,
|
||||
providers: {
|
||||
[notification.providers.PlatformNotification]: defaultEnabled.includes(attribute.name)
|
||||
},
|
||||
label: attribute.label
|
||||
}
|
||||
if (isCollection) {
|
||||
data.attachedToClass = _class
|
||||
}
|
||||
const id = `${notification.class.NotificationType}_${_class}_${attribute.name}` as Ref<NotificationType>
|
||||
builder.createDoc(notification.class.NotificationType, core.space.Model, data, id)
|
||||
}
|
||||
}
|
||||
|
@ -17,17 +17,18 @@ import core, {
|
||||
Account,
|
||||
AttachedDoc,
|
||||
Class,
|
||||
Collection,
|
||||
Data,
|
||||
Doc,
|
||||
DOMAIN_TX,
|
||||
generateId,
|
||||
Ref,
|
||||
TxCollectionCUD,
|
||||
TxCreateDoc,
|
||||
TxOperations,
|
||||
TxRemoveDoc
|
||||
} from '@hcengineering/core'
|
||||
import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@hcengineering/model'
|
||||
import notification, { LastView, Notification, NotificationType } from '@hcengineering/notification'
|
||||
import notification, { LastView, NotificationType } from '@hcengineering/notification'
|
||||
import { DOMAIN_NOTIFICATION } from '.'
|
||||
|
||||
async function fillNotificationText (client: MigrationClient): Promise<void> {
|
||||
@ -51,23 +52,11 @@ async function fillNotificationText (client: MigrationClient): Promise<void> {
|
||||
)
|
||||
}
|
||||
|
||||
async function fillNotificationType (client: MigrationUpgradeClient): Promise<void> {
|
||||
const notifications = await client.findAll(notification.class.Notification, { type: { $exists: false } })
|
||||
const txOp = new TxOperations(client, core.account.System)
|
||||
const promises = notifications.map(async (doc) => {
|
||||
const tx = await client.findOne(core.class.TxCUD, { _id: doc.tx })
|
||||
if (tx === undefined) return
|
||||
const type =
|
||||
tx._class === core.class.TxMixin
|
||||
? ('calendar:ids:ReminderNotification' as Ref<NotificationType>)
|
||||
: notification.ids.MentionNotification
|
||||
const objectTx = txOp.update(doc, { type })
|
||||
const ctx = await client.findOne<TxCreateDoc<Notification>>(core.class.TxCreateDoc, { objectId: doc._id })
|
||||
if (ctx === undefined) return await objectTx
|
||||
const updateTx = txOp.update(ctx, { 'attributes.type': type } as any)
|
||||
return await Promise.all([objectTx, updateTx])
|
||||
})
|
||||
await Promise.all(promises)
|
||||
async function removeSettings (client: MigrationClient): Promise<void> {
|
||||
const outdatedSettings = await client.find(DOMAIN_NOTIFICATION, { _class: notification.class.NotificationSetting })
|
||||
for (const setting of outdatedSettings) {
|
||||
await client.delete(DOMAIN_NOTIFICATION, setting._id)
|
||||
}
|
||||
}
|
||||
|
||||
async function createSpace (client: MigrationUpgradeClient): Promise<void> {
|
||||
@ -197,15 +186,66 @@ async function fillDocUpdatesHidder (client: MigrationClient): Promise<void> {
|
||||
)
|
||||
}
|
||||
|
||||
async function createCustomFieldTypes (client: MigrationUpgradeClient): Promise<void> {
|
||||
const txop = new TxOperations(client, core.account.System)
|
||||
const attributes = await client.findAll(core.class.Attribute, { isCustom: true })
|
||||
const groups = new Map(
|
||||
(await client.findAll(notification.class.NotificationGroup, {}))
|
||||
.filter((p) => p.objectClass !== undefined)
|
||||
.map((p) => [p.objectClass, p])
|
||||
)
|
||||
const types = new Set((await client.findAll(notification.class.NotificationType, {})).map((p) => p.attribute))
|
||||
for (const attribute of attributes) {
|
||||
if (attribute.hidden === true || attribute.readonly === true) continue
|
||||
if (types.has(attribute._id)) continue
|
||||
const group = groups.get(attribute.attributeOf)
|
||||
if (group === undefined) continue
|
||||
const isCollection: boolean = core.class.Collection === attribute.type._class
|
||||
const _class = attribute.attributeOf
|
||||
const objectClass = !isCollection ? _class : (attribute.type as Collection<AttachedDoc>).of
|
||||
const txClasses = !isCollection
|
||||
? [client.getHierarchy().isMixin(_class) ? core.class.TxMixin : core.class.TxUpdateDoc]
|
||||
: [core.class.TxCreateDoc, core.class.TxRemoveDoc]
|
||||
const data: Data<NotificationType> = {
|
||||
attribute: attribute._id,
|
||||
group: group._id,
|
||||
generated: true,
|
||||
objectClass,
|
||||
txClasses,
|
||||
hidden: false,
|
||||
providers: {
|
||||
[notification.providers.PlatformNotification]: false
|
||||
},
|
||||
label: attribute.label
|
||||
}
|
||||
if (isCollection) {
|
||||
data.attachedToClass = _class
|
||||
}
|
||||
const id = `${notification.class.NotificationType}_${_class}_${attribute.name}` as Ref<NotificationType>
|
||||
await txop.createDoc(notification.class.NotificationType, core.space.Model, data, id)
|
||||
}
|
||||
}
|
||||
|
||||
async function cleanOutdatedSettings (client: MigrationClient): Promise<void> {
|
||||
const res = await client.find(DOMAIN_NOTIFICATION, {
|
||||
_class: notification.class.NotificationSetting
|
||||
})
|
||||
for (const value of res) {
|
||||
await client.delete(DOMAIN_NOTIFICATION, value._id)
|
||||
}
|
||||
}
|
||||
|
||||
export const notificationOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {
|
||||
await removeSettings(client)
|
||||
await fillNotificationText(client)
|
||||
await migrateLastView(client)
|
||||
await fillCollaborators(client)
|
||||
await fillDocUpdatesHidder(client)
|
||||
await cleanOutdatedSettings(client)
|
||||
},
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {
|
||||
await createSpace(client)
|
||||
await fillNotificationType(client)
|
||||
await createCustomFieldTypes(client)
|
||||
}
|
||||
}
|
||||
|
@ -24,9 +24,6 @@ import { Application } from '@hcengineering/workbench'
|
||||
export default mergeIds(notificationId, notification, {
|
||||
string: {
|
||||
LastView: '' as IntlString,
|
||||
DM: '' as IntlString,
|
||||
DMNotification: '' as IntlString,
|
||||
MentionNotification: '' as IntlString,
|
||||
PlatformNotification: '' as IntlString,
|
||||
BrowserNotification: '' as IntlString,
|
||||
EmailNotification: '' as IntlString,
|
||||
@ -46,6 +43,7 @@ export default mergeIds(notificationId, notification, {
|
||||
category: {
|
||||
Notification: '' as Ref<ActionCategory>
|
||||
},
|
||||
groups: {},
|
||||
action: {
|
||||
Unsubscribe: '' as Ref<Action>,
|
||||
Hide: '' as Ref<Action>,
|
||||
|
@ -47,6 +47,7 @@
|
||||
"@hcengineering/workbench": "^0.6.4",
|
||||
"@hcengineering/model-tracker": "^0.6.0",
|
||||
"@hcengineering/model-presentation": "^0.6.0",
|
||||
"@hcengineering/model-notification": "^0.6.0",
|
||||
"@hcengineering/model-calendar": "^0.6.0",
|
||||
"@hcengineering/model-tags": "^0.6.0",
|
||||
"@anticrm/skillset": "^0.6.0"
|
||||
|
@ -59,6 +59,7 @@ import { KeyBinding, ViewOptionsModel } from '@hcengineering/view'
|
||||
import recruit from './plugin'
|
||||
import { createReviewModel, reviewTableConfig, reviewTableOptions } from './review'
|
||||
import { TOpinion, TReview } from './review-model'
|
||||
import { generateClassNotificationTypes } from '@hcengineering/model-notification'
|
||||
|
||||
export { recruitId } from '@hcengineering/recruit'
|
||||
export { recruitOperation } from './migration'
|
||||
@ -87,8 +88,7 @@ export class TVacancy extends TSpaceWithStates implements Vacancy {
|
||||
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)
|
||||
comments?: number
|
||||
|
||||
@Prop(Collection(chunter.class.Backlink), chunter.string.Comments)
|
||||
relations!: number
|
||||
relations!: number
|
||||
|
||||
@Prop(TypeString(), recruit.string.Vacancy)
|
||||
@Index(IndexKind.FullText)
|
||||
@ -151,6 +151,7 @@ export class TApplicant extends TTask implements Applicant {
|
||||
// We need to declare, to provide property with label
|
||||
@Prop(TypeRef(recruit.mixin.Candidate), recruit.string.Talent)
|
||||
@Index(IndexKind.Indexed)
|
||||
@ReadOnly()
|
||||
declare attachedTo: Ref<Candidate>
|
||||
|
||||
// We need to declare, to provide property with label
|
||||
@ -329,7 +330,7 @@ export function createModel (builder: Builder): void {
|
||||
},
|
||||
{
|
||||
id: assignedId,
|
||||
label: task.string.Assigned,
|
||||
label: task.string.AssignedToMe,
|
||||
icon: recruit.icon.AssignedToMe,
|
||||
component: task.component.AssignedTasks,
|
||||
position: 'event',
|
||||
@ -1086,6 +1087,81 @@ export function createModel (builder: Builder): void {
|
||||
component: recruit.component.ApplicantFilter
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationGroup,
|
||||
core.space.Model,
|
||||
{
|
||||
label: recruit.string.Application,
|
||||
icon: recruit.icon.Application,
|
||||
objectClass: recruit.class.Applicant
|
||||
},
|
||||
recruit.ids.ApplicationNotificationGroup
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationType,
|
||||
core.space.Model,
|
||||
{
|
||||
hidden: false,
|
||||
generated: false,
|
||||
label: task.string.AssignedToMe,
|
||||
group: recruit.ids.ApplicationNotificationGroup,
|
||||
field: 'assignee',
|
||||
txClasses: [core.class.TxCreateDoc, core.class.TxUpdateDoc],
|
||||
objectClass: recruit.class.Applicant,
|
||||
templates: {
|
||||
textTemplate: '{doc} was assigned to you by {sender}',
|
||||
htmlTemplate: '<p>{doc} was assigned to you by {sender}</p>',
|
||||
subjectTemplate: '{doc} was assigned to you'
|
||||
},
|
||||
providers: {
|
||||
[notification.providers.PlatformNotification]: true,
|
||||
[notification.providers.EmailNotification]: true
|
||||
}
|
||||
},
|
||||
recruit.ids.AssigneeNotification
|
||||
)
|
||||
|
||||
generateClassNotificationTypes(
|
||||
builder,
|
||||
recruit.class.Applicant,
|
||||
recruit.ids.ApplicationNotificationGroup,
|
||||
[],
|
||||
['comments', 'state', 'doneState']
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationGroup,
|
||||
core.space.Model,
|
||||
{
|
||||
label: recruit.string.Vacancy,
|
||||
icon: recruit.icon.Vacancy,
|
||||
objectClass: recruit.class.Vacancy
|
||||
},
|
||||
recruit.ids.VacancyNotificationGroup
|
||||
)
|
||||
|
||||
generateClassNotificationTypes(builder, recruit.class.Vacancy, recruit.ids.VacancyNotificationGroup, [], ['comments'])
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationGroup,
|
||||
core.space.Model,
|
||||
{
|
||||
label: recruit.string.Talent,
|
||||
icon: recruit.icon.CreateCandidate,
|
||||
objectClass: recruit.mixin.Candidate
|
||||
},
|
||||
recruit.ids.CandidateNotificationGroup
|
||||
)
|
||||
|
||||
generateClassNotificationTypes(
|
||||
builder,
|
||||
recruit.mixin.Candidate,
|
||||
recruit.ids.CandidateNotificationGroup,
|
||||
['vacancyMatch'],
|
||||
['comments']
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.FilterMode,
|
||||
core.space.Model,
|
||||
|
@ -14,6 +14,7 @@
|
||||
//
|
||||
|
||||
import type { Client, Doc, Ref } from '@hcengineering/core'
|
||||
import { NotificationGroup, NotificationType } from '@hcengineering/notification'
|
||||
import type { IntlString, Resource, Status } from '@hcengineering/platform'
|
||||
import { mergeIds } from '@hcengineering/platform'
|
||||
import { recruitId } from '@hcengineering/recruit'
|
||||
@ -67,6 +68,12 @@ export default mergeIds(recruitId, recruit, {
|
||||
validator: {
|
||||
ApplicantValidator: '' as Resource<<T extends Doc>(doc: T, client: Client) => Promise<Status>>
|
||||
},
|
||||
ids: {
|
||||
VacancyNotificationGroup: '' as Ref<NotificationGroup>,
|
||||
CandidateNotificationGroup: '' as Ref<NotificationGroup>,
|
||||
ApplicationNotificationGroup: '' as Ref<NotificationGroup>,
|
||||
AssigneeNotification: '' as Ref<NotificationType>
|
||||
},
|
||||
component: {
|
||||
CreateApplication: '' as AnyComponent,
|
||||
KanbanCard: '' as AnyComponent,
|
||||
|
@ -31,6 +31,7 @@
|
||||
"@hcengineering/server-chunter": "^0.6.0",
|
||||
"@hcengineering/server-core": "^0.6.1",
|
||||
"@hcengineering/chunter": "^0.6.5",
|
||||
"@hcengineering/notification": "^0.6.10",
|
||||
"@hcengineering/server-notification": "^0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import chunter from '@hcengineering/chunter'
|
||||
import serverNotification from '@hcengineering/server-notification'
|
||||
import serverCore, { ObjectDDParticipant } from '@hcengineering/server-core'
|
||||
import serverChunter from '@hcengineering/server-chunter'
|
||||
import notification from '@hcengineering/notification'
|
||||
export { serverChunterId } from '@hcengineering/server-chunter'
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
@ -44,7 +45,16 @@ export function createModel (builder: Builder): void {
|
||||
trigger: serverChunter.trigger.ChunterTrigger
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverChunter.trigger.DMTrigger
|
||||
builder.mixin(chunter.ids.DMNotification, notification.class.NotificationType, serverNotification.mixin.TypeMatch, {
|
||||
func: serverChunter.function.IsDirectMessagee
|
||||
})
|
||||
|
||||
builder.mixin(
|
||||
chunter.ids.ChannelNotification,
|
||||
notification.class.NotificationType,
|
||||
serverNotification.mixin.TypeMatch,
|
||||
{
|
||||
func: serverChunter.function.IsChannelMessagee
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -31,6 +31,8 @@
|
||||
"@hcengineering/server-lead": "^0.6.0",
|
||||
"@hcengineering/server-core": "^0.6.1",
|
||||
"@hcengineering/lead": "^0.6.0",
|
||||
"@hcengineering/model-lead": "^0.6.0",
|
||||
"@hcengineering/notification": "^0.6.10",
|
||||
"@hcengineering/server-notification": "^0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,8 @@
|
||||
import { Builder } from '@hcengineering/model'
|
||||
|
||||
import core from '@hcengineering/core'
|
||||
import lead from '@hcengineering/lead'
|
||||
import serverCore from '@hcengineering/server-core'
|
||||
import lead from '@hcengineering/model-lead'
|
||||
import notification from '@hcengineering/notification'
|
||||
import serverLead from '@hcengineering/server-lead'
|
||||
import serverNotification from '@hcengineering/server-notification'
|
||||
|
||||
@ -32,7 +32,12 @@ export function createModel (builder: Builder): void {
|
||||
presenter: serverLead.function.LeadTextPresenter
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverLead.trigger.OnLeadUpdate
|
||||
})
|
||||
builder.mixin(
|
||||
lead.ids.AssigneeNotification,
|
||||
notification.class.NotificationType,
|
||||
serverNotification.mixin.TypeMatch,
|
||||
{
|
||||
func: serverNotification.function.IsUserInFieldValue
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -29,6 +29,8 @@
|
||||
"@hcengineering/model": "^0.6.2",
|
||||
"@hcengineering/model-core": "^0.6.0",
|
||||
"@hcengineering/platform": "^0.6.8",
|
||||
"@hcengineering/notification": "^0.6.10",
|
||||
"@hcengineering/model-notification": "^0.6.0",
|
||||
"@hcengineering/server-notification": "^0.6.0",
|
||||
"@hcengineering/server-core": "^0.6.1"
|
||||
}
|
||||
|
@ -16,11 +16,18 @@
|
||||
|
||||
import { Builder, Mixin } from '@hcengineering/model'
|
||||
|
||||
import core from '@hcengineering/core'
|
||||
import core, { Account, Doc, Ref, Tx } from '@hcengineering/core'
|
||||
import { TClass } from '@hcengineering/model-core'
|
||||
import { TNotificationType } from '@hcengineering/model-notification'
|
||||
import notification, { NotificationType } from '@hcengineering/notification'
|
||||
import { Resource } from '@hcengineering/platform'
|
||||
import serverCore from '@hcengineering/server-core'
|
||||
import serverNotification, { HTMLPresenter, Presenter, TextPresenter } from '@hcengineering/server-notification'
|
||||
import serverCore, { TriggerControl } from '@hcengineering/server-core'
|
||||
import serverNotification, {
|
||||
HTMLPresenter,
|
||||
Presenter,
|
||||
TextPresenter,
|
||||
TypeMatch
|
||||
} from '@hcengineering/server-notification'
|
||||
|
||||
export { serverNotificationId } from '@hcengineering/server-notification'
|
||||
|
||||
@ -34,8 +41,15 @@ export class TTextPresenter extends TClass implements TextPresenter {
|
||||
presenter!: Resource<Presenter>
|
||||
}
|
||||
|
||||
@Mixin(serverNotification.mixin.TypeMatch, notification.class.NotificationType)
|
||||
export class TTypeMatch extends TNotificationType implements TypeMatch {
|
||||
func!: Resource<
|
||||
(tx: Tx, doc: Doc, user: Ref<Account>, type: NotificationType, control: TriggerControl) => Promise<boolean>
|
||||
>
|
||||
}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(THTMLPresenter, TTextPresenter)
|
||||
builder.createModel(THTMLPresenter, TTextPresenter, TTypeMatch)
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverNotification.trigger.OnBacklinkCreate
|
||||
@ -56,4 +70,12 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverNotification.trigger.OnAddCollborator
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverNotification.trigger.OnAttributeCreate
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverNotification.trigger.OnAttributeUpdate
|
||||
})
|
||||
}
|
||||
|
@ -30,7 +30,8 @@
|
||||
"@hcengineering/platform": "^0.6.8",
|
||||
"@hcengineering/server-recruit": "^0.6.0",
|
||||
"@hcengineering/server-core": "^0.6.1",
|
||||
"@hcengineering/recruit": "^0.6.11",
|
||||
"@hcengineering/model-recruit": "^0.6.0",
|
||||
"@hcengineering/notification": "^0.6.10",
|
||||
"@hcengineering/server-notification": "^0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,8 @@
|
||||
import { Builder } from '@hcengineering/model'
|
||||
|
||||
import core from '@hcengineering/core'
|
||||
import recruit from '@hcengineering/recruit'
|
||||
import recruit from '@hcengineering/model-recruit'
|
||||
import notification from '@hcengineering/notification'
|
||||
import serverCore from '@hcengineering/server-core'
|
||||
import serverNotification from '@hcengineering/server-notification'
|
||||
import serverRecruit from '@hcengineering/server-recruit'
|
||||
@ -43,4 +44,13 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverRecruit.trigger.OnRecruitUpdate
|
||||
})
|
||||
|
||||
builder.mixin(
|
||||
recruit.ids.AssigneeNotification,
|
||||
notification.class.NotificationType,
|
||||
serverNotification.mixin.TypeMatch,
|
||||
{
|
||||
func: serverNotification.function.IsUserInFieldValue
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -29,7 +29,9 @@
|
||||
"@hcengineering/model": "^0.6.2",
|
||||
"@hcengineering/tracker": "^0.6.6",
|
||||
"@hcengineering/server-core": "^0.6.1",
|
||||
"@hcengineering/notification": "^0.6.10",
|
||||
"@hcengineering/server-notification": "^0.6.0",
|
||||
"@hcengineering/model-tracker": "^0.6.0",
|
||||
"@hcengineering/server-tracker": "^0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,11 @@
|
||||
|
||||
import core from '@hcengineering/core'
|
||||
import { Builder } from '@hcengineering/model'
|
||||
import tracker from '@hcengineering/model-tracker'
|
||||
import notification from '@hcengineering/notification'
|
||||
import serverCore from '@hcengineering/server-core'
|
||||
import serverNotification from '@hcengineering/server-notification'
|
||||
import serverTracker from '@hcengineering/server-tracker'
|
||||
import tracker from '@hcengineering/tracker'
|
||||
|
||||
export { serverTrackerId } from '@hcengineering/server-tracker'
|
||||
|
||||
@ -38,4 +39,13 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverTracker.trigger.OnComponentRemove
|
||||
})
|
||||
|
||||
builder.mixin(
|
||||
tracker.ids.AssigneeNotification,
|
||||
notification.class.NotificationType,
|
||||
serverNotification.mixin.TypeMatch,
|
||||
{
|
||||
func: serverNotification.function.IsUserInFieldValue
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -322,9 +322,6 @@ export function createModel (builder: Builder): void {
|
||||
})
|
||||
|
||||
builder.mixin(task.class.Task, core.class.Class, notification.mixin.TrackedDoc, {})
|
||||
builder.mixin(task.class.Task, core.class.Class, notification.mixin.AnotherUserNotifications, {
|
||||
fields: ['assignee']
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
view.class.ActionCategory,
|
||||
@ -487,16 +484,16 @@ export function createModel (builder: Builder): void {
|
||||
task.action.ArchiveState
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationType,
|
||||
core.space.Model,
|
||||
{
|
||||
label: task.string.Assigned,
|
||||
hidden: false,
|
||||
textTemplate: '{doc} was assigned to you by {sender}',
|
||||
htmlTemplate: '<p>{doc} was assigned to you by {sender}</p>',
|
||||
subjectTemplate: '{doc} was assigned to you'
|
||||
},
|
||||
task.ids.AssigneedNotification
|
||||
)
|
||||
// builder.createDoc(
|
||||
// notification.class.NotificationType,
|
||||
// core.space.Model,
|
||||
// {
|
||||
// label: task.string.Assigned,
|
||||
// hidden: false,
|
||||
// textTemplate: '{doc} was assigned to you by {sender}',
|
||||
// htmlTemplate: '<p>{doc} was assigned to you by {sender}</p>',
|
||||
// subjectTemplate: '{doc} was assigned to you'
|
||||
// },
|
||||
// task.ids.AssigneedNotification
|
||||
// )
|
||||
}
|
||||
|
@ -37,6 +37,7 @@
|
||||
"@hcengineering/model-contact": "^0.6.1",
|
||||
"@hcengineering/model-attachment": "^0.6.0",
|
||||
"@hcengineering/notification": "^0.6.10",
|
||||
"@hcengineering/model-notification": "^0.6.0",
|
||||
"@hcengineering/tracker": "^0.6.6",
|
||||
"@hcengineering/tracker-resources": "^0.6.0",
|
||||
"@hcengineering/model-chunter": "^0.6.0",
|
||||
|
@ -80,6 +80,7 @@ import tracker from './plugin'
|
||||
|
||||
import presentation from '@hcengineering/model-presentation'
|
||||
import { defaultPriorities, issuePriorities } from '@hcengineering/tracker-resources/src/types'
|
||||
import { generateClassNotificationTypes } from '@hcengineering/model-notification'
|
||||
|
||||
export { trackerId } from '@hcengineering/tracker'
|
||||
export { trackerOperation } from './migration'
|
||||
@ -197,6 +198,7 @@ export class TIssue extends TAttachedDoc implements Issue {
|
||||
|
||||
@Prop(TypeNumber(), tracker.string.Number)
|
||||
@Index(IndexKind.FullText)
|
||||
@ReadOnly()
|
||||
number!: number
|
||||
|
||||
@Prop(TypeRef(contact.class.Employee), tracker.string.Assignee)
|
||||
@ -939,10 +941,6 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
builder.mixin(tracker.class.Issue, core.class.Class, notification.mixin.TrackedDoc, {})
|
||||
|
||||
builder.mixin(tracker.class.Issue, core.class.Class, notification.mixin.AnotherUserNotifications, {
|
||||
fields: ['assignee']
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.TypeIssuePriority, core.class.Class, view.mixin.AllValuesFunc, {
|
||||
func: tracker.function.GetAllPriority
|
||||
})
|
||||
@ -1799,6 +1797,49 @@ export function createModel (builder: Builder): void {
|
||||
tracker.viewlet.SprintList
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationGroup,
|
||||
core.space.Model,
|
||||
{
|
||||
label: tracker.string.Issues,
|
||||
icon: tracker.icon.Issues,
|
||||
objectClass: tracker.class.Issue
|
||||
},
|
||||
tracker.ids.TrackerNotificationGroup
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationType,
|
||||
core.space.Model,
|
||||
{
|
||||
hidden: false,
|
||||
generated: false,
|
||||
label: task.string.AssignedToMe,
|
||||
group: tracker.ids.TrackerNotificationGroup,
|
||||
field: 'assignee',
|
||||
txClasses: [core.class.TxCreateDoc, core.class.TxUpdateDoc],
|
||||
objectClass: tracker.class.Issue,
|
||||
templates: {
|
||||
textTemplate: '{doc} was assigned to you by {sender}',
|
||||
htmlTemplate: '<p>{doc} was assigned to you by {sender}</p>',
|
||||
subjectTemplate: '{doc} was assigned to you'
|
||||
},
|
||||
providers: {
|
||||
[notification.providers.PlatformNotification]: true,
|
||||
[notification.providers.EmailNotification]: true
|
||||
}
|
||||
},
|
||||
tracker.ids.AssigneeNotification
|
||||
)
|
||||
|
||||
generateClassNotificationTypes(
|
||||
builder,
|
||||
tracker.class.Issue,
|
||||
tracker.ids.TrackerNotificationGroup,
|
||||
[],
|
||||
['comments', 'status', 'priority']
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
|
@ -23,6 +23,7 @@ import type { AnyComponent } from '@hcengineering/ui/src/types'
|
||||
import { Action, ViewAction, Viewlet } from '@hcengineering/view'
|
||||
import { Application } from '@hcengineering/workbench'
|
||||
import { TxViewlet } from '@hcengineering/activity'
|
||||
import { NotificationGroup, NotificationType } from '@hcengineering/notification'
|
||||
|
||||
export default mergeIds(trackerId, tracker, {
|
||||
string: {
|
||||
@ -37,6 +38,7 @@ export default mergeIds(trackerId, tracker, {
|
||||
SearchIssue: '' as IntlString,
|
||||
Parent: '' as IntlString,
|
||||
CreatedOn: '' as IntlString,
|
||||
ChangeStatus: '' as IntlString,
|
||||
ConfigLabel: '' as IntlString,
|
||||
ConfigDescription: '' as IntlString
|
||||
},
|
||||
@ -61,7 +63,9 @@ export default mergeIds(trackerId, tracker, {
|
||||
SprintList: '' as Ref<Viewlet>
|
||||
},
|
||||
ids: {
|
||||
TxIssueCreated: '' as Ref<TxViewlet>
|
||||
TxIssueCreated: '' as Ref<TxViewlet>,
|
||||
TrackerNotificationGroup: '' as Ref<NotificationGroup>,
|
||||
AssigneeNotification: '' as Ref<NotificationType>
|
||||
},
|
||||
completion: {
|
||||
IssueQuery: '' as Resource<ObjectSearchFactory>,
|
||||
|
@ -25,6 +25,7 @@
|
||||
"Reference": "Reference",
|
||||
"Chat": "Chat",
|
||||
"In": "In",
|
||||
"MentionNotification": "Mentioned",
|
||||
"Replies": "Replies",
|
||||
"LastReply": "Last reply",
|
||||
"RepliesCount": "{replies, plural, =1 {# reply} other {# replies}}",
|
||||
@ -66,6 +67,8 @@
|
||||
"CopyLink": "Copy link",
|
||||
"FilterComments": "Comments",
|
||||
"FilterBacklinks": "Backlinks",
|
||||
"DM": "Direct message",
|
||||
"DMNotification": "Sent you a message",
|
||||
"ConfigLabel": "Chat",
|
||||
"ConfigDescription": "Extension to perform text communications"
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
"Content": "Содержимое",
|
||||
"Comment": "Комментарий",
|
||||
"Message": "Сообщение",
|
||||
"MentionNotification": "Упомянул",
|
||||
"Reference": "Ссылка",
|
||||
"Chat": "Чат",
|
||||
"In": "в",
|
||||
@ -66,6 +67,8 @@
|
||||
"CopyLink": "Копировать ссылку",
|
||||
"FilterComments": "Коментарии",
|
||||
"FilterBacklinks": "Упоминания",
|
||||
"DM": "Личное сообщение",
|
||||
"DMNotification": "Отправил сообщение",
|
||||
"ConfigLabel": "Чат",
|
||||
"ConfigDescription": "Расширение для текстовых переписок"
|
||||
}
|
||||
|
@ -40,7 +40,7 @@
|
||||
_class,
|
||||
space,
|
||||
space,
|
||||
chunter.class.ChunterSpace,
|
||||
chunterSpace?._class ?? chunter.class.ChunterSpace,
|
||||
'messages',
|
||||
{
|
||||
content: message,
|
||||
|
@ -28,6 +28,7 @@
|
||||
"dependencies": {
|
||||
"@hcengineering/platform": "^0.6.8",
|
||||
"@hcengineering/ui": "^0.6.6",
|
||||
"@hcengineering/notification": "^0.6.10",
|
||||
"@hcengineering/contact": "^0.6.14",
|
||||
"@hcengineering/core": "^0.6.23",
|
||||
"@hcengineering/preference": "^0.6.4"
|
||||
|
@ -13,8 +13,9 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Account, AttachedDoc, Class, Doc, Ref, RelatedDocument, Space, Timestamp } from '@hcengineering/core'
|
||||
import type { Employee } from '@hcengineering/contact'
|
||||
import type { Account, AttachedDoc, Class, Doc, Ref, RelatedDocument, Space, Timestamp } from '@hcengineering/core'
|
||||
import { NotificationType } from '@hcengineering/notification'
|
||||
import type { Asset, Plugin, Resource } from '@hcengineering/platform'
|
||||
import { IntlString, plugin } from '@hcengineering/platform'
|
||||
import type { Preference } from '@hcengineering/preference'
|
||||
@ -160,6 +161,12 @@ export default plugin(chunterId, {
|
||||
resolver: {
|
||||
Location: '' as Resource<(loc: Location) => Promise<ResolvedLocation | undefined>>
|
||||
},
|
||||
ids: {
|
||||
DMNotification: '' as Ref<NotificationType>,
|
||||
MentionNotification: '' as Ref<NotificationType>,
|
||||
ThreadNotification: '' as Ref<NotificationType>,
|
||||
ChannelNotification: '' as Ref<NotificationType>
|
||||
},
|
||||
app: {
|
||||
Chunter: '' as Ref<Doc>
|
||||
},
|
||||
|
@ -51,6 +51,9 @@
|
||||
"Export": "Export",
|
||||
"Separator": "Separator",
|
||||
"ChooseSeparator": "Choose separator",
|
||||
"RequestCreated": "Request created",
|
||||
"RequestUpdated": "Request updated",
|
||||
"RequestRemoved": "Request removed",
|
||||
"ConfigLabel": "Human resources",
|
||||
"ConfigDescription": "Extension to manage organization structure and employee work calendar"
|
||||
}
|
||||
|
@ -51,6 +51,9 @@
|
||||
"Export": "Экспортировать",
|
||||
"Separator": "Разделитель",
|
||||
"ChooseSeparator": "Выберите разделитель",
|
||||
"RequestCreated": "Запрос создан",
|
||||
"RequestUpdated": "Запрос изменен",
|
||||
"RequestRemoved": "Запрос удален",
|
||||
"ConfigLabel": "Производственный календарь",
|
||||
"ConfigDescription": "Расширение реализующее производственный календарь"
|
||||
}
|
||||
|
@ -4,10 +4,7 @@
|
||||
"Notification": "Notification",
|
||||
"Notifications": "Notifications",
|
||||
"NoNotifications": "No notifications",
|
||||
"MentionNotification": "Mentioned",
|
||||
"DM": "Direct message",
|
||||
"DMNotification": "Sent you a message",
|
||||
"EmailNotification": "by email",
|
||||
"EmailNotification": "Email",
|
||||
"PlatformNotification": "in platform",
|
||||
"Track": "Track",
|
||||
"DontTrack": "Don't track",
|
||||
@ -19,6 +16,8 @@
|
||||
"MarkAsUnread": "Mark as unread",
|
||||
"Archive": "Archive",
|
||||
"Inbox": "Inbox",
|
||||
"Collaborators": "Collaborators"
|
||||
"Collaborators": "Collaborators",
|
||||
"Change": "Change",
|
||||
"AddedRemoved": "Added/removed"
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,7 @@
|
||||
"Notification": "Увдомление",
|
||||
"Notifications": "Уведомления",
|
||||
"NoNotifications": "Нет уведомлений",
|
||||
"MentionNotification": "Упомянул",
|
||||
"DM": "Личное сообщение",
|
||||
"DMNotification": "Отправил сообщение",
|
||||
"EmailNotification": "по email",
|
||||
"EmailNotification": "Email",
|
||||
"PlatformNotification": "в системе",
|
||||
"Track": "Отслеживать",
|
||||
"DontTrack": "Не отслеживать",
|
||||
@ -19,6 +16,8 @@
|
||||
"MarkAsUnread": "Отметить непрочитанным",
|
||||
"Archive": "Архивировать",
|
||||
"Inbox": "Входящие",
|
||||
"Collaborators": "Участники"
|
||||
"Collaborators": "Участники",
|
||||
"Change": "Изменено",
|
||||
"AddedRemoved": "Добавлено/удалено"
|
||||
}
|
||||
}
|
||||
|
@ -14,13 +14,13 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { EmployeeAccount } from '@hcengineering/contact'
|
||||
import { Doc, getCurrentAccount, Ref, Space } from '@hcengineering/core'
|
||||
import { Doc, getCurrentAccount, Ref } from '@hcengineering/core'
|
||||
import {
|
||||
Notification as PlatformNotification,
|
||||
NotificationProvider,
|
||||
NotificationSetting,
|
||||
NotificationStatus,
|
||||
NotificationType
|
||||
NotificationType,
|
||||
Notification as PlatformNotification
|
||||
} from '@hcengineering/notification'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { getCurrentLocation, showPanel } from '@hcengineering/ui'
|
||||
@ -31,8 +31,6 @@
|
||||
const query = createQuery()
|
||||
const settingQuery = createQuery()
|
||||
const providersQuery = createQuery()
|
||||
const accountId = getCurrentAccount()._id
|
||||
const space = accountId as string as Ref<Space>
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
const lastViews = notificationClient.getLastViews()
|
||||
const lastViewId: Ref<Doc> = ((getCurrentAccount() as EmployeeAccount).employee + 'notification') as Ref<Doc>
|
||||
@ -46,27 +44,21 @@
|
||||
$: enabled &&
|
||||
providersQuery.query(
|
||||
notification.class.NotificationProvider,
|
||||
{ _id: notification.ids.BrowserNotification },
|
||||
{ _id: notification.providers.BrowserNotification },
|
||||
(res) => {
|
||||
provider = res[0]
|
||||
}
|
||||
)
|
||||
|
||||
$: enabled &&
|
||||
settingQuery.query(
|
||||
notification.class.NotificationSetting,
|
||||
{
|
||||
space
|
||||
},
|
||||
(res) => {
|
||||
settings = new Map(
|
||||
res.map((setting) => {
|
||||
return [setting.type, setting]
|
||||
})
|
||||
)
|
||||
settingsReceived = true
|
||||
}
|
||||
)
|
||||
settingQuery.query(notification.class.NotificationSetting, {}, (res) => {
|
||||
settings = new Map(
|
||||
res.map((setting) => {
|
||||
return [setting.type, setting]
|
||||
})
|
||||
)
|
||||
settingsReceived = true
|
||||
})
|
||||
|
||||
const alreadyShown = new Set<Ref<PlatformNotification>>()
|
||||
|
||||
@ -99,7 +91,7 @@
|
||||
const text = notification.text.replace(/<[^>]*>/g, '').trim()
|
||||
if (text === '') return
|
||||
const setting = settings.get(notification.type)
|
||||
const enabled = setting?.enabled ?? provider?.default
|
||||
const enabled = setting?.enabled
|
||||
if (!enabled) return
|
||||
if ((setting?.modifiedOn ?? notification.modifiedOn) < 0) return
|
||||
|
||||
|
@ -0,0 +1,60 @@
|
||||
<!--
|
||||
// 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 type { Asset, IntlString } from '@hcengineering/platform'
|
||||
import { Icon, Label } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let icon: Asset | undefined = undefined
|
||||
export let label: IntlString | undefined = undefined
|
||||
export let selected: boolean = false
|
||||
export let expandable = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="antiNav-element"
|
||||
class:selected
|
||||
class:expandable
|
||||
on:click|stopPropagation={() => {
|
||||
dispatch('click')
|
||||
}}
|
||||
>
|
||||
<div class="an-element__icon">
|
||||
{#if icon}
|
||||
<Icon {icon} size={'small'} />
|
||||
{/if}
|
||||
</div>
|
||||
<span class="an-element__label title">
|
||||
{#if label}<Label {label} />{/if}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.expandable {
|
||||
position: relative;
|
||||
&::after {
|
||||
content: '▶';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0.5rem;
|
||||
font-size: 0.375rem;
|
||||
color: var(--dark-color);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,138 @@
|
||||
<!--
|
||||
// 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 { IdMap, Ref, toIdMap } from '@hcengineering/core'
|
||||
import type {
|
||||
NotificationGroup,
|
||||
NotificationProvider,
|
||||
NotificationSetting,
|
||||
NotificationType
|
||||
} from '@hcengineering/notification'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Grid, Label, ToggleWithLabel } from '@hcengineering/ui'
|
||||
import notification from '../plugin'
|
||||
|
||||
export let group: Ref<NotificationGroup>
|
||||
export let settings: Map<Ref<NotificationType>, NotificationSetting[]>
|
||||
|
||||
const client = getClient()
|
||||
let types: NotificationType[] = []
|
||||
let typesMap: IdMap<NotificationType> = new Map()
|
||||
let providers: NotificationProvider[] = []
|
||||
let providersMap: IdMap<NotificationProvider> = new Map()
|
||||
|
||||
$: load(group)
|
||||
|
||||
async function load (group: Ref<NotificationGroup>) {
|
||||
types = await client.findAll(notification.class.NotificationType, { group })
|
||||
typesMap = toIdMap(types)
|
||||
providers = await client.findAll(notification.class.NotificationProvider, {})
|
||||
providersMap = toIdMap(providers)
|
||||
}
|
||||
|
||||
$: column = providers.length + 1
|
||||
|
||||
function getStatus (
|
||||
settings: Map<Ref<NotificationType>, NotificationSetting[]>,
|
||||
type: Ref<NotificationType>,
|
||||
provider: Ref<NotificationProvider>
|
||||
): boolean {
|
||||
const setting = getSetting(settings, type, provider)
|
||||
if (setting !== undefined) return setting.enabled
|
||||
const prov = providersMap.get(provider)
|
||||
if (prov === undefined) return false
|
||||
const typeValue = typesMap.get(type)
|
||||
if (typeValue === undefined) return false
|
||||
return typeValue?.providers?.[provider] ?? false
|
||||
}
|
||||
|
||||
function createHandler (type: Ref<NotificationType>, provider: Ref<NotificationProvider>): (evt: any) => void {
|
||||
return (evt: any) => change(type, provider, evt.detail)
|
||||
}
|
||||
|
||||
async function change (
|
||||
type: Ref<NotificationType>,
|
||||
provider: Ref<NotificationProvider>,
|
||||
value: boolean
|
||||
): Promise<void> {
|
||||
const current = getSetting(settings, type, provider)
|
||||
if (current === undefined) {
|
||||
await client.createDoc(notification.class.NotificationSetting, notification.space.Notifications, {
|
||||
attachedTo: provider,
|
||||
type,
|
||||
enabled: value
|
||||
})
|
||||
} else {
|
||||
await client.update(current, {
|
||||
enabled: value
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function getSetting (
|
||||
map: Map<Ref<NotificationType>, NotificationSetting[]>,
|
||||
type: Ref<NotificationType>,
|
||||
provider: Ref<NotificationProvider>
|
||||
): NotificationSetting | undefined {
|
||||
const typeMap = map.get(type)
|
||||
if (typeMap === undefined) return
|
||||
return typeMap.find((p) => p.attachedTo === provider)
|
||||
}
|
||||
|
||||
function getLabel (type: NotificationType): IntlString {
|
||||
if (type.attachedToClass !== undefined) {
|
||||
return notification.string.AddedRemoved
|
||||
}
|
||||
return notification.string.Change
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-grow container">
|
||||
<Grid {column} columnGap={5} rowGap={1.5}>
|
||||
{#each types as type}
|
||||
<div class="flex">
|
||||
{#if type.generated}
|
||||
<Label label={getLabel(type)} />:
|
||||
{/if}
|
||||
<Label label={type.label} />
|
||||
</div>
|
||||
{#each providers as provider (provider._id)}
|
||||
{#if type.providers[provider._id] !== undefined}
|
||||
<div class="toggle">
|
||||
<ToggleWithLabel
|
||||
label={provider.label}
|
||||
on={getStatus(settings, type._id, provider._id)}
|
||||
on:change={createHandler(type._id, provider._id)}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<div />
|
||||
{/if}
|
||||
{/each}
|
||||
{/each}
|
||||
</Grid>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
padding: 3rem;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.toggle {
|
||||
width: fit-content;
|
||||
}
|
||||
</style>
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// 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
|
||||
@ -13,166 +13,61 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { getCurrentAccount, Ref, Space, TxProcessor } from '@hcengineering/core'
|
||||
import type { NotificationProvider, NotificationSetting, NotificationType } from '@hcengineering/notification'
|
||||
import presentation, { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Button, Grid, Icon, Label, ToggleWithLabel } from '@hcengineering/ui'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import type { NotificationGroup, NotificationSetting, NotificationType } from '@hcengineering/notification'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
import notification from '../plugin'
|
||||
import GroupElement from './GroupElement.svelte'
|
||||
import NotificationGroupSetting from './NotificationGroupSetting.svelte'
|
||||
|
||||
const accountId = getCurrentAccount()._id
|
||||
const typeQuery = createQuery()
|
||||
const providersQuery = createQuery()
|
||||
const settingsQuery = createQuery()
|
||||
const client = getClient()
|
||||
const space = accountId as string as Ref<Space>
|
||||
|
||||
let disabled = true
|
||||
|
||||
let types: NotificationType[] = []
|
||||
let providers: NotificationProvider[] = []
|
||||
let settings: Map<Ref<NotificationType>, Map<Ref<NotificationProvider>, NotificationSetting>> = new Map<
|
||||
Ref<NotificationType>,
|
||||
Map<Ref<NotificationProvider>, NotificationSetting>
|
||||
>()
|
||||
let oldSettings: Map<Ref<NotificationType>, Map<Ref<NotificationProvider>, NotificationSetting>> = new Map<
|
||||
Ref<NotificationType>,
|
||||
Map<Ref<NotificationProvider>, NotificationSetting>
|
||||
>()
|
||||
|
||||
typeQuery.query(notification.class.NotificationType, { hidden: false }, (res) => (types = res))
|
||||
providersQuery.query(notification.class.NotificationProvider, {}, (res) => (providers = res))
|
||||
settingsQuery.query(notification.class.NotificationSetting, { space }, (res) => {
|
||||
settings = convertToMap(res)
|
||||
oldSettings = convertToMap(client.getHierarchy().clone(res))
|
||||
let groups: NotificationGroup[] = []
|
||||
client.findAll(notification.class.NotificationGroup, {}).then((res) => {
|
||||
groups = res
|
||||
group = res[0]._id
|
||||
})
|
||||
|
||||
function convertToMap (
|
||||
settings: NotificationSetting[]
|
||||
): Map<Ref<NotificationType>, Map<Ref<NotificationProvider>, NotificationSetting>> {
|
||||
const result = new Map<Ref<NotificationType>, Map<Ref<NotificationProvider>, NotificationSetting>>()
|
||||
for (const setting of settings) {
|
||||
setSetting(result, setting)
|
||||
let settings: Map<Ref<NotificationType>, NotificationSetting[]> = new Map()
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
query.query(notification.class.NotificationSetting, {}, (res) => {
|
||||
console.log('settings updated')
|
||||
settings = new Map()
|
||||
for (const value of res) {
|
||||
const arr = settings.get(value.type) ?? []
|
||||
arr.push(value)
|
||||
settings.set(value.type, arr)
|
||||
}
|
||||
return result
|
||||
}
|
||||
settings = settings
|
||||
})
|
||||
|
||||
function change (type: Ref<NotificationType>, provider: Ref<NotificationProvider>, value: boolean): void {
|
||||
const current = getSetting(settings, type, provider)
|
||||
if (current === undefined) {
|
||||
const tx = client.txFactory.createTxCreateDoc(notification.class.NotificationSetting, space, {
|
||||
provider,
|
||||
type,
|
||||
enabled: value
|
||||
})
|
||||
const setting = TxProcessor.createDoc2Doc(tx)
|
||||
setSetting(settings, setting)
|
||||
} else {
|
||||
current.enabled = value
|
||||
}
|
||||
disabled = false
|
||||
}
|
||||
|
||||
function getSetting (
|
||||
map: Map<Ref<NotificationType>, Map<Ref<NotificationProvider>, NotificationSetting>>,
|
||||
type: Ref<NotificationType>,
|
||||
provider: Ref<NotificationProvider>
|
||||
): NotificationSetting | undefined {
|
||||
const typeMap = map.get(type)
|
||||
if (typeMap === undefined) return
|
||||
return typeMap.get(provider)
|
||||
}
|
||||
|
||||
function setSetting (
|
||||
result: Map<Ref<NotificationType>, Map<Ref<NotificationProvider>, NotificationSetting>>,
|
||||
setting: NotificationSetting
|
||||
): void {
|
||||
let typeMap = result.get(setting.type)
|
||||
if (typeMap === undefined) {
|
||||
typeMap = new Map<Ref<NotificationProvider>, NotificationSetting>()
|
||||
result.set(setting.type, typeMap)
|
||||
}
|
||||
typeMap.set(setting.provider, setting)
|
||||
}
|
||||
|
||||
async function save (): Promise<void> {
|
||||
disabled = true
|
||||
const promises: Promise<any>[] = []
|
||||
for (const type of settings.values()) {
|
||||
for (const setting of type.values()) {
|
||||
const old = getSetting(oldSettings, setting.type, setting.provider)
|
||||
if (old === undefined) {
|
||||
promises.push(client.createDoc(setting._class, setting.space, setting))
|
||||
} else if (old.enabled !== setting.enabled) {
|
||||
promises.push(
|
||||
client.updateDoc(old._class, old.space, old._id, {
|
||||
enabled: setting.enabled
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
await Promise.all(promises)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
|
||||
$: column = providers.length + 1
|
||||
|
||||
function getStatus (
|
||||
map: Map<Ref<NotificationType>, Map<Ref<NotificationProvider>, NotificationSetting>>,
|
||||
type: Ref<NotificationType>,
|
||||
provider: Ref<NotificationProvider>
|
||||
): boolean {
|
||||
const setting = getSetting(map, type, provider)
|
||||
if (setting !== undefined) return setting.enabled
|
||||
const prov = providers.find((p) => p._id === provider)
|
||||
if (prov === undefined) return false
|
||||
return prov.default
|
||||
}
|
||||
const createHandler = (type: Ref<NotificationType>, provider: Ref<NotificationProvider>): ((evt: any) => void) => {
|
||||
return (evt: any) => change(type, provider, evt.detail)
|
||||
}
|
||||
let group: Ref<NotificationGroup> | undefined = undefined
|
||||
</script>
|
||||
|
||||
<div class="antiComponent">
|
||||
<div class="ac-header short divide">
|
||||
<div class="ac-header__icon"><Icon icon={notification.icon.Notifications} size={'medium'} /></div>
|
||||
<div class="ac-header__title"><Label label={notification.string.Notifications} /></div>
|
||||
</div>
|
||||
<div class="flex-row-stretch flex-grow container">
|
||||
<div class="flex-col flex-grow">
|
||||
<div class="flex-grow">
|
||||
<Grid {column} columnGap={5} rowGap={1.5}>
|
||||
{#each types as type (type._id)}
|
||||
<Label label={type.label} />
|
||||
{#each providers as provider (provider._id)}
|
||||
<ToggleWithLabel
|
||||
label={provider.label}
|
||||
on={getStatus(settings, type._id, provider._id)}
|
||||
on:change={createHandler(type._id, provider._id)}
|
||||
/>
|
||||
{/each}
|
||||
{/each}
|
||||
</Grid>
|
||||
</div>
|
||||
<div class="flex-row-reverse">
|
||||
<Button
|
||||
label={presentation.string.Save}
|
||||
{disabled}
|
||||
kind={'primary'}
|
||||
on:click={() => {
|
||||
save()
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex h-full">
|
||||
<div class="antiPanel-navigator filled indent">
|
||||
<div class="antiNav-header">
|
||||
<span class="fs-title overflow-label">
|
||||
<Label label={notification.string.Notifications} />
|
||||
</span>
|
||||
</div>
|
||||
{#each groups as gr}
|
||||
<GroupElement
|
||||
icon={gr.icon}
|
||||
label={gr.label}
|
||||
selected={gr._id === group}
|
||||
on:click={() => {
|
||||
group = gr._id
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="antiPanel-component border-left filled">
|
||||
{#if group}
|
||||
<NotificationGroupSetting {group} {settings} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
padding: 3rem;
|
||||
}
|
||||
</style>
|
||||
|
@ -26,6 +26,8 @@ export default mergeIds(notificationId, notification, {
|
||||
Remove: '' as IntlString,
|
||||
RemoveAll: '' as IntlString,
|
||||
MarkAsRead: '' as IntlString,
|
||||
MarkAllAsRead: '' as IntlString
|
||||
MarkAllAsRead: '' as IntlString,
|
||||
Change: '' as IntlString,
|
||||
AddedRemoved: '' as IntlString
|
||||
}
|
||||
})
|
||||
|
@ -34,6 +34,7 @@
|
||||
"dependencies": {
|
||||
"@hcengineering/platform": "^0.6.8",
|
||||
"@hcengineering/core": "^0.6.23",
|
||||
"@hcengineering/preference": "^0.6.4",
|
||||
"@hcengineering/setting": "^0.6.5",
|
||||
"@hcengineering/ui": "^0.6.6"
|
||||
},
|
||||
|
@ -13,12 +13,25 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Account, AttachedDoc, Class, Doc, Mixin, Ref, Space, Timestamp, TxCUD } from '@hcengineering/core'
|
||||
import {
|
||||
Account,
|
||||
AnyAttribute,
|
||||
AttachedDoc,
|
||||
Class,
|
||||
Doc,
|
||||
Mixin,
|
||||
Ref,
|
||||
Space,
|
||||
Timestamp,
|
||||
Tx,
|
||||
TxCUD
|
||||
} from '@hcengineering/core'
|
||||
import type { Asset, IntlString, Plugin, Resource } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import { IntegrationType } from '@hcengineering/setting'
|
||||
import { AnyComponent } from '@hcengineering/ui'
|
||||
import { Writable } from './types'
|
||||
import { Preference } from '@hcengineering/preference'
|
||||
export * from './types'
|
||||
|
||||
/**
|
||||
@ -64,9 +77,17 @@ export enum NotificationStatus {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface NotificationType extends Doc {
|
||||
hidden: boolean
|
||||
export interface NotificationGroup extends Doc {
|
||||
label: IntlString
|
||||
icon: Asset
|
||||
// using for autogenerated settings
|
||||
objectClass?: Ref<Class<Doc>>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface NotificationTemplate {
|
||||
textTemplate: string
|
||||
htmlTemplate: string
|
||||
subjectTemplate: string
|
||||
@ -75,17 +96,40 @@ export interface NotificationType extends Doc {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface NotificationProvider extends Doc {
|
||||
export interface NotificationType extends Doc {
|
||||
// For show/hide with attributes
|
||||
attribute?: Ref<AnyAttribute>
|
||||
// Is autogenerated
|
||||
generated: boolean
|
||||
// allowed to to change setting (probably we should show it, but disable toggle??)
|
||||
hidden: boolean
|
||||
label: IntlString
|
||||
default: boolean
|
||||
group: Ref<NotificationGroup>
|
||||
txClasses: Ref<Class<Tx>>[]
|
||||
objectClass: Ref<Class<Doc>>
|
||||
// check parent doc class
|
||||
attachedToClass?: Ref<Class<Doc>>
|
||||
// use for update/mixin txes
|
||||
field?: string
|
||||
// allowed providers and default value for it
|
||||
providers: Record<Ref<NotificationProvider>, boolean>
|
||||
// templates for email (and browser/push?)
|
||||
templates?: NotificationTemplate
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface NotificationSetting extends Doc {
|
||||
export interface NotificationProvider extends Doc {
|
||||
label: IntlString
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface NotificationSetting extends Preference {
|
||||
attachedTo: Ref<NotificationProvider>
|
||||
type: Ref<NotificationType>
|
||||
provider: Ref<NotificationProvider>
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
@ -181,15 +225,16 @@ const notification = plugin(notificationId, {
|
||||
NotificationType: '' as Ref<Class<NotificationType>>,
|
||||
NotificationProvider: '' as Ref<Class<NotificationProvider>>,
|
||||
NotificationSetting: '' as Ref<Class<NotificationSetting>>,
|
||||
DocUpdates: '' as Ref<Class<DocUpdates>>
|
||||
DocUpdates: '' as Ref<Class<DocUpdates>>,
|
||||
NotificationGroup: '' as Ref<Class<NotificationGroup>>
|
||||
},
|
||||
ids: {
|
||||
MentionNotification: '' as Ref<NotificationType>,
|
||||
DMNotification: '' as Ref<NotificationType>,
|
||||
NotificationSettings: '' as Ref<Doc>
|
||||
},
|
||||
providers: {
|
||||
PlatformNotification: '' as Ref<NotificationProvider>,
|
||||
BrowserNotification: '' as Ref<NotificationProvider>,
|
||||
EmailNotification: '' as Ref<NotificationProvider>,
|
||||
NotificationSettings: '' as Ref<Doc>
|
||||
EmailNotification: '' as Ref<NotificationProvider>
|
||||
},
|
||||
integrationType: {
|
||||
MobileApp: '' as Ref<IntegrationType>
|
||||
|
@ -71,7 +71,7 @@
|
||||
"CantStatusDelete": "Can't delete status",
|
||||
"CantStatusDeleteError": "There are objects in the given state. Move or delete them first.",
|
||||
"Tasks": "Tasks",
|
||||
"Assigned": "Assigned to me",
|
||||
"AssignedToMe": "Assigned to me",
|
||||
"TodoItems": "Todos",
|
||||
"Dashboard": "Dashboard",
|
||||
"AllTime": "All time",
|
||||
|
@ -71,7 +71,7 @@
|
||||
"CantStatusDelete": "Невозможно удалить статус",
|
||||
"CantStatusDeleteError": "Есть объекты с данным статусом. Сначала переместите или удалите их. ",
|
||||
"Tasks": "Задачи",
|
||||
"Assigned": "Назначения",
|
||||
"AssignedToMe": "Назначено на меня",
|
||||
"TodoItems": "Todos",
|
||||
"Dashboard": "Дашборд",
|
||||
"AllTime": "Все время",
|
||||
|
@ -68,7 +68,6 @@ export default mergeIds(taskId, task, {
|
||||
RelatedIssues: '' as IntlString,
|
||||
|
||||
Tasks: '' as IntlString,
|
||||
Assigned: '' as IntlString,
|
||||
Task: '' as IntlString,
|
||||
AllTime: '' as IntlString
|
||||
},
|
||||
|
@ -229,6 +229,7 @@ const task = plugin(taskId, {
|
||||
Projects: '' as IntlString,
|
||||
ManageProjectStatues: '' as IntlString,
|
||||
TodoItems: '' as IntlString,
|
||||
AssignedToMe: '' as IntlString,
|
||||
Dashboard: '' as IntlString
|
||||
},
|
||||
class: {
|
||||
|
@ -288,6 +288,7 @@
|
||||
"Saved": "Saved...",
|
||||
"CreatedIssue": "Created issue",
|
||||
"CreatedSubIssue": "Created sub-issue",
|
||||
"ChangeStatus": "Change status",
|
||||
"ConfigLabel": "Tracker",
|
||||
"ConfigDescription": "Extension to manage work items and do all jobs done."
|
||||
},
|
||||
|
@ -288,6 +288,7 @@
|
||||
"Saved": "Сохранено...",
|
||||
"CreatedIssue": "Создал(а) задачу",
|
||||
"CreatedSubIssue": "Создал(а) подзадачу",
|
||||
"ChangeStatus": "Изменение статуса",
|
||||
"ConfigLabel": "Трекер",
|
||||
"ConfigDescription": "Расширение по управлению задачами, чтобы все было в срок."
|
||||
},
|
||||
|
@ -150,7 +150,8 @@
|
||||
notificationQuery.query(
|
||||
notification.class.DocUpdates,
|
||||
{
|
||||
user: account._id
|
||||
user: account._id,
|
||||
hidden: false
|
||||
},
|
||||
(res) => {
|
||||
hasNotification = res.some((p) => p.txes.length > 0)
|
||||
|
@ -13,16 +13,10 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import chunter, {
|
||||
chunterId,
|
||||
ChunterMessage,
|
||||
ChunterSpace,
|
||||
Comment,
|
||||
Message,
|
||||
ThreadMessage
|
||||
} from '@hcengineering/chunter'
|
||||
import chunter, { chunterId, ChunterSpace, Comment, Message, ThreadMessage } from '@hcengineering/chunter'
|
||||
import { EmployeeAccount } from '@hcengineering/contact'
|
||||
import core, {
|
||||
Account,
|
||||
Class,
|
||||
concatLink,
|
||||
Doc,
|
||||
@ -39,11 +33,10 @@ import core, {
|
||||
TxRemoveDoc,
|
||||
TxUpdateDoc
|
||||
} from '@hcengineering/core'
|
||||
import notification, { Collaborators } from '@hcengineering/notification'
|
||||
import notification, { Collaborators, NotificationType } from '@hcengineering/notification'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
import serverCore, { TriggerControl } from '@hcengineering/server-core'
|
||||
import { getEmployeeAccountById } from '@hcengineering/server-notification'
|
||||
import { createNotificationTxes, getDocCollaborators, getMixinTx } from '@hcengineering/server-notification-resources'
|
||||
import { getDocCollaborators, getMixinTx } from '@hcengineering/server-notification-resources'
|
||||
import { workbenchId } from '@hcengineering/workbench'
|
||||
|
||||
/**
|
||||
@ -288,52 +281,41 @@ export async function ChunterTrigger (tx: Tx, control: TriggerControl): Promise<
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function DMTrigger (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
if (tx._class !== core.class.TxCollectionCUD) return []
|
||||
const hierarchy = control.hierarchy
|
||||
const ctx = tx as TxCollectionCUD<ChunterSpace, Message>
|
||||
if (!hierarchy.isDerived(ctx.tx.objectClass, chunter.class.Message)) {
|
||||
return []
|
||||
}
|
||||
const actualTx = TxProcessor.extractTx(tx)
|
||||
if (actualTx._class !== core.class.TxCreateDoc) {
|
||||
return []
|
||||
}
|
||||
const doc = TxProcessor.createDoc2Doc(actualTx as TxCreateDoc<ChunterMessage>)
|
||||
const dms = await control.findAll(chunter.class.DirectMessage, { _id: doc.space })
|
||||
if (dms.length === 0) {
|
||||
return []
|
||||
}
|
||||
const sender = await getEmployeeAccountById(ctx.tx.modifiedBy, control)
|
||||
if (sender === undefined) return []
|
||||
const res: Tx[] = []
|
||||
for (const member of dms[0].members) {
|
||||
const receiver = await getEmployeeAccountById(member, control)
|
||||
if (receiver === undefined) continue
|
||||
if (receiver._id === sender._id) continue
|
||||
const createNotificationTx = await createNotificationTxes(
|
||||
control,
|
||||
ctx,
|
||||
notification.ids.DMNotification,
|
||||
doc,
|
||||
sender,
|
||||
receiver,
|
||||
doc.content
|
||||
)
|
||||
res.push(...createNotificationTx)
|
||||
}
|
||||
return res
|
||||
export async function IsDirectMessagee (
|
||||
tx: Tx,
|
||||
doc: Doc,
|
||||
user: Ref<Account>,
|
||||
type: NotificationType,
|
||||
control: TriggerControl
|
||||
): Promise<boolean> {
|
||||
const space = (await control.findAll(chunter.class.DirectMessage, { _id: doc.space }))[0]
|
||||
return space !== undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function IsChannelMessagee (
|
||||
tx: Tx,
|
||||
doc: Doc,
|
||||
user: Ref<Account>,
|
||||
type: NotificationType,
|
||||
control: TriggerControl
|
||||
): Promise<boolean> {
|
||||
const space = (await control.findAll(chunter.class.Channel, { _id: doc.space }))[0]
|
||||
return space !== undefined
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
export default async () => ({
|
||||
trigger: {
|
||||
ChunterTrigger,
|
||||
DMTrigger
|
||||
ChunterTrigger
|
||||
},
|
||||
function: {
|
||||
CommentRemove,
|
||||
ChannelHTMLPresenter: channelHTMLPresenter,
|
||||
ChannelTextPresenter: channelTextPresenter
|
||||
ChannelTextPresenter: channelTextPresenter,
|
||||
IsDirectMessagee,
|
||||
IsChannelMessagee
|
||||
}
|
||||
})
|
||||
|
@ -17,7 +17,7 @@ import { Class, Doc, DocumentQuery, FindOptions, FindResult, Hierarchy, Ref } fr
|
||||
import type { Plugin, Resource } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import { TriggerFunc } from '@hcengineering/server-core'
|
||||
import { Presenter } from '@hcengineering/server-notification'
|
||||
import { Presenter, TypeMatchFunc } from '@hcengineering/server-notification'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -29,8 +29,7 @@ export const serverChunterId = 'server-chunter' as Plugin
|
||||
*/
|
||||
export default plugin(serverChunterId, {
|
||||
trigger: {
|
||||
ChunterTrigger: '' as Resource<TriggerFunc>,
|
||||
DMTrigger: '' as Resource<TriggerFunc>
|
||||
ChunterTrigger: '' as Resource<TriggerFunc>
|
||||
},
|
||||
function: {
|
||||
CommentRemove: '' as Resource<
|
||||
@ -45,6 +44,8 @@ export default plugin(serverChunterId, {
|
||||
) => Promise<Doc[]>
|
||||
>,
|
||||
ChannelHTMLPresenter: '' as Resource<Presenter>,
|
||||
ChannelTextPresenter: '' as Resource<Presenter>
|
||||
ChannelTextPresenter: '' as Resource<Presenter>,
|
||||
IsDirectMessagee: '' as TypeMatchFunc,
|
||||
IsChannelMessagee: '' as TypeMatchFunc
|
||||
}
|
||||
})
|
||||
|
@ -39,7 +39,7 @@ import notification, { NotificationType } from '@hcengineering/notification'
|
||||
import { translate } from '@hcengineering/platform'
|
||||
import { TriggerControl } from '@hcengineering/server-core'
|
||||
import { getEmployee, getEmployeeAccountById } from '@hcengineering/server-notification'
|
||||
import { getContent } from '@hcengineering/server-notification-resources'
|
||||
import { getContent, isAllowed } from '@hcengineering/server-notification-resources'
|
||||
|
||||
async function getOldDepartment (
|
||||
currentTx: TxMixin<Employee, Staff> | TxUpdateDoc<Employee>,
|
||||
@ -209,6 +209,17 @@ async function getEmailNotification (
|
||||
}
|
||||
}
|
||||
|
||||
// should respect employee settings
|
||||
const accounts = await control.modelDb.findAll(contact.class.EmployeeAccount, {
|
||||
employee: { $in: Array.from(contacts.values()) as Ref<Employee>[] }
|
||||
})
|
||||
for (const account of accounts) {
|
||||
const allowed = await isAllowed(control, account._id, type, notification.providers.EmailNotification)
|
||||
if (!allowed) {
|
||||
contacts.delete(account.employee)
|
||||
}
|
||||
}
|
||||
|
||||
const channels = await control.findAll(contact.class.Channel, {
|
||||
provider: contact.channelProvider.Email,
|
||||
attachedTo: { $in: Array.from(contacts) }
|
||||
|
@ -13,21 +13,10 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import core, {
|
||||
AttachedDoc,
|
||||
concatLink,
|
||||
Doc,
|
||||
Tx,
|
||||
TxCollectionCUD,
|
||||
TxCreateDoc,
|
||||
TxCUD,
|
||||
TxProcessor,
|
||||
TxUpdateDoc
|
||||
} from '@hcengineering/core'
|
||||
import lead, { Lead, leadId } from '@hcengineering/lead'
|
||||
import { concatLink, Doc } from '@hcengineering/core'
|
||||
import { Lead, leadId } from '@hcengineering/lead'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
import serverCore, { TriggerControl } from '@hcengineering/server-core'
|
||||
import { addAssigneeNotification } from '@hcengineering/server-task-resources'
|
||||
import view from '@hcengineering/view'
|
||||
import { workbenchId } from '@hcengineering/workbench'
|
||||
|
||||
@ -50,68 +39,10 @@ export async function leadTextPresenter (doc: Doc): Promise<string> {
|
||||
return `LEAD-${lead.number}`
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function OnLeadUpdate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const actualTx = TxProcessor.extractTx(tx)
|
||||
|
||||
const res: Tx[] = []
|
||||
|
||||
const cud = actualTx as TxCUD<Doc>
|
||||
|
||||
if (actualTx._class === core.class.TxCreateDoc) {
|
||||
await handleLeadCreate(control, cud, res, tx)
|
||||
}
|
||||
|
||||
if (actualTx._class === core.class.TxUpdateDoc) {
|
||||
await handleLeadUpdate(control, cud, res, tx)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
async function handleLeadCreate (control: TriggerControl, cud: TxCUD<Doc>, res: Tx[], tx: Tx): Promise<void> {
|
||||
if (control.hierarchy.isDerived(cud.objectClass, lead.class.Lead)) {
|
||||
const createTx = cud as TxCreateDoc<Lead>
|
||||
const leadValue = TxProcessor.createDoc2Doc(createTx)
|
||||
if (leadValue.assignee != null) {
|
||||
await addAssigneeNotification(
|
||||
control,
|
||||
res,
|
||||
leadValue,
|
||||
leadValue.assignee,
|
||||
tx as TxCollectionCUD<Lead, AttachedDoc>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLeadUpdate (control: TriggerControl, cud: TxCUD<Doc>, res: Tx[], tx: Tx): Promise<void> {
|
||||
if (control.hierarchy.isDerived(cud.objectClass, lead.class.Lead)) {
|
||||
const updateTx = cud as TxUpdateDoc<Lead>
|
||||
if (updateTx.operations.assignee != null) {
|
||||
const leadValue = (await control.findAll(lead.class.Lead, { _id: updateTx.objectId }, { limit: 1 })).shift()
|
||||
|
||||
if (leadValue?.assignee != null) {
|
||||
await addAssigneeNotification(
|
||||
control,
|
||||
res,
|
||||
leadValue,
|
||||
leadValue.assignee,
|
||||
tx as TxCollectionCUD<Lead, AttachedDoc>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
export default async () => ({
|
||||
function: {
|
||||
LeadHTMLPresenter: leadHTMLPresenter,
|
||||
LeadTextPresenter: leadTextPresenter
|
||||
},
|
||||
trigger: {
|
||||
OnLeadUpdate
|
||||
}
|
||||
})
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
import type { Plugin, Resource } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import { TriggerFunc } from '@hcengineering/server-core'
|
||||
import { Presenter } from '@hcengineering/server-notification'
|
||||
|
||||
/**
|
||||
@ -30,8 +29,5 @@ export default plugin(serverLeadId, {
|
||||
function: {
|
||||
LeadHTMLPresenter: '' as Resource<Presenter>,
|
||||
LeadTextPresenter: '' as Resource<Presenter>
|
||||
},
|
||||
trigger: {
|
||||
OnLeadUpdate: '' as Resource<TriggerFunc>
|
||||
}
|
||||
})
|
||||
|
@ -22,13 +22,14 @@ import core, {
|
||||
ArrOf,
|
||||
AttachedDoc,
|
||||
Class,
|
||||
Collection,
|
||||
Data,
|
||||
Doc,
|
||||
DocumentUpdate,
|
||||
Hierarchy,
|
||||
MixinUpdate,
|
||||
Ref,
|
||||
RefTo,
|
||||
Space,
|
||||
Tx,
|
||||
TxCUD,
|
||||
TxCollectionCUD,
|
||||
@ -92,28 +93,9 @@ export async function OnBacklinkCreate (tx: Tx, control: TriggerControl): Promis
|
||||
}
|
||||
)
|
||||
res.push(collabTx)
|
||||
res = res.concat(
|
||||
await createCollabDocInfo(
|
||||
[receiver._id],
|
||||
tx as TxCUD<Doc>,
|
||||
doc._id,
|
||||
doc._class,
|
||||
control,
|
||||
tx._id as Ref<TxCUD<Doc>>
|
||||
)
|
||||
)
|
||||
}
|
||||
res = res.concat(await createCollabDocInfo([receiver._id], control, tx as TxCUD<Doc>, doc))
|
||||
}
|
||||
const notifyTx = await createNotificationTxes(
|
||||
control,
|
||||
ptx,
|
||||
notification.ids.MentionNotification,
|
||||
doc,
|
||||
sender,
|
||||
receiver,
|
||||
backlink.message
|
||||
)
|
||||
res = [...res, ...notifyTx]
|
||||
return res
|
||||
}
|
||||
|
||||
@ -132,18 +114,22 @@ function checkTx (ptx: TxCollectionCUD<Doc, Backlink>, hierarchy: Hierarchy): bo
|
||||
return true
|
||||
}
|
||||
|
||||
async function isAllowed (
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function isAllowed (
|
||||
control: TriggerControl,
|
||||
receiver: EmployeeAccount,
|
||||
receiver: Ref<EmployeeAccount>,
|
||||
typeId: Ref<NotificationType>,
|
||||
providerId: Ref<NotificationProvider>
|
||||
): Promise<boolean> {
|
||||
const setting = (
|
||||
await control.findAll(
|
||||
notification.class.NotificationSetting,
|
||||
{
|
||||
provider: providerId,
|
||||
type: notification.ids.MentionNotification,
|
||||
space: receiver._id as unknown as Ref<Space>
|
||||
attachedTo: providerId,
|
||||
type: typeId,
|
||||
modifiedBy: receiver
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
@ -151,13 +137,13 @@ async function isAllowed (
|
||||
if (setting !== undefined) {
|
||||
return setting.enabled
|
||||
}
|
||||
const provider = (
|
||||
await control.modelDb.findAll(notification.class.NotificationProvider, {
|
||||
_id: providerId
|
||||
const type = (
|
||||
await control.modelDb.findAll(notification.class.NotificationType, {
|
||||
_id: typeId
|
||||
})
|
||||
)[0]
|
||||
if (provider === undefined) return false
|
||||
return provider.default
|
||||
if (type === undefined) return false
|
||||
return type.providers[providerId] ?? false
|
||||
}
|
||||
|
||||
async function getTextPart (doc: Doc, control: TriggerControl): Promise<string | undefined> {
|
||||
@ -205,10 +191,11 @@ export async function getContent (
|
||||
|
||||
const textPart = await getTextPart(doc, control)
|
||||
if (textPart === undefined) return
|
||||
const text = fillTemplate(notificationType.textTemplate, sender, textPart, data)
|
||||
if (notificationType.templates === undefined) return
|
||||
const text = fillTemplate(notificationType.templates.textTemplate, sender, textPart, data)
|
||||
const htmlPart = await getHtmlPart(doc, control)
|
||||
const html = fillTemplate(notificationType.htmlTemplate, sender, htmlPart ?? textPart, data)
|
||||
const subject = fillTemplate(notificationType.subjectTemplate, sender, textPart, data)
|
||||
const html = fillTemplate(notificationType.templates.htmlTemplate, sender, htmlPart ?? textPart, data)
|
||||
const subject = fillTemplate(notificationType.templates.subjectTemplate, sender, textPart, data)
|
||||
return {
|
||||
text,
|
||||
html,
|
||||
@ -216,36 +203,33 @@ export async function getContent (
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function createNotificationTxes (
|
||||
async function createEmailNotificationTxes (
|
||||
control: TriggerControl,
|
||||
ptx: TxCollectionCUD<Doc, AttachedDoc>,
|
||||
tx: Tx,
|
||||
type: Ref<NotificationType>,
|
||||
doc: Doc | undefined,
|
||||
sender: EmployeeAccount,
|
||||
receiver: EmployeeAccount,
|
||||
senderId: Ref<EmployeeAccount>,
|
||||
receiverId: Ref<EmployeeAccount>,
|
||||
data: string = ''
|
||||
): Promise<Tx[]> {
|
||||
const res: Tx[] = []
|
||||
): Promise<Tx | undefined> {
|
||||
const sender = (await control.modelDb.findAll(contact.class.EmployeeAccount, { _id: senderId }))[0]
|
||||
|
||||
if (sender === undefined) return
|
||||
|
||||
const receiver = (await control.modelDb.findAll(contact.class.EmployeeAccount, { _id: receiverId }))[0]
|
||||
if (receiver === undefined) return
|
||||
|
||||
const senderName = formatName(sender.name)
|
||||
|
||||
const content = await getContent(doc, senderName, type, control, data)
|
||||
|
||||
if (content !== undefined && (await isAllowed(control, receiver, notification.ids.EmailNotification))) {
|
||||
const emailTx = await getEmailNotificationTx(ptx, senderName, content.text, content.html, content.subject, receiver)
|
||||
if (emailTx !== undefined) {
|
||||
res.push(emailTx)
|
||||
}
|
||||
if (content !== undefined) {
|
||||
return await getEmailNotificationTx(tx, senderName, content.text, content.html, content.subject, receiver)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
async function getEmailNotificationTx (
|
||||
ptx: TxCollectionCUD<Doc, AttachedDoc>,
|
||||
tx: Tx,
|
||||
sender: string,
|
||||
text: string,
|
||||
html: string,
|
||||
@ -259,8 +243,8 @@ async function getEmailNotificationTx (
|
||||
space: core.space.DerivedTx,
|
||||
objectClass: notification.class.EmailNotification,
|
||||
objectSpace: notification.space.Notifications,
|
||||
modifiedOn: ptx.modifiedOn,
|
||||
modifiedBy: ptx.modifiedBy,
|
||||
modifiedOn: tx.modifiedOn,
|
||||
modifiedBy: tx.modifiedBy,
|
||||
attributes: {
|
||||
status: 'new',
|
||||
sender,
|
||||
@ -410,47 +394,152 @@ export async function getDocCollaborators (
|
||||
return Array.from(collaborators.values())
|
||||
}
|
||||
|
||||
function fieldUpdated (field: string, ops: DocumentUpdate<Doc> | MixinUpdate<Doc, Doc>): boolean {
|
||||
if ((ops as any)[field] !== undefined) return true
|
||||
if ((ops.$pull as any)?.[field] !== undefined) return true
|
||||
if ((ops.$push as any)?.[field] !== undefined) return true
|
||||
return false
|
||||
}
|
||||
|
||||
function isTypeMatched (
|
||||
control: TriggerControl,
|
||||
type: NotificationType,
|
||||
tx: TxCUD<Doc>,
|
||||
extractedTx: TxCUD<Doc>
|
||||
): boolean {
|
||||
const targetClass = control.hierarchy.getBaseClass(type.objectClass)
|
||||
if (!type.txClasses.includes(extractedTx._class)) return false
|
||||
if (!control.hierarchy.isDerived(extractedTx.objectClass, targetClass)) return false
|
||||
if (tx._class === core.class.TxCollectionCUD && type.attachedToClass !== undefined) {
|
||||
if (!control.hierarchy.isDerived(tx.objectClass, type.attachedToClass)) return false
|
||||
}
|
||||
if (type.field !== undefined) {
|
||||
if (extractedTx._class === core.class.TxUpdateDoc) {
|
||||
if (!fieldUpdated(type.field, (extractedTx as TxUpdateDoc<Doc>).operations)) return false
|
||||
}
|
||||
if (extractedTx._class === core.class.TxMixin) {
|
||||
if (!fieldUpdated(type.field, (extractedTx as TxMixin<Doc, Doc>).attributes)) return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async function getMatchedTypes (control: TriggerControl, tx: TxCUD<Doc>): Promise<NotificationType[]> {
|
||||
const allTypes = await control.modelDb.findAll(notification.class.NotificationType, {})
|
||||
const extractedTx = TxProcessor.extractTx(tx) as TxCUD<Doc>
|
||||
const filtered: NotificationType[] = []
|
||||
for (const type of allTypes) {
|
||||
if (isTypeMatched(control, type, tx, extractedTx)) {
|
||||
filtered.push(type)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
interface NotifyResult {
|
||||
allowed: boolean
|
||||
emails: NotificationType[]
|
||||
}
|
||||
|
||||
async function isShouldNotify (
|
||||
control: TriggerControl,
|
||||
tx: TxCUD<Doc>,
|
||||
object: Doc,
|
||||
user: Ref<Account>
|
||||
): Promise<NotifyResult> {
|
||||
let allowed = false
|
||||
const emailTypes: NotificationType[] = []
|
||||
const types = await getMatchedTypes(control, tx)
|
||||
for (const type of types) {
|
||||
if (control.hierarchy.hasMixin(type, serverNotification.mixin.TypeMatch)) {
|
||||
const mixin = control.hierarchy.as(type, serverNotification.mixin.TypeMatch)
|
||||
if (mixin.func !== undefined) {
|
||||
const f = await getResource(mixin.func)
|
||||
const res = await f(tx, object, user, type, control)
|
||||
if (!res) continue
|
||||
}
|
||||
}
|
||||
if (await isAllowed(control, user as Ref<EmployeeAccount>, type._id, notification.providers.PlatformNotification)) {
|
||||
allowed = true
|
||||
}
|
||||
if (await isAllowed(control, user as Ref<EmployeeAccount>, type._id, notification.providers.EmailNotification)) {
|
||||
emailTypes.push(type)
|
||||
}
|
||||
}
|
||||
return {
|
||||
allowed,
|
||||
emails: emailTypes
|
||||
}
|
||||
}
|
||||
|
||||
async function getNotificationTxes (
|
||||
control: TriggerControl,
|
||||
object: Doc,
|
||||
originTx: TxCUD<Doc>,
|
||||
target: Ref<Account>,
|
||||
docUpdates: DocUpdates[]
|
||||
): Promise<Tx[]> {
|
||||
if (originTx.modifiedBy === target) return []
|
||||
const res: Tx[] = []
|
||||
const allowed = await isShouldNotify(control, originTx, object, target)
|
||||
if (allowed.allowed) {
|
||||
const current = docUpdates.find((p) => p.user === target)
|
||||
if (current === undefined) {
|
||||
res.push(
|
||||
control.txFactory.createTxCreateDoc(notification.class.DocUpdates, notification.space.Notifications, {
|
||||
user: target,
|
||||
attachedTo: object._id,
|
||||
attachedToClass: object._class,
|
||||
hidden: false,
|
||||
lastTx: originTx._id,
|
||||
lastTxTime: originTx.modifiedOn,
|
||||
txes: [[originTx._id, originTx.modifiedOn]]
|
||||
})
|
||||
)
|
||||
} else {
|
||||
res.push(
|
||||
control.txFactory.createTxUpdateDoc(current._class, current.space, current._id, {
|
||||
$push: {
|
||||
txes: [originTx._id, originTx.modifiedOn]
|
||||
}
|
||||
})
|
||||
)
|
||||
res.push(
|
||||
control.txFactory.createTxUpdateDoc(current._class, current.space, current._id, {
|
||||
lastTx: originTx._id,
|
||||
lastTxTime: originTx.modifiedOn,
|
||||
hidden: false
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
for (const type of allowed.emails) {
|
||||
const emailTx = await createEmailNotificationTxes(
|
||||
control,
|
||||
originTx,
|
||||
type._id,
|
||||
object,
|
||||
originTx.modifiedBy as Ref<EmployeeAccount>,
|
||||
target as Ref<EmployeeAccount>
|
||||
)
|
||||
if (emailTx !== undefined) {
|
||||
res.push(emailTx)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
async function createCollabDocInfo (
|
||||
collaborators: Ref<Account>[],
|
||||
tx: TxCUD<Doc>,
|
||||
objectId: Ref<Doc>,
|
||||
objectClass: Ref<Class<Doc>>,
|
||||
control: TriggerControl,
|
||||
txId?: Ref<TxCUD<Doc>>
|
||||
): Promise<TxCUD<DocUpdates>[]> {
|
||||
const res: TxCUD<DocUpdates>[] = []
|
||||
originTx: TxCUD<Doc>,
|
||||
object: Doc
|
||||
): Promise<Tx[]> {
|
||||
let res: Tx[] = []
|
||||
const targets = new Set(collaborators)
|
||||
const docs = await control.findAll(notification.class.DocUpdates, { attachedTo: objectId })
|
||||
for (const doc of docs) {
|
||||
if (tx.modifiedBy === doc.user || !targets.delete(doc.user)) continue
|
||||
res.push(
|
||||
control.txFactory.createTxUpdateDoc(doc._class, doc.space, doc._id, {
|
||||
$push: {
|
||||
txes: [txId ?? tx._id, tx.modifiedOn]
|
||||
}
|
||||
})
|
||||
)
|
||||
res.push(
|
||||
control.txFactory.createTxUpdateDoc(doc._class, doc.space, doc._id, {
|
||||
lastTx: txId ?? tx._id,
|
||||
lastTxTime: tx.modifiedOn,
|
||||
hidden: false
|
||||
})
|
||||
)
|
||||
}
|
||||
const docUpdates = await control.findAll(notification.class.DocUpdates, { attachedTo: object._id })
|
||||
for (const target of targets) {
|
||||
if (tx.modifiedBy === target) continue
|
||||
res.push(
|
||||
control.txFactory.createTxCreateDoc(notification.class.DocUpdates, notification.space.Notifications, {
|
||||
user: target,
|
||||
attachedTo: objectId,
|
||||
attachedToClass: objectClass,
|
||||
hidden: false,
|
||||
lastTx: txId ?? tx._id,
|
||||
lastTxTime: tx.modifiedOn,
|
||||
txes: [[txId ?? tx._id, tx.modifiedOn]]
|
||||
})
|
||||
)
|
||||
res = res.concat(await getNotificationTxes(control, object, originTx, target, docUpdates))
|
||||
}
|
||||
return res
|
||||
}
|
||||
@ -481,7 +570,7 @@ export function getMixinTx (
|
||||
export async function createCollaboratorDoc (
|
||||
tx: TxCreateDoc<Doc>,
|
||||
control: TriggerControl,
|
||||
txId?: Ref<TxCUD<Doc>>
|
||||
originTx: TxCUD<Doc>
|
||||
): Promise<Tx[]> {
|
||||
const res: Tx[] = []
|
||||
const hierarchy = control.hierarchy
|
||||
@ -491,7 +580,7 @@ export async function createCollaboratorDoc (
|
||||
const collaborators = await getDocCollaborators(doc, mixin, control)
|
||||
|
||||
const mixinTx = getMixinTx(tx, control, collaborators)
|
||||
const notificationTxes = await createCollabDocInfo(collaborators, tx, doc._id, doc._class, control, txId)
|
||||
const notificationTxes = await createCollabDocInfo(collaborators, control, originTx, doc)
|
||||
res.push(mixinTx)
|
||||
res.push(...notificationTxes)
|
||||
}
|
||||
@ -501,15 +590,19 @@ export async function createCollaboratorDoc (
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function collaboratorDocHandler (tx: Tx, control: TriggerControl, txId?: Ref<TxCUD<Doc>>): Promise<Tx[]> {
|
||||
export async function collaboratorDocHandler (
|
||||
tx: TxCUD<Doc>,
|
||||
control: TriggerControl,
|
||||
originTx?: TxCUD<Doc>
|
||||
): Promise<Tx[]> {
|
||||
switch (tx._class) {
|
||||
case core.class.TxCreateDoc:
|
||||
if (tx.space === core.space.DerivedTx) return []
|
||||
return await createCollaboratorDoc(tx as TxCreateDoc<Doc>, control, txId)
|
||||
return await createCollaboratorDoc(tx as TxCreateDoc<Doc>, control, originTx ?? tx)
|
||||
case core.class.TxUpdateDoc:
|
||||
case core.class.TxMixin:
|
||||
if (tx.space === core.space.DerivedTx) return []
|
||||
return await updateCollaboratorDoc(tx as TxUpdateDoc<Doc>, control, txId)
|
||||
return await updateCollaboratorDoc(tx as TxUpdateDoc<Doc>, control, originTx ?? tx)
|
||||
case core.class.TxRemoveDoc:
|
||||
return await removeCollaboratorDoc(tx as TxRemoveDoc<Doc>, control)
|
||||
case core.class.TxCollectionCUD:
|
||||
@ -521,15 +614,13 @@ export async function collaboratorDocHandler (tx: Tx, control: TriggerControl, t
|
||||
|
||||
async function collectionCollabDoc (tx: TxCollectionCUD<Doc, AttachedDoc>, control: TriggerControl): Promise<Tx[]> {
|
||||
const actualTx = TxProcessor.extractTx(tx)
|
||||
let res = await collaboratorDocHandler(actualTx, control, tx._id)
|
||||
let res = await collaboratorDocHandler(actualTx as TxCUD<Doc>, control, tx)
|
||||
if ([core.class.TxCreateDoc, core.class.TxRemoveDoc].includes(actualTx._class)) {
|
||||
const doc = (await control.findAll(tx.objectClass, { _id: tx.objectId }, { limit: 1 }))[0]
|
||||
if (doc !== undefined) {
|
||||
if (control.hierarchy.hasMixin(doc, notification.mixin.Collaborators)) {
|
||||
const collabMixin = control.hierarchy.as(doc, notification.mixin.Collaborators)
|
||||
res = res.concat(
|
||||
await createCollabDocInfo(collabMixin.collaborators, tx, tx.objectId, tx.objectClass, control, tx._id)
|
||||
)
|
||||
res = res.concat(await createCollabDocInfo(collabMixin.collaborators, control, tx, doc))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -591,7 +682,7 @@ function isMixinTx (tx: TxUpdateDoc<Doc> | TxMixin<Doc, Doc>): tx is TxMixin<Doc
|
||||
async function updateCollaboratorDoc (
|
||||
tx: TxUpdateDoc<Doc> | TxMixin<Doc, Doc>,
|
||||
control: TriggerControl,
|
||||
txId?: Ref<TxCUD<Doc>>
|
||||
originTx: TxCUD<Doc>
|
||||
): Promise<Tx[]> {
|
||||
const hierarchy = control.hierarchy
|
||||
let res: Tx[] = []
|
||||
@ -619,19 +710,12 @@ async function updateCollaboratorDoc (
|
||||
)
|
||||
}
|
||||
res = res.concat(
|
||||
await createCollabDocInfo(
|
||||
[...collabMixin.collaborators, ...newCollaborators],
|
||||
tx,
|
||||
doc._id,
|
||||
doc._class,
|
||||
control,
|
||||
txId
|
||||
)
|
||||
await createCollabDocInfo([...collabMixin.collaborators, ...newCollaborators], control, originTx, doc)
|
||||
)
|
||||
} else {
|
||||
const collaborators = await getDocCollaborators(doc, mixin, control)
|
||||
res.push(getMixinTx(tx, control, collaborators))
|
||||
res = res.concat(await createCollabDocInfo(collaborators, tx, doc._id, doc._class, control, txId))
|
||||
res = res.concat(await createCollabDocInfo(collaborators, control, originTx, doc))
|
||||
}
|
||||
|
||||
return res
|
||||
@ -675,6 +759,77 @@ export async function OnAddCollborator (tx: Tx, control: TriggerControl): Promis
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function isUserInFieldValue (
|
||||
tx: Tx,
|
||||
doc: Doc,
|
||||
user: Ref<Account>,
|
||||
type: NotificationType,
|
||||
control: TriggerControl
|
||||
): Promise<boolean> {
|
||||
if (type.field === undefined) return false
|
||||
const value = (doc as any)[type.field]
|
||||
if (value === undefined) return false
|
||||
if (Array.isArray(value)) {
|
||||
return value.includes(user)
|
||||
} else {
|
||||
return value === user
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function OnAttributeCreate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
if (tx._class !== core.class.TxCreateDoc) return []
|
||||
const ctx = tx as TxCreateDoc<AnyAttribute>
|
||||
if (ctx.objectClass !== core.class.Attribute) return []
|
||||
const attribute = TxProcessor.createDoc2Doc(ctx)
|
||||
const group = (await control.findAll(notification.class.NotificationGroup, { objectClass: attribute.attributeOf }))[0]
|
||||
if (group === undefined) return []
|
||||
const isCollection: boolean = core.class.Collection === attribute.type._class
|
||||
const objectClass = !isCollection ? attribute.attributeOf : (attribute.type as Collection<AttachedDoc>).of
|
||||
const txClasses = !isCollection
|
||||
? [control.hierarchy.isMixin(attribute.attributeOf) ? core.class.TxMixin : core.class.TxUpdateDoc]
|
||||
: [core.class.TxCreateDoc, core.class.TxRemoveDoc]
|
||||
const data: Data<NotificationType> = {
|
||||
group: group._id,
|
||||
generated: true,
|
||||
objectClass,
|
||||
txClasses,
|
||||
hidden: false,
|
||||
providers: {
|
||||
[notification.providers.PlatformNotification]: false
|
||||
},
|
||||
label: attribute.label
|
||||
}
|
||||
if (isCollection) {
|
||||
data.attachedToClass = attribute.attributeOf
|
||||
}
|
||||
const id =
|
||||
`${notification.class.NotificationType}_${attribute.attributeOf}_${attribute.name}` as Ref<NotificationType>
|
||||
const res = control.txFactory.createTxCreateDoc(notification.class.NotificationType, core.space.Model, data, id)
|
||||
return [res]
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function OnAttributeUpdate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
if (tx._class !== core.class.TxUpdateDoc) return []
|
||||
const ctx = tx as TxUpdateDoc<AnyAttribute>
|
||||
if (ctx.objectClass !== core.class.Attribute) return []
|
||||
if (ctx.operations.hidden === undefined) return []
|
||||
const type = (await control.findAll(notification.class.NotificationType, { attribute: ctx.objectId }))[0]
|
||||
if (type === undefined) return []
|
||||
const res = control.txFactory.createTxUpdateDoc(type._class, type.space, type._id, {
|
||||
hidden: ctx.operations.hidden
|
||||
})
|
||||
return [res]
|
||||
}
|
||||
|
||||
export * from './types'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
@ -684,6 +839,11 @@ export default async () => ({
|
||||
CollaboratorDocHandler: collaboratorDocHandler,
|
||||
OnUpdateLastView,
|
||||
UpdateLastView,
|
||||
OnAddCollborator
|
||||
OnAddCollborator,
|
||||
OnAttributeCreate,
|
||||
OnAttributeUpdate
|
||||
},
|
||||
function: {
|
||||
IsUserInFieldValue: isUserInFieldValue
|
||||
}
|
||||
})
|
||||
|
@ -15,8 +15,8 @@
|
||||
//
|
||||
|
||||
import contact, { Employee, EmployeeAccount } from '@hcengineering/contact'
|
||||
import { Account, Class, Doc, Mixin, Ref, TxCreateDoc, TxFactory, TxUpdateDoc } from '@hcengineering/core'
|
||||
import notification, { LastView } from '@hcengineering/notification'
|
||||
import { Account, Class, Doc, Mixin, Ref, Tx, TxCreateDoc, TxFactory, TxUpdateDoc } from '@hcengineering/core'
|
||||
import notification, { LastView, NotificationType } from '@hcengineering/notification'
|
||||
import { Plugin, Resource, plugin } from '@hcengineering/platform'
|
||||
import type { TriggerControl, TriggerFunc } from '@hcengineering/server-core'
|
||||
|
||||
@ -166,19 +166,39 @@ export interface TextPresenter extends Class<Doc> {
|
||||
presenter: Resource<Presenter>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type TypeMatchFunc = Resource<
|
||||
(tx: Tx, doc: Doc, user: Ref<Account>, type: NotificationType, control: TriggerControl) => Promise<boolean>
|
||||
>
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface TypeMatch extends NotificationType {
|
||||
func: TypeMatchFunc
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export default plugin(serverNotificationId, {
|
||||
mixin: {
|
||||
HTMLPresenter: '' as Ref<Mixin<HTMLPresenter>>,
|
||||
TextPresenter: '' as Ref<Mixin<TextPresenter>>
|
||||
TextPresenter: '' as Ref<Mixin<TextPresenter>>,
|
||||
TypeMatch: '' as Ref<Mixin<TypeMatch>>
|
||||
},
|
||||
trigger: {
|
||||
OnBacklinkCreate: '' as Resource<TriggerFunc>,
|
||||
UpdateLastView: '' as Resource<TriggerFunc>,
|
||||
OnUpdateLastView: '' as Resource<TriggerFunc>,
|
||||
CollaboratorDocHandler: '' as Resource<TriggerFunc>,
|
||||
OnAddCollborator: '' as Resource<TriggerFunc>
|
||||
OnAddCollborator: '' as Resource<TriggerFunc>,
|
||||
OnAttributeCreate: '' as Resource<TriggerFunc>,
|
||||
OnAttributeUpdate: '' as Resource<TriggerFunc>
|
||||
},
|
||||
function: {
|
||||
IsUserInFieldValue: '' as TypeMatchFunc
|
||||
}
|
||||
})
|
||||
|
@ -15,11 +15,9 @@
|
||||
|
||||
import contact from '@hcengineering/contact'
|
||||
import core, {
|
||||
AttachedDoc,
|
||||
concatLink,
|
||||
Doc,
|
||||
Tx,
|
||||
TxCollectionCUD,
|
||||
TxCreateDoc,
|
||||
TxCUD,
|
||||
TxProcessor,
|
||||
@ -29,7 +27,6 @@ import core, {
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
import recruit, { Applicant, recruitId, Vacancy } from '@hcengineering/recruit'
|
||||
import serverCore, { TriggerControl } from '@hcengineering/server-core'
|
||||
import { addAssigneeNotification } from '@hcengineering/server-task-resources'
|
||||
import { workbenchId } from '@hcengineering/workbench'
|
||||
|
||||
function getSequenceId (doc: Vacancy | Applicant, control: TriggerControl): string {
|
||||
@ -96,12 +93,10 @@ export async function OnRecruitUpdate (tx: Tx, control: TriggerControl): Promise
|
||||
|
||||
if (actualTx._class === core.class.TxCreateDoc) {
|
||||
handleVacancyCreate(control, cud, actualTx, res)
|
||||
await handleApplicantCreate(control, cud, res, tx)
|
||||
}
|
||||
|
||||
if (actualTx._class === core.class.TxUpdateDoc) {
|
||||
await handleVacancyUpdate(control, cud, res)
|
||||
await handleApplicantUpdate(control, cud, res, tx)
|
||||
}
|
||||
if (actualTx._class === core.class.TxRemoveDoc) {
|
||||
await handleVacancyRemove(control, cud, actualTx)
|
||||
@ -189,43 +184,6 @@ async function handleVacancyRemove (control: TriggerControl, cud: TxCUD<Doc>, ac
|
||||
}
|
||||
}
|
||||
|
||||
async function handleApplicantUpdate (control: TriggerControl, cud: TxCUD<Doc>, res: Tx[], tx: Tx): Promise<void> {
|
||||
if (control.hierarchy.isDerived(cud.objectClass, recruit.class.Applicant)) {
|
||||
const updateTx = cud as TxUpdateDoc<Applicant>
|
||||
if (updateTx.operations.assignee != null) {
|
||||
const applicant = (
|
||||
await control.findAll(recruit.class.Applicant, { _id: updateTx.objectId }, { limit: 1 })
|
||||
).shift()
|
||||
|
||||
if (applicant?.assignee != null) {
|
||||
await addAssigneeNotification(
|
||||
control,
|
||||
res,
|
||||
applicant,
|
||||
applicant.assignee,
|
||||
tx as TxCollectionCUD<Applicant, AttachedDoc>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handleApplicantCreate (control: TriggerControl, cud: TxCUD<Doc>, res: Tx[], tx: Tx): Promise<void> {
|
||||
if (control.hierarchy.isDerived(cud.objectClass, recruit.class.Applicant)) {
|
||||
const createTx = cud as TxCreateDoc<Applicant>
|
||||
const applicant = TxProcessor.createDoc2Doc(createTx)
|
||||
if (applicant.assignee != null) {
|
||||
await addAssigneeNotification(
|
||||
control,
|
||||
res,
|
||||
applicant,
|
||||
applicant.assignee,
|
||||
tx as TxCollectionCUD<Applicant, AttachedDoc>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleVacancyCreate (control: TriggerControl, cud: TxCUD<Doc>, actualTx: Tx, res: Tx[]): void {
|
||||
if (control.hierarchy.isDerived(cud.objectClass, recruit.class.Vacancy)) {
|
||||
const createTx = actualTx as TxCreateDoc<Vacancy>
|
||||
|
@ -13,39 +13,6 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Employee } from '@hcengineering/contact'
|
||||
import { AttachedDoc, Doc, Ref, Tx, TxCollectionCUD } from '@hcengineering/core'
|
||||
import { TriggerControl } from '@hcengineering/server-core'
|
||||
import { getEmployeeAccount, getEmployeeAccountById } from '@hcengineering/server-notification'
|
||||
import { createNotificationTxes } from '@hcengineering/server-notification-resources'
|
||||
import task from '@hcengineering/task'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function addAssigneeNotification (
|
||||
control: TriggerControl,
|
||||
res: Tx[],
|
||||
issue: Doc,
|
||||
assignee: Ref<Employee>,
|
||||
ptx: TxCollectionCUD<AttachedDoc, AttachedDoc>
|
||||
): Promise<void> {
|
||||
const sender = await getEmployeeAccountById(ptx.modifiedBy, control)
|
||||
if (sender === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const receiver = await getEmployeeAccount(assignee, control)
|
||||
if (receiver === undefined) {
|
||||
return
|
||||
}
|
||||
if (sender._id === receiver._id) return
|
||||
|
||||
const result = await createNotificationTxes(control, ptx, task.ids.AssigneedNotification, issue, sender, receiver)
|
||||
|
||||
res.push(...result)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
export default async () => ({
|
||||
function: {}
|
||||
|
@ -29,6 +29,7 @@
|
||||
"dependencies": {
|
||||
"@hcengineering/server-notification": "^0.6.0",
|
||||
"@hcengineering/platform": "^0.6.8",
|
||||
"@hcengineering/server-core": "^0.6.1"
|
||||
"@hcengineering/server-core": "^0.6.1",
|
||||
"@hcengineering/core": "^0.6.23"
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Employee } from '@hcengineering/contact'
|
||||
import core, {
|
||||
AttachedDoc,
|
||||
concatLink,
|
||||
@ -32,7 +31,6 @@ import core, {
|
||||
} from '@hcengineering/core'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
import serverCore, { TriggerControl } from '@hcengineering/server-core'
|
||||
import { addAssigneeNotification } from '@hcengineering/server-task-resources'
|
||||
import tracker, { Component, Issue, IssueParentInfo, TimeSpendReport, trackerId } from '@hcengineering/tracker'
|
||||
import { workbenchId } from '@hcengineering/workbench'
|
||||
|
||||
@ -71,19 +69,6 @@ export async function issueTextPresenter (doc: Doc, control: TriggerControl): Pr
|
||||
return issueName
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function addTrackerAssigneeNotification (
|
||||
control: TriggerControl,
|
||||
res: Tx[],
|
||||
issue: Issue,
|
||||
assignee: Ref<Employee>,
|
||||
ptx: TxCollectionCUD<Issue, AttachedDoc>
|
||||
): Promise<void> {
|
||||
await addAssigneeNotification(control, res, issue, assignee, ptx)
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -137,15 +122,6 @@ export async function OnIssueUpdate (tx: Tx, control: TriggerControl): Promise<T
|
||||
const res: Tx[] = []
|
||||
updateIssueParentEstimations(issue, res, control, [], issue.parents)
|
||||
|
||||
if (issue.assignee != null) {
|
||||
await addTrackerAssigneeNotification(
|
||||
control,
|
||||
res,
|
||||
issue,
|
||||
issue.assignee,
|
||||
tx as TxCollectionCUD<Issue, AttachedDoc>
|
||||
)
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
@ -279,10 +255,6 @@ async function doIssueUpdate (
|
||||
return currentIssue
|
||||
}
|
||||
|
||||
if (updateTx.operations.assignee != null) {
|
||||
await addTrackerAssigneeNotification(control, res, await getCurrentIssue(), updateTx.operations.assignee, tx)
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(updateTx.operations, 'attachedTo')) {
|
||||
const [newParent] = await control.findAll(
|
||||
tracker.class.Issue,
|
||||
|
@ -285,7 +285,7 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar
|
||||
}
|
||||
if (typeof query === 'string') {
|
||||
if (!spaces.includes(query)) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {}))
|
||||
return { $in: [] }
|
||||
}
|
||||
} else if (query.$in != null) {
|
||||
query.$in = query.$in.filter((p) => spaces.includes(p))
|
||||
|
Loading…
Reference in New Issue
Block a user