Auto lastview subscribe (#1130)

Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
Denis Bykhov 2022-03-11 15:06:46 +06:00 committed by GitHub
parent e63274efb5
commit 776b657e79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 294 additions and 13 deletions

View File

@ -19,6 +19,7 @@ import core from '@anticrm/core'
import inventory from '@anticrm/inventory'
import view from '@anticrm/view'
import serverInventory from '@anticrm/server-inventory'
import serverCore from '@anticrm/server-core'
export function createModel (builder: Builder): void {
builder.mixin(inventory.class.Product, core.class.Class, view.mixin.HTMLPresenter, {
@ -28,4 +29,12 @@ export function createModel (builder: Builder): void {
builder.mixin(inventory.class.Product, core.class.Class, view.mixin.TextPresenter, {
presenter: serverInventory.function.ProductTextPresenter
})
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
trigger: serverInventory.trigger.OnProductCreate
})
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
trigger: serverInventory.trigger.OnProductUpdate
})
}

View File

@ -24,4 +24,8 @@ export function createModel (builder: Builder): void {
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
trigger: serverNotification.trigger.OnBacklinkCreate
})
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
trigger: serverNotification.trigger.UpdateLastView
})
}

View File

@ -19,6 +19,7 @@ import core from '@anticrm/core'
import task from '@anticrm/task'
import view from '@anticrm/view'
import serverTask from '@anticrm/server-task'
import serverCore from '@anticrm/server-core'
export function createModel (builder: Builder): void {
builder.mixin(task.class.Issue, core.class.Class, view.mixin.HTMLPresenter, {
@ -28,4 +29,12 @@ export function createModel (builder: Builder): void {
builder.mixin(task.class.Issue, core.class.Class, view.mixin.TextPresenter, {
presenter: serverTask.function.IssueTextPresenter
})
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
trigger: serverTask.trigger.OnTaskCreate
})
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
trigger: serverTask.trigger.OnTaskUpdate
})
}

View File

@ -22,8 +22,8 @@
const notificationClient = NotificationClientImpl.getClient()
const lastViews = notificationClient.getLastViews()
$: subscribed = $lastViews.has(value._id)
$: lastView = $lastViews.get(value._id)
$: subscribed = lastView !== undefined && lastView !== -1
</script>
{#if !subscribed}

View File

@ -24,7 +24,7 @@
const lastViews = notificationClient.getLastViews()
$: lastView = $lastViews.get(value._id)
$: hasNotification = lastView !== undefined && lastView < value.modifiedOn
$: hasNotification = lastView !== undefined && lastView !== -1 && lastView < value.modifiedOn
</script>
{#if hasNotification}

View File

@ -63,7 +63,7 @@ export class NotificationClientImpl implements NotificationClient {
const user = getCurrentAccount()._id
const lastView = time ?? new Date().getTime()
const current = this.lastViews.get(_id)
if (current !== undefined) {
if (current !== undefined && current.lastView !== -1) {
if (current.lastView < lastView || force) {
const u = client.txFactory.createTxUpdateDoc(current._class, current.space, current._id, {
lastView: lastView
@ -89,7 +89,9 @@ export class NotificationClientImpl implements NotificationClient {
const user = getCurrentAccount()._id
const current = await client.findOne(notification.class.LastView, { attachedTo: _id, user })
if (current !== undefined) {
await client.removeDoc(current._class, current.space, current._id)
await client.updateDoc(current._class, current.space, current._id, {
lastView: -1
})
}
}
}

View File

@ -29,6 +29,7 @@
"@anticrm/core": "~0.6.11",
"@anticrm/platform": "~0.6.5",
"@anticrm/server-core": "~0.6.0",
"@anticrm/server-notification": "~0.6.0",
"@anticrm/inventory": "~0.6.0",
"@anticrm/view": "~0.6.0",
"@anticrm/login": "~0.6.1",

View File

@ -13,13 +13,73 @@
// limitations under the License.
//
import { Doc } from '@anticrm/core'
import core, { AttachedDoc, Doc, Tx, TxCollectionCUD, TxCreateDoc, TxProcessor, TxUpdateDoc } from '@anticrm/core'
import inventory, { Product } from '@anticrm/inventory'
import login from '@anticrm/login'
import { getMetadata } from '@anticrm/platform'
import { TriggerControl } from '@anticrm/server-core'
import { getUpdateLastViewTx } from '@anticrm/server-notification'
import view from '@anticrm/view'
import workbench from '@anticrm/workbench'
const extractTx = (tx: Tx): Tx => {
if (tx._class === core.class.TxCollectionCUD) {
const ctx = (tx as TxCollectionCUD<Doc, AttachedDoc>)
if (ctx.tx._class === core.class.TxCreateDoc) {
const create = ctx.tx as TxCreateDoc<AttachedDoc>
create.attributes.attachedTo = ctx.objectId
create.attributes.attachedToClass = ctx.objectClass
create.attributes.collection = ctx.collection
return create
}
return ctx
}
return tx
}
/**
* @public
*/
export async function OnProductCreate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
const actualTx = extractTx(tx)
if (actualTx._class !== core.class.TxCreateDoc) {
return []
}
const createTx = actualTx as TxCreateDoc<Product>
if (!control.hierarchy.isDerived(createTx.objectClass, inventory.class.Product)) {
return []
}
const doc = TxProcessor.createDoc2Doc(createTx)
const lastViewTx = await getUpdateLastViewTx(control.findAll, doc._id, doc._class, createTx.modifiedOn, createTx.modifiedBy)
return lastViewTx !== undefined ? [lastViewTx] : []
}
/**
* @public
*/
export async function OnProductUpdate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
const actualTx = extractTx(tx)
if (actualTx._class !== core.class.TxUpdateDoc) {
return []
}
const updateTx = actualTx as TxUpdateDoc<Product>
if (!control.hierarchy.isDerived(updateTx.objectClass, inventory.class.Product)) {
return []
}
const lastViewTx = await getUpdateLastViewTx(control.findAll, updateTx.objectId, updateTx.objectClass, updateTx.modifiedOn, updateTx.modifiedBy)
return lastViewTx !== undefined ? [lastViewTx] : []
}
/**
* @public
*/
@ -39,6 +99,10 @@ export function productTextPresenter (doc: Doc): string {
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export default async () => ({
trigger: {
OnProductCreate,
OnProductUpdate
},
function: {
ProductHTMLPresenter: productHTMLPresenter,
ProductTextPresenter: productTextPresenter

View File

@ -16,6 +16,7 @@
import type { Resource, Plugin } from '@anticrm/platform'
import { plugin } from '@anticrm/platform'
import { Doc } from '@anticrm/core'
import { TriggerFunc } from '@anticrm/server-core'
/**
* @public
@ -26,6 +27,10 @@ export const serverInventoryId = 'server-inventory' as Plugin
* @public
*/
export default plugin(serverInventoryId, {
trigger: {
OnProductCreate: '' as Resource<TriggerFunc>,
OnProductUpdate: '' as Resource<TriggerFunc>
},
function: {
ProductHTMLPresenter: '' as Resource<(doc: Doc) => string>,
ProductTextPresenter: '' as Resource<(doc: Doc) => string>

View File

@ -29,6 +29,7 @@
"@anticrm/core": "~0.6.11",
"@anticrm/platform": "~0.6.5",
"@anticrm/server-core": "~0.6.0",
"@anticrm/server-notification": "~0.6.0",
"@anticrm/notification": "~0.6.0",
"@anticrm/chunter": "~0.6.1",
"@anticrm/view": "~0.6.0",

View File

@ -16,12 +16,29 @@
import chunter, { Backlink } from '@anticrm/chunter'
import contact, { Employee, EmployeeAccount, formatName } from '@anticrm/contact'
import core, { Class, Data, Doc, generateId, Hierarchy, Obj, Ref, Space, Tx, TxCollectionCUD, TxCreateDoc, TxProcessor } from '@anticrm/core'
import core, { AttachedDoc, Class, Data, Doc, generateId, Hierarchy, Obj, Ref, Space, Tx, TxCollectionCUD, TxCreateDoc, TxCUD, TxProcessor } from '@anticrm/core'
import notification, { EmailNotification, Notification, NotificationStatus } from '@anticrm/notification'
import { getResource } from '@anticrm/platform'
import type { TriggerControl } from '@anticrm/server-core'
import { getUpdateLastViewTx } from '@anticrm/server-notification'
import view, { HTMLPresenter, TextPresenter } from '@anticrm/view'
const extractTx = (tx: Tx): Tx => {
if (tx._class === core.class.TxCollectionCUD) {
const ctx = (tx as TxCollectionCUD<Doc, AttachedDoc>)
if (ctx.tx._class === core.class.TxCreateDoc) {
const create = ctx.tx as TxCreateDoc<AttachedDoc>
create.attributes.attachedTo = ctx.objectId
create.attributes.attachedToClass = ctx.objectClass
create.attributes.collection = ctx.collection
return create
}
return ctx
}
return tx
}
/**
* @public
*/
@ -56,6 +73,60 @@ export async function OnBacklinkCreate (tx: Tx, control: TriggerControl): Promis
return result
}
/**
* @public
*/
export async function UpdateLastView (tx: Tx, control: TriggerControl): Promise<Tx[]> {
const actualTx = extractTx(tx)
if (![core.class.TxUpdateDoc, core.class.TxCreateDoc, core.class.TxMixin].includes(actualTx._class)) {
return []
}
const result: Tx[] = []
switch (actualTx._class) {
case core.class.TxCreateDoc: {
const createTx = actualTx as TxCreateDoc<Doc>
if (control.hierarchy.isDerived(createTx.objectClass, core.class.AttachedDoc)) {
const doc = TxProcessor.createDoc2Doc(createTx as TxCreateDoc<AttachedDoc>)
const attachedTx = await getUpdateLastViewTx(control.findAll, doc.attachedTo, doc.attachedToClass, createTx.modifiedOn, createTx.modifiedBy)
if (attachedTx !== undefined) {
result.push(attachedTx)
}
} else {
const doc = TxProcessor.createDoc2Doc(createTx)
const tx = await getUpdateLastViewTx(control.findAll, doc._id, doc._class, createTx.modifiedOn, createTx.modifiedBy)
if (tx !== undefined) {
result.push(tx)
}
}
break
}
case core.class.TxUpdateDoc:
case core.class.TxMixin: {
const tx = actualTx as TxCUD<Doc>
const doc = (await control.findAll(tx.objectClass, { _id: tx.objectId }, { limit: 1 }))[0]
if (control.hierarchy.isDerived(doc._class, core.class.AttachedDoc)) {
const attachedDoc = doc as AttachedDoc
const attachedTx = await getUpdateLastViewTx(control.findAll, attachedDoc.attachedTo, attachedDoc.attachedToClass, tx.modifiedOn, tx.modifiedBy)
if (attachedTx !== undefined) {
result.push(attachedTx)
}
} else {
const resTx = await getUpdateLastViewTx(control.findAll, doc._id, doc._class, tx.modifiedOn, tx.modifiedBy)
if (resTx !== undefined) {
result.push(resTx)
}
}
break
}
default:
break
}
return result
}
async function getPlatformNotificationTx (ptx: TxCollectionCUD<Doc, Backlink>, control: TriggerControl): Promise<TxCollectionCUD<Doc, Notification> | undefined> {
const attached = (await control.modelDb.findAll(contact.class.EmployeeAccount, {
employee: ptx.objectId as Ref<Employee>
@ -191,6 +262,7 @@ function getTextPresenter (_class: Ref<Class<Doc>>, hierarchy: Hierarchy): TextP
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export default async () => ({
trigger: {
OnBacklinkCreate
OnBacklinkCreate,
UpdateLastView
}
})

View File

@ -29,6 +29,7 @@
"dependencies": {
"@anticrm/core": "~0.6.11",
"@anticrm/platform": "~0.6.5",
"@anticrm/notification": "~0.6.0",
"@anticrm/server-core": "~0.6.0"
}
}

View File

@ -14,20 +14,55 @@
// limitations under the License.
//
import core, { Account, Class, Doc, Ref, TxCreateDoc, TxFactory, TxUpdateDoc } from '@anticrm/core'
import type { Resource, Plugin } from '@anticrm/platform'
import { plugin } from '@anticrm/platform'
import type { TriggerFunc } from '@anticrm/server-core'
import type { TriggerControl, TriggerFunc } from '@anticrm/server-core'
import notification, { LastView } from '@anticrm/notification'
/**
* @public
*/
export const serverNotificationId = 'server-notification' as Plugin
/**
* @public
*/
export async function getUpdateLastViewTx (findAll: TriggerControl['findAll'], attachedTo: Ref<Doc>, attachedToClass: Ref<Class<Doc>>, lastView: number, user: Ref<Account>): Promise<TxUpdateDoc<LastView> | TxCreateDoc<LastView> | undefined> {
const current = (await findAll(notification.class.LastView, {
attachedTo,
attachedToClass,
user
}, { limit: 1 }))[0]
const factory = new TxFactory(user)
if (current !== undefined) {
if (current.lastView === -1) {
return
}
const u = factory.createTxUpdateDoc(current._class, current.space, current._id, {
lastView
})
u.space = core.space.DerivedTx
return u
} else {
const u = factory.createTxCreateDoc(notification.class.LastView, notification.space.Notifications, {
user,
lastView,
attachedTo,
attachedToClass,
collection: 'lastViews'
})
u.space = core.space.DerivedTx
return u
}
}
/**
* @public
*/
export default plugin(serverNotificationId, {
trigger: {
OnBacklinkCreate: '' as Resource<TriggerFunc>
OnBacklinkCreate: '' as Resource<TriggerFunc>,
UpdateLastView: '' as Resource<TriggerFunc>
}
})

View File

@ -29,6 +29,7 @@
"@anticrm/core": "~0.6.11",
"@anticrm/platform": "~0.6.5",
"@anticrm/server-core": "~0.6.0",
"@anticrm/server-notification": "~0.6.0",
"@anticrm/task": "~0.6.0",
"@anticrm/view": "~0.6.0",
"@anticrm/login": "~0.6.1",

View File

@ -13,12 +13,14 @@
// limitations under the License.
//
import task, { Issue } from '@anticrm/task'
import { Doc } from '@anticrm/core'
import core, { Doc, Tx, TxCreateDoc, TxProcessor, TxUpdateDoc } from '@anticrm/core'
import login from '@anticrm/login'
import { getMetadata } from '@anticrm/platform'
import workbench from '@anticrm/workbench'
import { TriggerControl } from '@anticrm/server-core'
import { getUpdateLastViewTx } from '@anticrm/server-notification'
import task, { Issue, Task } from '@anticrm/task'
import view from '@anticrm/view'
import workbench from '@anticrm/workbench'
/**
* @public
@ -37,8 +39,78 @@ export function issueTextPresenter (doc: Doc): string {
return `Task-${issue.number}`
}
/**
* @public
*/
export async function OnTaskCreate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
if (tx._class !== core.class.TxCreateDoc) {
return []
}
const createTx = tx as TxCreateDoc<Task>
if (!control.hierarchy.isDerived(createTx.objectClass, task.class.Task)) {
return []
}
const doc = TxProcessor.createDoc2Doc(createTx)
const txes: Tx[] = []
const mainTx = await getUpdateLastViewTx(control.findAll, doc._id, doc._class, createTx.modifiedOn, createTx.modifiedBy)
if (mainTx !== undefined) {
txes.push(mainTx)
}
if (doc.assignee != null) {
const assignee = (await control.modelDb.findAll(core.class.Account, { emoloyee: doc.assignee }, { limit: 1 }))[0]
if (assignee !== undefined) {
const assigneeTx = await getUpdateLastViewTx(control.findAll, doc._id, doc._class, createTx.modifiedOn, assignee._id)
if (assigneeTx !== undefined) {
txes.push(assigneeTx)
}
}
}
return txes
}
/**
* @public
*/
export async function OnTaskUpdate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
if (tx._class !== core.class.TxUpdateDoc) {
return []
}
const updateTx = tx as TxUpdateDoc<Task>
if (!control.hierarchy.isDerived(updateTx.objectClass, task.class.Task)) {
return []
}
const txes: Tx[] = []
const mainTx = await getUpdateLastViewTx(control.findAll, updateTx.objectId, updateTx.objectClass, updateTx.modifiedOn, updateTx.modifiedBy)
if (mainTx !== undefined) {
txes.push(mainTx)
}
if (updateTx.operations.assignee != null) {
const assignee = (await control.modelDb.findAll(core.class.Account, { emoloyee: updateTx.operations.assignee }, { limit: 1 }))[0]
if (assignee !== undefined) {
const assigneeTx = await getUpdateLastViewTx(control.findAll, updateTx.objectId, updateTx.objectClass, updateTx.modifiedOn, assignee._id)
if (assigneeTx !== undefined) {
txes.push(assigneeTx)
}
}
}
return txes
}
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export default async () => ({
trigger: {
OnTaskCreate,
OnTaskUpdate
},
function: {
IssueHTMLPresenter: issueHTMLPresenter,
IssueTextPresenter: issueTextPresenter

View File

@ -16,6 +16,7 @@
import type { Resource, Plugin } from '@anticrm/platform'
import { plugin } from '@anticrm/platform'
import { Doc } from '@anticrm/core'
import { TriggerFunc } from '@anticrm/server-core'
/**
* @public
@ -26,6 +27,10 @@ export const serverTaskId = 'server-task' as Plugin
* @public
*/
export default plugin(serverTaskId, {
trigger: {
OnTaskCreate: '' as Resource<TriggerFunc>,
OnTaskUpdate: '' as Resource<TriggerFunc>
},
function: {
IssueHTMLPresenter: '' as Resource<(doc: Doc) => string>,
IssueTextPresenter: '' as Resource<(doc: Doc) => string>