mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 03:14:40 +03:00
Cleanup code (#5736)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
parent
14c1977660
commit
0f79b9fed7
@ -32,7 +32,6 @@ import {
|
||||
type IgnoreActivity,
|
||||
type Reaction,
|
||||
type SavedMessage,
|
||||
type TxViewlet,
|
||||
type ReplyProvider
|
||||
} from '@hcengineering/activity'
|
||||
import contact, { type Person } from '@hcengineering/contact'
|
||||
@ -93,21 +92,6 @@ export class TActivityAttributeUpdatesPresenter extends TClass implements Activi
|
||||
@Mixin(activity.mixin.IgnoreActivity, core.class.Class)
|
||||
export class TIgnoreActivity extends TClass implements IgnoreActivity {}
|
||||
|
||||
@Model(activity.class.TxViewlet, core.class.Doc, DOMAIN_MODEL)
|
||||
export class TTxViewlet extends TDoc implements TxViewlet {
|
||||
icon!: Asset
|
||||
objectClass!: Ref<Class<Doc>>
|
||||
txClass!: Ref<Class<Tx>>
|
||||
// Component to display on.
|
||||
component!: AnyComponent
|
||||
// Filter
|
||||
match?: DocumentQuery<Tx>
|
||||
label!: IntlString
|
||||
display!: 'inline' | 'content' | 'emphasized'
|
||||
editable!: boolean
|
||||
hideOnRemove!: boolean
|
||||
}
|
||||
|
||||
@Model(activity.class.ActivityMessage, core.class.AttachedDoc, DOMAIN_ACTIVITY)
|
||||
export class TActivityMessage extends TAttachedDoc implements ActivityMessage {
|
||||
@Prop(TypeBoolean(), activity.string.Pinned)
|
||||
@ -267,7 +251,6 @@ export class TUserMentionInfo extends TAttachedDoc {
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(
|
||||
TTxViewlet,
|
||||
TActivityDoc,
|
||||
TActivityMessagesFilter,
|
||||
TActivityMessage,
|
||||
|
@ -97,34 +97,6 @@ export function createModel (builder: Builder): void {
|
||||
editor: attachment.component.Photos
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.TxViewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
objectClass: attachment.class.Attachment,
|
||||
icon: attachment.icon.Attachment,
|
||||
txClass: core.class.TxCreateDoc,
|
||||
component: attachment.activity.TxAttachmentCreate,
|
||||
label: attachment.string.AddAttachment,
|
||||
display: 'emphasized'
|
||||
},
|
||||
attachment.ids.TxAttachmentCreate
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.TxViewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
objectClass: attachment.class.Attachment,
|
||||
icon: attachment.icon.Attachment,
|
||||
txClass: core.class.TxRemoveDoc,
|
||||
component: attachment.activity.TxAttachmentCreate,
|
||||
label: attachment.string.RemovedAttachment,
|
||||
display: 'inline'
|
||||
},
|
||||
attachment.ids.TxAttachmentRemove
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.DocUpdateMessageViewlet,
|
||||
core.space.Model,
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { TxViewlet, ActivityMessage, DocUpdateMessageViewlet } from '@hcengineering/activity'
|
||||
import type { ActivityMessage, DocUpdateMessageViewlet } from '@hcengineering/activity'
|
||||
import { attachmentId } from '@hcengineering/attachment'
|
||||
import attachment from '@hcengineering/attachment-resources/src/plugin'
|
||||
import type { Ref, Doc } from '@hcengineering/core'
|
||||
@ -44,13 +44,10 @@ export default mergeIds(attachmentId, attachment, {
|
||||
ContentType: '' as IntlString
|
||||
},
|
||||
ids: {
|
||||
TxAttachmentCreate: '' as Ref<TxViewlet>,
|
||||
TxAttachmentRemove: '' as Ref<TxViewlet>,
|
||||
AttachmentCreatedActivityViewlet: '' as Ref<DocUpdateMessageViewlet>,
|
||||
AttachmentRemovedActivityViewlet: '' as Ref<DocUpdateMessageViewlet>
|
||||
},
|
||||
activity: {
|
||||
TxAttachmentCreate: '' as AnyComponent,
|
||||
AttachmentsUpdatedMessage: '' as AnyComponent
|
||||
},
|
||||
category: {
|
||||
|
@ -239,22 +239,6 @@ export function createModel (builder: Builder): void {
|
||||
calendar.ids.ReminderNotification
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.TxViewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
objectClass: calendar.class.Event,
|
||||
icon: calendar.icon.Reminder,
|
||||
txClass: core.class.TxUpdateDoc,
|
||||
label: calendar.string.Reminder,
|
||||
component: calendar.activity.ReminderViewlet,
|
||||
display: 'emphasized',
|
||||
editable: false,
|
||||
hideOnRemove: true
|
||||
},
|
||||
calendar.ids.ReminderViewlet
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.DocUpdateMessageViewlet,
|
||||
core.space.Model,
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { type DocUpdateMessageViewlet, type TxViewlet } from '@hcengineering/activity'
|
||||
import { type DocUpdateMessageViewlet } from '@hcengineering/activity'
|
||||
import { calendarId } from '@hcengineering/calendar'
|
||||
import calendar from '@hcengineering/calendar-resources/src/plugin'
|
||||
import { type Ref } from '@hcengineering/core'
|
||||
@ -65,7 +65,6 @@ export default mergeIds(calendarId, calendar, {
|
||||
CalendarEvent: '' as Ref<Viewlet>
|
||||
},
|
||||
ids: {
|
||||
ReminderViewlet: '' as Ref<TxViewlet>,
|
||||
UpdateRemainderActivityViewlet: '' as Ref<DocUpdateMessageViewlet>,
|
||||
CalendarNotificationGroup: '' as Ref<NotificationGroup>
|
||||
}
|
||||
|
@ -17,39 +17,30 @@ import activity, { type ActivityMessage } from '@hcengineering/activity'
|
||||
import {
|
||||
type Channel,
|
||||
chunterId,
|
||||
type ChunterMessage,
|
||||
type ChunterMessageExtension,
|
||||
type DirectMessage,
|
||||
type Message,
|
||||
type DirectMessageInput,
|
||||
type ChatMessage,
|
||||
type ChatMessageViewlet,
|
||||
type ChunterSpace,
|
||||
type ObjectChatPanel,
|
||||
type ThreadMessage
|
||||
} from '@hcengineering/chunter'
|
||||
import contact, { type Person } from '@hcengineering/contact'
|
||||
import contact from '@hcengineering/contact'
|
||||
import {
|
||||
type Account,
|
||||
type Class,
|
||||
type Doc,
|
||||
type Domain,
|
||||
DOMAIN_MODEL,
|
||||
type Ref,
|
||||
type Space,
|
||||
type Timestamp,
|
||||
IndexKind
|
||||
} from '@hcengineering/core'
|
||||
import {
|
||||
ArrOf,
|
||||
type Builder,
|
||||
Collection as PropCollection,
|
||||
Collection,
|
||||
Index,
|
||||
Mixin,
|
||||
Model,
|
||||
Prop,
|
||||
ReadOnly,
|
||||
TypeMarkup,
|
||||
TypeRef,
|
||||
TypeString,
|
||||
@ -57,11 +48,10 @@ import {
|
||||
UX
|
||||
} from '@hcengineering/model'
|
||||
import attachment from '@hcengineering/model-attachment'
|
||||
import core, { TAttachedDoc, TClass, TDoc, TSpace } from '@hcengineering/model-core'
|
||||
import core, { TClass, TDoc, TSpace } from '@hcengineering/model-core'
|
||||
import notification, { notificationActionTemplates } from '@hcengineering/model-notification'
|
||||
import view, { createAction, template, actionTemplates as viewTemplates } from '@hcengineering/model-view'
|
||||
import workbench from '@hcengineering/model-workbench'
|
||||
import { type AnyComponent } from '@hcengineering/ui/src/types'
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
import { TActivityMessage } from '@hcengineering/model-activity'
|
||||
|
||||
@ -73,10 +63,7 @@ export { chunterOperation } from './migration'
|
||||
export const DOMAIN_CHUNTER = 'chunter' as Domain
|
||||
|
||||
@Model(chunter.class.ChunterSpace, core.class.Space)
|
||||
export class TChunterSpace extends TSpace implements ChunterSpace {
|
||||
@Prop(TypeTimestamp(), chunter.string.LastMessage)
|
||||
lastMessage?: Timestamp
|
||||
}
|
||||
export class TChunterSpace extends TSpace implements ChunterSpace {}
|
||||
|
||||
@Model(chunter.class.Channel, chunter.class.ChunterSpace)
|
||||
@UX(chunter.string.Channel, chunter.icon.Hashtag, undefined, undefined, undefined, chunter.string.Channels)
|
||||
@ -90,50 +77,6 @@ export class TChannel extends TChunterSpace implements Channel {
|
||||
@UX(chunter.string.DirectMessage, contact.icon.Person, undefined, undefined, undefined, chunter.string.DirectMessages)
|
||||
export class TDirectMessage extends TChunterSpace implements DirectMessage {}
|
||||
|
||||
@Model(chunter.class.ChunterMessage, core.class.AttachedDoc, DOMAIN_CHUNTER)
|
||||
export class TChunterMessage extends TAttachedDoc implements ChunterMessage {
|
||||
@Prop(TypeMarkup(), chunter.string.Content)
|
||||
@Index(IndexKind.FullText)
|
||||
content!: string
|
||||
|
||||
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
|
||||
attachments?: number
|
||||
|
||||
@Prop(TypeRef(core.class.Account), chunter.string.CreateBy)
|
||||
@ReadOnly()
|
||||
createBy!: Ref<Account>
|
||||
|
||||
@Prop(TypeTimestamp(), chunter.string.Edit)
|
||||
editedOn?: Timestamp
|
||||
|
||||
@Prop(Collection(activity.class.Reaction), activity.string.Reactions)
|
||||
reactions?: number
|
||||
}
|
||||
|
||||
@Mixin(chunter.mixin.ChunterMessageExtension, chunter.class.ChunterMessage)
|
||||
export class TChunterMessageExtension extends TChunterMessage implements ChunterMessageExtension {}
|
||||
|
||||
@Model(chunter.class.Message, chunter.class.ChunterMessage)
|
||||
@UX(chunter.string.Message, undefined, 'MSG')
|
||||
export class TMessage extends TChunterMessage implements Message {
|
||||
declare attachedTo: Ref<Space>
|
||||
|
||||
declare attachedToClass: Ref<Class<Space>>
|
||||
|
||||
@Prop(ArrOf(TypeRef(contact.class.Person)), chunter.string.Replies)
|
||||
replies?: Ref<Person>[]
|
||||
|
||||
repliesCount?: number
|
||||
|
||||
@Prop(TypeTimestamp(), activity.string.LastReply)
|
||||
lastReply?: Timestamp
|
||||
}
|
||||
|
||||
@Mixin(chunter.mixin.DirectMessageInput, core.class.Class)
|
||||
export class TDirectMessageInput extends TClass implements DirectMessageInput {
|
||||
component!: AnyComponent
|
||||
}
|
||||
|
||||
@Model(chunter.class.ChatMessage, activity.class.ActivityMessage)
|
||||
@UX(chunter.string.Message, chunter.icon.Thread, undefined, undefined, undefined, chunter.string.Threads)
|
||||
export class TChatMessage extends TActivityMessage implements ChatMessage {
|
||||
@ -206,11 +149,7 @@ export function createModel (builder: Builder): void {
|
||||
builder.createModel(
|
||||
TChunterSpace,
|
||||
TChannel,
|
||||
TMessage,
|
||||
TChunterMessage,
|
||||
TChunterMessageExtension,
|
||||
TDirectMessage,
|
||||
TDirectMessageInput,
|
||||
TChatMessage,
|
||||
TThreadMessage,
|
||||
TChatMessageViewlet,
|
||||
@ -233,12 +172,6 @@ export function createModel (builder: Builder): void {
|
||||
encode: chunter.function.GetChunterSpaceLinkFragment
|
||||
})
|
||||
|
||||
builder.mixin(spaceClass, core.class.Class, workbench.mixin.SpaceView, {
|
||||
view: {
|
||||
class: chunter.class.Message
|
||||
}
|
||||
})
|
||||
|
||||
builder.mixin(spaceClass, core.class.Class, view.mixin.ObjectEditor, {
|
||||
editor: chunter.component.EditChannel
|
||||
})
|
||||
@ -248,10 +181,6 @@ export function createModel (builder: Builder): void {
|
||||
})
|
||||
})
|
||||
|
||||
builder.mixin(chunter.class.Message, core.class.Class, notification.mixin.ClassCollaborators, {
|
||||
fields: ['createdBy', 'replies']
|
||||
})
|
||||
|
||||
builder.mixin(chunter.class.DirectMessage, core.class.Class, view.mixin.ObjectTitle, {
|
||||
titleProvider: chunter.function.DirectTitleProvider
|
||||
})
|
||||
@ -276,22 +205,6 @@ export function createModel (builder: Builder): void {
|
||||
presenter: chunter.component.ChannelPreview
|
||||
})
|
||||
|
||||
builder.mixin(chunter.class.DirectMessage, core.class.Class, chunter.mixin.DirectMessageInput, {
|
||||
component: chunter.component.DirectMessageInput
|
||||
})
|
||||
|
||||
builder.mixin(chunter.class.Message, core.class.Class, notification.mixin.NotificationObjectPresenter, {
|
||||
presenter: chunter.component.ThreadParentPresenter
|
||||
})
|
||||
|
||||
builder.mixin(chunter.class.Message, core.class.Class, view.mixin.ObjectPanel, {
|
||||
component: chunter.component.ThreadViewPanel
|
||||
})
|
||||
|
||||
builder.mixin(chunter.class.Message, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: chunter.component.MessagePresenter
|
||||
})
|
||||
|
||||
builder.mixin(chunter.class.Channel, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: chunter.component.ChannelPresenter
|
||||
})
|
||||
@ -438,10 +351,6 @@ export function createModel (builder: Builder): void {
|
||||
chunter.action.CopyChatMessageLink
|
||||
)
|
||||
|
||||
builder.mixin(chunter.class.ChunterMessage, core.class.Class, view.mixin.ClassFilters, {
|
||||
filters: ['space', '_class']
|
||||
})
|
||||
|
||||
builder.mixin(chunter.class.Channel, core.class.Class, view.mixin.ClassFilters, {
|
||||
filters: []
|
||||
})
|
||||
|
@ -21,7 +21,8 @@ import core, {
|
||||
type Doc,
|
||||
type Domain,
|
||||
type Ref,
|
||||
type Space
|
||||
type Space,
|
||||
DOMAIN_TX
|
||||
} from '@hcengineering/core'
|
||||
import {
|
||||
tryMigrate,
|
||||
@ -35,6 +36,7 @@ import notification from '@hcengineering/notification'
|
||||
import contactPlugin, { type PersonAccount } from '@hcengineering/contact'
|
||||
|
||||
import chunter from './plugin'
|
||||
import { DOMAIN_CHUNTER } from './index'
|
||||
|
||||
export const DOMAIN_COMMENT = 'comment' as Domain
|
||||
|
||||
@ -175,6 +177,21 @@ async function removeBacklinks (client: MigrationClient): Promise<void> {
|
||||
})
|
||||
}
|
||||
|
||||
async function removeOldClasses (client: MigrationClient): Promise<void> {
|
||||
const classes = [
|
||||
'chunter:class:ChunterMessage',
|
||||
'chunter:class:Message',
|
||||
'chunter:class:Comment',
|
||||
'chunter:class:Backlink'
|
||||
] as Ref<Class<Doc>>[]
|
||||
|
||||
for (const _class of classes) {
|
||||
await client.deleteMany(DOMAIN_CHUNTER, { _class })
|
||||
await client.deleteMany(DOMAIN_TX, { objectClass: _class })
|
||||
await client.deleteMany(DOMAIN_TX, { 'tx.objectClass': _class })
|
||||
}
|
||||
}
|
||||
|
||||
export const chunterOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {
|
||||
await tryMigrate(client, chunterId, [
|
||||
@ -207,6 +224,12 @@ export const chunterOperation: MigrateOperation = {
|
||||
(msg) => (msg as ThreadMessage).objectClass
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
state: 'remove-old-classes',
|
||||
func: async (client) => {
|
||||
await removeOldClasses(client)
|
||||
}
|
||||
}
|
||||
])
|
||||
},
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { ActivityMessage, TxViewlet } from '@hcengineering/activity'
|
||||
import type { ActivityMessage } from '@hcengineering/activity'
|
||||
import { chunterId, type Channel } from '@hcengineering/chunter'
|
||||
import chunter from '@hcengineering/chunter-resources/src/plugin'
|
||||
import { type Client, type Doc, type Ref } from '@hcengineering/core'
|
||||
@ -26,8 +26,6 @@ import type { Action, ActionCategory, ViewAction, Viewlet, ViewletDescriptor } f
|
||||
export default mergeIds(chunterId, chunter, {
|
||||
component: {
|
||||
ChannelPresenter: '' as AnyComponent,
|
||||
DirectMessagePresenter: '' as AnyComponent,
|
||||
MessagePresenter: '' as AnyComponent,
|
||||
DmPresenter: '' as AnyComponent,
|
||||
ChannelsPanel: '' as AnyComponent,
|
||||
Chat: '' as AnyComponent,
|
||||
@ -80,17 +78,8 @@ export default mergeIds(chunterId, chunter, {
|
||||
Channels: '' as Ref<Viewlet>
|
||||
},
|
||||
ids: {
|
||||
TxCommentCreate: '' as Ref<TxViewlet>,
|
||||
TxCommentRemove: '' as Ref<TxViewlet>,
|
||||
TxMessageCreate: '' as Ref<TxViewlet>,
|
||||
TxChatMessageCreate: '' as Ref<TxViewlet>,
|
||||
TxChatMessageRemove: '' as Ref<TxViewlet>,
|
||||
ChunterNotificationGroup: '' as Ref<NotificationGroup>
|
||||
},
|
||||
activity: {
|
||||
TxCommentCreate: '' as AnyComponent,
|
||||
TxMessageCreate: '' as AnyComponent
|
||||
},
|
||||
space: {
|
||||
General: '' as Ref<Channel>,
|
||||
Random: '' as Ref<Channel>
|
||||
|
@ -376,15 +376,6 @@ export function createModel (builder: Builder): void {
|
||||
index: 100
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.TxViewlet, core.space.Model, {
|
||||
objectClass: contact.class.Person,
|
||||
icon: contact.icon.Person,
|
||||
txClass: core.class.TxUpdateDoc,
|
||||
labelComponent: contact.activity.TxNameChange,
|
||||
display: 'inline',
|
||||
match: { 'operations.name': { $exists: true } }
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.DocUpdateMessageViewlet, core.space.Model, {
|
||||
objectClass: contact.class.Person,
|
||||
action: 'update',
|
||||
|
@ -27,7 +27,6 @@ import { type ChatMessageViewlet } from '@hcengineering/chunter'
|
||||
|
||||
export default mergeIds(contactId, contact, {
|
||||
activity: {
|
||||
TxNameChange: '' as AnyComponent,
|
||||
NameChangedActivityMessage: '' as AnyComponent
|
||||
},
|
||||
component: {
|
||||
|
@ -244,21 +244,6 @@ export function createModel (builder: Builder): void {
|
||||
)
|
||||
|
||||
// Workflow
|
||||
builder.createDoc(
|
||||
activity.class.TxViewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
objectClass: documents.class.DocumentRequest,
|
||||
icon: request.icon.Requests,
|
||||
txClass: core.class.TxCreateDoc,
|
||||
component: request.activity.TxCreateRequest,
|
||||
label: request.string.CreatedRequest,
|
||||
labelComponent: request.activity.RequestLabel,
|
||||
display: 'emphasized'
|
||||
},
|
||||
request.ids.TxRequestCreate
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
|
@ -162,20 +162,6 @@ export function createModel (builder: Builder): void {
|
||||
gmail.integrationType.Gmail
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.TxViewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
objectClass: gmail.class.Message,
|
||||
icon: contact.icon.Email,
|
||||
txClass: core.class.TxCreateDoc,
|
||||
label: gmail.string.HaveWrittenEmail,
|
||||
labelComponent: gmail.activity.TxWriteMessage,
|
||||
display: 'inline'
|
||||
},
|
||||
gmail.ids.TxSharedCreate
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.DocUpdateMessageViewlet,
|
||||
core.space.Model,
|
||||
@ -189,22 +175,6 @@ export function createModel (builder: Builder): void {
|
||||
gmail.ids.GmailWriteMessageActivityViewlet
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.TxViewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
objectClass: gmail.class.SharedMessages,
|
||||
icon: contact.icon.Email,
|
||||
txClass: core.class.TxCreateDoc,
|
||||
component: gmail.activity.TxSharedCreate,
|
||||
label: gmail.string.SharedMessages,
|
||||
display: 'content',
|
||||
editable: true,
|
||||
hideOnRemove: true
|
||||
},
|
||||
gmail.ids.TxSharedCreate
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.DocUpdateMessageViewlet,
|
||||
core.space.Model,
|
||||
@ -272,24 +242,6 @@ export function createModel (builder: Builder): void {
|
||||
gmail.ids.EmailNotification
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.TxViewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
objectClass: gmail.class.Message,
|
||||
icon: contact.icon.Email,
|
||||
txClass: core.class.TxCreateDoc,
|
||||
match: {
|
||||
'attributes.incoming': true
|
||||
},
|
||||
label: gmail.string.NewIncomingMessage,
|
||||
display: 'inline',
|
||||
editable: false,
|
||||
hideOnRemove: true
|
||||
},
|
||||
gmail.ids.NewMessageNotification
|
||||
)
|
||||
|
||||
builder.mixin(gmail.class.Message, core.class.Class, core.mixin.FullTextSearchContext, {
|
||||
parentPropagate: false
|
||||
})
|
||||
|
@ -19,7 +19,7 @@ import { type IntlString, mergeIds, type Resource } from '@hcengineering/platfor
|
||||
import { gmailId } from '@hcengineering/gmail'
|
||||
import gmail from '@hcengineering/gmail-resources/src/plugin'
|
||||
import type { AnyComponent } from '@hcengineering/ui/src/types'
|
||||
import type { DocUpdateMessageViewlet, TxViewlet } from '@hcengineering/activity'
|
||||
import type { DocUpdateMessageViewlet } from '@hcengineering/activity'
|
||||
import { type Action } from '@hcengineering/view'
|
||||
import { type NotificationGroup } from '@hcengineering/notification'
|
||||
|
||||
@ -43,15 +43,11 @@ export default mergeIds(gmailId, gmail, {
|
||||
ConfigDescription: '' as IntlString
|
||||
},
|
||||
ids: {
|
||||
TxSharedCreate: '' as Ref<TxViewlet>,
|
||||
NewMessageNotification: '' as Ref<TxViewlet>,
|
||||
EmailNotificationGroup: '' as Ref<NotificationGroup>,
|
||||
GmailSharedMessageActivityViewlet: '' as Ref<DocUpdateMessageViewlet>,
|
||||
GmailWriteMessageActivityViewlet: '' as Ref<DocUpdateMessageViewlet>
|
||||
},
|
||||
activity: {
|
||||
TxSharedCreate: '' as AnyComponent,
|
||||
TxWriteMessage: '' as AnyComponent,
|
||||
GmailSharedMessage: '' as AnyComponent,
|
||||
GmailWriteMessage: '' as AnyComponent
|
||||
},
|
||||
|
@ -32,7 +32,8 @@ import {
|
||||
type Ref,
|
||||
type Space,
|
||||
type Timestamp,
|
||||
type Tx
|
||||
type Tx,
|
||||
type IndexingConfiguration
|
||||
} from '@hcengineering/core'
|
||||
import {
|
||||
ArrOf,
|
||||
@ -61,8 +62,6 @@ import {
|
||||
type CommonInboxNotification,
|
||||
type CommonNotificationType,
|
||||
type DocNotifyContext,
|
||||
type DocUpdateTx,
|
||||
type DocUpdates,
|
||||
type InboxNotification,
|
||||
type MentionInboxNotification,
|
||||
type NotificationContextPresenter,
|
||||
@ -78,7 +77,7 @@ import {
|
||||
type PushSubscription,
|
||||
type PushSubscriptionKeys
|
||||
} from '@hcengineering/notification'
|
||||
import { getEmbeddedLabel, type Asset, type IntlString, type Resource } from '@hcengineering/platform'
|
||||
import { type Asset, type IntlString, type Resource } from '@hcengineering/platform'
|
||||
import setting from '@hcengineering/setting'
|
||||
import { type AnyComponent, type Location } from '@hcengineering/ui/src/types'
|
||||
|
||||
@ -185,25 +184,6 @@ export class TNotificationContextPresenter extends TClass implements Notificatio
|
||||
labelPresenter?: AnyComponent
|
||||
}
|
||||
|
||||
@Model(notification.class.DocUpdates, core.class.Doc, DOMAIN_NOTIFICATION)
|
||||
export class TDocUpdates extends TDoc implements DocUpdates {
|
||||
@Prop(TypeRef(core.class.Account), core.string.Account)
|
||||
@Index(IndexKind.Indexed)
|
||||
user!: Ref<Account>
|
||||
|
||||
@Prop(TypeRef(core.class.Account), core.string.AttachedTo)
|
||||
@Index(IndexKind.Indexed)
|
||||
attachedTo!: Ref<Doc>
|
||||
|
||||
@Prop(TypeRef(core.class.Account), getEmbeddedLabel('Hidden'))
|
||||
// @Index(IndexKind.Indexed)
|
||||
hidden!: boolean
|
||||
|
||||
attachedToClass!: Ref<Class<Doc>>
|
||||
lastTxTime?: Timestamp
|
||||
txes!: DocUpdateTx[]
|
||||
}
|
||||
|
||||
@Model(notification.class.DocNotifyContext, core.class.Doc, DOMAIN_NOTIFICATION)
|
||||
export class TDocNotifyContext extends TDoc implements DocNotifyContext {
|
||||
@Prop(TypeRef(core.class.Account), core.string.Account)
|
||||
@ -339,7 +319,6 @@ export function createModel (builder: Builder): void {
|
||||
TNotificationPreferencesGroup,
|
||||
TClassCollaborators,
|
||||
TCollaborators,
|
||||
TDocUpdates,
|
||||
TNotificationObjectPresenter,
|
||||
TNotificationPreview,
|
||||
TDocNotifyContext,
|
||||
@ -455,36 +434,6 @@ export function createModel (builder: Builder): void {
|
||||
notification.ids.CollaboratoAddNotification
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.TxViewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
objectClass: notification.mixin.Collaborators,
|
||||
icon: notification.icon.Notifications,
|
||||
txClass: core.class.TxMixin,
|
||||
component: notification.activity.TxCollaboratorsChange,
|
||||
display: 'inline',
|
||||
editable: false,
|
||||
hideOnRemove: true
|
||||
},
|
||||
notification.ids.TxCollaboratorsChange
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.TxViewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
objectClass: chunter.class.DirectMessage,
|
||||
icon: chunter.icon.Chunter,
|
||||
txClass: core.class.TxCreateDoc,
|
||||
component: notification.activity.TxDmCreation,
|
||||
display: 'inline',
|
||||
editable: false,
|
||||
hideOnRemove: true
|
||||
},
|
||||
notification.ids.TxDmCreation
|
||||
)
|
||||
|
||||
builder.createDoc(notification.class.ActivityNotificationViewlet, core.space.Model, {
|
||||
presenter: notification.component.NotificationCollaboratorsChanged,
|
||||
messageMatch: {
|
||||
@ -688,6 +637,16 @@ export function createModel (builder: Builder): void {
|
||||
indexes: [{ user: 1, archived: 1 }],
|
||||
disabled: [{ modifiedOn: 1 }, { modifiedBy: 1 }, { createdBy: 1 }, { isViewed: 1 }, { hidden: 1 }]
|
||||
})
|
||||
|
||||
builder.mixin<Class<DocNotifyContext>, IndexingConfiguration<DocNotifyContext>>(
|
||||
notification.class.DocNotifyContext,
|
||||
core.class.Class,
|
||||
core.mixin.IndexConfiguration,
|
||||
{
|
||||
searchDisabled: true,
|
||||
indexes: []
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function generateClassNotificationTypes (
|
||||
|
@ -13,20 +13,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import activity, { type ActivityMessage, type DocUpdateMessage } from '@hcengineering/activity'
|
||||
import {
|
||||
DOMAIN_TX,
|
||||
TxProcessor,
|
||||
generateId,
|
||||
type AttachedDoc,
|
||||
type Doc,
|
||||
type Domain,
|
||||
type Ref,
|
||||
type TxCUD,
|
||||
type TxCollectionCUD,
|
||||
type Class,
|
||||
type DocumentQuery
|
||||
} from '@hcengineering/core'
|
||||
import { type Doc, type Ref, type Class, type DocumentQuery, DOMAIN_TX } from '@hcengineering/core'
|
||||
import {
|
||||
createDefaultSpace,
|
||||
tryMigrate,
|
||||
@ -35,189 +22,10 @@ import {
|
||||
type MigrationClient,
|
||||
type MigrationUpgradeClient
|
||||
} from '@hcengineering/model'
|
||||
import notification, {
|
||||
notificationId,
|
||||
type ActivityInboxNotification,
|
||||
type DocNotifyContext,
|
||||
type DocUpdateTx,
|
||||
type DocUpdates
|
||||
} from '@hcengineering/notification'
|
||||
import notification, { notificationId, type DocNotifyContext } from '@hcengineering/notification'
|
||||
|
||||
import { DOMAIN_NOTIFICATION } from './index'
|
||||
|
||||
interface InboxData {
|
||||
context: DocNotifyContext
|
||||
notifications: ActivityInboxNotification[]
|
||||
}
|
||||
|
||||
const DOMAIN_ACTIVITY = 'activity' as Domain
|
||||
|
||||
async function getActivityMessages (
|
||||
client: MigrationClient,
|
||||
contexts: {
|
||||
context: DocNotifyContext
|
||||
txes: DocUpdateTx[]
|
||||
}[]
|
||||
): Promise<ActivityInboxNotification[]> {
|
||||
const result: ActivityInboxNotification[] = []
|
||||
const txes = contexts.flatMap((it) => it.txes)
|
||||
const docUpdateMessages = await client.find<DocUpdateMessage>(DOMAIN_ACTIVITY, {
|
||||
_class: activity.class.DocUpdateMessage,
|
||||
txId: { $in: txes.map((it) => it._id) },
|
||||
attachedTo: { $in: contexts.map((it) => it.context.attachedTo) }
|
||||
})
|
||||
|
||||
if (docUpdateMessages.length > 0) {
|
||||
docUpdateMessages.forEach((message) => {
|
||||
const ctx = contexts.find((it) => it.context.attachedTo === message.attachedTo)
|
||||
if (ctx === undefined) {
|
||||
return
|
||||
}
|
||||
const tx = ctx.txes.find((it) => it._id === (message.txId as any))
|
||||
if (tx == null) {
|
||||
return
|
||||
}
|
||||
result.push({
|
||||
_id: generateId(),
|
||||
_class: notification.class.ActivityInboxNotification,
|
||||
space: ctx.context.space,
|
||||
user: ctx.context.user,
|
||||
isViewed: !tx.isNew,
|
||||
attachedTo: message._id,
|
||||
attachedToClass: message._class,
|
||||
docNotifyContext: ctx.context._id,
|
||||
title: tx.title,
|
||||
body: tx.body,
|
||||
intlParams: tx.intlParams,
|
||||
intlParamsNotLocalized: tx.intlParamsNotLocalized,
|
||||
modifiedOn: tx.modifiedOn,
|
||||
modifiedBy: tx.modifiedBy,
|
||||
createdOn: tx.modifiedOn,
|
||||
createdBy: tx.modifiedBy
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const originTx: TxCUD<Doc>[] = await client.find<TxCUD<Doc>>(DOMAIN_TX, { _id: { $in: txes.map((it) => it._id) } })
|
||||
|
||||
if (originTx.length === 0) {
|
||||
return result
|
||||
}
|
||||
|
||||
const innerTx = originTx.map((it) => TxProcessor.extractTx(it as TxCollectionCUD<Doc, AttachedDoc>) as TxCUD<Doc>)
|
||||
|
||||
;(
|
||||
await client.find<ActivityMessage>(DOMAIN_ACTIVITY, {
|
||||
_id: { $in: innerTx.map((it) => it.objectId as Ref<ActivityMessage>) }
|
||||
})
|
||||
)
|
||||
.filter(({ _class }) => client.hierarchy.isDerived(_class, activity.class.ActivityMessage))
|
||||
.forEach((message) => {
|
||||
const tx = originTx.find((q) => (TxProcessor.extractTx(q) as TxCUD<Doc>).objectId === message._id)
|
||||
if (tx == null) {
|
||||
return
|
||||
}
|
||||
|
||||
const ctx = contexts.find((it) => it.context.attachedTo === message.attachedTo)
|
||||
if (ctx === undefined) {
|
||||
return
|
||||
}
|
||||
const docTx = ctx.txes.find((it) => it._id === tx._id)
|
||||
if (docTx == null) {
|
||||
return
|
||||
}
|
||||
result.push({
|
||||
_id: generateId(),
|
||||
_class: notification.class.ActivityInboxNotification,
|
||||
space: ctx.context.space,
|
||||
user: ctx.context.user,
|
||||
isViewed: !docTx.isNew,
|
||||
attachedTo: message._id,
|
||||
attachedToClass: message._class,
|
||||
docNotifyContext: ctx.context._id,
|
||||
title: docTx.title,
|
||||
body: docTx.body,
|
||||
intlParams: docTx.intlParams,
|
||||
intlParamsNotLocalized: docTx.intlParamsNotLocalized,
|
||||
modifiedOn: docTx.modifiedOn,
|
||||
modifiedBy: docTx.modifiedBy,
|
||||
createdOn: docTx.modifiedOn,
|
||||
createdBy: docTx.modifiedBy
|
||||
})
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
async function getInboxData (client: MigrationClient, docUpdates: DocUpdates[]): Promise<InboxData[]> {
|
||||
const toProcess = docUpdates.filter((it) => !it.hidden && client.hierarchy.hasClass(it.attachedToClass))
|
||||
|
||||
const contexts = toProcess.map((docUpdate) => {
|
||||
const newTxIndex = docUpdate.txes.findIndex(({ isNew }) => isNew)
|
||||
|
||||
const context: DocNotifyContext = {
|
||||
_id: docUpdate._id,
|
||||
_class: notification.class.DocNotifyContext,
|
||||
space: docUpdate.space,
|
||||
user: docUpdate.user,
|
||||
attachedTo: docUpdate.attachedTo,
|
||||
attachedToClass: docUpdate.attachedToClass,
|
||||
hidden: docUpdate.hidden,
|
||||
lastViewedTimestamp: newTxIndex !== -1 ? docUpdate.txes[newTxIndex - 1]?.modifiedOn : docUpdate.lastTxTime,
|
||||
lastUpdateTimestamp: docUpdate.lastTxTime,
|
||||
modifiedBy: docUpdate.modifiedBy,
|
||||
modifiedOn: docUpdate.modifiedOn,
|
||||
createdBy: docUpdate.createdBy,
|
||||
createdOn: docUpdate.createdOn
|
||||
}
|
||||
|
||||
return {
|
||||
context,
|
||||
txes: docUpdate.txes
|
||||
}
|
||||
})
|
||||
|
||||
const notifications = await getActivityMessages(client, contexts)
|
||||
return contexts.map((it) => ({
|
||||
context: it.context,
|
||||
notifications: notifications.filter((nit) => nit.docNotifyContext === it.context._id)
|
||||
}))
|
||||
}
|
||||
|
||||
async function migrateInboxNotifications (client: MigrationClient): Promise<void> {
|
||||
let processing = 0
|
||||
while (true) {
|
||||
const docUpdates = await client.find<DocUpdates>(
|
||||
DOMAIN_NOTIFICATION,
|
||||
{
|
||||
_class: notification.class.DocUpdates
|
||||
},
|
||||
{ limit: 1000 }
|
||||
)
|
||||
|
||||
console.log('notifications processing:', processing)
|
||||
|
||||
if (docUpdates.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
processing += docUpdates.length
|
||||
|
||||
const data: InboxData[] = (await getInboxData(client, docUpdates)).filter(
|
||||
(data): data is InboxData => data !== undefined
|
||||
)
|
||||
|
||||
await client.deleteMany(DOMAIN_NOTIFICATION, { _id: { $in: docUpdates.map(({ _id }) => _id) } })
|
||||
await client.create(
|
||||
DOMAIN_NOTIFICATION,
|
||||
data.map(({ context }) => context)
|
||||
)
|
||||
await client.create(
|
||||
DOMAIN_NOTIFICATION,
|
||||
data.flatMap(({ notifications }) => notifications)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function removeNotifications (
|
||||
client: MigrationClient,
|
||||
query: DocumentQuery<DocNotifyContext>
|
||||
@ -262,12 +70,6 @@ export async function removeNotifications (
|
||||
|
||||
export const notificationOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {
|
||||
await tryMigrate(client, notificationId, [
|
||||
{
|
||||
state: 'inbox-notifications',
|
||||
func: migrateInboxNotifications
|
||||
}
|
||||
])
|
||||
await tryMigrate(client, notificationId, [
|
||||
{
|
||||
state: 'delete-hidden-notifications',
|
||||
@ -284,6 +86,15 @@ export const notificationOperation: MigrateOperation = {
|
||||
}
|
||||
}
|
||||
])
|
||||
await tryMigrate(client, notificationId, [
|
||||
{
|
||||
state: 'remove-old-classes',
|
||||
func: async (client) => {
|
||||
await client.deleteMany(DOMAIN_NOTIFICATION, { _class: 'notification:class:DocUpdates' as Ref<Class<Doc>> })
|
||||
await client.deleteMany(DOMAIN_TX, { objectClass: 'notification:class:DocUpdates' as Ref<Class<Doc>> })
|
||||
}
|
||||
}
|
||||
])
|
||||
},
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {
|
||||
await tryUpgrade(client, notificationId, [
|
||||
|
@ -20,7 +20,7 @@ import { type IntlString, type Resource, mergeIds } from '@hcengineering/platfor
|
||||
import { type AnyComponent } from '@hcengineering/ui/src/types'
|
||||
import { type Action, type ActionCategory, type ViewAction } from '@hcengineering/view'
|
||||
import { type Application } from '@hcengineering/workbench'
|
||||
import { type DocUpdateMessageViewlet, type TxViewlet } from '@hcengineering/activity'
|
||||
import { type DocUpdateMessageViewlet } from '@hcengineering/activity'
|
||||
|
||||
export default mergeIds(notificationId, notification, {
|
||||
string: {
|
||||
@ -41,12 +41,7 @@ export default mergeIds(notificationId, notification, {
|
||||
Notification: '' as Ref<Application>,
|
||||
Inbox: '' as Ref<Application>
|
||||
},
|
||||
activity: {
|
||||
TxDmCreation: '' as AnyComponent
|
||||
},
|
||||
ids: {
|
||||
TxCollaboratorsChange: '' as Ref<TxViewlet>,
|
||||
TxDmCreation: '' as Ref<TxViewlet>,
|
||||
CollaboratorsChangedMessage: '' as Ref<DocUpdateMessageViewlet>
|
||||
},
|
||||
component: {
|
||||
|
@ -151,20 +151,6 @@ export function createModel (builder: Builder): void {
|
||||
['comments', 'approved', 'rejected', 'status']
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.TxViewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
objectClass: request.class.Request,
|
||||
icon: request.icon.Requests,
|
||||
txClass: core.class.TxCreateDoc,
|
||||
component: request.activity.TxCreateRequest,
|
||||
label: request.string.CreatedRequest,
|
||||
labelComponent: request.activity.RequestLabel,
|
||||
display: 'emphasized'
|
||||
},
|
||||
request.ids.TxRequestCreate
|
||||
)
|
||||
builder.createDoc(core.class.DomainIndexConfiguration, core.space.Model, {
|
||||
domain: DOMAIN_REQUEST,
|
||||
disabled: [
|
||||
|
@ -19,20 +19,14 @@ import { mergeIds } from '@hcengineering/platform'
|
||||
import { requestId } from '@hcengineering/request'
|
||||
import request from '@hcengineering/request-resources/src/plugin'
|
||||
import { type AnyComponent } from '@hcengineering/ui/src/types'
|
||||
import type { TxViewlet } from '@hcengineering/activity'
|
||||
import type { NotificationGroup, NotificationType } from '@hcengineering/notification'
|
||||
|
||||
export default mergeIds(requestId, request, {
|
||||
activity: {
|
||||
TxCreateRequest: '' as AnyComponent,
|
||||
RequestLabel: '' as AnyComponent
|
||||
},
|
||||
component: {
|
||||
EditRequest: '' as AnyComponent,
|
||||
NotificationRequestView: '' as AnyComponent
|
||||
},
|
||||
ids: {
|
||||
TxRequestCreate: '' as Ref<TxViewlet>,
|
||||
RequestNotificationGroup: '' as Ref<NotificationGroup>,
|
||||
CreateRequestNotification: '' as Ref<NotificationType>
|
||||
},
|
||||
|
@ -355,22 +355,6 @@ export function createModel (builder: Builder): void {
|
||||
setting.ids.SettingApp
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.TxViewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
objectClass: setting.class.Integration,
|
||||
icon: setting.icon.Integrations,
|
||||
txClass: core.class.TxUpdateDoc,
|
||||
label: setting.string.IntegrationWith,
|
||||
labelComponent: setting.activity.TxIntegrationDisable,
|
||||
display: 'inline',
|
||||
editable: false,
|
||||
hideOnRemove: true
|
||||
},
|
||||
setting.ids.TxIntegrationDisable
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.DocUpdateMessageViewlet,
|
||||
core.space.Model,
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { DocUpdateMessageViewlet, TxViewlet } from '@hcengineering/activity'
|
||||
import type { DocUpdateMessageViewlet } from '@hcengineering/activity'
|
||||
import { type Doc, type Ref } from '@hcengineering/core'
|
||||
import { type IntlString, mergeIds, type Resource } from '@hcengineering/platform'
|
||||
import { settingId } from '@hcengineering/setting'
|
||||
@ -24,11 +24,7 @@ import { type TemplateFieldFunc } from '@hcengineering/templates'
|
||||
import { type NotificationGroup, type NotificationType } from '@hcengineering/notification'
|
||||
|
||||
export default mergeIds(settingId, setting, {
|
||||
activity: {
|
||||
TxIntegrationDisable: '' as AnyComponent
|
||||
},
|
||||
ids: {
|
||||
TxIntegrationDisable: '' as Ref<TxViewlet>,
|
||||
EnumSetting: '' as Ref<Doc>,
|
||||
Configure: '' as Ref<Doc>,
|
||||
SettingNotificationGroup: '' as Ref<NotificationGroup>,
|
||||
|
@ -123,19 +123,6 @@ export function createModel (builder: Builder): void {
|
||||
telegram.templateField.IntegrationOwnerTG
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.TxViewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
objectClass: telegram.class.Message,
|
||||
icon: contact.icon.Telegram,
|
||||
txClass: core.class.TxCreateDoc,
|
||||
component: telegram.activity.TxMessage,
|
||||
display: 'inline'
|
||||
},
|
||||
telegram.ids.TxMessage
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.DocUpdateMessageViewlet,
|
||||
core.space.Model,
|
||||
@ -177,22 +164,6 @@ export function createModel (builder: Builder): void {
|
||||
telegram.integrationType.Telegram
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.TxViewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
objectClass: telegram.class.SharedMessages,
|
||||
icon: contact.icon.Telegram,
|
||||
txClass: core.class.TxCreateDoc,
|
||||
component: telegram.activity.TxSharedCreate,
|
||||
label: telegram.string.SharedMessages,
|
||||
display: 'content',
|
||||
editable: true,
|
||||
hideOnRemove: true
|
||||
},
|
||||
telegram.ids.TxSharedCreate
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.DocUpdateMessageViewlet,
|
||||
core.space.Model,
|
||||
@ -235,24 +206,6 @@ export function createModel (builder: Builder): void {
|
||||
telegram.ids.NewMessageNotification
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.TxViewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
objectClass: telegram.class.Message,
|
||||
icon: contact.icon.Telegram,
|
||||
txClass: core.class.TxCreateDoc,
|
||||
match: {
|
||||
'attributes.incoming': true
|
||||
},
|
||||
label: telegram.string.NewIncomingMessage,
|
||||
display: 'inline',
|
||||
editable: false,
|
||||
hideOnRemove: true
|
||||
},
|
||||
telegram.ids.NewMessageNotificationViewlet
|
||||
)
|
||||
|
||||
builder.mixin(telegram.class.Message, core.class.Class, core.mixin.FullTextSearchContext, {
|
||||
parentPropagate: false,
|
||||
childProcessingAllowed: true
|
||||
|
@ -19,7 +19,7 @@ import { type IntlString, type Resource, mergeIds } from '@hcengineering/platfor
|
||||
import { telegramId } from '@hcengineering/telegram'
|
||||
import telegram from '@hcengineering/telegram-resources/src/plugin'
|
||||
import type { AnyComponent } from '@hcengineering/ui/src/types'
|
||||
import type { DocUpdateMessageViewlet, TxViewlet } from '@hcengineering/activity'
|
||||
import type { DocUpdateMessageViewlet } from '@hcengineering/activity'
|
||||
import { type TemplateFieldFunc } from '@hcengineering/templates'
|
||||
import { type NotificationGroup } from '@hcengineering/notification'
|
||||
|
||||
@ -39,9 +39,6 @@ export default mergeIds(telegramId, telegram, {
|
||||
NewIncomingMessage: '' as IntlString
|
||||
},
|
||||
ids: {
|
||||
TxMessage: '' as Ref<TxViewlet>,
|
||||
TxSharedCreate: '' as Ref<TxViewlet>,
|
||||
NewMessageNotificationViewlet: '' as Ref<TxViewlet>,
|
||||
NotificationGroup: '' as Ref<NotificationGroup>,
|
||||
TelegramMessageSharedActivityViewlet: '' as Ref<DocUpdateMessageViewlet>,
|
||||
TelegramMessageCreatedActivityViewlet: '' as Ref<DocUpdateMessageViewlet>
|
||||
@ -51,8 +48,6 @@ export default mergeIds(telegramId, telegram, {
|
||||
GetIntegrationOwnerTG: '' as Resource<TemplateFieldFunc>
|
||||
},
|
||||
activity: {
|
||||
TxMessage: '' as AnyComponent,
|
||||
TxSharedCreate: '' as AnyComponent,
|
||||
TelegramMessageCreated: '' as AnyComponent
|
||||
}
|
||||
})
|
||||
|
@ -21,7 +21,6 @@ import { type Action, type ActionCategory } from '@hcengineering/view'
|
||||
import { timeId } from '@hcengineering/time'
|
||||
import time from '@hcengineering/time-resources/src/plugin'
|
||||
import { type NotificationGroup, type NotificationType } from '@hcengineering/notification'
|
||||
import { type TxViewlet } from '@hcengineering/activity'
|
||||
|
||||
export default mergeIds(timeId, time, {
|
||||
action: {
|
||||
@ -59,8 +58,7 @@ export default mergeIds(timeId, time, {
|
||||
},
|
||||
ids: {
|
||||
TimeNotificationGroup: '' as Ref<NotificationGroup>,
|
||||
ToDoCreated: '' as Ref<NotificationType>,
|
||||
TxToDoCreated: '' as Ref<TxViewlet>
|
||||
ToDoCreated: '' as Ref<NotificationType>
|
||||
},
|
||||
function: {
|
||||
ToDoTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>
|
||||
|
@ -540,19 +540,6 @@ export function createModel (builder: Builder): void {
|
||||
component: tracker.component.EditIssueTemplate
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.TxViewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
objectClass: tracker.class.Issue,
|
||||
icon: tracker.icon.Issue,
|
||||
txClass: core.class.TxCreateDoc,
|
||||
labelComponent: tracker.activity.TxIssueCreated,
|
||||
display: 'inline'
|
||||
},
|
||||
tracker.ids.TxIssueCreated
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.DocUpdateMessageViewlet,
|
||||
core.space.Model,
|
||||
|
@ -13,7 +13,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
import { type DocUpdateMessageViewlet, type TxViewlet } from '@hcengineering/activity'
|
||||
import { type DocUpdateMessageViewlet } from '@hcengineering/activity'
|
||||
import { type ChatMessageViewlet } from '@hcengineering/chunter'
|
||||
import { type StatusCategory, type Doc, type Ref } from '@hcengineering/core'
|
||||
import { type ObjectSearchCategory, type ObjectSearchFactory } from '@hcengineering/model-presentation'
|
||||
@ -48,7 +48,6 @@ export default mergeIds(trackerId, tracker, {
|
||||
Extensions: '' as IntlString
|
||||
},
|
||||
activity: {
|
||||
TxIssueCreated: '' as AnyComponent,
|
||||
StatusIcon: '' as AnyComponent,
|
||||
PriorityIcon: '' as AnyComponent
|
||||
},
|
||||
@ -77,7 +76,6 @@ export default mergeIds(trackerId, tracker, {
|
||||
ProjectList: '' as Ref<Viewlet>
|
||||
},
|
||||
ids: {
|
||||
TxIssueCreated: '' as Ref<TxViewlet>,
|
||||
TrackerNotificationGroup: '' as Ref<NotificationGroup>,
|
||||
AssigneeNotification: '' as Ref<NotificationType>,
|
||||
BaseProjectType: '' as Ref<ProjectType>,
|
||||
|
@ -1,58 +1,8 @@
|
||||
import activity, { type DisplayTx, type SavedMessage } from '@hcengineering/activity'
|
||||
import core, {
|
||||
type Class,
|
||||
type Doc,
|
||||
type Hierarchy,
|
||||
type Ref,
|
||||
SortingOrder,
|
||||
type Tx,
|
||||
type TxCreateDoc,
|
||||
type TxCUD,
|
||||
type TxMixin,
|
||||
TxProcessor,
|
||||
type TxUpdateDoc,
|
||||
type WithLookup
|
||||
} from '@hcengineering/core'
|
||||
import activity, { type SavedMessage } from '@hcengineering/activity'
|
||||
import { SortingOrder, type WithLookup } from '@hcengineering/core'
|
||||
import { writable } from 'svelte/store'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
|
||||
// TODO: remove old code
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ActivityKey = string
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
// TODO: remove old code
|
||||
export function activityKey (objectClass: Ref<Class<Doc>>, txClass: Ref<Class<Tx>>): ActivityKey {
|
||||
return objectClass + ':' + txClass
|
||||
}
|
||||
|
||||
// TODO: remove old code
|
||||
export function newDisplayTx (
|
||||
tx: TxCUD<Doc>,
|
||||
hierarchy: Hierarchy,
|
||||
isOwnTx: boolean,
|
||||
originTx: TxCUD<Doc> = tx
|
||||
): DisplayTx {
|
||||
const createTx = hierarchy.isDerived(tx._class, core.class.TxCreateDoc) ? (tx as TxCreateDoc<Doc>) : undefined
|
||||
return {
|
||||
tx,
|
||||
isOwnTx,
|
||||
txes: [],
|
||||
createTx,
|
||||
updateTx: hierarchy.isDerived(tx._class, core.class.TxUpdateDoc) ? (tx as TxUpdateDoc<Doc>) : undefined,
|
||||
updated: false,
|
||||
removed: false,
|
||||
mixin: false,
|
||||
mixinTx: hierarchy.isDerived(tx._class, core.class.TxMixin) ? (tx as TxMixin<Doc, Doc>) : undefined,
|
||||
doc: createTx !== undefined ? TxProcessor.createDoc2Doc(createTx) : undefined,
|
||||
originTx
|
||||
}
|
||||
}
|
||||
|
||||
export const savedMessagesStore = writable<Array<WithLookup<SavedMessage>>>([])
|
||||
|
||||
const savedMessagesQuery = createQuery(true)
|
||||
|
@ -1,411 +1,14 @@
|
||||
import { get } from 'svelte/store'
|
||||
import type { ActivityMessage, DisplayTx, Reaction, TxViewlet } from '@hcengineering/activity'
|
||||
import core, {
|
||||
type AttachedDoc,
|
||||
type Class,
|
||||
type Client,
|
||||
type Collection,
|
||||
type Doc,
|
||||
type Hierarchy,
|
||||
type Obj,
|
||||
type Ref,
|
||||
type TxCUD,
|
||||
type TxCollectionCUD,
|
||||
type TxCreateDoc,
|
||||
type TxMixin,
|
||||
type TxOperations,
|
||||
TxProcessor,
|
||||
type TxUpdateDoc,
|
||||
matchQuery,
|
||||
getCurrentAccount,
|
||||
isOtherHour
|
||||
} from '@hcengineering/core'
|
||||
import { type Asset, type IntlString, getResource, translate } from '@hcengineering/platform'
|
||||
import { getAttributePresenterClass, getClient } from '@hcengineering/presentation'
|
||||
import {
|
||||
type AnyComponent,
|
||||
type AnySvelteComponent,
|
||||
ErrorPresenter,
|
||||
themeStore,
|
||||
type Location,
|
||||
getEventPositionElement,
|
||||
closePopup,
|
||||
showPopup,
|
||||
EmojiPopup
|
||||
} from '@hcengineering/ui'
|
||||
import view, { type AttributeModel, type BuildModelKey, type BuildModelOptions } from '@hcengineering/view'
|
||||
import { getObjectPresenter } from '@hcengineering/view-resources'
|
||||
import type { ActivityMessage, Reaction } from '@hcengineering/activity'
|
||||
import core, { type Doc, type Ref, type TxOperations, getCurrentAccount, isOtherHour } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { type Location, getEventPositionElement, closePopup, showPopup, EmojiPopup } from '@hcengineering/ui'
|
||||
import { type AttributeModel } from '@hcengineering/view'
|
||||
import preference from '@hcengineering/preference'
|
||||
|
||||
import { type ActivityKey, activityKey, savedMessagesStore } from './activity'
|
||||
import { savedMessagesStore } from './activity'
|
||||
import activity from './plugin'
|
||||
|
||||
const valueTypes: ReadonlyArray<Ref<Class<Doc>>> = [
|
||||
core.class.TypeString,
|
||||
core.class.EnumOf,
|
||||
core.class.TypeNumber,
|
||||
core.class.TypeDate,
|
||||
core.class.TypeMarkup,
|
||||
core.class.TypeCollaborativeMarkup
|
||||
]
|
||||
|
||||
export type TxDisplayViewlet =
|
||||
| (Pick<TxViewlet, 'icon' | 'label' | 'display' | 'editable' | 'hideOnRemove' | 'labelComponent' | 'labelParams'> & {
|
||||
component?: AnyComponent | AnySvelteComponent
|
||||
pseudo: boolean
|
||||
})
|
||||
| undefined
|
||||
|
||||
async function createPseudoViewlet (
|
||||
client: TxOperations,
|
||||
dtx: DisplayTx,
|
||||
label: IntlString,
|
||||
display: 'inline' | 'content' | 'emphasized' = 'inline'
|
||||
): Promise<TxDisplayViewlet> {
|
||||
const docClass: Class<Doc> = client.getModel().getObject(dtx.tx.objectClass)
|
||||
|
||||
const language = get(themeStore).language
|
||||
let trLabel = docClass.label !== undefined ? await translate(docClass.label, {}, language) : undefined
|
||||
if (dtx.collectionAttribute !== undefined) {
|
||||
const itemLabel = (dtx.collectionAttribute.type as Collection<AttachedDoc>).itemLabel
|
||||
if (itemLabel !== undefined) {
|
||||
trLabel = await translate(itemLabel, {}, language)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if it is attached doc and collection have title override.
|
||||
const presenter = await getObjectPresenter(client, dtx.tx.objectClass, { key: 'doc-presenter' }, false, false)
|
||||
if (presenter !== undefined) {
|
||||
let collection = ''
|
||||
if (dtx.collectionAttribute?.label !== undefined) {
|
||||
collection = await translate(dtx.collectionAttribute.label, {}, language)
|
||||
}
|
||||
return {
|
||||
display,
|
||||
icon: docClass.icon ?? activity.icon.Activity,
|
||||
label,
|
||||
labelParams: {
|
||||
_class: trLabel,
|
||||
collection
|
||||
},
|
||||
component: presenter.presenter,
|
||||
pseudo: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getDTxProps (dtx: DisplayTx): any {
|
||||
return { tx: dtx.tx, value: dtx.doc, isOwnTx: dtx.isOwnTx, prevValue: dtx.prevDoc }
|
||||
}
|
||||
|
||||
function getViewlet (
|
||||
viewlets: Map<ActivityKey, TxViewlet[]>,
|
||||
dtx: DisplayTx,
|
||||
hierarchy: Hierarchy
|
||||
): TxDisplayViewlet | undefined {
|
||||
let key: string
|
||||
if (dtx.mixinTx?.mixin !== undefined && dtx.tx._id === dtx.mixinTx._id) {
|
||||
key = activityKey(dtx.mixinTx.mixin, dtx.tx._class)
|
||||
} else {
|
||||
key = activityKey(dtx.tx.objectClass, dtx.tx._class)
|
||||
}
|
||||
const vl = viewlets.get(key)
|
||||
if (vl !== undefined) {
|
||||
for (const viewlet of vl) {
|
||||
if (viewlet.match === undefined) {
|
||||
return { ...viewlet, pseudo: false }
|
||||
}
|
||||
const res = matchQuery([dtx.tx], viewlet.match, dtx.tx._class, hierarchy)
|
||||
if (res.length > 0) {
|
||||
return { ...viewlet, pseudo: false }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateViewlet (
|
||||
client: TxOperations,
|
||||
viewlets: Map<ActivityKey, TxViewlet[]>,
|
||||
dtx: DisplayTx
|
||||
): Promise<{
|
||||
viewlet: TxDisplayViewlet
|
||||
id: Ref<TxCUD<Doc>>
|
||||
model: AttributeModel[]
|
||||
props: any
|
||||
modelIcon: Asset | undefined
|
||||
iconComponent: AnyComponent | undefined
|
||||
}> {
|
||||
let viewlet = getViewlet(viewlets, dtx, client.getHierarchy())
|
||||
|
||||
const props = getDTxProps(dtx)
|
||||
let model: AttributeModel[] = []
|
||||
let modelIcon: Asset | undefined
|
||||
let iconComponent: AnyComponent | undefined
|
||||
|
||||
if (viewlet === undefined) {
|
||||
;({ viewlet, model } = await checkInlineViewlets(dtx, viewlet, client, model, dtx.isOwnTx))
|
||||
if (model !== undefined) {
|
||||
// Check for State attribute
|
||||
for (const a of model) {
|
||||
if (a.icon !== undefined) {
|
||||
modelIcon = a.icon
|
||||
break
|
||||
}
|
||||
}
|
||||
for (const a of model) {
|
||||
if (a.attribute?.iconComponent !== undefined) {
|
||||
iconComponent = a.attribute?.iconComponent
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return { viewlet, id: dtx.tx._id, model, props, modelIcon, iconComponent }
|
||||
}
|
||||
|
||||
async function checkInlineViewlets (
|
||||
dtx: DisplayTx,
|
||||
viewlet: TxDisplayViewlet,
|
||||
client: TxOperations,
|
||||
model: AttributeModel[],
|
||||
isOwn: boolean
|
||||
): Promise<{ viewlet: TxDisplayViewlet, model: AttributeModel[] }> {
|
||||
if (dtx.collectionAttribute !== undefined && (dtx.txDocIds?.size ?? 0) > 1) {
|
||||
// Check if we have a class presenter we could have a pseudo viewlet based on class presenter.
|
||||
viewlet = await createPseudoViewlet(client, dtx, activity.string.CollectionUpdated, 'inline')
|
||||
} else if (dtx.tx._class === core.class.TxCreateDoc) {
|
||||
// Check if we have a class presenter we could have a pseudo viewlet based on class presenter.
|
||||
viewlet = await createPseudoViewlet(client, dtx, isOwn ? activity.string.DocCreated : activity.string.DocAdded)
|
||||
} else if (dtx.tx._class === core.class.TxRemoveDoc) {
|
||||
viewlet = await createPseudoViewlet(client, dtx, activity.string.DocDeleted)
|
||||
} else if (dtx.tx._class === core.class.TxUpdateDoc || dtx.tx._class === core.class.TxMixin) {
|
||||
model = await createUpdateModel(dtx, client, model)
|
||||
}
|
||||
return { viewlet, model }
|
||||
}
|
||||
|
||||
async function getAttributePresenter (
|
||||
client: Client,
|
||||
_class: Ref<Class<Obj>>,
|
||||
key: string,
|
||||
preserveKey: BuildModelKey
|
||||
): Promise<AttributeModel> {
|
||||
const hierarchy = client.getHierarchy()
|
||||
const attribute = hierarchy.getAttribute(_class, key)
|
||||
const presenterClass = getAttributePresenterClass(hierarchy, attribute)
|
||||
const isCollectionAttr = presenterClass.category === 'collection'
|
||||
const mixin = isCollectionAttr ? view.mixin.CollectionPresenter : view.mixin.ActivityAttributePresenter
|
||||
let presenterMixin = hierarchy.classHierarchyMixin(presenterClass.attrClass, mixin)
|
||||
if (presenterMixin?.presenter === undefined && mixin === view.mixin.ActivityAttributePresenter) {
|
||||
presenterMixin = hierarchy.classHierarchyMixin(presenterClass.attrClass, view.mixin.AttributePresenter)
|
||||
if (presenterMixin?.presenter === undefined) {
|
||||
throw new Error('attribute presenter not found for ' + JSON.stringify(preserveKey))
|
||||
}
|
||||
} else if (presenterMixin?.presenter === undefined) {
|
||||
throw new Error('attribute presenter not found for ' + JSON.stringify(preserveKey))
|
||||
}
|
||||
const resultKey = preserveKey.sortingKey ?? preserveKey.key
|
||||
const sortingKey = Array.isArray(resultKey)
|
||||
? resultKey
|
||||
: attribute.type._class === core.class.ArrOf
|
||||
? resultKey + '.length'
|
||||
: resultKey
|
||||
const presenter = await getResource(presenterMixin.presenter)
|
||||
|
||||
return {
|
||||
key: preserveKey.key,
|
||||
sortingKey,
|
||||
_class: presenterClass.attrClass,
|
||||
label: preserveKey.label ?? attribute.shortLabel ?? attribute.label,
|
||||
presenter,
|
||||
props: preserveKey.props,
|
||||
icon: presenterMixin.icon,
|
||||
attribute,
|
||||
collectionAttr: isCollectionAttr,
|
||||
isLookup: false
|
||||
}
|
||||
}
|
||||
|
||||
async function buildModel (options: BuildModelOptions): Promise<AttributeModel[]> {
|
||||
// eslint-disable-next-line array-callback-return
|
||||
const model = options.keys
|
||||
.map((key) => (typeof key === 'string' ? { key } : key))
|
||||
.map(async (key) => {
|
||||
try {
|
||||
return await getAttributePresenter(options.client, options._class, key.key, key)
|
||||
} catch (err: any) {
|
||||
if (options.ignoreMissing ?? false) {
|
||||
return undefined
|
||||
}
|
||||
const stringKey = key.label ?? key.key
|
||||
console.error('Failed to find presenter for', key, err)
|
||||
const errorPresenter: AttributeModel = {
|
||||
key: '',
|
||||
sortingKey: '',
|
||||
presenter: ErrorPresenter,
|
||||
label: stringKey as IntlString,
|
||||
_class: core.class.TypeString,
|
||||
props: { error: err },
|
||||
collectionAttr: false,
|
||||
isLookup: false
|
||||
}
|
||||
return errorPresenter
|
||||
}
|
||||
})
|
||||
return (await Promise.all(model)).filter((a) => a !== undefined) as AttributeModel[]
|
||||
}
|
||||
|
||||
async function createUpdateModel (
|
||||
dtx: DisplayTx,
|
||||
client: TxOperations,
|
||||
model: AttributeModel[]
|
||||
): Promise<AttributeModel[]> {
|
||||
if (dtx.updateTx !== undefined) {
|
||||
const _class = dtx.updateTx.objectClass
|
||||
const ops = {
|
||||
client,
|
||||
_class,
|
||||
keys: Object.entries(dtx.updateTx.operations)
|
||||
.flatMap(([id, val]) => (['$push', '$pull'].includes(id) ? Object.keys(val) : id))
|
||||
.filter((id) => !id.startsWith('$')),
|
||||
ignoreMissing: true
|
||||
}
|
||||
const hiddenAttrs = getHiddenAttrs(client, _class)
|
||||
model = (await buildModel(ops)).filter((x) => !hiddenAttrs.has(x.key))
|
||||
} else if (dtx.mixinTx !== undefined) {
|
||||
const _class = dtx.mixinTx.mixin
|
||||
const ops = {
|
||||
client,
|
||||
_class,
|
||||
keys: Object.keys(dtx.mixinTx.attributes).filter((id) => !id.startsWith('$')),
|
||||
ignoreMissing: true
|
||||
}
|
||||
const hiddenAttrs = getHiddenAttrs(client, _class)
|
||||
model = (await buildModel(ops)).filter((x) => !hiddenAttrs.has(x.key))
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
function getHiddenAttrs (client: TxOperations, _class: Ref<Class<Doc>>): Set<string> {
|
||||
return new Set(
|
||||
[...client.getHierarchy().getAllAttributes(_class).entries()]
|
||||
.filter(([, attr]) => attr.hidden === true)
|
||||
.map(([k]) => k)
|
||||
)
|
||||
}
|
||||
|
||||
function getModifiedAttributes (tx: DisplayTx): any[] {
|
||||
if (tx.tx._class === core.class.TxUpdateDoc) {
|
||||
return ([tx.tx, ...tx.txes.map(({ tx }) => tx)] as unknown as Array<TxUpdateDoc<Doc>>).map(
|
||||
({ operations }) => operations
|
||||
)
|
||||
}
|
||||
if (tx.tx._class === core.class.TxMixin) {
|
||||
return ([tx.tx, ...tx.txes.map(({ tx }) => tx)] as unknown as Array<TxMixin<Doc, Doc>>).map(
|
||||
({ attributes }) => attributes
|
||||
)
|
||||
}
|
||||
return [{}]
|
||||
}
|
||||
|
||||
async function buildRemovedDoc (
|
||||
client: TxOperations,
|
||||
objectId: Ref<Doc>,
|
||||
_class: Ref<Class<Doc>>
|
||||
): Promise<Doc | undefined> {
|
||||
const isAttached = client.getHierarchy().isDerived(_class, core.class.AttachedDoc)
|
||||
const txes = await client.findAll<TxCUD<Doc>>(
|
||||
isAttached ? core.class.TxCollectionCUD : core.class.TxCUD,
|
||||
isAttached
|
||||
? { 'tx.objectId': objectId as Ref<AttachedDoc> }
|
||||
: {
|
||||
objectId
|
||||
},
|
||||
{ sort: { modifiedOn: 1 } }
|
||||
)
|
||||
const createTx = isAttached
|
||||
? txes.find((tx) => (tx as TxCollectionCUD<Doc, AttachedDoc>).tx._class === core.class.TxCreateDoc)
|
||||
: txes.find((tx) => tx._class === core.class.TxCreateDoc)
|
||||
if (createTx === undefined) return
|
||||
let doc = TxProcessor.createDoc2Doc(createTx as TxCreateDoc<Doc>)
|
||||
for (let tx of txes) {
|
||||
tx = TxProcessor.extractTx(tx) as TxCUD<Doc>
|
||||
if (tx._class === core.class.TxUpdateDoc) {
|
||||
doc = TxProcessor.updateDoc2Doc(doc, tx as TxUpdateDoc<Doc>)
|
||||
} else if (tx._class === core.class.TxMixin) {
|
||||
const mixinTx = tx as TxMixin<Doc, Doc>
|
||||
doc = TxProcessor.updateMixin4Doc(doc, mixinTx)
|
||||
}
|
||||
}
|
||||
return doc
|
||||
}
|
||||
|
||||
async function getAllRealValues (
|
||||
client: TxOperations,
|
||||
values: any[],
|
||||
_class: Ref<Class<Doc>>
|
||||
): Promise<[any[], boolean]> {
|
||||
if (values.length === 0) return [[], false]
|
||||
if (values.some((value) => typeof value !== 'string')) {
|
||||
return [values, false]
|
||||
}
|
||||
|
||||
if (valueTypes.includes(_class)) {
|
||||
return [values, false]
|
||||
}
|
||||
|
||||
const realValues = await client.findAll(_class, { _id: { $in: values } })
|
||||
const realValuesIds = realValues.map(({ _id }) => _id)
|
||||
const res = [
|
||||
...realValues,
|
||||
...(await Promise.all(
|
||||
values
|
||||
.filter((value) => !realValuesIds.includes(value))
|
||||
.map(async (value) => await buildRemovedDoc(client, value, _class))
|
||||
))
|
||||
].filter((v) => v != null)
|
||||
return [res, true]
|
||||
}
|
||||
|
||||
function combineAttributes (attributes: any[], key: string, operator: string, arrayKey: string): any[] {
|
||||
return Array.from(
|
||||
new Set(
|
||||
attributes.flatMap((attr) =>
|
||||
Array.isArray(attr[operator]?.[key]?.[arrayKey]) ? attr[operator]?.[key]?.[arrayKey] : attr[operator]?.[key]
|
||||
)
|
||||
)
|
||||
).filter((v) => v != null)
|
||||
}
|
||||
|
||||
interface TxAttributeValue {
|
||||
set: any
|
||||
isObjectSet: boolean
|
||||
added: any[]
|
||||
isObjectAdded: boolean
|
||||
removed: any[]
|
||||
isObjectRemoved: boolean
|
||||
}
|
||||
|
||||
export async function getValue (client: TxOperations, m: AttributeModel, tx: DisplayTx): Promise<TxAttributeValue> {
|
||||
const utxs = getModifiedAttributes(tx)
|
||||
const added = await getAllRealValues(client, combineAttributes(utxs, m.key, '$push', '$each'), m._class)
|
||||
const removed = await getAllRealValues(client, combineAttributes(utxs, m.key, '$pull', '$in'), m._class)
|
||||
const value: TxAttributeValue = {
|
||||
set: utxs[0][m.key],
|
||||
isObjectSet: false,
|
||||
added: added[0],
|
||||
isObjectAdded: added[1],
|
||||
removed: removed[0],
|
||||
isObjectRemoved: removed[1]
|
||||
}
|
||||
if (value.set !== undefined) {
|
||||
const res = await getAllRealValues(client, [value.set], m._class)
|
||||
value.set = res[0][0]
|
||||
value.isObjectSet = res[1]
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
export async function updateDocReactions (
|
||||
client: TxOperations,
|
||||
reactions: Reaction[],
|
||||
|
@ -17,9 +17,7 @@ import { Person } from '@hcengineering/contact'
|
||||
import {
|
||||
Account,
|
||||
AttachedDoc,
|
||||
Attribute,
|
||||
Class,
|
||||
Collection,
|
||||
Doc,
|
||||
DocumentQuery,
|
||||
Mixin,
|
||||
@ -27,83 +25,13 @@ import {
|
||||
type RelatedDocument,
|
||||
Timestamp,
|
||||
Tx,
|
||||
TxCreateDoc,
|
||||
TxCUD,
|
||||
TxMixin,
|
||||
TxUpdateDoc
|
||||
TxCUD
|
||||
} from '@hcengineering/core'
|
||||
import type { Asset, IntlString, Plugin, Resource } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import { Preference } from '@hcengineering/preference'
|
||||
import type { AnyComponent } from '@hcengineering/ui'
|
||||
|
||||
// TODO: remove TxViewlet
|
||||
/**
|
||||
* Define an display for all transaction kinds for particular class.
|
||||
* @public
|
||||
*/
|
||||
export interface TxViewlet extends Doc {
|
||||
icon: Asset
|
||||
objectClass: Ref<Class<Doc>>
|
||||
txClass: Ref<Class<Tx>>
|
||||
// Component to display on.
|
||||
component?: AnyComponent
|
||||
|
||||
// If defined, will be added to label displayed
|
||||
labelComponent?: AnyComponent
|
||||
|
||||
// Filter
|
||||
match?: DocumentQuery<Tx>
|
||||
|
||||
// Label will be displayed right after author
|
||||
label?: IntlString
|
||||
labelParams?: any
|
||||
// Do component need to be emphasized or not.
|
||||
display: 'inline' | 'content' | 'emphasized'
|
||||
|
||||
// If defined and true, will show context menu with Edit action, and will pass 'edit:true' to viewlet properties.
|
||||
editable?: boolean
|
||||
|
||||
// If defined and true, will hide all transactions from object in case it is deleted.
|
||||
hideOnRemove?: boolean
|
||||
}
|
||||
|
||||
// TODO: remove DisplayTx
|
||||
/**
|
||||
* Transaction being displayed.
|
||||
* @public
|
||||
*/
|
||||
export interface DisplayTx {
|
||||
// Source tx
|
||||
tx: TxCUD<Doc>
|
||||
|
||||
// A set of collapsed transactions.
|
||||
txes: DisplayTx[]
|
||||
txDocIds?: Set<Ref<Doc>>
|
||||
|
||||
// type check for createTx
|
||||
createTx?: TxCreateDoc<Doc>
|
||||
|
||||
// Type check for updateTx
|
||||
updateTx?: TxUpdateDoc<Doc>
|
||||
|
||||
// Type check for updateTx
|
||||
mixinTx?: TxMixin<Doc, Doc>
|
||||
|
||||
// Document in case it is required.
|
||||
doc?: Doc
|
||||
// Previous document in case it is required.
|
||||
prevDoc?: Doc
|
||||
|
||||
updated: boolean
|
||||
mixin: boolean
|
||||
removed: boolean
|
||||
isOwnTx: boolean
|
||||
|
||||
collectionAttribute?: Attribute<Collection<AttachedDoc>>
|
||||
originTx: TxCUD<Doc>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -323,7 +251,6 @@ export default plugin(activityId, {
|
||||
IgnoreActivity: '' as Ref<Mixin<IgnoreActivity>>
|
||||
},
|
||||
class: {
|
||||
TxViewlet: '' as Ref<Class<TxViewlet>>,
|
||||
DocUpdateMessage: '' as Ref<Class<DocUpdateMessage>>,
|
||||
ActivityMessage: '' as Ref<Class<ActivityMessage>>,
|
||||
ActivityInfoMessage: '' as Ref<Class<ActivityInfoMessage>>,
|
||||
|
@ -1,31 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
//
|
||||
// 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 { Attachment } from '@hcengineering/attachment'
|
||||
import core, { TxCUD, TxCreateDoc, TxProcessor } from '@hcengineering/core'
|
||||
import AttachmentPresenter from '../AttachmentPresenter.svelte'
|
||||
import AttachmentName from '../AttachmentName.svelte'
|
||||
|
||||
export let tx: TxCUD<Attachment>
|
||||
export let value: any
|
||||
|
||||
const doc = tx._class === core.class.TxCreateDoc ? TxProcessor.createDoc2Doc(tx as TxCreateDoc<Attachment>) : value
|
||||
</script>
|
||||
|
||||
{#if tx._class === core.class.TxRemoveDoc}
|
||||
<AttachmentName value={doc} />
|
||||
{:else}
|
||||
<AttachmentPresenter value={doc} />
|
||||
{/if}
|
@ -20,7 +20,6 @@ import preference from '@hcengineering/preference'
|
||||
import { PDFViewer, deleteFile, getClient, uploadFile } from '@hcengineering/presentation'
|
||||
import activity, { type ActivityMessage, type DocUpdateMessage } from '@hcengineering/activity'
|
||||
|
||||
import TxAttachmentCreate from './components/activity/TxAttachmentCreate.svelte'
|
||||
import AddAttachment from './components/AddAttachment.svelte'
|
||||
import AttachmentDocList from './components/AttachmentDocList.svelte'
|
||||
import AttachmentDroppable from './components/AttachmentDroppable.svelte'
|
||||
@ -261,7 +260,6 @@ export default async (): Promise<Resources> => ({
|
||||
PDFViewer
|
||||
},
|
||||
activity: {
|
||||
TxAttachmentCreate,
|
||||
AttachmentsUpdatedMessage
|
||||
},
|
||||
helper: {
|
||||
|
@ -1,52 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2022 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 { Event } from '@hcengineering/calendar'
|
||||
import { Ref, TxUpdateDoc } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { DateTimePresenter, showPanel } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import calendar from '../../plugin'
|
||||
|
||||
export let tx: TxUpdateDoc<Event>
|
||||
|
||||
const client = getClient()
|
||||
|
||||
async function getEvent (_id: Ref<Event>): Promise<Event | undefined> {
|
||||
return await client.findOne(calendar.class.Event, { _id })
|
||||
}
|
||||
|
||||
function click (event: Event): void {
|
||||
showPanel(view.component.EditDoc, event._id, event._class, 'content')
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex">
|
||||
{#await getEvent(tx.objectId) then event}
|
||||
{#if event}
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<span
|
||||
class="over-underline caption-color flex-row-center"
|
||||
on:click={() => {
|
||||
click(event)
|
||||
}}
|
||||
>{event.title}
|
||||
</span>
|
||||
 
|
||||
<DateTimePresenter value={event.date} />
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
@ -29,7 +29,6 @@ import IntegrationConnect from './components/IntegrationConnect.svelte'
|
||||
import PersonsPresenter from './components/PersonsPresenter.svelte'
|
||||
import SaveEventReminder from './components/SaveEventReminder.svelte'
|
||||
import UpdateRecInstancePopup from './components/UpdateRecInstancePopup.svelte'
|
||||
import ReminderViewlet from './components/activity/ReminderViewlet.svelte'
|
||||
import CalendarIntegrationIcon from './components/icons/Calendar.svelte'
|
||||
import EventElement from './components/EventElement.svelte'
|
||||
import CalendarEventPresenter from './components/CalendarEventPresenter.svelte'
|
||||
@ -192,9 +191,6 @@ export default async (): Promise<Resources> => ({
|
||||
IntegrationConfigure,
|
||||
ConnectApp
|
||||
},
|
||||
activity: {
|
||||
ReminderViewlet
|
||||
},
|
||||
actionImpl: {
|
||||
SaveEventReminder: saveEventReminder,
|
||||
DeleteRecEvent: deleteRecEvent
|
||||
|
@ -15,12 +15,8 @@
|
||||
|
||||
import calendar, { calendarId } from '@hcengineering/calendar'
|
||||
import { type IntlString, mergeIds } from '@hcengineering/platform'
|
||||
import { type AnyComponent } from '@hcengineering/ui'
|
||||
|
||||
export default mergeIds(calendarId, calendar, {
|
||||
activity: {
|
||||
ReminderViewlet: '' as AnyComponent
|
||||
},
|
||||
string: {
|
||||
Events: '' as IntlString,
|
||||
RemindMeAt: '' as IntlString,
|
||||
|
@ -1,63 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { AttachmentRefInput } from '@hcengineering/attachment-resources'
|
||||
import chunter, { DirectMessage, Message, getDirectChannel } from '@hcengineering/chunter'
|
||||
import { PersonAccount } from '@hcengineering/contact'
|
||||
import { Ref, generateId, getCurrentAccount } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
|
||||
export let account: PersonAccount
|
||||
export let loading: boolean = true
|
||||
|
||||
const client = getClient()
|
||||
|
||||
const me = getCurrentAccount()._id
|
||||
|
||||
const _class = chunter.class.Message
|
||||
let messageId: Ref<Message> = generateId()
|
||||
|
||||
let space: Ref<DirectMessage> | undefined
|
||||
|
||||
$: _getDirectChannel(account?._id)
|
||||
async function _getDirectChannel (account?: Ref<PersonAccount>): Promise<void> {
|
||||
if (account === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
space = await getDirectChannel(client, me as Ref<PersonAccount>, account)
|
||||
}
|
||||
|
||||
async function onMessage (event: CustomEvent) {
|
||||
if (space === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const { message, attachments } = event.detail
|
||||
await client.addCollection(
|
||||
_class,
|
||||
space,
|
||||
space,
|
||||
chunter.class.DirectMessage,
|
||||
'messages',
|
||||
{
|
||||
content: message,
|
||||
createBy: me,
|
||||
attachments
|
||||
},
|
||||
messageId
|
||||
)
|
||||
|
||||
messageId = generateId()
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if space !== undefined}
|
||||
<div class="reference">
|
||||
<AttachmentRefInput bind:loading {space} {_class} objectId={messageId} on:message={onMessage} />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.reference {
|
||||
margin: 1.25rem 2.5rem;
|
||||
}
|
||||
</style>
|
@ -1,25 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { chunterId, DirectMessage, Message } from '@hcengineering/chunter'
|
||||
import { createQuery, getClient, MessageViewer } from '@hcengineering/presentation'
|
||||
import { NavLink } from '@hcengineering/view-resources'
|
||||
import chunter from '../plugin'
|
||||
import { getDmName } from '../utils'
|
||||
|
||||
export let value: Message
|
||||
const client = getClient()
|
||||
let dm: DirectMessage | undefined
|
||||
const query = createQuery()
|
||||
|
||||
$: query.query(chunter.class.DirectMessage, { _id: value.space }, (result) => {
|
||||
dm = result[0]
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if dm}
|
||||
{#await getDmName(client, dm) then name}
|
||||
<NavLink app={chunterId} space={value.space}>
|
||||
<span class="label">{name}</span>
|
||||
</NavLink>
|
||||
<div><MessageViewer message={value.content} /></div>
|
||||
{/await}
|
||||
{/if}
|
@ -26,10 +26,8 @@ import ChunterBrowser from './components/chat/specials/ChunterBrowser.svelte'
|
||||
import ConvertDmToPrivateChannelModal from './components/ConvertDmToPrivateChannel.svelte'
|
||||
import CreateChannel from './components/chat/create/CreateChannel.svelte'
|
||||
import CreateDirectChat from './components/chat/create/CreateDirectChat.svelte'
|
||||
import DirectMessagePresenter from './components/DirectMessagePresenter.svelte'
|
||||
import DmHeader from './components/DmHeader.svelte'
|
||||
import DmPresenter from './components/DmPresenter.svelte'
|
||||
import DirectMessageInput from './components/DirectMessageInput.svelte'
|
||||
import EditChannel from './components/EditChannel.svelte'
|
||||
import ChannelPreview from './components/ChannelPreview.svelte'
|
||||
import ThreadView from './components/threads/ThreadView.svelte'
|
||||
@ -160,12 +158,10 @@ export default async (): Promise<Resources> => ({
|
||||
ChannelHeader,
|
||||
ChannelPanel,
|
||||
ChannelPresenter,
|
||||
DirectMessagePresenter,
|
||||
ChannelPreview,
|
||||
ChunterBrowser,
|
||||
DmHeader,
|
||||
DmPresenter,
|
||||
DirectMessageInput,
|
||||
EditChannel,
|
||||
ThreadView,
|
||||
SavedMessages,
|
||||
|
@ -32,7 +32,6 @@ export default mergeIds(chunterId, chunter, {
|
||||
EditChannel: '' as AnyComponent,
|
||||
ChannelPreview: '' as AnyComponent,
|
||||
MessagePreview: '' as AnyComponent,
|
||||
DirectMessageInput: '' as AnyComponent,
|
||||
CreateDocChannel: '' as AnyComponent,
|
||||
SavedMessages: '' as AnyComponent,
|
||||
Threads: '' as AnyComponent,
|
||||
|
@ -14,8 +14,7 @@
|
||||
//
|
||||
|
||||
import { ActivityMessage, ActivityMessageViewlet } from '@hcengineering/activity'
|
||||
import type { Person } from '@hcengineering/contact'
|
||||
import type { Account, AttachedDoc, Class, Doc, Markup, Mixin, Ref, Space, Timestamp } from '@hcengineering/core'
|
||||
import type { Class, Doc, Markup, Mixin, Ref, Space, Timestamp } from '@hcengineering/core'
|
||||
import { NotificationType } from '@hcengineering/notification'
|
||||
import type { Asset, Plugin } from '@hcengineering/platform'
|
||||
import { IntlString, plugin } from '@hcengineering/platform'
|
||||
@ -25,9 +24,7 @@ import { Action } from '@hcengineering/view'
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ChunterSpace extends Space {
|
||||
lastMessage?: Timestamp
|
||||
}
|
||||
export interface ChunterSpace extends Space {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -41,42 +38,6 @@ export interface Channel extends ChunterSpace {
|
||||
*/
|
||||
export interface DirectMessage extends ChunterSpace {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated use ChatMessage instead
|
||||
*/
|
||||
export interface ChunterMessage extends AttachedDoc {
|
||||
content: Markup
|
||||
attachments?: number
|
||||
createBy: Ref<Account>
|
||||
editedOn?: Timestamp
|
||||
reactions?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ChunterMessageExtension extends ChunterMessage {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated use ChatMessage instead
|
||||
*/
|
||||
export interface Message extends ChunterMessage {
|
||||
attachedTo: Ref<Space>
|
||||
attachedToClass: Ref<Class<Space>>
|
||||
replies?: Ref<Person>[]
|
||||
repliesCount?: number
|
||||
lastReply?: Timestamp
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface DirectMessageInput extends Class<Doc> {
|
||||
component: AnyComponent
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -141,8 +102,6 @@ export default plugin(chunterId, {
|
||||
ThreadMessagePreview: '' as AnyComponent
|
||||
},
|
||||
class: {
|
||||
Message: '' as Ref<Class<Message>>,
|
||||
ChunterMessage: '' as Ref<Class<ChunterMessage>>,
|
||||
ThreadMessage: '' as Ref<Class<ThreadMessage>>,
|
||||
ChunterSpace: '' as Ref<Class<ChunterSpace>>,
|
||||
Channel: '' as Ref<Class<Channel>>,
|
||||
@ -151,8 +110,6 @@ export default plugin(chunterId, {
|
||||
ChatMessageViewlet: '' as Ref<Class<ChatMessageViewlet>>
|
||||
},
|
||||
mixin: {
|
||||
DirectMessageInput: '' as Ref<Mixin<DirectMessageInput>>,
|
||||
ChunterMessageExtension: '' as Ref<Mixin<ChunterMessageExtension>>,
|
||||
ObjectChatPanel: '' as Ref<Mixin<ObjectChatPanel>>
|
||||
},
|
||||
string: {
|
||||
|
@ -1,34 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2023 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Person, formatName } from '@hcengineering/contact'
|
||||
import { TxUpdateDoc } from '@hcengineering/core'
|
||||
import contact from '../../plugin'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
import activity from '@hcengineering/activity'
|
||||
|
||||
export let tx: TxUpdateDoc<Person>
|
||||
|
||||
const val = tx.operations.name
|
||||
</script>
|
||||
|
||||
{#if val}
|
||||
<span class="lower"><Label label={activity.string.Changed} /></span>
|
||||
<span class="lower"><Label label={contact.string.Name} /></span>
|
||||
<span class="lower"><Label label={activity.string.To} /></span>
|
||||
<span class="strong overflow-label">
|
||||
{formatName(val)}
|
||||
</span>
|
||||
{/if}
|
@ -117,7 +117,6 @@ import UsersList from './components/UsersList.svelte'
|
||||
import UsersPopup from './components/UsersPopup.svelte'
|
||||
import ActivityChannelPresenter from './components/activity/ActivityChannelPresenter.svelte'
|
||||
import NameChangedActivityMessage from './components/activity/NameChangedActivityMessage.svelte'
|
||||
import TxNameChange from './components/activity/TxNameChange.svelte'
|
||||
import IconAddMember from './components/icons/AddMember.svelte'
|
||||
import ExpandRightDouble from './components/icons/ExpandRightDouble.svelte'
|
||||
import IconMembers from './components/icons/Members.svelte'
|
||||
@ -320,7 +319,6 @@ export default async (): Promise<Resources> => ({
|
||||
OpenChannel: openChannelURL
|
||||
},
|
||||
activity: {
|
||||
TxNameChange,
|
||||
NameChangedActivityMessage
|
||||
},
|
||||
component: {
|
||||
|
@ -1,40 +0,0 @@
|
||||
<!--
|
||||
//
|
||||
// Copyright © 2023 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { Request } from '@hcengineering/request'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
import { ControlledDocument } from '@hcengineering/controlled-documents'
|
||||
|
||||
import documents from '../../plugin'
|
||||
|
||||
export let value: Request
|
||||
|
||||
let controlledDoc: ControlledDocument | undefined
|
||||
const docQuery = createQuery()
|
||||
$: docQuery.query(documents.class.ControlledDocument, { _id: value.attachedTo as Ref<ControlledDocument> }, (rev) => {
|
||||
;[controlledDoc] = rev
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if controlledDoc}
|
||||
<span>
|
||||
<Label label={documents.string.DocumentApprovalRequest} />
|
||||
"{controlledDoc.title}"
|
||||
</span>
|
||||
{/if}
|
@ -1,40 +0,0 @@
|
||||
<!--
|
||||
//
|
||||
// Copyright © 2023 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { Request } from '@hcengineering/request'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
import { ControlledDocument } from '@hcengineering/controlled-documents'
|
||||
|
||||
import documents from '../../plugin'
|
||||
|
||||
export let value: Request
|
||||
|
||||
let controlledDoc: ControlledDocument | undefined
|
||||
const docQuery = createQuery()
|
||||
$: docQuery.query(documents.class.ControlledDocument, { _id: value.attachedTo as Ref<ControlledDocument> }, (rev) => {
|
||||
;[controlledDoc] = rev
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if controlledDoc}
|
||||
<span>
|
||||
<Label label={documents.string.DocumentReviewRequest} />
|
||||
"{controlledDoc.title}"
|
||||
</span>
|
||||
{/if}
|
@ -80,10 +80,8 @@ import DocumentVersionPresenter from './components/document/presenters/DocumentV
|
||||
|
||||
import DocumentSectionDeletePopup from './components/DocumentSectionDeletePopup.svelte'
|
||||
|
||||
import TxCreateReviewRequest from './components/activity/TxCreateReviewRequest.svelte'
|
||||
import DocumentReviewRequest from './components/requests/DocumentReviewRequest.svelte'
|
||||
import DocumentReviewRequestPresenter from './components/requests/DocumentReviewRequestPresenter.svelte'
|
||||
import TxCreateApprovalRequest from './components/activity/TxCreateApprovalRequest.svelte'
|
||||
import DocumentApprovalRequest from './components/requests/DocumentApprovalRequest.svelte'
|
||||
import DocumentApprovalRequestPresenter from './components/requests/DocumentApprovalRequestPresenter.svelte'
|
||||
import ControlledStateFilterValuePresenter from './components/document/presenters/ControlledStateFilterValuePresenter.svelte'
|
||||
@ -331,10 +329,8 @@ export default async (): Promise<Resources> => ({
|
||||
DocumentSectionDeletePopup,
|
||||
CollaborativeSectionPresenter,
|
||||
AttachmentsSectionPresenter,
|
||||
TxCreateReviewRequest,
|
||||
DocumentReviewRequest,
|
||||
DocumentReviewRequestPresenter,
|
||||
TxCreateApprovalRequest,
|
||||
DocumentApprovalRequest,
|
||||
DocumentApprovalRequestPresenter,
|
||||
StateFilterValuePresenter,
|
||||
|
@ -42,11 +42,9 @@ export default mergeIds(documentsId, documents, {
|
||||
|
||||
DocumentReviewRequest: '' as AnyComponent,
|
||||
DocumentReviewRequestPresenter: '' as AnyComponent,
|
||||
TxCreateReviewRequest: '' as AnyComponent,
|
||||
|
||||
DocumentApprovalRequest: '' as AnyComponent,
|
||||
DocumentApprovalRequestPresenter: '' as AnyComponent,
|
||||
TxCreateApprovalRequest: '' as AnyComponent,
|
||||
|
||||
CreateDocumentsSpace: '' as AnyComponent
|
||||
},
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { TxViewlet } from '@hcengineering/activity'
|
||||
import {
|
||||
type Class,
|
||||
type Doc,
|
||||
@ -282,8 +281,6 @@ export const documentsPlugin = plugin(documentsId, {
|
||||
ConfigDescription: '' as IntlString
|
||||
},
|
||||
ids: {
|
||||
TxReviewRequestCreated: '' as Ref<TxViewlet>,
|
||||
TxApprovalRequestCreated: '' as Ref<TxViewlet>,
|
||||
NoParent: '' as Ref<DocumentMeta>,
|
||||
NoProject: '' as Ref<Project>
|
||||
},
|
||||
|
@ -1,25 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 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 { TxCreateDoc } from '@hcengineering/core'
|
||||
import { TxProcessor } from '@hcengineering/core'
|
||||
import { SharedMessages } from '@hcengineering/gmail'
|
||||
import SharedMessagesView from '../SharedMessages.svelte'
|
||||
|
||||
export let tx: TxCreateDoc<SharedMessages>
|
||||
</script>
|
||||
|
||||
<SharedMessagesView value={TxProcessor.createDoc2Doc(tx)} />
|
@ -1,34 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2023 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Message } from '@hcengineering/gmail'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { showPopup } from '@hcengineering/ui'
|
||||
import Main from '../Main.svelte'
|
||||
|
||||
export let value: Message
|
||||
|
||||
async function click () {
|
||||
const client = getClient()
|
||||
const channel = await client.findOne(value.attachedToClass, { _id: value.attachedTo })
|
||||
if (channel !== undefined) {
|
||||
showPopup(Main, { _id: channel.attachedTo, _class: channel.attachedToClass, message: value }, 'float')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<span class="over-underline overflow-label" on:click={click}>{value.subject}</span>
|
@ -17,8 +17,6 @@
|
||||
import { concatLink } from '@hcengineering/core'
|
||||
import { getMetadata, type Resources } from '@hcengineering/platform'
|
||||
import presentation from '@hcengineering/presentation'
|
||||
import TxSharedCreate from './components/activity/TxSharedCreate.svelte'
|
||||
import TxWriteMessage from './components/activity/TxWriteMessage.svelte'
|
||||
import GmailWriteMessage from './components/activity/GmailWriteMessage.svelte'
|
||||
import GmailSharedMessage from './components/activity/GmailSharedMessage.svelte'
|
||||
import Configure from './components/Configure.svelte'
|
||||
@ -38,8 +36,6 @@ export default async (): Promise<Resources> => ({
|
||||
Configure
|
||||
},
|
||||
activity: {
|
||||
TxSharedCreate,
|
||||
TxWriteMessage,
|
||||
GmailWriteMessage,
|
||||
GmailSharedMessage
|
||||
},
|
||||
|
@ -1,249 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2023 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import activity, { TxViewlet } from '@hcengineering/activity'
|
||||
import { activityKey, ActivityKey } from '@hcengineering/activity-resources'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import { Employee, getName, PersonAccount } from '@hcengineering/contact'
|
||||
import { Avatar, employeeByIdStore, personAccountByIdStore } from '@hcengineering/contact-resources'
|
||||
import core, { Account, Class, Doc, getCurrentAccount, Ref } from '@hcengineering/core'
|
||||
import notification, { DocUpdates } from '@hcengineering/notification'
|
||||
import { ActionContext, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { AnySvelteComponent, Button, Loading, Scroller } from '@hcengineering/ui'
|
||||
|
||||
import NotificationView from './NotificationView.svelte'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
|
||||
export let accountId: Ref<Account>
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
let docs: DocUpdates[] = []
|
||||
let filteredDocs: DocUpdates[] = []
|
||||
let loading = true
|
||||
const me = getCurrentAccount()._id
|
||||
|
||||
$: query.query(
|
||||
notification.class.DocUpdates,
|
||||
{
|
||||
user: me,
|
||||
hidden: false
|
||||
},
|
||||
(res) => {
|
||||
docs = res
|
||||
updateDocs(accountId, true)
|
||||
loading = false
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
lastTxTime: -1
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
$: updateDocs(accountId)
|
||||
function updateDocs (accountId: Ref<Account>, forceUpdate = false) {
|
||||
if (loading && !forceUpdate) {
|
||||
return
|
||||
}
|
||||
|
||||
const filtered: DocUpdates[] = []
|
||||
|
||||
for (const doc of docs) {
|
||||
if (doc.txes.length === 0) continue
|
||||
const txes = doc.txes.filter((p) => p.modifiedBy === accountId)
|
||||
if (txes.length > 0) {
|
||||
filtered.push({
|
||||
...doc,
|
||||
txes
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
filteredDocs = filtered
|
||||
}
|
||||
|
||||
function markAsRead (index: number) {
|
||||
if (filteredDocs[index] !== undefined) {
|
||||
filteredDocs[index].txes.forEach((p) => (p.isNew = false))
|
||||
filteredDocs[index].txes = filteredDocs[index].txes
|
||||
filteredDocs = filteredDocs
|
||||
}
|
||||
}
|
||||
|
||||
function changeSelected (index: number) {
|
||||
if (filteredDocs[index] !== undefined) {
|
||||
dispatch('change', filteredDocs[index])
|
||||
markAsRead(index)
|
||||
} else if (filteredDocs.length > 0) {
|
||||
if (index < filteredDocs.length - 1) {
|
||||
selected++
|
||||
} else {
|
||||
selected--
|
||||
}
|
||||
changeSelected(selected)
|
||||
} else {
|
||||
selected = 0
|
||||
dispatch('change', undefined)
|
||||
}
|
||||
}
|
||||
|
||||
let viewlets: Map<ActivityKey, TxViewlet[]>
|
||||
|
||||
const descriptors = createQuery()
|
||||
descriptors.query(activity.class.TxViewlet, {}, (result) => {
|
||||
viewlets = new Map()
|
||||
for (const res of result) {
|
||||
const key = activityKey(res.objectClass, res.txClass)
|
||||
const arr = viewlets.get(key) ?? []
|
||||
arr.push(res)
|
||||
viewlets.set(key, arr)
|
||||
}
|
||||
viewlets = viewlets
|
||||
})
|
||||
|
||||
let selected = 0
|
||||
|
||||
let employee: Employee | undefined = undefined
|
||||
$: newTxes = filteredDocs.reduce((acc, cur) => acc + cur.txes.filter((p) => p.isNew).length, 0) // items.length
|
||||
$: account = $personAccountByIdStore.get(accountId as Ref<PersonAccount>)
|
||||
$: employee = account ? $employeeByIdStore.get(account.person as Ref<Employee>) : undefined
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
async function openDM () {
|
||||
const res = await client.findAll(chunter.class.DirectMessage, { members: accountId })
|
||||
const current = res.find((p) => p.members.includes(me) && p.members.length === 2)
|
||||
if (current !== undefined) {
|
||||
dispatch('dm', current._id)
|
||||
} else {
|
||||
const id = await client.createDoc(chunter.class.DirectMessage, core.space.Space, {
|
||||
name: '',
|
||||
description: '',
|
||||
private: true,
|
||||
archived: false,
|
||||
members: [me, accountId]
|
||||
})
|
||||
dispatch('dm', id)
|
||||
}
|
||||
}
|
||||
|
||||
function onKeydown (key: KeyboardEvent): void {
|
||||
if (key.code === 'ArrowUp') {
|
||||
key.stopPropagation()
|
||||
key.preventDefault()
|
||||
selected--
|
||||
changeSelected(selected)
|
||||
}
|
||||
if (key.code === 'ArrowDown') {
|
||||
key.stopPropagation()
|
||||
key.preventDefault()
|
||||
selected++
|
||||
changeSelected(selected)
|
||||
}
|
||||
if (key.code === 'Enter') {
|
||||
key.preventDefault()
|
||||
key.stopPropagation()
|
||||
changeSelected(selected)
|
||||
}
|
||||
}
|
||||
|
||||
let dmInput: AnySvelteComponent | undefined = undefined
|
||||
$: dmInputRes = hierarchy.classHierarchyMixin(
|
||||
chunter.class.DirectMessage as Ref<Class<Doc>>,
|
||||
chunter.mixin.DirectMessageInput
|
||||
)?.component
|
||||
$: if (dmInputRes) {
|
||||
getResource(dmInputRes).then((res) => (dmInput = res))
|
||||
}
|
||||
</script>
|
||||
|
||||
<ActionContext
|
||||
context={{
|
||||
mode: 'browser'
|
||||
}}
|
||||
/>
|
||||
<div class="flex-between header bottom-divider">
|
||||
<div class="flex-row-center">
|
||||
{#if employee}
|
||||
<Avatar size={'smaller'} person={employee} name={employee.name} />
|
||||
<span class="font-medium mx-2">{getName(client.getHierarchy(), employee)}</span>
|
||||
{/if}
|
||||
{#if newTxes > 0}
|
||||
<span class="counter">
|
||||
{newTxes}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
{#if me !== accountId}
|
||||
<Button label={chunter.string.Message} kind="primary" on:click={openDM} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="inbox-activity">
|
||||
<Scroller noStretch>
|
||||
{#if loading}
|
||||
<Loading />
|
||||
{:else}
|
||||
{#each filteredDocs as item, i (item._id)}
|
||||
<div class="with-hover">
|
||||
<NotificationView
|
||||
value={item}
|
||||
selected={false}
|
||||
{viewlets}
|
||||
on:keydown={onKeydown}
|
||||
on:click={() => {
|
||||
selected = i
|
||||
changeSelected(selected)
|
||||
}}
|
||||
preview
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</Scroller>
|
||||
</div>
|
||||
{#if dmInput && account}
|
||||
<svelte:component this={dmInput} {account} bind:loading />
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.header {
|
||||
flex-shrink: 0;
|
||||
padding: 0.625rem 1.25rem 0.625rem 1.75rem;
|
||||
min-width: 0;
|
||||
min-height: 3.25rem;
|
||||
background-color: var(--theme-comp-header-color);
|
||||
}
|
||||
|
||||
.counter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 1.375rem;
|
||||
width: 1.375rem;
|
||||
color: var(--theme-inbox-people-notify);
|
||||
background-color: var(--theme-inbox-people-counter-bgcolor);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.with-hover {
|
||||
&:hover {
|
||||
background-color: var(--theme-inbox-activitymsg-bgcolor);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,187 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2023 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import activity, { TxViewlet } from '@hcengineering/activity'
|
||||
import { activityKey, ActivityKey } from '@hcengineering/activity-resources'
|
||||
import { PersonAccount } from '@hcengineering/contact'
|
||||
import { personAccountByIdStore } from '@hcengineering/contact-resources'
|
||||
import { Account, Doc, getCurrentAccount, Ref } from '@hcengineering/core'
|
||||
import notification, { DocUpdates } from '@hcengineering/notification'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Loading, Scroller } from '@hcengineering/ui'
|
||||
|
||||
import PeopleNotificationView from './PeopleNotificationsView.svelte'
|
||||
|
||||
export let filter: 'all' | 'read' | 'unread' = 'all'
|
||||
export let _id: Ref<Doc> | undefined
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
let docs: DocUpdates[] = []
|
||||
let map = new Map<Ref<Account>, DocUpdates[]>()
|
||||
let accounts: PersonAccount[] = []
|
||||
let loading = true
|
||||
|
||||
$: query.query(
|
||||
notification.class.DocUpdates,
|
||||
{
|
||||
user: getCurrentAccount()._id,
|
||||
hidden: false
|
||||
},
|
||||
async (res) => {
|
||||
docs = res
|
||||
await getFiltered(docs, filter)
|
||||
loading = false
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
lastTxTime: -1
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
async function getFiltered (docs: DocUpdates[], filter: 'all' | 'read' | 'unread'): Promise<void> {
|
||||
const filtered: DocUpdates[] = []
|
||||
for (const doc of docs) {
|
||||
if (doc.txes.length === 0) continue
|
||||
if (filter === 'read') {
|
||||
const txes = doc.txes.filter((p) => !p.isNew)
|
||||
if (txes.length > 0) {
|
||||
filtered.push({
|
||||
...doc,
|
||||
txes
|
||||
})
|
||||
}
|
||||
} else if (filter === 'unread') {
|
||||
const txes = doc.txes.filter((p) => p.isNew)
|
||||
if (txes.length > 0) {
|
||||
filtered.push({
|
||||
...doc,
|
||||
txes
|
||||
})
|
||||
}
|
||||
} else {
|
||||
filtered.push(doc)
|
||||
}
|
||||
}
|
||||
map.clear()
|
||||
for (const item of filtered) {
|
||||
for (const tx of item.txes) {
|
||||
const arr = map.get(tx.modifiedBy) ?? []
|
||||
if (arr.findIndex((p) => p._id === item._id) === -1) {
|
||||
arr.push(item)
|
||||
map.set(tx.modifiedBy, arr)
|
||||
}
|
||||
}
|
||||
}
|
||||
map = map
|
||||
accounts = Array.from(map.keys())
|
||||
.map((p) => $personAccountByIdStore.get(p as Ref<PersonAccount>))
|
||||
.filter((p) => p !== undefined) as PersonAccount[]
|
||||
if (_id === undefined) {
|
||||
changeSelected(selected)
|
||||
} else {
|
||||
const index = filtered.findIndex((p) => p.attachedTo === _id)
|
||||
if (index === -1) {
|
||||
changeSelected(selected)
|
||||
} else {
|
||||
const doc = filtered[index]
|
||||
const acc = accounts.findIndex((p) => p._id === doc.txes[doc.txes.length - 1].modifiedBy)
|
||||
if (acc !== -1) {
|
||||
selected = acc
|
||||
changeSelected(selected)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: getFiltered(docs, filter)
|
||||
|
||||
function changeSelected (index: number) {
|
||||
if (accounts[index] === undefined) {
|
||||
if (accounts.length > 0) {
|
||||
if (index < accounts.length - 1) {
|
||||
selected++
|
||||
} else {
|
||||
selected--
|
||||
}
|
||||
changeSelected(selected)
|
||||
} else {
|
||||
selected = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let viewlets: Map<ActivityKey, TxViewlet[]>
|
||||
|
||||
const descriptors = createQuery()
|
||||
descriptors.query(activity.class.TxViewlet, {}, (result) => {
|
||||
viewlets = new Map()
|
||||
for (const res of result) {
|
||||
const key = activityKey(res.objectClass, res.txClass)
|
||||
const arr = viewlets.get(key) ?? []
|
||||
arr.push(res)
|
||||
viewlets.set(key, arr)
|
||||
}
|
||||
viewlets = viewlets
|
||||
})
|
||||
|
||||
let selected = 0
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
function onKeydown (key: KeyboardEvent): void {
|
||||
if (key.code === 'ArrowUp') {
|
||||
key.stopPropagation()
|
||||
key.preventDefault()
|
||||
selected--
|
||||
changeSelected(selected)
|
||||
}
|
||||
if (key.code === 'ArrowDown') {
|
||||
key.stopPropagation()
|
||||
key.preventDefault()
|
||||
selected++
|
||||
changeSelected(selected)
|
||||
}
|
||||
if (key.code === 'Enter') {
|
||||
key.preventDefault()
|
||||
key.stopPropagation()
|
||||
dispatch('open', accounts[selected]._id)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="inbox-activity py-2">
|
||||
<Scroller noStretch>
|
||||
{#if loading}
|
||||
<Loading />
|
||||
{:else}
|
||||
{#each accounts as account, i}
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<PeopleNotificationView
|
||||
value={account}
|
||||
items={map.get(account._id) ?? []}
|
||||
selected={selected === i}
|
||||
{viewlets}
|
||||
on:keydown={onKeydown}
|
||||
on:open
|
||||
on:open={() => {
|
||||
selected = i
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
</Scroller>
|
||||
</div>
|
@ -1,128 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2023 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { TxViewlet } from '@hcengineering/activity'
|
||||
import { ActivityKey } from '@hcengineering/activity-resources'
|
||||
import { PersonAccount, getName } from '@hcengineering/contact'
|
||||
import { Avatar, personByIdStore } from '@hcengineering/contact-resources'
|
||||
import core, { Doc, TxCUD, TxProcessor } from '@hcengineering/core'
|
||||
import notification, { DocUpdates } from '@hcengineering/notification'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { ActionIcon, AnySvelteComponent, Label, TimeSince } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
|
||||
import TxView from './TxView.svelte'
|
||||
import ArrowRight from './icons/ArrowRight.svelte'
|
||||
|
||||
export let value: PersonAccount
|
||||
export let items: DocUpdates[]
|
||||
export let viewlets: Map<ActivityKey, TxViewlet[]>
|
||||
export let selected: boolean
|
||||
|
||||
$: firstItem = items[0]
|
||||
|
||||
$: employee = $personByIdStore.get(value.person)
|
||||
|
||||
$: newTxes = items.reduce((acc, cur) => acc + cur.txes.filter((p) => p.isNew && p.modifiedBy === value._id).length, 0) // items.length
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let doc: Doc | undefined = undefined
|
||||
let tx: TxCUD<Doc> | undefined = undefined
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
$: txRef = firstItem ? firstItem.txes[firstItem.txes.length - 1]._id : undefined
|
||||
|
||||
$: txRef &&
|
||||
client.findOne(core.class.TxCUD, { _id: txRef }).then((res) => {
|
||||
if (res !== undefined) {
|
||||
tx = TxProcessor.extractTx(res) as TxCUD<Doc>
|
||||
} else {
|
||||
tx = res
|
||||
}
|
||||
})
|
||||
|
||||
let presenter: AnySvelteComponent | undefined = undefined
|
||||
$: presenterRes =
|
||||
hierarchy.classHierarchyMixin(firstItem.attachedToClass, notification.mixin.NotificationObjectPresenter)
|
||||
?.presenter ?? hierarchy.classHierarchyMixin(firstItem.attachedToClass, view.mixin.ObjectPresenter)?.presenter
|
||||
$: if (presenterRes) {
|
||||
getResource(presenterRes).then((res) => (presenter = res))
|
||||
}
|
||||
|
||||
const docQuery = createQuery()
|
||||
$: docQuery.query(firstItem.attachedToClass, { _id: firstItem.attachedTo }, (res) => {
|
||||
;[doc] = res
|
||||
})
|
||||
|
||||
let div: HTMLDivElement
|
||||
|
||||
$: if (selected && div != null) div.focus()
|
||||
</script>
|
||||
|
||||
{#if doc}
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="inbox-activity__container"
|
||||
on:keydown
|
||||
class:selected
|
||||
tabindex="-1"
|
||||
bind:this={div}
|
||||
on:click={() => dispatch('open', value._id)}
|
||||
>
|
||||
{#if newTxes > 0 && !selected}<div class="notify people" />{/if}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="inbox-activity__content shrink flex-grow clear-mins" class:read={newTxes === 0}>
|
||||
<div class="flex-row-center gap-2">
|
||||
<Avatar person={employee} size={'small'} name={employee?.name} />
|
||||
{#if employee}
|
||||
<span class="font-medium">{getName(client.getHierarchy(), employee)}</span>
|
||||
{:else}
|
||||
<span class="font-medium"><Label label={core.string.System} /></span>
|
||||
{/if}
|
||||
{#if newTxes > 0}
|
||||
<div class="counter people">
|
||||
{newTxes}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="arrow">
|
||||
<ActionIcon
|
||||
icon={ArrowRight}
|
||||
size="medium"
|
||||
action={() => {
|
||||
dispatch('open', value._id)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clear-mins flex-no-shrink mt-4">
|
||||
{#if presenter}
|
||||
<svelte:component this={presenter} value={doc} inline disabled inbox />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex-between flex-baseline mt-3">
|
||||
{#if tx && firstItem}
|
||||
<TxView {tx} {viewlets} objectId={firstItem.attachedTo} />
|
||||
{/if}
|
||||
<div class="time">
|
||||
<TimeSince value={tx?.modifiedOn} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
@ -1,81 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2023 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { PersonAccount } from '@hcengineering/contact'
|
||||
import { PersonAccountRefPresenter } from '@hcengineering/contact-resources'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { Collaborators } from '@hcengineering/notification'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { IconAdd, IconDelete, Label } from '@hcengineering/ui'
|
||||
import notification from '../../plugin'
|
||||
|
||||
// export let tx: TxMixin<Doc, Collaborators>
|
||||
export let value: Collaborators
|
||||
export let prevValue: Collaborators | undefined = undefined
|
||||
|
||||
interface Diff {
|
||||
added: Ref<PersonAccount>[]
|
||||
removed: Ref<PersonAccount>[]
|
||||
}
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
function buildDiff (value: Collaborators, prev: Collaborators | undefined): Diff | undefined {
|
||||
if (prev === undefined) return
|
||||
const added: Ref<PersonAccount>[] = []
|
||||
const removed: Ref<PersonAccount>[] = []
|
||||
const mixin = hierarchy.as(value, notification.mixin.Collaborators)
|
||||
const prevMixin = hierarchy.as(prev, notification.mixin.Collaborators)
|
||||
const prevSet = new Set(prevMixin?.collaborators ?? [])
|
||||
const newSet = new Set(mixin.collaborators)
|
||||
|
||||
for (const newCollab of mixin.collaborators) {
|
||||
if (!prevSet.has(newCollab)) added.push(newCollab as Ref<PersonAccount>)
|
||||
}
|
||||
|
||||
for (const oldCollab of prevMixin?.collaborators ?? []) {
|
||||
if (!newSet.has(oldCollab)) removed.push(oldCollab as Ref<PersonAccount>)
|
||||
}
|
||||
|
||||
return {
|
||||
added,
|
||||
removed
|
||||
}
|
||||
}
|
||||
|
||||
$: diff = buildDiff(value, prevValue)
|
||||
</script>
|
||||
|
||||
{#if diff}
|
||||
<Label label={notification.string.ChangeCollaborators} />
|
||||
{#if diff.added.length > 0}
|
||||
<div class="antiHSpacer" />
|
||||
<IconAdd size={'x-small'} fill={'var(--theme-trans-color)'} />
|
||||
{#each diff.added as add}
|
||||
<PersonAccountRefPresenter value={add} disabled inline />
|
||||
<div class="antiHSpacer" />
|
||||
{/each}
|
||||
{/if}
|
||||
{#if diff.removed.length > 0}
|
||||
<div class="antiHSpacer" />
|
||||
<IconDelete size={'x-small'} fill={'var(--theme-trans-color)'} />
|
||||
{#each diff.removed as removed}
|
||||
<PersonAccountRefPresenter value={removed} disabled inline />
|
||||
<div class="antiHSpacer" />
|
||||
{/each}
|
||||
{/if}
|
||||
{:else}
|
||||
<Label label={notification.string.YouAddedCollaborators} />
|
||||
{/if}
|
@ -1,24 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2023 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Label } from '@hcengineering/ui'
|
||||
|
||||
import notification from '../../plugin'
|
||||
|
||||
// export let tx: TxCreateDoc<DirectMessage>
|
||||
// export let value: DirectMessage
|
||||
</script>
|
||||
|
||||
<Label label={notification.string.YouHaveJoinedTheConversation} />
|
@ -19,8 +19,6 @@ import { type Resources } from '@hcengineering/platform'
|
||||
import Inbox from './components/inbox/Inbox.svelte'
|
||||
import NotificationSettings from './components/NotificationSettings.svelte'
|
||||
import NotificationPresenter from './components/NotificationPresenter.svelte'
|
||||
import TxCollaboratorsChange from './components/activity/TxCollaboratorsChange.svelte'
|
||||
import TxDmCreation from './components/activity/TxDmCreation.svelte'
|
||||
import DocNotifyContextPresenter from './components/DocNotifyContextPresenter.svelte'
|
||||
import CollaboratorsChanged from './components/activity/CollaboratorsChanged.svelte'
|
||||
import ActivityInboxNotificationPresenter from './components/inbox/ActivityInboxNotificationPresenter.svelte'
|
||||
@ -67,10 +65,6 @@ export default async (): Promise<Resources> => ({
|
||||
NotificationCollaboratorsChanged,
|
||||
ReactionNotificationPresenter
|
||||
},
|
||||
activity: {
|
||||
TxCollaboratorsChange,
|
||||
TxDmCreation
|
||||
},
|
||||
function: {
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
GetInboxNotificationsClient: InboxNotificationsClientImpl.getClient,
|
||||
|
@ -27,7 +27,6 @@ import {
|
||||
Space,
|
||||
Timestamp,
|
||||
Tx,
|
||||
TxCUD,
|
||||
TxOperations
|
||||
} from '@hcengineering/core'
|
||||
import type { Asset, IntlString, Metadata, Plugin, Resource } from '@hcengineering/platform'
|
||||
@ -193,34 +192,6 @@ export interface Collaborators extends Doc {
|
||||
collaborators: Ref<Account>[]
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated
|
||||
*/
|
||||
export interface DocUpdateTx {
|
||||
_id: Ref<TxCUD<Doc>>
|
||||
modifiedBy: Ref<Account>
|
||||
modifiedOn: Timestamp
|
||||
isNew: boolean
|
||||
title?: IntlString
|
||||
body?: IntlString
|
||||
intlParams?: Record<string, string | number>
|
||||
intlParamsNotLocalized?: Record<string, IntlString>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated
|
||||
*/
|
||||
export interface DocUpdates extends Doc {
|
||||
user: Ref<Account>
|
||||
attachedTo: Ref<Doc>
|
||||
attachedToClass: Ref<Class<Doc>>
|
||||
hidden: boolean
|
||||
lastTxTime?: Timestamp
|
||||
txes: DocUpdateTx[]
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -362,7 +333,6 @@ const notification = plugin(notificationId, {
|
||||
CommonNotificationType: '' as Ref<Class<CommonNotificationType>>,
|
||||
NotificationProvider: '' as Ref<Class<NotificationProvider>>,
|
||||
NotificationSetting: '' as Ref<Class<NotificationSetting>>,
|
||||
DocUpdates: '' as Ref<Class<DocUpdates>>,
|
||||
NotificationGroup: '' as Ref<Class<NotificationGroup>>,
|
||||
NotificationPreferencesGroup: '' as Ref<Class<NotificationPreferencesGroup>>,
|
||||
DocNotifyContext: '' as Ref<Class<DocNotifyContext>>,
|
||||
@ -397,9 +367,6 @@ const notification = plugin(notificationId, {
|
||||
NotificationCollaboratorsChanged: '' as AnyComponent,
|
||||
ReactionNotificationPresenter: '' as AnyComponent
|
||||
},
|
||||
activity: {
|
||||
TxCollaboratorsChange: '' as AnyComponent
|
||||
},
|
||||
action: {
|
||||
PinDocNotifyContext: '' as Ref<Action>,
|
||||
UnpinDocNotifyContext: '' as Ref<Action>,
|
||||
|
@ -20,7 +20,6 @@
|
||||
import request from '../plugin'
|
||||
import RequestActions from './RequestActions.svelte'
|
||||
import RequestDetail from './RequestDetail.svelte'
|
||||
import TxView from './TxView.svelte'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Doc } from '@hcengineering/core'
|
||||
|
||||
@ -52,7 +51,6 @@
|
||||
</DocNavLink>
|
||||
</span>
|
||||
{/if}
|
||||
<TxView tx={object.tx} />
|
||||
</div>
|
||||
<RequestDetail value={object} />
|
||||
<RequestActions value={object} />
|
||||
|
@ -13,13 +13,26 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Request } from '@hcengineering/request'
|
||||
import TxView from './TxView.svelte'
|
||||
import request, { Request } from '@hcengineering/request'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Doc } from '@hcengineering/core'
|
||||
import { ObjectPresenter } from '@hcengineering/view-resources'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
|
||||
export let value: Request
|
||||
export let inline: boolean = false
|
||||
|
||||
const client = getClient()
|
||||
|
||||
let object: Doc | undefined
|
||||
|
||||
$: void client.findOne(value.attachedToClass, { _id: value.attachedTo }).then((res) => {
|
||||
object = res
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="flex">
|
||||
<TxView tx={value.tx} />
|
||||
<div class="flex-row-center flex-gap-2">
|
||||
<Label label={request.string.Request} />
|
||||
{#if object}
|
||||
<ObjectPresenter objectId={object._id} _class={object._class} props={{ disableClick: true }} />
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -1,226 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2022 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 { DisplayTx, TxViewlet } from '@hcengineering/activity'
|
||||
import { ActivityKey, getValue, newDisplayTx, updateViewlet } from '@hcengineering/activity-resources'
|
||||
import activity from '@hcengineering/activity-resources/src/plugin'
|
||||
import { PersonAccount } from '@hcengineering/contact'
|
||||
import { personAccountByIdStore } from '@hcengineering/contact-resources'
|
||||
import core, { AnyAttribute, Doc, Ref, Tx, TxCUD } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
import type { AttributeModel } from '@hcengineering/view'
|
||||
import { ObjectPresenter } from '@hcengineering/view-resources'
|
||||
import request from '../plugin'
|
||||
|
||||
export let tx: Tx
|
||||
const viewlets: Map<ActivityKey, TxViewlet[]> = new Map<ActivityKey, TxViewlet[]>()
|
||||
const client = getClient()
|
||||
|
||||
let ptx: DisplayTx | undefined
|
||||
|
||||
let employee: PersonAccount | undefined
|
||||
let model: AttributeModel[] = []
|
||||
|
||||
$: if (tx._id !== ptx?.tx._id) {
|
||||
ptx = newDisplayTx(tx as TxCUD<Doc>, client.getHierarchy(), false)
|
||||
if (tx.modifiedBy !== employee?._id) {
|
||||
employee = undefined
|
||||
}
|
||||
model = []
|
||||
}
|
||||
|
||||
$: ptx &&
|
||||
updateViewlet(client, viewlets, ptx).then((result) => {
|
||||
if (result.id === tx._id) {
|
||||
model = result.model
|
||||
}
|
||||
})
|
||||
|
||||
$: employee = $personAccountByIdStore.get(tx.modifiedBy as Ref<PersonAccount>)
|
||||
|
||||
function isMessageType (attr?: AnyAttribute): boolean {
|
||||
return attr?.type._class === core.class.TypeMarkup
|
||||
}
|
||||
|
||||
async function updateMessageType (model: AttributeModel[], tx: DisplayTx): Promise<boolean> {
|
||||
for (const m of model) {
|
||||
if (isMessageType(m.attribute)) {
|
||||
return true
|
||||
}
|
||||
const val = await getValue(client, m, tx)
|
||||
if (val.added.length > 1 || val.removed.length > 1) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
let hasMessageType = false
|
||||
$: ptx &&
|
||||
updateMessageType(model, ptx).then((res) => {
|
||||
hasMessageType = res
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if model.length > 0}
|
||||
<div class="flex-between msgactivity-container">
|
||||
<div class="flex-grow flex-col clear-mins" class:mention={isMessageType(model[0]?.attribute)}>
|
||||
<div class="flex-between">
|
||||
<div class="flex-row-center flex-grow label">
|
||||
{#if ptx?.updateTx}
|
||||
{#each model as m}
|
||||
{#await getValue(client, m, ptx) then value}
|
||||
{#if value.set === null || value.set === undefined}
|
||||
<span class="lower"><Label label={activity.string.Unset} /><Label label={m.label} /></span>
|
||||
{:else if value.added.length}
|
||||
<span class="lower" class:flex-grow={hasMessageType}>
|
||||
<Label label={request.string.Add} />
|
||||
<Label label={activity.string.To} />
|
||||
<Label label={m.label} />
|
||||
</span>
|
||||
<div class="strong">
|
||||
<div class="flex flex-wrap gap-2" class:emphasized={value.added.length > 1}>
|
||||
{#each value.added as cvalue}
|
||||
{#if value.isObjectAdded}
|
||||
<ObjectPresenter value={cvalue} />
|
||||
{:else}
|
||||
<svelte:component this={m.presenter} value={cvalue} />
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{:else if value.removed.length}
|
||||
<span class="lower" class:flex-grow={hasMessageType}>
|
||||
<Label label={request.string.Remove} />
|
||||
<Label label={activity.string.From} />
|
||||
<Label label={m.label} />
|
||||
</span>
|
||||
<div class="strong">
|
||||
<div class="flex flex-wrap gap-2 flex-grow" class:emphasized={value.removed.length > 1}>
|
||||
{#each value.removed as cvalue}
|
||||
{#if value.isObjectRemoved}
|
||||
<ObjectPresenter value={cvalue} />
|
||||
{:else}
|
||||
<svelte:component this={m.presenter} value={cvalue} />
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<span class="lower" class:flex-grow={hasMessageType}>
|
||||
<Label label={request.string.Change} />
|
||||
<Label label={m.label} />
|
||||
<Label label={activity.string.To} />
|
||||
</span>
|
||||
<div
|
||||
class="strong"
|
||||
class:message={isMessageType(m.attribute)}
|
||||
class:emphasized={isMessageType(m.attribute)}
|
||||
>
|
||||
{#if value.isObjectSet}
|
||||
<ObjectPresenter value={value.set} />
|
||||
{:else}
|
||||
<svelte:component this={m.presenter} value={value.set} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/await}
|
||||
{/each}
|
||||
{:else if ptx?.mixinTx}
|
||||
{#each model as m}
|
||||
{#await getValue(client, m, ptx) then value}
|
||||
{#if value.set === null}
|
||||
<span>
|
||||
<Label label={activity.string.Unset} /> <span class="lower"><Label label={m.label} /></span>
|
||||
</span>
|
||||
{:else}
|
||||
<span>
|
||||
<Label label={request.string.Change} />
|
||||
<span class="lower"><Label label={m.label} /></span>
|
||||
<Label label={activity.string.To} />
|
||||
</span>
|
||||
<div
|
||||
class="strong"
|
||||
class:message={isMessageType(m.attribute)}
|
||||
class:emphasized={isMessageType(m.attribute)}
|
||||
>
|
||||
<svelte:component this={m.presenter} value={value.set} />
|
||||
</div>
|
||||
{/if}
|
||||
{/await}
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.mention {
|
||||
position: relative;
|
||||
margin-top: 0.25rem;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -0.75rem;
|
||||
left: -0.625rem;
|
||||
right: -0.625rem;
|
||||
background-color: var(--accent-bg-color);
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 0.5rem;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
.mention::after {
|
||||
top: -0.5rem;
|
||||
}
|
||||
|
||||
.msgactivity-container {
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.emphasized {
|
||||
margin-top: 0.5rem;
|
||||
background-color: var(--theme-bg-color);
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
& > * {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
& > *:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.strong {
|
||||
font-weight: 500;
|
||||
color: var(--accent-color);
|
||||
}
|
||||
}
|
||||
|
||||
.message {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
</style>
|
@ -1,25 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2022 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 { Request } from '@hcengineering/request'
|
||||
import RequestPresenter from '../RequestPresenter.svelte'
|
||||
|
||||
export let value: Request
|
||||
export let isOwnTx: boolean
|
||||
</script>
|
||||
|
||||
{#if !isOwnTx}
|
||||
<RequestPresenter {value} />
|
||||
{/if}
|
@ -1,32 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Request } from '@hcengineering/request'
|
||||
import RequestLabel from '../RequestLabel.svelte'
|
||||
|
||||
export let value: Request
|
||||
export let isOwnTx: boolean
|
||||
let request: Request | undefined = undefined
|
||||
|
||||
const query = createQuery()
|
||||
query.query(value._class, { _id: value._id }, (res) => {
|
||||
;[request] = res
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if request}
|
||||
<RequestLabel value={request} {isOwnTx} />
|
||||
{/if}
|
@ -14,8 +14,6 @@
|
||||
//
|
||||
|
||||
import { type Resources } from '@hcengineering/platform'
|
||||
import TxCreateRequest from './components/activity/TxCreateRequest.svelte'
|
||||
import RequestLabel from './components/activity/TxRequestLabel.svelte'
|
||||
import EditRequest from './components/EditRequest.svelte'
|
||||
import RequestPresenter from './components/RequestPresenter.svelte'
|
||||
import RequestView from './components/RequestView.svelte'
|
||||
@ -25,10 +23,6 @@ export { default as RequestStatusPresenter } from './components/RequestStatusPre
|
||||
export { default as RequestDetailPopup } from './components/RequestDetailPopup.svelte'
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
activity: {
|
||||
RequestLabel,
|
||||
TxCreateRequest
|
||||
},
|
||||
component: {
|
||||
EditRequest,
|
||||
RequestPresenter,
|
||||
|
@ -29,7 +29,6 @@ export default mergeIds(requestId, request, {
|
||||
Remove: '' as IntlString,
|
||||
Completed: '' as IntlString,
|
||||
Reject: '' as IntlString,
|
||||
Request: '' as IntlString,
|
||||
Rejected: '' as IntlString,
|
||||
Comment: '' as IntlString,
|
||||
PleaseTypeMessage: '' as IntlString,
|
||||
|
@ -78,7 +78,8 @@ const request = plugin(requestId, {
|
||||
RequestView: '' as AnyComponent
|
||||
},
|
||||
string: {
|
||||
Requests: '' as IntlString
|
||||
Requests: '' as IntlString,
|
||||
Request: '' as IntlString
|
||||
},
|
||||
icon: {
|
||||
Requests: '' as Asset
|
||||
|
@ -1,42 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2022 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 { TxUpdateDoc } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Integration } from '@hcengineering/setting'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
import setting from '../../plugin'
|
||||
|
||||
export let tx: TxUpdateDoc<Integration>
|
||||
// export let doc: Integration
|
||||
|
||||
const client = getClient()
|
||||
|
||||
async function getTypeLabel (tx: TxUpdateDoc<Integration>): Promise<IntlString | undefined> {
|
||||
const doc = await client.findOne(setting.class.Integration, { _id: tx.objectId })
|
||||
if (doc === undefined) return
|
||||
const type = await client.findOne(setting.class.IntegrationType, { _id: doc.type })
|
||||
return type?.label
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
{#await getTypeLabel(tx) then typeLabel}
|
||||
{#if typeLabel}
|
||||
<Label label={typeLabel} />
|
||||
{/if}
|
||||
{/await}
|
||||
<Label label={setting.string.IntegrationDisabled} />
|
@ -18,7 +18,6 @@ import { type Resources } from '@hcengineering/platform'
|
||||
import { getClient, MessageBox } from '@hcengineering/presentation'
|
||||
import { showPopup } from '@hcengineering/ui'
|
||||
import { deleteObject } from '@hcengineering/view-resources/src/utils'
|
||||
import TxIntegrationDisable from './components/activity/TxIntegrationDisable.svelte'
|
||||
import ClassSetting from './components/ClassSetting.svelte'
|
||||
import CreateMixin from './components/CreateMixin.svelte'
|
||||
import EditEnum from './components/EditEnum.svelte'
|
||||
@ -89,9 +88,6 @@ async function DeleteMixin (object: Mixin<Class<Doc>>): Promise<void> {
|
||||
}
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
activity: {
|
||||
TxIntegrationDisable
|
||||
},
|
||||
component: {
|
||||
Settings,
|
||||
Spaces,
|
||||
|
@ -1,31 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2023 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { HTMLViewer } from '@hcengineering/presentation'
|
||||
import { TelegramMessage } from '@hcengineering/telegram'
|
||||
export let value: TelegramMessage
|
||||
</script>
|
||||
|
||||
<div class="content lines-limit-2">
|
||||
<HTMLViewer value={value.content} />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.content {
|
||||
min-width: 0;
|
||||
min-height: 1rem;
|
||||
max-height: 2.125rem;
|
||||
}
|
||||
</style>
|
@ -1,25 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 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 { TxCreateDoc } from '@hcengineering/core'
|
||||
import { TxProcessor } from '@hcengineering/core'
|
||||
import { SharedTelegramMessages } from '@hcengineering/telegram'
|
||||
import SharedMessages from '../SharedMessages.svelte'
|
||||
|
||||
export let tx: TxCreateDoc<SharedTelegramMessages>
|
||||
</script>
|
||||
|
||||
<SharedMessages value={TxProcessor.createDoc2Doc(tx)} />
|
@ -21,9 +21,7 @@ import presentation from '@hcengineering/presentation'
|
||||
import Chat from './components/Chat.svelte'
|
||||
import Connect from './components/Connect.svelte'
|
||||
import Reconnect from './components/Reconnect.svelte'
|
||||
import TxMessage from './components/activity/TxMessage.svelte'
|
||||
import IconTelegram from './components/icons/TelegramColor.svelte'
|
||||
import TxSharedCreate from './components/activity/TxSharedCreate.svelte'
|
||||
import TelegramMessageCreated from './components/activity/TelegramMessageCreated.svelte'
|
||||
import MessagePresenter from './components/MessagePresenter.svelte'
|
||||
|
||||
@ -41,8 +39,6 @@ export default async (): Promise<Resources> => ({
|
||||
MessagePresenter
|
||||
},
|
||||
activity: {
|
||||
TxSharedCreate,
|
||||
TxMessage,
|
||||
TelegramMessageCreated
|
||||
},
|
||||
function: {
|
||||
|
@ -1,32 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2023 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Issue } from '@hcengineering/tracker'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
import tracker from '../../plugin'
|
||||
import IssuePresenter from '../issues/IssuePresenter.svelte'
|
||||
|
||||
export let value: Issue
|
||||
export let isOwnTx: boolean
|
||||
|
||||
$: isSub = value.attachedTo !== tracker.ids.NoParent && !isOwnTx
|
||||
</script>
|
||||
|
||||
<div class="lower">
|
||||
<Label label={isSub ? tracker.string.CreatedSubIssue : tracker.string.CreatedIssue} />
|
||||
</div>
|
||||
{#if !isOwnTx}
|
||||
<IssuePresenter {value} />
|
||||
{/if}
|
@ -145,7 +145,6 @@ import {
|
||||
import { ComponentAggregationManager, grouppingComponentManager } from './component'
|
||||
import PriorityIcon from './components/activity/PriorityIcon.svelte'
|
||||
import StatusIcon from './components/activity/StatusIcon.svelte'
|
||||
import TxIssueCreated from './components/activity/TxIssueCreated.svelte'
|
||||
import DeleteComponentPresenter from './components/components/DeleteComponentPresenter.svelte'
|
||||
import IssueStatusIcon from './components/issues/IssueStatusIcon.svelte'
|
||||
import MoveIssues from './components/issues/Move.svelte'
|
||||
@ -593,7 +592,6 @@ export async function importTasks (tasks: File, space: Ref<Project>): Promise<vo
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
activity: {
|
||||
TxIssueCreated,
|
||||
PriorityIcon,
|
||||
StatusIcon
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user