mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 11:01:54 +03:00
Notification (#934)
Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
parent
bf2e0aba30
commit
e026683db0
@ -56,6 +56,7 @@ specifiers:
|
|||||||
'@rush-temp/model-gmail': file:./projects/model-gmail.tgz
|
'@rush-temp/model-gmail': file:./projects/model-gmail.tgz
|
||||||
'@rush-temp/model-inventory': file:./projects/model-inventory.tgz
|
'@rush-temp/model-inventory': file:./projects/model-inventory.tgz
|
||||||
'@rush-temp/model-lead': file:./projects/model-lead.tgz
|
'@rush-temp/model-lead': file:./projects/model-lead.tgz
|
||||||
|
'@rush-temp/model-notification': file:./projects/model-notification.tgz
|
||||||
'@rush-temp/model-presentation': file:./projects/model-presentation.tgz
|
'@rush-temp/model-presentation': file:./projects/model-presentation.tgz
|
||||||
'@rush-temp/model-recruit': file:./projects/model-recruit.tgz
|
'@rush-temp/model-recruit': file:./projects/model-recruit.tgz
|
||||||
'@rush-temp/model-rig': file:./projects/model-rig.tgz
|
'@rush-temp/model-rig': file:./projects/model-rig.tgz
|
||||||
@ -70,6 +71,8 @@ specifiers:
|
|||||||
'@rush-temp/model-view': file:./projects/model-view.tgz
|
'@rush-temp/model-view': file:./projects/model-view.tgz
|
||||||
'@rush-temp/model-workbench': file:./projects/model-workbench.tgz
|
'@rush-temp/model-workbench': file:./projects/model-workbench.tgz
|
||||||
'@rush-temp/mongo': file:./projects/mongo.tgz
|
'@rush-temp/mongo': file:./projects/mongo.tgz
|
||||||
|
'@rush-temp/notification': file:./projects/notification.tgz
|
||||||
|
'@rush-temp/notification-resources': file:./projects/notification-resources.tgz
|
||||||
'@rush-temp/panel': file:./projects/panel.tgz
|
'@rush-temp/panel': file:./projects/panel.tgz
|
||||||
'@rush-temp/platform': file:./projects/platform.tgz
|
'@rush-temp/platform': file:./projects/platform.tgz
|
||||||
'@rush-temp/platform-rig': file:./projects/platform-rig.tgz
|
'@rush-temp/platform-rig': file:./projects/platform-rig.tgz
|
||||||
@ -251,6 +254,7 @@ dependencies:
|
|||||||
'@rush-temp/model-gmail': file:projects/model-gmail.tgz_typescript@4.5.4
|
'@rush-temp/model-gmail': file:projects/model-gmail.tgz_typescript@4.5.4
|
||||||
'@rush-temp/model-inventory': file:projects/model-inventory.tgz_typescript@4.5.4
|
'@rush-temp/model-inventory': file:projects/model-inventory.tgz_typescript@4.5.4
|
||||||
'@rush-temp/model-lead': file:projects/model-lead.tgz_typescript@4.5.4
|
'@rush-temp/model-lead': file:projects/model-lead.tgz_typescript@4.5.4
|
||||||
|
'@rush-temp/model-notification': file:projects/model-notification.tgz_typescript@4.5.4
|
||||||
'@rush-temp/model-presentation': file:projects/model-presentation.tgz_typescript@4.5.4
|
'@rush-temp/model-presentation': file:projects/model-presentation.tgz_typescript@4.5.4
|
||||||
'@rush-temp/model-recruit': file:projects/model-recruit.tgz_typescript@4.5.4
|
'@rush-temp/model-recruit': file:projects/model-recruit.tgz_typescript@4.5.4
|
||||||
'@rush-temp/model-rig': file:projects/model-rig.tgz_37f79b97d0d86442e45d380c86f520c5
|
'@rush-temp/model-rig': file:projects/model-rig.tgz_37f79b97d0d86442e45d380c86f520c5
|
||||||
@ -265,6 +269,8 @@ dependencies:
|
|||||||
'@rush-temp/model-view': file:projects/model-view.tgz_typescript@4.5.4
|
'@rush-temp/model-view': file:projects/model-view.tgz_typescript@4.5.4
|
||||||
'@rush-temp/model-workbench': file:projects/model-workbench.tgz_typescript@4.5.4
|
'@rush-temp/model-workbench': file:projects/model-workbench.tgz_typescript@4.5.4
|
||||||
'@rush-temp/mongo': file:projects/mongo.tgz
|
'@rush-temp/mongo': file:projects/mongo.tgz
|
||||||
|
'@rush-temp/notification': file:projects/notification.tgz
|
||||||
|
'@rush-temp/notification-resources': file:projects/notification-resources.tgz_ac194b5590200ebf8338e0f86ec190f4
|
||||||
'@rush-temp/panel': file:projects/panel.tgz_096c09b0b673a57c275d9767a12070b1
|
'@rush-temp/panel': file:projects/panel.tgz_096c09b0b673a57c275d9767a12070b1
|
||||||
'@rush-temp/platform': file:projects/platform.tgz
|
'@rush-temp/platform': file:projects/platform.tgz
|
||||||
'@rush-temp/platform-rig': file:projects/platform-rig.tgz_37f79b97d0d86442e45d380c86f520c5
|
'@rush-temp/platform-rig': file:projects/platform-rig.tgz_37f79b97d0d86442e45d380c86f520c5
|
||||||
@ -12094,7 +12100,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/gmail-resources.tgz_096c09b0b673a57c275d9767a12070b1:
|
file:projects/gmail-resources.tgz_096c09b0b673a57c275d9767a12070b1:
|
||||||
resolution: {integrity: sha512-zZWhkD/HZH3/ljH7WhD2IG9NhQ6b1JPTgG2Rc35pGwScC2uzyI8xT3cq4K/jbsYLZ3BXj31BYOno7Mqo6pFcZg==, tarball: file:projects/gmail-resources.tgz}
|
resolution: {integrity: sha512-C7jgVQmN2v6HBKZgVAzWPYRaWrRHhfWpIRIRUyBUmspQreX2ArmCFCjq+1CHYBh9lGcnBZGgaNN1zQ1aTzSFLA==, tarball: file:projects/gmail-resources.tgz}
|
||||||
id: file:projects/gmail-resources.tgz
|
id: file:projects/gmail-resources.tgz
|
||||||
name: '@rush-temp/gmail-resources'
|
name: '@rush-temp/gmail-resources'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
@ -12425,7 +12431,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/model-all.tgz_typescript@4.5.4:
|
file:projects/model-all.tgz_typescript@4.5.4:
|
||||||
resolution: {integrity: sha512-KRaYm65Scg01RukljQmwVKLg143zgzLtjWBJQizBtdAU0X8XoHFnW6cOK1Pa3O15Et812fs3HhWWTUj32GJnoQ==, tarball: file:projects/model-all.tgz}
|
resolution: {integrity: sha512-Q1uN1ojy8Xc1Q2hsrrBqQrwdhkJgfGu90yb/mtHmDDb+WCguVe1JlPL9jbLV0f39wY5qnCzByDApK2FHftgVlA==, tarball: file:projects/model-all.tgz}
|
||||||
id: file:projects/model-all.tgz
|
id: file:projects/model-all.tgz
|
||||||
name: '@rush-temp/model-all'
|
name: '@rush-temp/model-all'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
@ -12617,6 +12623,27 @@ packages:
|
|||||||
- typescript
|
- typescript
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
file:projects/model-notification.tgz_typescript@4.5.4:
|
||||||
|
resolution: {integrity: sha512-q/JNneErW66xgCE5Yc/7bPkeUXwiEUJw95gbYt5uDTeuuzK0TrP77epbfSaWqkH7kP9SjqKrBwWPdm+M64q98g==, tarball: file:projects/model-notification.tgz}
|
||||||
|
id: file:projects/model-notification.tgz
|
||||||
|
name: '@rush-temp/model-notification'
|
||||||
|
version: 0.0.0
|
||||||
|
dependencies:
|
||||||
|
'@rushstack/heft': 0.41.8
|
||||||
|
'@types/heft-jest': 1.0.2
|
||||||
|
'@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
|
||||||
|
'@typescript-eslint/parser': 5.7.0_eslint@7.32.0+typescript@4.5.4
|
||||||
|
eslint: 7.32.0
|
||||||
|
eslint-config-standard-with-typescript: 21.0.1_ce2fa0c4dfa1c256100cababd749a13a
|
||||||
|
eslint-plugin-import: 2.25.3_eslint@7.32.0
|
||||||
|
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||||
|
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||||
|
prettier: 2.5.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
- typescript
|
||||||
|
dev: false
|
||||||
|
|
||||||
file:projects/model-presentation.tgz_typescript@4.5.4:
|
file:projects/model-presentation.tgz_typescript@4.5.4:
|
||||||
resolution: {integrity: sha512-cBkfqfxIgaViuDI4bj0NDuVg1NnzoSjL2mxvRRRn1Dnqkq+2K00cKyx67Pil9dHwH636h2HEYKoM4VNsT3Os1w==, tarball: file:projects/model-presentation.tgz}
|
resolution: {integrity: sha512-cBkfqfxIgaViuDI4bj0NDuVg1NnzoSjL2mxvRRRn1Dnqkq+2K00cKyx67Pil9dHwH636h2HEYKoM4VNsT3Os1w==, tarball: file:projects/model-presentation.tgz}
|
||||||
id: file:projects/model-presentation.tgz
|
id: file:projects/model-presentation.tgz
|
||||||
@ -12931,6 +12958,59 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
file:projects/notification-resources.tgz_ac194b5590200ebf8338e0f86ec190f4:
|
||||||
|
resolution: {integrity: sha512-TgP/VzfwZ+LGqaToY+rKYjH683GBuSHetD9bM85Jg6mtCtU09uogF3mwqKCAU1gyKtiVYLA63VmAb8MkxwV56A==, tarball: file:projects/notification-resources.tgz}
|
||||||
|
id: file:projects/notification-resources.tgz
|
||||||
|
name: '@rush-temp/notification-resources'
|
||||||
|
version: 0.0.0
|
||||||
|
dependencies:
|
||||||
|
'@rushstack/heft': 0.41.8
|
||||||
|
'@types/heft-jest': 1.0.2
|
||||||
|
'@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
|
||||||
|
'@typescript-eslint/parser': 5.7.0_eslint@7.32.0+typescript@4.5.4
|
||||||
|
eslint: 7.32.0
|
||||||
|
eslint-config-standard-with-typescript: 21.0.1_ce2fa0c4dfa1c256100cababd749a13a
|
||||||
|
eslint-plugin-import: 2.25.3_eslint@7.32.0
|
||||||
|
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||||
|
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||||
|
prettier: 2.5.1
|
||||||
|
svelte: 3.44.3
|
||||||
|
svelte-check: 2.3.0_4374c622c67ed7479ff0e44c29d09bce
|
||||||
|
typescript: 4.5.4
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@babel/core'
|
||||||
|
- coffeescript
|
||||||
|
- less
|
||||||
|
- node-sass
|
||||||
|
- postcss
|
||||||
|
- postcss-load-config
|
||||||
|
- pug
|
||||||
|
- sass
|
||||||
|
- stylus
|
||||||
|
- sugarss
|
||||||
|
- supports-color
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
file:projects/notification.tgz:
|
||||||
|
resolution: {integrity: sha512-VOGvKSMA5Utp3zuIYzvaHujYwS2ObDBtjrzKJzWDn8RtQo8zW8nOYjEOIDDIvXKGZlpl/5v62pDsEBiCQ/yNhw==, tarball: file:projects/notification.tgz}
|
||||||
|
name: '@rush-temp/notification'
|
||||||
|
version: 0.0.0
|
||||||
|
dependencies:
|
||||||
|
'@rushstack/heft': 0.41.8
|
||||||
|
'@types/heft-jest': 1.0.2
|
||||||
|
'@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
|
||||||
|
'@typescript-eslint/parser': 5.7.0_eslint@7.32.0+typescript@4.5.4
|
||||||
|
eslint: 7.32.0
|
||||||
|
eslint-config-standard-with-typescript: 21.0.1_ce2fa0c4dfa1c256100cababd749a13a
|
||||||
|
eslint-plugin-import: 2.25.3_eslint@7.32.0
|
||||||
|
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||||
|
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||||
|
prettier: 2.5.1
|
||||||
|
typescript: 4.5.4
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: false
|
||||||
|
|
||||||
file:projects/panel.tgz_096c09b0b673a57c275d9767a12070b1:
|
file:projects/panel.tgz_096c09b0b673a57c275d9767a12070b1:
|
||||||
resolution: {integrity: sha512-RxiNEW/PUGlF6/JrOvYWaUVfmC4kVnrd1tUQBpUM9tcRQebTwjPIUF1+V19xT63Rer6sRmz4SWo9v6/BtRAJlw==, tarball: file:projects/panel.tgz}
|
resolution: {integrity: sha512-RxiNEW/PUGlF6/JrOvYWaUVfmC4kVnrd1tUQBpUM9tcRQebTwjPIUF1+V19xT63Rer6sRmz4SWo9v6/BtRAJlw==, tarball: file:projects/panel.tgz}
|
||||||
id: file:projects/panel.tgz
|
id: file:projects/panel.tgz
|
||||||
@ -13047,7 +13127,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/presentation.tgz_096c09b0b673a57c275d9767a12070b1:
|
file:projects/presentation.tgz_096c09b0b673a57c275d9767a12070b1:
|
||||||
resolution: {integrity: sha512-QhfoHLyegkl3JsleXaZkSWsVQgikJM9jdqkla2mRW3kHyQAYWPBITUnVVWu1qBADViJupNZdYmYEcm7oeMF5og==, tarball: file:projects/presentation.tgz}
|
resolution: {integrity: sha512-jijMKCYTGrkmr9ShyFIPRnr3Uxyy8gyk8SDmwr9Wl4VevuSO9S/vHE0Pln+bEcKDtnzM/Vzh3GJmwUq3aPV3Aw==, tarball: file:projects/presentation.tgz}
|
||||||
id: file:projects/presentation.tgz
|
id: file:projects/presentation.tgz
|
||||||
name: '@rush-temp/presentation'
|
name: '@rush-temp/presentation'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
@ -13083,7 +13163,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/prod.tgz_sass@1.45.0+typescript@4.5.4:
|
file:projects/prod.tgz_sass@1.45.0+typescript@4.5.4:
|
||||||
resolution: {integrity: sha512-mb0NOzQOQI/mZjzWLO+zp8F57x5koGPRF0qdrONOBHgKJ+wKpbSrVyW911S0nM8b/mVimVqaXCI2XdjAvYO1mg==, tarball: file:projects/prod.tgz}
|
resolution: {integrity: sha512-8jdkx/UlSEQLfZMUQ/XwQoqOiGAZtR6yJehJqk8TrjkUWKD4y0kroErjmQZ1sv014LnaWuux3mjbte+9b79I7w==, tarball: file:projects/prod.tgz}
|
||||||
id: file:projects/prod.tgz
|
id: file:projects/prod.tgz
|
||||||
name: '@rush-temp/prod'
|
name: '@rush-temp/prod'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
@ -13157,7 +13237,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/recruit-resources.tgz_096c09b0b673a57c275d9767a12070b1:
|
file:projects/recruit-resources.tgz_096c09b0b673a57c275d9767a12070b1:
|
||||||
resolution: {integrity: sha512-p9e/9INuzm1qqr+zo6pCU0XRtFlQPObX3USd3gRr4hSt/YnFOD5+flKwk4LMCPtLrubdFhTAPX+Ydu1cHJrnnQ==, tarball: file:projects/recruit-resources.tgz}
|
resolution: {integrity: sha512-NgcfcFmKVD0ojJbtjT3yvD//m1x3koZpBdkLryhSKtQMhzcuZD9GpBmVbRnPtLGBrDMSALasx1NB5JMxxDPLFQ==, tarball: file:projects/recruit-resources.tgz}
|
||||||
id: file:projects/recruit-resources.tgz
|
id: file:projects/recruit-resources.tgz
|
||||||
name: '@rush-temp/recruit-resources'
|
name: '@rush-temp/recruit-resources'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
@ -13611,7 +13691,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/telegram-resources.tgz_096c09b0b673a57c275d9767a12070b1:
|
file:projects/telegram-resources.tgz_096c09b0b673a57c275d9767a12070b1:
|
||||||
resolution: {integrity: sha512-ZneaA8uvCmjuSf+zjIrPM1lzhi1At5Giud4UOzKwz2o0lrC4+npRTtCIZ52ZKjjNoOTmgf/hr9RYrqwCMH+z4g==, tarball: file:projects/telegram-resources.tgz}
|
resolution: {integrity: sha512-hreZkJ3tRwh2rXC7K/WlkA99TDatMLuHwtC2dTXqivRNEKhwH0RHDXgO1jrjulj7uSNj5+0ksOeQtlWBYmhRiw==, tarball: file:projects/telegram-resources.tgz}
|
||||||
id: file:projects/telegram-resources.tgz
|
id: file:projects/telegram-resources.tgz
|
||||||
name: '@rush-temp/telegram-resources'
|
name: '@rush-temp/telegram-resources'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
|
@ -97,6 +97,8 @@
|
|||||||
"@anticrm/templates": "~0.6.0",
|
"@anticrm/templates": "~0.6.0",
|
||||||
"@anticrm/templates-assets": "~0.6.0",
|
"@anticrm/templates-assets": "~0.6.0",
|
||||||
"@anticrm/templates-resources": "~0.6.0",
|
"@anticrm/templates-resources": "~0.6.0",
|
||||||
|
"@anticrm/notification": "~0.6.0",
|
||||||
|
"@anticrm/notification-resources": "~0.6.0",
|
||||||
"@anticrm/core": "~0.6.16",
|
"@anticrm/core": "~0.6.16",
|
||||||
"@anticrm/rekoni": "~0.6.0"
|
"@anticrm/rekoni": "~0.6.0"
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ import { gmailId } from '@anticrm/gmail'
|
|||||||
import { imageCropperId } from '@anticrm/image-cropper'
|
import { imageCropperId } from '@anticrm/image-cropper'
|
||||||
import { inventoryId } from '@anticrm/inventory'
|
import { inventoryId } from '@anticrm/inventory'
|
||||||
import { templatesId } from '@anticrm/templates'
|
import { templatesId } from '@anticrm/templates'
|
||||||
|
import { notificationId } from '@anticrm/notification'
|
||||||
import rekoni from '@anticrm/rekoni'
|
import rekoni from '@anticrm/rekoni'
|
||||||
|
|
||||||
import '@anticrm/login-assets'
|
import '@anticrm/login-assets'
|
||||||
@ -86,4 +87,6 @@ export async function configurePlatform() {
|
|||||||
addLocation(imageCropperId, () => import(/* webpackChunkName: "image-cropper" */ '@anticrm/image-cropper-resources'))
|
addLocation(imageCropperId, () => import(/* webpackChunkName: "image-cropper" */ '@anticrm/image-cropper-resources'))
|
||||||
addLocation(inventoryId, () => import(/* webpackChunkName: "inventory" */ '@anticrm/inventory-resources'))
|
addLocation(inventoryId, () => import(/* webpackChunkName: "inventory" */ '@anticrm/inventory-resources'))
|
||||||
addLocation(templatesId, () => import(/* webpackChunkName: "templates" */ '@anticrm/templates-resources'))
|
addLocation(templatesId, () => import(/* webpackChunkName: "templates" */ '@anticrm/templates-resources'))
|
||||||
|
addLocation(templatesId, () => import(/* webpackChunkName: "templates" */ '@anticrm/templates-resources'))
|
||||||
|
addLocation(notificationId, () => import(/* webpackChunkName: "notification" */ '@anticrm/notification-resources'))
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
"@anticrm/model-inventory": "~0.6.0",
|
"@anticrm/model-inventory": "~0.6.0",
|
||||||
"@anticrm/model-presentation": "~0.6.0",
|
"@anticrm/model-presentation": "~0.6.0",
|
||||||
"@anticrm/model-templates": "~0.6.0",
|
"@anticrm/model-templates": "~0.6.0",
|
||||||
|
"@anticrm/model-notification": "~0.6.0",
|
||||||
"@anticrm/model-text-editor": "~0.6.0",
|
"@anticrm/model-text-editor": "~0.6.0",
|
||||||
"@anticrm/core": "~0.6.13"
|
"@anticrm/core": "~0.6.13"
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ import { createModel as templatesModel } from '@anticrm/model-templates'
|
|||||||
import { createModel as textEditorModel } from '@anticrm/model-text-editor'
|
import { createModel as textEditorModel } from '@anticrm/model-text-editor'
|
||||||
import { createModel as viewModel } from '@anticrm/model-view'
|
import { createModel as viewModel } from '@anticrm/model-view'
|
||||||
import { createModel as workbenchModel } from '@anticrm/model-workbench'
|
import { createModel as workbenchModel } from '@anticrm/model-workbench'
|
||||||
|
import { createModel as notificationModel } from '@anticrm/model-notification'
|
||||||
|
|
||||||
const builder = new Builder()
|
const builder = new Builder()
|
||||||
|
|
||||||
@ -57,7 +58,7 @@ const builders = [
|
|||||||
presentationModel,
|
presentationModel,
|
||||||
templatesModel,
|
templatesModel,
|
||||||
textEditorModel,
|
textEditorModel,
|
||||||
|
notificationModel,
|
||||||
serverCoreModel,
|
serverCoreModel,
|
||||||
serverAttachmentModel,
|
serverAttachmentModel,
|
||||||
serverContactModel,
|
serverContactModel,
|
||||||
|
@ -70,6 +70,8 @@ export class TChannel extends TAttachedDoc implements Channel {
|
|||||||
|
|
||||||
@Prop(TypeString(), 'Value' as IntlString)
|
@Prop(TypeString(), 'Value' as IntlString)
|
||||||
value!: string
|
value!: string
|
||||||
|
|
||||||
|
items?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
@Model(contact.class.Person, contact.class.Contact)
|
@Model(contact.class.Person, contact.class.Contact)
|
||||||
|
7
models/notification/.eslintrc.js
Normal file
7
models/notification/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: ['./node_modules/@anticrm/model-rig/profiles/default/config/eslint.config.json'],
|
||||||
|
parserOptions: {
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
project: './tsconfig.json'
|
||||||
|
}
|
||||||
|
}
|
4
models/notification/.npmignore
Normal file
4
models/notification/.npmignore
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*
|
||||||
|
!/lib/**
|
||||||
|
!CHANGELOG.md
|
||||||
|
/lib/**/__tests__/
|
18
models/notification/config/rig.json
Normal file
18
models/notification/config/rig.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// The "rig.json" file directs tools to look for their config files in an external package.
|
||||||
|
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Required) The name of the rig package to inherit from.
|
||||||
|
* It should be an NPM package name with the "-rig" suffix.
|
||||||
|
*/
|
||||||
|
"rigPackageName": "@anticrm/model-rig"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Optional) Selects a config profile from the rig package. The name must consist of
|
||||||
|
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
|
||||||
|
* If omitted, then the "default" profile will be used."
|
||||||
|
*/
|
||||||
|
// "rigProfile": "your-profile-name"
|
||||||
|
}
|
34
models/notification/package.json
Normal file
34
models/notification/package.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "@anticrm/model-notification",
|
||||||
|
"version": "0.6.0",
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"author": "Anticrm Platform Contributors",
|
||||||
|
"license": "EPL-2.0",
|
||||||
|
"scripts": {
|
||||||
|
"build": "heft build",
|
||||||
|
"build:watch": "tsc",
|
||||||
|
"lint:fix": "eslint --fix src",
|
||||||
|
"lint": "eslint src",
|
||||||
|
"format": "prettier --write src && eslint --fix src"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@anticrm/model-rig": "~0.6.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||||
|
"eslint-plugin-import": "^2.25.3",
|
||||||
|
"eslint-plugin-promise": "^5.1.1",
|
||||||
|
"eslint-plugin-node": "^11.1.0",
|
||||||
|
"eslint": "^7.32.0",
|
||||||
|
"@types/heft-jest": "^1.0.2",
|
||||||
|
"@typescript-eslint/parser": "^5.4.0",
|
||||||
|
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||||
|
"prettier": "^2.4.1",
|
||||||
|
"@rushstack/heft": "^0.41.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@anticrm/core": "~0.6.11",
|
||||||
|
"@anticrm/model": "~0.6.0",
|
||||||
|
"@anticrm/platform": "~0.6.5",
|
||||||
|
"@anticrm/model-core": "~0.6.0",
|
||||||
|
"@anticrm/notification": "~0.6.0"
|
||||||
|
}
|
||||||
|
}
|
36
models/notification/src/index.ts
Normal file
36
models/notification/src/index.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||||
|
// Copyright © 2021, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
import type { Account, Domain, Ref, Timestamp } from '@anticrm/core'
|
||||||
|
import { Builder, Model, Prop, TypeRef, TypeTimestamp } from '@anticrm/model'
|
||||||
|
import core, { TAttachedDoc } from '@anticrm/model-core'
|
||||||
|
import notificaton, { LastView } from '@anticrm/notification'
|
||||||
|
import type { IntlString } from '@anticrm/platform'
|
||||||
|
|
||||||
|
export const DOMAIN_NOTIFICATION = 'notification' as Domain
|
||||||
|
|
||||||
|
@Model(notificaton.class.LastView, core.class.AttachedDoc, DOMAIN_NOTIFICATION)
|
||||||
|
export class TLastView extends TAttachedDoc implements LastView {
|
||||||
|
@Prop(TypeTimestamp(), 'Last View' as IntlString)
|
||||||
|
lastView!: Timestamp
|
||||||
|
|
||||||
|
@Prop(TypeRef(core.class.Account), 'Modified By' as IntlString)
|
||||||
|
user!: Ref<Account>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createModel (builder: Builder): void {
|
||||||
|
builder.createModel(TLastView)
|
||||||
|
}
|
8
models/notification/tsconfig.json
Normal file
8
models/notification/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "./node_modules/@anticrm/model-rig/profiles/default/tsconfig.json",
|
||||||
|
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./lib",
|
||||||
|
}
|
||||||
|
}
|
@ -55,6 +55,7 @@ export default plugin(coreId, {
|
|||||||
},
|
},
|
||||||
space: {
|
space: {
|
||||||
Tx: '' as Ref<Space>,
|
Tx: '' as Ref<Space>,
|
||||||
|
DerivedTx: '' as Ref<Space>,
|
||||||
Model: '' as Ref<Space>
|
Model: '' as Ref<Space>
|
||||||
},
|
},
|
||||||
account: {
|
account: {
|
||||||
|
@ -30,7 +30,6 @@ export { default as AttributesBar } from './components/AttributesBar.svelte'
|
|||||||
export { default as AttributeBarEditor } from './components/AttributeBarEditor.svelte'
|
export { default as AttributeBarEditor } from './components/AttributeBarEditor.svelte'
|
||||||
export { default as AttributeEditor } from './components/AttributeEditor.svelte'
|
export { default as AttributeEditor } from './components/AttributeEditor.svelte'
|
||||||
export { default as Card } from './components/Card.svelte'
|
export { default as Card } from './components/Card.svelte'
|
||||||
export { default as Channels } from './components/Channels.svelte'
|
|
||||||
export { default as PDFViewer } from './components/PDFViewer.svelte'
|
export { default as PDFViewer } from './components/PDFViewer.svelte'
|
||||||
export { default as MessageBox } from './components/MessageBox.svelte'
|
export { default as MessageBox } from './components/MessageBox.svelte'
|
||||||
export { default as SpaceCreateCard } from './components/SpaceCreateCard.svelte'
|
export { default as SpaceCreateCard } from './components/SpaceCreateCard.svelte'
|
||||||
|
@ -22,11 +22,9 @@ import login from '@anticrm/login'
|
|||||||
import { getMetadata } from '@anticrm/platform'
|
import { getMetadata } from '@anticrm/platform'
|
||||||
import { LiveQuery as LQ } from '@anticrm/query'
|
import { LiveQuery as LQ } from '@anticrm/query'
|
||||||
import { onDestroy } from 'svelte'
|
import { onDestroy } from 'svelte'
|
||||||
import contact, { ChannelProvider } from '@anticrm/contact'
|
|
||||||
|
|
||||||
let liveQuery: LQ
|
let liveQuery: LQ
|
||||||
let client: TxOperations
|
let client: TxOperations
|
||||||
let channelProviders: Promise<ChannelProvider[]> | undefined
|
|
||||||
|
|
||||||
class UIClient extends TxOperations implements Client {
|
class UIClient extends TxOperations implements Client {
|
||||||
constructor (client: Client, private readonly liveQuery: LQ) {
|
constructor (client: Client, private readonly liveQuery: LQ) {
|
||||||
@ -49,7 +47,6 @@ export function setClient (_client: Client): void {
|
|||||||
_client.notify = (tx: Tx) => {
|
_client.notify = (tx: Tx) => {
|
||||||
liveQuery.tx(tx).catch((err) => console.log(err))
|
liveQuery.tx(tx).catch((err) => console.log(err))
|
||||||
}
|
}
|
||||||
channelProviders = client.findAll(contact.class.ChannelProvider, {})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LiveQuery {
|
export class LiveQuery {
|
||||||
@ -113,12 +110,3 @@ export function getAttributePresenterClass (attribute: AnyAttribute): Ref<Class<
|
|||||||
}
|
}
|
||||||
return attrClass
|
return attrClass
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getChannelProviders (): Promise<Map<Ref<ChannelProvider>, ChannelProvider>> {
|
|
||||||
const cp = (await channelProviders) ?? []
|
|
||||||
const map = new Map<Ref<ChannelProvider>, ChannelProvider>()
|
|
||||||
for (const provider of cp) {
|
|
||||||
map.set(provider._id, provider)
|
|
||||||
}
|
|
||||||
return map
|
|
||||||
}
|
|
||||||
|
@ -116,6 +116,7 @@
|
|||||||
bind:this={componentInstance}
|
bind:this={componentInstance}
|
||||||
_id={props._id}
|
_id={props._id}
|
||||||
_class={props._class}
|
_class={props._class}
|
||||||
|
rightSection={props.rightSection}
|
||||||
on:update={fitPopup}
|
on:update={fitPopup}
|
||||||
on:close={_close}
|
on:close={_close}
|
||||||
/>
|
/>
|
||||||
|
@ -7,6 +7,7 @@ export interface PanelProps {
|
|||||||
_id: string
|
_id: string
|
||||||
_class: string
|
_class: string
|
||||||
element?: PopupAlignment
|
element?: PopupAlignment
|
||||||
|
rightSection?: AnyComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
export const panelstore = writable < {panel?: PanelProps|undefined}>({ panel: undefined })
|
export const panelstore = writable < {panel?: PanelProps|undefined}>({ panel: undefined })
|
||||||
@ -23,7 +24,8 @@ export function showPanel (
|
|||||||
component: AnyComponent,
|
component: AnyComponent,
|
||||||
_id: string,
|
_id: string,
|
||||||
_class: string,
|
_class: string,
|
||||||
element?: PopupAlignment
|
element?: PopupAlignment,
|
||||||
|
rightSection?: AnyComponent
|
||||||
): void {
|
): void {
|
||||||
const newLoc = encodeURIComponent([component, _id, _class].join('|'))
|
const newLoc = encodeURIComponent([component, _id, _class].join('|'))
|
||||||
if (currentLocation === newLoc) {
|
if (currentLocation === newLoc) {
|
||||||
@ -31,7 +33,7 @@ export function showPanel (
|
|||||||
}
|
}
|
||||||
currentLocation = newLoc
|
currentLocation = newLoc
|
||||||
panelstore.update(() => {
|
panelstore.update(() => {
|
||||||
return { panel: { component, _id, _class, element } }
|
return { panel: { component, _id, _class, element, rightSection } }
|
||||||
})
|
})
|
||||||
const location = getCurrentLocation()
|
const location = getCurrentLocation()
|
||||||
if (location.fragment !== currentLocation) {
|
if (location.fragment !== currentLocation) {
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
"svelte": "^3.37.0",
|
"svelte": "^3.37.0",
|
||||||
"@anticrm/text-editor": "~0.6.0",
|
"@anticrm/text-editor": "~0.6.0",
|
||||||
"@anticrm/contact": "~0.6.2",
|
"@anticrm/contact": "~0.6.2",
|
||||||
|
"@anticrm/contact-resources": "~0.6.0",
|
||||||
"@anticrm/view-resources": "~0.6.0",
|
"@anticrm/view-resources": "~0.6.0",
|
||||||
"@anticrm/view": "~0.6.0"
|
"@anticrm/view": "~0.6.0"
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
"@anticrm/core": "~0.6.11",
|
"@anticrm/core": "~0.6.11",
|
||||||
"@anticrm/view": "~0.6.0",
|
"@anticrm/view": "~0.6.0",
|
||||||
"@anticrm/attachment-resources": "~0.6.0",
|
"@anticrm/attachment-resources": "~0.6.0",
|
||||||
|
"@anticrm/notification-resources": "~0.6.0",
|
||||||
"@anticrm/panel": "~0.6.0",
|
"@anticrm/panel": "~0.6.0",
|
||||||
"@anticrm/view-resources": "~0.6.0",
|
"@anticrm/view-resources": "~0.6.0",
|
||||||
"@anticrm/attachment": "~0.6.1"
|
"@anticrm/attachment": "~0.6.1"
|
||||||
|
@ -16,9 +16,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Channel } from '@anticrm/contact'
|
import { Channel } from '@anticrm/contact'
|
||||||
import type { AttachedData, Doc, Ref } from '@anticrm/core'
|
import type { AttachedData, Doc, Ref } from '@anticrm/core'
|
||||||
import presentation, { Channels } from '@anticrm/presentation'
|
import presentation from '@anticrm/presentation'
|
||||||
import { CircleButton, IconAdd, Label, showPopup } from '@anticrm/ui'
|
import { CircleButton, IconAdd, Label, showPopup } from '@anticrm/ui'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
import ChannelsView from './ChannelsView.svelte'
|
||||||
import contact from '../plugin'
|
import contact from '../plugin'
|
||||||
|
|
||||||
export let integrations: Set<Ref<Doc>> | undefined = undefined
|
export let integrations: Set<Ref<Doc>> | undefined = undefined
|
||||||
@ -40,7 +41,7 @@
|
|||||||
/>
|
/>
|
||||||
<span><Label label={presentation.string.AddSocialLinks} /></span>
|
<span><Label label={presentation.string.AddSocialLinks} /></span>
|
||||||
{:else}
|
{:else}
|
||||||
<Channels value={channels} size={'small'} {integrations} on:click />
|
<ChannelsView value={channels} size={'small'} {integrations} on:click />
|
||||||
<div class="ml-1">
|
<div class="ml-1">
|
||||||
<CircleButton
|
<CircleButton
|
||||||
icon={contact.icon.Edit}
|
icon={contact.icon.Edit}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { AttachedData, Class, Doc, Ref } from '@anticrm/core'
|
import type { Class, Doc, Ref } from '@anticrm/core'
|
||||||
import { createQuery, getClient } from '@anticrm/presentation'
|
import { createQuery, getClient } from '@anticrm/presentation'
|
||||||
|
|
||||||
import { ChannelProvider, Channel } from '@anticrm/contact'
|
import { ChannelProvider, Channel } from '@anticrm/contact'
|
||||||
@ -35,40 +35,72 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const query = createQuery()
|
const query = createQuery()
|
||||||
query.query(contact.class.Channel, {
|
query.query(
|
||||||
attachedTo: attachedTo
|
contact.class.Channel,
|
||||||
}, (res) => {
|
{
|
||||||
channels = res
|
attachedTo: attachedTo
|
||||||
})
|
},
|
||||||
|
(res) => {
|
||||||
|
channels = res
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
|
|
||||||
async function save (newValues: AttachedData<Channel>[]): Promise<void> {
|
async function save (newValues: Channel[]): Promise<void> {
|
||||||
const currentProviders = new Set(channels.map((p) => p.provider))
|
const currentProviders = new Set(channels.map((p) => p.provider))
|
||||||
const promises = []
|
const promises = []
|
||||||
for (const value of newValues) {
|
for (const value of newValues) {
|
||||||
const oldChannel = findValue(value.provider)
|
const oldChannel = findValue(value.provider)
|
||||||
if (oldChannel === undefined) {
|
if (oldChannel === undefined) {
|
||||||
if (value.value.length === 0) continue
|
if (value.value.length === 0) continue
|
||||||
promises.push(client.addCollection(contact.class.Channel, contact.space.Contacts, attachedTo, attachedClass, 'channels', {
|
promises.push(
|
||||||
value: value.value,
|
client.addCollection(contact.class.Channel, contact.space.Contacts, attachedTo, attachedClass, 'channels', {
|
||||||
provider: value.provider
|
value: value.value,
|
||||||
}))
|
provider: value.provider
|
||||||
|
})
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
currentProviders.delete(value.provider)
|
currentProviders.delete(value.provider)
|
||||||
if (value.value === oldChannel.value) continue
|
if (value.value === oldChannel.value) continue
|
||||||
promises.push(client.updateCollection(oldChannel._class, oldChannel.space, oldChannel._id, oldChannel.attachedTo, oldChannel.attachedToClass, oldChannel.collection, {
|
promises.push(
|
||||||
value: value.value
|
client.updateCollection(
|
||||||
}))
|
oldChannel._class,
|
||||||
|
oldChannel.space,
|
||||||
|
oldChannel._id,
|
||||||
|
oldChannel.attachedTo,
|
||||||
|
oldChannel.attachedToClass,
|
||||||
|
oldChannel.collection,
|
||||||
|
{
|
||||||
|
value: value.value
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const value of currentProviders) {
|
for (const value of currentProviders) {
|
||||||
const oldChannel = findValue(value)
|
const oldChannel = findValue(value)
|
||||||
if (oldChannel === undefined) continue
|
if (oldChannel === undefined) continue
|
||||||
promises.push(client.removeCollection(oldChannel._class, oldChannel.space, oldChannel._id, oldChannel.attachedTo, oldChannel.attachedToClass, oldChannel.collection))
|
promises.push(
|
||||||
|
client.removeCollection(
|
||||||
|
oldChannel._class,
|
||||||
|
oldChannel.space,
|
||||||
|
oldChannel._id,
|
||||||
|
oldChannel.attachedTo,
|
||||||
|
oldChannel.attachedToClass,
|
||||||
|
oldChannel.collection
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Promise.all(promises)
|
Promise.all(promises)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Channels {channels} {integrations} on:change={(e) => { save(e.detail) }} on:click />
|
<Channels
|
||||||
|
{channels}
|
||||||
|
{integrations}
|
||||||
|
on:change={(e) => {
|
||||||
|
save(e.detail)
|
||||||
|
}}
|
||||||
|
on:click
|
||||||
|
/>
|
||||||
|
@ -15,9 +15,26 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Channel } from '@anticrm/contact'
|
import type { Channel } from '@anticrm/contact'
|
||||||
import { Channels } from '@anticrm/presentation'
|
import ChannelsView from './ChannelsView.svelte'
|
||||||
|
import { showPanel } from '@anticrm/ui'
|
||||||
|
import view from '@anticrm/view'
|
||||||
|
|
||||||
export let value: Channel[] | Channel | null
|
export let value: Channel[] | Channel | null
|
||||||
|
|
||||||
|
function click (ev: any) {
|
||||||
|
if (ev.detail.presenter !== undefined && Array.isArray(value)) {
|
||||||
|
const channel = value[0]
|
||||||
|
if (channel !== undefined) {
|
||||||
|
showPanel(
|
||||||
|
view.component.EditDoc,
|
||||||
|
channel.attachedTo,
|
||||||
|
channel.attachedToClass,
|
||||||
|
'full',
|
||||||
|
ev.detail.presenter
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Channels {value} size={'small'} reverse />
|
<ChannelsView {value} size={'small'} reverse on:click={click} />
|
||||||
|
@ -15,19 +15,21 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Channel, ChannelProvider } from '@anticrm/contact'
|
import type { Channel, ChannelProvider } from '@anticrm/contact'
|
||||||
import type { AttachedData, Doc, Ref } from '@anticrm/core'
|
import type { AttachedData, Doc, Ref, Timestamp } from '@anticrm/core'
|
||||||
import type { Asset, IntlString } from '@anticrm/platform'
|
import type { Asset, IntlString } from '@anticrm/platform'
|
||||||
import type { AnyComponent } from '@anticrm/ui'
|
import type { AnyComponent } from '@anticrm/ui'
|
||||||
import { CircleButton, Tooltip } from '@anticrm/ui'
|
import { CircleButton, Tooltip } from '@anticrm/ui'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import { getClient } from '..'
|
|
||||||
import { getChannelProviders } from '../utils'
|
import { getChannelProviders } from '../utils'
|
||||||
import ChannelsPopup from './ChannelsPopup.svelte'
|
import ChannelsPopup from './ChannelsPopup.svelte'
|
||||||
|
import { NotificationClient } from '@anticrm/notification-resources'
|
||||||
|
|
||||||
export let value: AttachedData<Channel>[] | AttachedData<Channel> | null
|
export let value: AttachedData<Channel>[] | Channel | null
|
||||||
export let size: 'small' | 'medium' | 'large' | 'x-large' = 'large'
|
export let size: 'small' | 'medium' | 'large' | 'x-large' = 'large'
|
||||||
export let reverse: boolean = false
|
export let reverse: boolean = false
|
||||||
export let integrations: Set<Ref<Doc>> = new Set<Ref<Doc>>()
|
export let integrations: Set<Ref<Doc>> = new Set<Ref<Doc>>()
|
||||||
|
const notificationClient = NotificationClient.getClient()
|
||||||
|
const lastViews = notificationClient.getLastViews()
|
||||||
|
|
||||||
interface Item {
|
interface Item {
|
||||||
label: IntlString
|
label: IntlString
|
||||||
@ -35,19 +37,25 @@
|
|||||||
value: string
|
value: string
|
||||||
presenter?: AnyComponent
|
presenter?: AnyComponent
|
||||||
integration: boolean
|
integration: boolean
|
||||||
|
notification: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = getClient()
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
function getProvider (item: AttachedData<Channel>, map: Map<Ref<ChannelProvider>, ChannelProvider>): any | undefined {
|
function getProvider (
|
||||||
|
item: AttachedData<Channel>,
|
||||||
|
map: Map<Ref<ChannelProvider>, ChannelProvider>,
|
||||||
|
lastViews: Map<Ref<Doc>, Timestamp>
|
||||||
|
): any | undefined {
|
||||||
const provider = map.get(item.provider)
|
const provider = map.get(item.provider)
|
||||||
if (provider) {
|
if (provider) {
|
||||||
|
const notification = (item as Channel)._id !== undefined ? isNew((item as Channel), lastViews) : false
|
||||||
return {
|
return {
|
||||||
label: provider.label as IntlString,
|
label: provider.label as IntlString,
|
||||||
icon: provider.icon as Asset,
|
icon: provider.icon as Asset,
|
||||||
value: item.value,
|
value: item.value,
|
||||||
presenter: provider.presenter,
|
presenter: provider.presenter,
|
||||||
|
notification,
|
||||||
integration: provider.integrationType !== undefined ? integrations.has(provider.integrationType) : false
|
integration: provider.integrationType !== undefined ? integrations.has(provider.integrationType) : false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -55,18 +63,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function update (value: AttachedData<Channel>[] | AttachedData<Channel>) {
|
function isNew (item: Channel, lastViews: Map<Ref<Doc>, Timestamp>): boolean {
|
||||||
|
const lastView = (item as Channel)._id !== undefined ? lastViews.get((item as Channel)._id) : undefined
|
||||||
|
return lastView ? lastView < item.modifiedOn : (item.items ?? 0) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
async function update (value: AttachedData<Channel>[] | Channel | null, lastViews: Map<Ref<Doc>, Timestamp>) {
|
||||||
|
if (value === null) {
|
||||||
|
displayItems = []
|
||||||
|
return
|
||||||
|
}
|
||||||
const result = []
|
const result = []
|
||||||
const map = await getChannelProviders()
|
const map = await getChannelProviders()
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
for (const item of value) {
|
for (const item of value) {
|
||||||
const provider = getProvider(item, map)
|
const provider = getProvider(item, map, lastViews)
|
||||||
if (provider !== undefined) {
|
if (provider !== undefined) {
|
||||||
result.push(provider)
|
result.push(provider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const provider = getProvider(value, map)
|
const provider = getProvider(value, map, lastViews)
|
||||||
if (provider !== undefined) {
|
if (provider !== undefined) {
|
||||||
result.push(provider)
|
result.push(provider)
|
||||||
}
|
}
|
||||||
@ -74,7 +91,7 @@
|
|||||||
displayItems = result
|
displayItems = result
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (value) update(value)
|
$: if (value) update(value, $lastViews)
|
||||||
|
|
||||||
let displayItems: Item[] = []
|
let displayItems: Item[] = []
|
||||||
let divHTML: HTMLElement
|
let divHTML: HTMLElement
|
||||||
@ -94,7 +111,7 @@
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Tooltip component={ChannelsPopup} props={{ value: item }} label={undefined} anchor={divHTML}>
|
<Tooltip component={ChannelsPopup} props={{ value: item }} label={undefined} anchor={divHTML}>
|
||||||
<CircleButton icon={item.icon} {size} primary={item.integration} />
|
<CircleButton icon={item.icon} {size} primary={item.integration || item.notification} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
@ -23,7 +23,7 @@
|
|||||||
import { Channel, Organization } from '@anticrm/contact'
|
import { Channel, Organization } from '@anticrm/contact'
|
||||||
import contact from '../plugin'
|
import contact from '../plugin'
|
||||||
import Company from './icons/Company.svelte'
|
import Company from './icons/Company.svelte'
|
||||||
import { generateId } from '@anticrm/core'
|
import { AttachedData, generateId } from '@anticrm/core'
|
||||||
import Channels from './Channels.svelte'
|
import Channels from './Channels.svelte'
|
||||||
|
|
||||||
export function canClose (): boolean {
|
export function canClose (): boolean {
|
||||||
@ -51,7 +51,7 @@
|
|||||||
dispatch('close')
|
dispatch('close')
|
||||||
}
|
}
|
||||||
|
|
||||||
let channels: Channel[] = []
|
let channels: AttachedData<Channel>[] = []
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import { Data, generateId } from '@anticrm/core'
|
import { AttachedData, Data, generateId } from '@anticrm/core'
|
||||||
import { getResource } from '@anticrm/platform'
|
import { getResource } from '@anticrm/platform'
|
||||||
|
|
||||||
import { getClient, Card, EditableAvatar } from '@anticrm/presentation'
|
import { getClient, Card, EditableAvatar } from '@anticrm/presentation'
|
||||||
@ -72,7 +72,7 @@
|
|||||||
dispatch('close')
|
dispatch('close')
|
||||||
}
|
}
|
||||||
|
|
||||||
let channels: Channel[] = []
|
let channels: AttachedData<Channel>[] = []
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { AttachedData, Ref } from '@anticrm/core'
|
import { AttachedData, Ref } from '@anticrm/core'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import { EditBox, Button, ScrollBox } from '@anticrm/ui'
|
import { EditBox, Button, ScrollBox } from '@anticrm/ui'
|
||||||
import { getClient } from '@anticrm/presentation'
|
import { getClient } from '@anticrm/presentation'
|
||||||
@ -42,14 +42,14 @@
|
|||||||
for (const provider of providers) {
|
for (const provider of providers) {
|
||||||
const i = findValue(provider._id)
|
const i = findValue(provider._id)
|
||||||
if (i !== -1) {
|
if (i !== -1) {
|
||||||
newValues.push({ provider: provider._id, value: values[i].value })
|
newValues.push(values[i])
|
||||||
} else {
|
} else {
|
||||||
newValues.push({ provider: provider._id, value: '' })
|
newValues.push({ provider: provider._id, value: '' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function filterUndefined (channels: AttachedData<Channel>[]): AttachedData<Channel>[] {
|
function filterUndefined (channels: AttachedData<Channel>[]): AttachedData<Channel>[] {
|
||||||
return channels.filter((channel) => channel.value !== undefined && channel.value.length > 0)
|
return channels.filter((channel) => channel.value !== undefined && channel.value.length > 0)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -21,6 +21,7 @@ import { Avatar, ObjectSearchResult, UserInfo } from '@anticrm/presentation'
|
|||||||
import ChannelsEditor from './components/ChannelsEditor.svelte'
|
import ChannelsEditor from './components/ChannelsEditor.svelte'
|
||||||
import Channels from './components/Channels.svelte'
|
import Channels from './components/Channels.svelte'
|
||||||
import ChannelsPresenter from './components/ChannelsPresenter.svelte'
|
import ChannelsPresenter from './components/ChannelsPresenter.svelte'
|
||||||
|
import ChannelsView from './components/ChannelsView.svelte'
|
||||||
import ContactPresenter from './components/ContactPresenter.svelte'
|
import ContactPresenter from './components/ContactPresenter.svelte'
|
||||||
import Contacts from './components/Contacts.svelte'
|
import Contacts from './components/Contacts.svelte'
|
||||||
import CreateOrganization from './components/CreateOrganization.svelte'
|
import CreateOrganization from './components/CreateOrganization.svelte'
|
||||||
@ -34,7 +35,7 @@ import PersonPresenter from './components/PersonPresenter.svelte'
|
|||||||
import SocialEditor from './components/SocialEditor.svelte'
|
import SocialEditor from './components/SocialEditor.svelte'
|
||||||
import contact from './plugin'
|
import contact from './plugin'
|
||||||
|
|
||||||
export { Channels, ChannelsEditor, ContactPresenter }
|
export { Channels, ChannelsEditor, ContactPresenter, ChannelsView }
|
||||||
|
|
||||||
async function queryContact (_class: Ref<Class<Contact>>, client: Client, search: string): Promise<ObjectSearchResult[]> {
|
async function queryContact (_class: Ref<Class<Contact>>, client: Client, search: string): Promise<ObjectSearchResult[]> {
|
||||||
return (await client.findAll(_class, { name: { $like: `%${search}%` } }, { limit: 200 })).map(e => ({
|
return (await client.findAll(_class, { name: { $like: `%${search}%` } }, { limit: 200 })).map(e => ({
|
||||||
|
33
plugins/contact-resources/src/utils.ts
Normal file
33
plugins/contact-resources/src/utils.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
import contact, { ChannelProvider } from '@anticrm/contact'
|
||||||
|
import { Ref } from '@anticrm/core'
|
||||||
|
import { getClient } from '@anticrm/presentation'
|
||||||
|
|
||||||
|
let channelProviders: Promise<ChannelProvider[]> | undefined
|
||||||
|
|
||||||
|
const client = getClient()
|
||||||
|
channelProviders = client.findAll(contact.class.ChannelProvider, {})
|
||||||
|
|
||||||
|
export async function getChannelProviders (): Promise<Map<Ref<ChannelProvider>, ChannelProvider>> {
|
||||||
|
const cp = (await channelProviders) ?? []
|
||||||
|
const map = new Map<Ref<ChannelProvider>, ChannelProvider>()
|
||||||
|
for (const provider of cp) {
|
||||||
|
map.set(provider._id, provider)
|
||||||
|
}
|
||||||
|
return map
|
||||||
|
}
|
@ -43,6 +43,7 @@ export interface ChannelProvider extends Doc, UXObject {
|
|||||||
export interface Channel extends AttachedDoc {
|
export interface Channel extends AttachedDoc {
|
||||||
provider: Ref<ChannelProvider>
|
provider: Ref<ChannelProvider>
|
||||||
value: string
|
value: string
|
||||||
|
items?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
"@anticrm/setting": "~0.6.0",
|
"@anticrm/setting": "~0.6.0",
|
||||||
"@anticrm/chunter": "~0.6.0",
|
"@anticrm/chunter": "~0.6.0",
|
||||||
"@anticrm/chunter-resources": "~0.6.0",
|
"@anticrm/chunter-resources": "~0.6.0",
|
||||||
|
"@anticrm/notification-resources": "~0.6.0",
|
||||||
"@anticrm/login": "~0.6.1",
|
"@anticrm/login": "~0.6.1",
|
||||||
"@anticrm/core": "~0.6.11"
|
"@anticrm/core": "~0.6.11"
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createQuery, getClient } from '@anticrm/presentation'
|
import { createQuery, getClient } from '@anticrm/presentation'
|
||||||
import { Message, SharedMessage } from '@anticrm/gmail'
|
import { Message, SharedMessage } from '@anticrm/gmail'
|
||||||
@ -25,12 +24,14 @@
|
|||||||
import setting from '@anticrm/setting'
|
import setting from '@anticrm/setting'
|
||||||
import Connect from './Connect.svelte'
|
import Connect from './Connect.svelte'
|
||||||
import Messages from './Messages.svelte'
|
import Messages from './Messages.svelte'
|
||||||
|
import { NotificationClient } from '@anticrm/notification-resources'
|
||||||
|
|
||||||
export let object: Contact
|
export let object: Contact
|
||||||
export let channel: Channel
|
export let channel: Channel
|
||||||
export let newMessage: boolean
|
export let newMessage: boolean
|
||||||
|
|
||||||
const EMAIL_REGEX = /(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))/
|
const EMAIL_REGEX =
|
||||||
|
/(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))/
|
||||||
|
|
||||||
let messages: Message[] = []
|
let messages: Message[] = []
|
||||||
let accounts: EmployeeAccount[] = []
|
let accounts: EmployeeAccount[] = []
|
||||||
@ -42,22 +43,31 @@
|
|||||||
const accauntsQuery = createQuery()
|
const accauntsQuery = createQuery()
|
||||||
const settingsQuery = createQuery()
|
const settingsQuery = createQuery()
|
||||||
const accountId = getCurrentAccount()._id
|
const accountId = getCurrentAccount()._id
|
||||||
|
const notificationClient = NotificationClient.getClient()
|
||||||
|
|
||||||
$: messagesQuery.query(
|
function updateMessagesQuery (channelId: Ref<Channel>): void {
|
||||||
gmail.class.Message,
|
messagesQuery.query(
|
||||||
{ attachedTo: channel._id },
|
gmail.class.Message,
|
||||||
(res) => {
|
{ attachedTo: channelId },
|
||||||
messages = res
|
(res) => {
|
||||||
},
|
messages = res
|
||||||
{ sort: { modifiedOn: SortingOrder.Descending } }
|
notificationClient.updateLastView(channelId, channel._class)
|
||||||
)
|
const accountsIds = new Set(messages.map((p) => p.modifiedBy as Ref<EmployeeAccount>))
|
||||||
|
updateAccountsQuery(accountsIds)
|
||||||
|
},
|
||||||
|
{ sort: { modifiedOn: SortingOrder.Descending } }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
$: accountsIds = messages.map((p) => p.modifiedBy as Ref<EmployeeAccount>)
|
$: updateMessagesQuery(channel._id)
|
||||||
$: accauntsQuery.query(contact.class.EmployeeAccount, { _id: { $in: accountsIds }}, (result) => {
|
|
||||||
accounts = result
|
|
||||||
})
|
|
||||||
|
|
||||||
$: settingsQuery.query(
|
function updateAccountsQuery (accountsIds: Set<Ref<EmployeeAccount>>): void {
|
||||||
|
accauntsQuery.query(contact.class.EmployeeAccount, { _id: { $in: Array.from(accountsIds) } }, (result) => {
|
||||||
|
accounts = result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsQuery.query(
|
||||||
setting.class.Integration,
|
setting.class.Integration,
|
||||||
{ type: gmail.integrationType.Gmail, space: accountId as string as Ref<Space> },
|
{ type: gmail.integrationType.Gmail, space: accountId as string as Ref<Space> },
|
||||||
(res) => {
|
(res) => {
|
||||||
@ -68,9 +78,17 @@
|
|||||||
|
|
||||||
async function share (): Promise<void> {
|
async function share (): Promise<void> {
|
||||||
const selectedMessages = messages.filter((m) => selected.has(m._id as string as Ref<SharedMessage>))
|
const selectedMessages = messages.filter((m) => selected.has(m._id as string as Ref<SharedMessage>))
|
||||||
await client.addCollection(gmail.class.SharedMessages, object.space, object._id, object._class, 'gmailSharedMessages', {
|
await client.addCollection(
|
||||||
messages: convertMessages(selectedMessages, accounts)
|
gmail.class.SharedMessages,
|
||||||
})
|
object.space,
|
||||||
|
object._id,
|
||||||
|
object._class,
|
||||||
|
'gmailSharedMessages',
|
||||||
|
{
|
||||||
|
messages: convertMessages(selectedMessages, accounts)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||||
clear()
|
clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +109,6 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getName (message: Message, accounts: EmployeeAccount[], sender: boolean): string {
|
function getName (message: Message, accounts: EmployeeAccount[], sender: boolean): string {
|
||||||
if (message.incoming === sender) {
|
if (message.incoming === sender) {
|
||||||
return `${formatName(object.name)} (${channel.value})`
|
return `${formatName(object.name)} (${channel.value})`
|
||||||
|
@ -66,7 +66,7 @@
|
|||||||
{#if currentMessage.copy?.length}
|
{#if currentMessage.copy?.length}
|
||||||
<Label label={gmail.string.Copy} />: {currentMessage.copy.join(', ')}
|
<Label label={gmail.string.Copy} />: {currentMessage.copy.join(', ')}
|
||||||
{/if}
|
{/if}
|
||||||
<div class="flex-col clear-mins mt-9">
|
<div class="flex-col h-full clear-mins mt-9">
|
||||||
<FullMessageContent content={currentMessage.content} />
|
<FullMessageContent content={currentMessage.content} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,20 +15,18 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let content: string
|
export let content: string
|
||||||
|
|
||||||
let editor: HTMLDivElement
|
|
||||||
$: if (editor) editor.innerHTML = content
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div bind:this={editor} class="input clear-mins" />
|
<iframe srcdoc={content} title="e-mail" />
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.input {
|
iframe {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
padding: 1rem;
|
border: none;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
height: 100%;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
color: #1f212b;
|
color: #1f212b;
|
||||||
border-radius: .5rem;
|
|
||||||
|
|
||||||
:global(a) {
|
:global(a) {
|
||||||
font: inherit;
|
font: inherit;
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
{#if message.copy?.length}
|
{#if message.copy?.length}
|
||||||
<Label label={gmail.string.Copy} />: {message.copy.join(', ')}
|
<Label label={gmail.string.Copy} />: {message.copy.join(', ')}
|
||||||
{/if}
|
{/if}
|
||||||
<div class="flex-col clear-mins mt-5">
|
<div class="flex-col h-full clear-mins mt-5">
|
||||||
<FullMessageContent content={message.content} />
|
<FullMessageContent content={message.content} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -51,7 +51,7 @@
|
|||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
background-color: var(--theme-button-bg-focused);
|
background-color: var(--theme-button-bg-focused);
|
||||||
border: 1px solid var(--theme-button-border-enabled);
|
border: 1px solid var(--theme-button-border-enabled);
|
||||||
border-radius: .75rem;
|
border-radius: 0.75rem;
|
||||||
box-shadow: 0px 10px 20px rgba(0, 0, 0, .2);
|
box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -20,18 +20,24 @@
|
|||||||
import FullMessage from './FullMessage.svelte'
|
import FullMessage from './FullMessage.svelte'
|
||||||
import Chats from './Chats.svelte'
|
import Chats from './Chats.svelte'
|
||||||
import { getClient } from '@anticrm/presentation'
|
import { getClient } from '@anticrm/presentation'
|
||||||
|
import { NotificationClient } from '@anticrm/notification-resources'
|
||||||
|
|
||||||
export let object: Contact
|
export let object: Contact
|
||||||
let newMessage: boolean = false
|
let newMessage: boolean = false
|
||||||
let currentMessage: SharedMessage | undefined = undefined
|
let currentMessage: SharedMessage | undefined = undefined
|
||||||
let channel: Channel | undefined = undefined
|
let channel: Channel | undefined = undefined
|
||||||
|
const notificationClient = NotificationClient.getClient()
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
|
|
||||||
client.findOne(contact.class.Channel, {
|
client
|
||||||
attachedTo: object._id,
|
.findOne(contact.class.Channel, {
|
||||||
provider: contact.channelProvider.Email
|
attachedTo: object._id,
|
||||||
}).then((res) => channel = res)
|
provider: contact.channelProvider.Email
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
channel = res
|
||||||
|
})
|
||||||
|
|
||||||
function back () {
|
function back () {
|
||||||
if (newMessage) {
|
if (newMessage) {
|
||||||
@ -40,8 +46,11 @@
|
|||||||
return (currentMessage = undefined)
|
return (currentMessage = undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectHandler (e: CustomEvent) {
|
async function selectHandler (e: CustomEvent): Promise<void> {
|
||||||
currentMessage = e.detail
|
currentMessage = e.detail
|
||||||
|
if (channel !== undefined) {
|
||||||
|
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -15,9 +15,10 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { SharedMessage } from '@anticrm/gmail'
|
import type { SharedMessage } from '@anticrm/gmail'
|
||||||
import { CheckBox } from '@anticrm/ui'
|
import { CheckBox, Label } from '@anticrm/ui'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import { getTime } from '../utils'
|
import { getTime } from '../utils'
|
||||||
|
import gmail from '../plugin'
|
||||||
|
|
||||||
export let message: SharedMessage
|
export let message: SharedMessage
|
||||||
export let selected: boolean = false
|
export let selected: boolean = false
|
||||||
@ -37,10 +38,14 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<div class="flex-col message" class:selected>
|
<div class="flex-col message" class:selected>
|
||||||
<div class="flex-between text-sm mb-1">
|
<div class="flex-between text-sm mb-1">
|
||||||
<div class="content-trans-color overflow-label mr-4">From: <span class="content-accent-color">{message.sender}</span></div>
|
<div class="content-trans-color overflow-label mr-4">
|
||||||
|
<Label label={gmail.string.From} /><span class="content-accent-color">{message.sender}</span>
|
||||||
|
</div>
|
||||||
<div class="content-trans-color">{getTime(message.modifiedOn)}</div>
|
<div class="content-trans-color">{getTime(message.modifiedOn)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-trans-color text-sm overflow-label mr-4 mb-4">To: <span class="content-accent-color">{message.receiver}</span></div>
|
<div class="content-trans-color text-sm overflow-label mr-4 mb-4">
|
||||||
|
<Label label={gmail.string.To} /><span class="content-accent-color">{message.receiver}</span>
|
||||||
|
</div>
|
||||||
<div class="fs-title overflow-label mb-1">
|
<div class="fs-title overflow-label mb-1">
|
||||||
{message.subject}
|
{message.subject}
|
||||||
</div>
|
</div>
|
||||||
@ -60,10 +65,12 @@
|
|||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
background-color: var(--theme-incoming-msg);
|
background-color: var(--theme-incoming-msg);
|
||||||
border-radius: .75rem;
|
border-radius: 0.75rem;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
|
||||||
&.selected { background-color: var(--primary-button-enabled); }
|
&.selected {
|
||||||
|
background-color: var(--primary-button-enabled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -24,10 +24,12 @@
|
|||||||
import { Channel, Contact, formatName } from '@anticrm/contact'
|
import { Channel, Contact, formatName } from '@anticrm/contact'
|
||||||
import { TextEditor } from '@anticrm/text-editor'
|
import { TextEditor } from '@anticrm/text-editor'
|
||||||
import plugin from '../plugin'
|
import plugin from '../plugin'
|
||||||
|
import { NotificationClient } from '@anticrm/notification-resources'
|
||||||
|
|
||||||
export let object: Contact
|
export let object: Contact
|
||||||
export let channel: Channel
|
export let channel: Channel
|
||||||
export let currentMessage: SharedMessage | undefined
|
export let currentMessage: SharedMessage | undefined
|
||||||
|
const notificationClient = NotificationClient.getClient()
|
||||||
|
|
||||||
let editor: TextEditor
|
let editor: TextEditor
|
||||||
let copy: string = ''
|
let copy: string = ''
|
||||||
@ -42,7 +44,7 @@
|
|||||||
const url = getMetadata(login.metadata.GmailUrl) ?? ''
|
const url = getMetadata(login.metadata.GmailUrl) ?? ''
|
||||||
|
|
||||||
async function sendMsg () {
|
async function sendMsg () {
|
||||||
fetch(url + '/send', {
|
await fetch(url + '/send', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: 'Bearer ' + getMetadata(login.metadata.LoginToken),
|
Authorization: 'Bearer ' + getMetadata(login.metadata.LoginToken),
|
||||||
@ -53,6 +55,7 @@
|
|||||||
copy: copy.split(',').map((m) => m.trim())
|
copy: copy.split(',').map((m) => m.trim())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||||
dispatch('close')
|
dispatch('close')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,7 +133,7 @@
|
|||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
color: #1f212b;
|
color: #1f212b;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: .5rem;
|
border-radius: 0.5rem;
|
||||||
|
|
||||||
:global(.ProseMirror) {
|
:global(.ProseMirror) {
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
7
plugins/notification-resources/.eslintrc.js
Normal file
7
plugins/notification-resources/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: ['./node_modules/@anticrm/platform-rig/profiles/default/config/eslint.config.json'],
|
||||||
|
parserOptions: {
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
project: './tsconfig.json'
|
||||||
|
}
|
||||||
|
}
|
4
plugins/notification-resources/.npmignore
Normal file
4
plugins/notification-resources/.npmignore
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*
|
||||||
|
!/lib/**
|
||||||
|
!CHANGELOG.md
|
||||||
|
/lib/**/__tests__/
|
18
plugins/notification-resources/config/rig.json
Normal file
18
plugins/notification-resources/config/rig.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// The "rig.json" file directs tools to look for their config files in an external package.
|
||||||
|
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Required) The name of the rig package to inherit from.
|
||||||
|
* It should be an NPM package name with the "-rig" suffix.
|
||||||
|
*/
|
||||||
|
"rigPackageName": "@anticrm/platform-rig"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Optional) Selects a config profile from the rig package. The name must consist of
|
||||||
|
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
|
||||||
|
* If omitted, then the "default" profile will be used."
|
||||||
|
*/
|
||||||
|
// "rigProfile": "your-profile-name"
|
||||||
|
}
|
37
plugins/notification-resources/package.json
Normal file
37
plugins/notification-resources/package.json
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"name": "@anticrm/notification-resources",
|
||||||
|
"version": "0.6.0",
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"author": "Anticrm Platform Contributors",
|
||||||
|
"license": "EPL-2.0",
|
||||||
|
"scripts": {
|
||||||
|
"build": "heft build",
|
||||||
|
"build:watch": "tsc",
|
||||||
|
"lint:fix": "eslint --fix src",
|
||||||
|
"lint": "eslint src",
|
||||||
|
"format": "prettier --write src && eslint --fix src",
|
||||||
|
"svelte-check": "svelte-check"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@anticrm/platform-rig": "~0.6.0",
|
||||||
|
"@types/heft-jest": "^1.0.2",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||||
|
"eslint-plugin-import": "^2.25.3",
|
||||||
|
"eslint-plugin-promise": "^5.1.1",
|
||||||
|
"eslint-plugin-node": "^11.1.0",
|
||||||
|
"eslint": "^7.32.0",
|
||||||
|
"@typescript-eslint/parser": "^5.4.0",
|
||||||
|
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||||
|
"prettier": "^2.4.1",
|
||||||
|
"@rushstack/heft": "^0.41.1",
|
||||||
|
"typescript": "^4.3.5",
|
||||||
|
"svelte-check": "~2.3.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@anticrm/presentation": "~0.6.2",
|
||||||
|
"@anticrm/platform": "~0.6.5",
|
||||||
|
"@anticrm/core": "~0.6.11",
|
||||||
|
"@anticrm/notification": "~0.6.0",
|
||||||
|
"svelte": "^3.37.0"
|
||||||
|
}
|
||||||
|
}
|
21
plugins/notification-resources/src/index.ts
Normal file
21
plugins/notification-resources/src/index.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||||
|
// Copyright © 2021, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
import { Resources } from '@anticrm/platform'
|
||||||
|
|
||||||
|
export default async (): Promise<Resources> => ({})
|
||||||
|
|
||||||
|
export * from './utils'
|
89
plugins/notification-resources/src/utils.ts
Normal file
89
plugins/notification-resources/src/utils.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
//
|
||||||
|
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||||
|
// Copyright © 2021, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
import { Class, Doc, getCurrentAccount, Ref, Timestamp } from '@anticrm/core'
|
||||||
|
import notification, { LastView } from '@anticrm/notification'
|
||||||
|
import { createQuery, getClient } from '@anticrm/presentation'
|
||||||
|
import { writable, Writable } from 'svelte/store'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export class NotificationClient {
|
||||||
|
protected static _instance: NotificationClient | undefined = undefined
|
||||||
|
private readonly lastViews = new Map<Ref<Doc>, LastView>()
|
||||||
|
private readonly lastViewsStore = writable(new Map<Ref<Doc>, Timestamp>())
|
||||||
|
|
||||||
|
private readonly lastViewQuery = createQuery()
|
||||||
|
|
||||||
|
private constructor () {
|
||||||
|
this.lastViewQuery.query(notification.class.LastView, { user: getCurrentAccount()._id }, (result) => {
|
||||||
|
const res: Map<Ref<Doc>, Timestamp> = new Map<Ref<Doc>, Timestamp>()
|
||||||
|
result.forEach((p) => {
|
||||||
|
res.set(p.attachedTo, p.lastView)
|
||||||
|
this.lastViews.set(p.attachedTo, p)
|
||||||
|
})
|
||||||
|
this.lastViewsStore.set(res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static getClient (): NotificationClient {
|
||||||
|
if (NotificationClient._instance === undefined) {
|
||||||
|
NotificationClient._instance = new NotificationClient()
|
||||||
|
}
|
||||||
|
return NotificationClient._instance
|
||||||
|
}
|
||||||
|
|
||||||
|
getLastViews (): Writable<Map<Ref<Doc>, Timestamp>> {
|
||||||
|
return this.lastViewsStore
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateLastView (
|
||||||
|
_id: Ref<Doc>,
|
||||||
|
_class: Ref<Class<Doc>>,
|
||||||
|
time?: Timestamp,
|
||||||
|
force: boolean = false
|
||||||
|
): Promise<void> {
|
||||||
|
const client = getClient()
|
||||||
|
const user = getCurrentAccount()._id
|
||||||
|
const lastView = time ?? new Date().getTime()
|
||||||
|
const current = this.lastViews.get(_id)
|
||||||
|
if (current !== undefined) {
|
||||||
|
if (current.lastView < lastView || force) {
|
||||||
|
await client.updateDoc(current._class, current.space, current._id, {
|
||||||
|
lastView: lastView
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (force) {
|
||||||
|
await client.createDoc(notification.class.LastView, notification.space.Notifications, {
|
||||||
|
user,
|
||||||
|
lastView,
|
||||||
|
attachedTo: _id,
|
||||||
|
attachedToClass: _class,
|
||||||
|
collection: 'lastViews'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async unsubscribe (_id: Ref<Doc>): Promise<void> {
|
||||||
|
const client = getClient()
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
plugins/notification-resources/svelte.config.js
Normal file
5
plugins/notification-resources/svelte.config.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const sveltePreprocess = require('svelte-preprocess')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
preprocess: sveltePreprocess()
|
||||||
|
};
|
8
plugins/notification-resources/tsconfig.json
Normal file
8
plugins/notification-resources/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "./node_modules/@anticrm/platform-rig/profiles/default/tsconfig.json",
|
||||||
|
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./lib",
|
||||||
|
}
|
||||||
|
}
|
7
plugins/notification/.eslintrc.js
Normal file
7
plugins/notification/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: ['./node_modules/@anticrm/platform-rig/profiles/default/config/eslint.config.json'],
|
||||||
|
parserOptions: {
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
project: './tsconfig.json'
|
||||||
|
}
|
||||||
|
}
|
4
plugins/notification/.npmignore
Normal file
4
plugins/notification/.npmignore
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*
|
||||||
|
!/lib/**
|
||||||
|
!CHANGELOG.md
|
||||||
|
/lib/**/__tests__/
|
18
plugins/notification/config/rig.json
Normal file
18
plugins/notification/config/rig.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// The "rig.json" file directs tools to look for their config files in an external package.
|
||||||
|
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Required) The name of the rig package to inherit from.
|
||||||
|
* It should be an NPM package name with the "-rig" suffix.
|
||||||
|
*/
|
||||||
|
"rigPackageName": "@anticrm/platform-rig"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Optional) Selects a config profile from the rig package. The name must consist of
|
||||||
|
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
|
||||||
|
* If omitted, then the "default" profile will be used."
|
||||||
|
*/
|
||||||
|
// "rigProfile": "your-profile-name"
|
||||||
|
}
|
32
plugins/notification/package.json
Normal file
32
plugins/notification/package.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "@anticrm/notification",
|
||||||
|
"version": "0.6.0",
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"author": "Anticrm Platform Contributors",
|
||||||
|
"license": "EPL-2.0",
|
||||||
|
"scripts": {
|
||||||
|
"build": "heft build",
|
||||||
|
"build:watch": "tsc",
|
||||||
|
"lint:fix": "eslint --fix src",
|
||||||
|
"lint": "eslint src",
|
||||||
|
"format": "prettier --write src && eslint --fix src"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@anticrm/platform-rig": "~0.6.0",
|
||||||
|
"@types/heft-jest": "^1.0.2",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||||
|
"eslint-plugin-import": "^2.25.3",
|
||||||
|
"eslint-plugin-promise": "^5.1.1",
|
||||||
|
"eslint-plugin-node": "^11.1.0",
|
||||||
|
"eslint": "^7.32.0",
|
||||||
|
"@typescript-eslint/parser": "^5.4.0",
|
||||||
|
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||||
|
"prettier": "^2.4.1",
|
||||||
|
"@rushstack/heft": "^0.41.1",
|
||||||
|
"typescript": "^4.3.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@anticrm/platform": "~0.6.5",
|
||||||
|
"@anticrm/core": "~0.6.11"
|
||||||
|
}
|
||||||
|
}
|
46
plugins/notification/src/index.ts
Normal file
46
plugins/notification/src/index.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
//
|
||||||
|
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||||
|
// Copyright © 2021, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
import type { Account, AttachedDoc, Class, Ref, Space, Timestamp } from '@anticrm/core'
|
||||||
|
import type { Plugin } from '@anticrm/platform'
|
||||||
|
import { plugin } from '@anticrm/platform'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface LastView extends AttachedDoc {
|
||||||
|
lastView: Timestamp
|
||||||
|
user: Ref<Account>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export const notificationId = 'notification' as Plugin
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
const notification = plugin(notificationId, {
|
||||||
|
class: {
|
||||||
|
LastView: '' as Ref<Class<LastView>>
|
||||||
|
},
|
||||||
|
space: {
|
||||||
|
Notifications: '' as Ref<Space>
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default notification
|
9
plugins/notification/tsconfig.json
Normal file
9
plugins/notification/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"extends": "./node_modules/@anticrm/platform-rig/profiles/default/tsconfig.json",
|
||||||
|
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./lib",
|
||||||
|
"lib": ["esnext", "dom"]
|
||||||
|
}
|
||||||
|
}
|
@ -14,12 +14,24 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Avatar } from '@anticrm/presentation'
|
import { Avatar, createQuery } from '@anticrm/presentation'
|
||||||
import type { Candidate } from '@anticrm/recruit'
|
import type { Candidate } from '@anticrm/recruit'
|
||||||
import { Channels } from '@anticrm/presentation'
|
import { ChannelsView } from '@anticrm/contact-resources'
|
||||||
import { formatName } from '@anticrm/contact'
|
import contact, { Channel, formatName } from '@anticrm/contact'
|
||||||
|
|
||||||
export let candidate: Candidate
|
export let candidate: Candidate
|
||||||
|
|
||||||
|
let channels: Channel[] = []
|
||||||
|
const channelsQuery = createQuery()
|
||||||
|
channelsQuery.query(
|
||||||
|
contact.class.Channel,
|
||||||
|
{
|
||||||
|
attachedTo: candidate._id
|
||||||
|
},
|
||||||
|
(res) => {
|
||||||
|
channels = res
|
||||||
|
}
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex-col h-full card-container">
|
<div class="flex-col h-full card-container">
|
||||||
@ -29,7 +41,7 @@
|
|||||||
<div class="name">{formatName(candidate.name)}</div>
|
<div class="name">{formatName(candidate.name)}</div>
|
||||||
<div class="description">{candidate.title ?? ''}</div>
|
<div class="description">{candidate.title ?? ''}</div>
|
||||||
<div class="description">{candidate.city ?? ''}</div>
|
<div class="description">{candidate.city ?? ''}</div>
|
||||||
<div class="footer"><Channels value={candidate.channels} size={'small'} /></div>
|
<div class="footer"><ChannelsView value={channels} size={'small'} on:click /></div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -14,16 +14,15 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import attachment from '@anticrm/attachment'
|
import attachment from '@anticrm/attachment'
|
||||||
import contact, { Channel, ChannelProvider, combineName, Person } from '@anticrm/contact'
|
import contact,{ Channel,ChannelProvider,combineName,Person } from '@anticrm/contact'
|
||||||
import { Channels } from '@anticrm/contact-resources'
|
import { Channels } from '@anticrm/contact-resources'
|
||||||
import type { AttachedData, Data, MixinData, Ref } from '@anticrm/core'
|
import { AttachedData, Data,generateId,MixinData,Ref } from '@anticrm/core'
|
||||||
import { generateId } from '@anticrm/core'
|
|
||||||
import login from '@anticrm/login'
|
import login from '@anticrm/login'
|
||||||
import { getMetadata, getResource, setPlatformStatus, unknownError } from '@anticrm/platform'
|
import { getMetadata,getResource,setPlatformStatus,unknownError } from '@anticrm/platform'
|
||||||
import { Card, EditableAvatar, getClient, getFileUrl, PDFViewer } from '@anticrm/presentation'
|
import { Card,EditableAvatar,getClient,getFileUrl,PDFViewer } from '@anticrm/presentation'
|
||||||
import type { Candidate } from '@anticrm/recruit'
|
import type { Candidate } from '@anticrm/recruit'
|
||||||
import { recognizeDocument } from '@anticrm/rekoni'
|
import { recognizeDocument } from '@anticrm/rekoni'
|
||||||
import { EditBox, IconFile as FileIcon, Label, Link, showPopup, Spinner } from '@anticrm/ui'
|
import { EditBox,IconFile as FileIcon,Label,Link,showPopup,Spinner } from '@anticrm/ui'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import recruit from '../plugin'
|
import recruit from '../plugin'
|
||||||
import FileUpload from './icons/FileUpload.svelte'
|
import FileUpload from './icons/FileUpload.svelte'
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
|
|
||||||
{#if object !== undefined && candidate !== undefined}
|
{#if object !== undefined && candidate !== undefined}
|
||||||
<div class="flex-between">
|
<div class="flex-between">
|
||||||
<div class="card"><CandidateCard {candidate} /></div>
|
<div class="card"><CandidateCard {candidate} on:click /></div>
|
||||||
<div class="arrows"><ExpandRightDouble /></div>
|
<div class="arrows"><ExpandRightDouble /></div>
|
||||||
<div class="card"><VacancyCard {vacancy} /></div>
|
<div class="card"><VacancyCard {vacancy} /></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
"@anticrm/setting": "~0.6.0",
|
"@anticrm/setting": "~0.6.0",
|
||||||
"@anticrm/chunter": "~0.6.0",
|
"@anticrm/chunter": "~0.6.0",
|
||||||
"@anticrm/login": "~0.6.1",
|
"@anticrm/login": "~0.6.1",
|
||||||
"@anticrm/core": "~0.6.11"
|
"@anticrm/core": "~0.6.11",
|
||||||
|
"@anticrm/notification-resources": "~0.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,28 +14,34 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact,{ Channel,Contact,EmployeeAccount,formatName } from '@anticrm/contact'
|
import contact, { Channel, Contact, EmployeeAccount, formatName } from '@anticrm/contact'
|
||||||
import { getCurrentAccount,Ref,SortingOrder,Space } from '@anticrm/core'
|
import { getCurrentAccount, Ref, SortingOrder, Space } from '@anticrm/core'
|
||||||
import login from '@anticrm/login'
|
import login from '@anticrm/login'
|
||||||
import { getMetadata } from '@anticrm/platform'
|
import { getMetadata } from '@anticrm/platform'
|
||||||
import { createQuery,getClient } from '@anticrm/presentation'
|
import { createQuery, getClient } from '@anticrm/presentation'
|
||||||
import setting from '@anticrm/setting'
|
import setting from '@anticrm/setting'
|
||||||
import type { SharedTelegramMessage, TelegramMessage } from '@anticrm/telegram'
|
import type { SharedTelegramMessage, TelegramMessage } from '@anticrm/telegram'
|
||||||
import { ReferenceInput } from '@anticrm/text-editor'
|
import { ReferenceInput } from '@anticrm/text-editor'
|
||||||
import { ActionIcon,Button,IconShare,ScrollBox,showPopup } from '@anticrm/ui'
|
import { ActionIcon, Button, IconShare, ScrollBox, showPopup } from '@anticrm/ui'
|
||||||
import telegram from '../plugin'
|
import telegram from '../plugin'
|
||||||
import Connect from './Connect.svelte'
|
import Connect from './Connect.svelte'
|
||||||
import TelegramIcon from './icons/Telegram.svelte'
|
import TelegramIcon from './icons/Telegram.svelte'
|
||||||
import Messages from './Messages.svelte'
|
import Messages from './Messages.svelte'
|
||||||
|
import { NotificationClient } from '@anticrm/notification-resources'
|
||||||
|
|
||||||
export let object: Contact
|
export let object: Contact
|
||||||
let channel: Channel | undefined = undefined
|
let channel: Channel | undefined = undefined
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
|
const notificationClient = NotificationClient.getClient()
|
||||||
|
|
||||||
client.findOne(contact.class.Channel, {
|
client
|
||||||
attachedTo: object._id,
|
.findOne(contact.class.Channel, {
|
||||||
provider: contact.channelProvider.Telegram
|
attachedTo: object._id,
|
||||||
}).then((res) => channel = res)
|
provider: contact.channelProvider.Telegram
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
channel = res
|
||||||
|
})
|
||||||
|
|
||||||
let messages: TelegramMessage[] = []
|
let messages: TelegramMessage[] = []
|
||||||
let accounts: EmployeeAccount[] = []
|
let accounts: EmployeeAccount[] = []
|
||||||
@ -49,16 +55,31 @@
|
|||||||
const settingsQuery = createQuery()
|
const settingsQuery = createQuery()
|
||||||
const accountId = getCurrentAccount()._id
|
const accountId = getCurrentAccount()._id
|
||||||
|
|
||||||
$: channel && messagesQuery.query(telegram.class.Message, { modifiedBy: accountId, attachedTo: channel._id }, (res) => {
|
function updateMessagesQuery (channelId: Ref<Channel>): void {
|
||||||
messages = res
|
messagesQuery.query(
|
||||||
}, { sort: { modifiedOn: SortingOrder.Ascending }})
|
telegram.class.Message,
|
||||||
|
{ attachedTo: channelId },
|
||||||
|
(res) => {
|
||||||
|
messages = res.reverse()
|
||||||
|
if (channel !== undefined) {
|
||||||
|
notificationClient.updateLastView(channel._id, channel._class)
|
||||||
|
}
|
||||||
|
const accountsIds = new Set(messages.map((p) => p.modifiedBy as Ref<EmployeeAccount>))
|
||||||
|
updateAccountsQuery(accountsIds)
|
||||||
|
},
|
||||||
|
{ sort: { modifiedOn: SortingOrder.Descending }, limit: 500 }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
$: accountsIds = messages.map((p) => p.modifiedBy as Ref<EmployeeAccount>)
|
$: channel && updateMessagesQuery(channel._id)
|
||||||
$: accauntsQuery.query(contact.class.EmployeeAccount, { _id: { $in: accountsIds }}, (result) => {
|
|
||||||
accounts = result
|
|
||||||
})
|
|
||||||
|
|
||||||
$: settingsQuery.query(
|
function updateAccountsQuery (accountsIds: Set<Ref<EmployeeAccount>>): void {
|
||||||
|
accauntsQuery.query(contact.class.EmployeeAccount, { _id: { $in: Array.from(accountsIds) } }, (result) => {
|
||||||
|
accounts = result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsQuery.query(
|
||||||
setting.class.Integration,
|
setting.class.Integration,
|
||||||
{ type: telegram.integrationType.Telegram, space: accountId as string as Ref<Space> },
|
{ type: telegram.integrationType.Telegram, space: accountId as string as Ref<Space> },
|
||||||
(res) => {
|
(res) => {
|
||||||
@ -67,7 +88,7 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
async function sendMsg (to: string, msg: string) {
|
async function sendMsg (to: string, msg: string) {
|
||||||
return await fetch(url + '/send-msg', {
|
const res = await fetch(url + '/send-msg', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: 'Bearer ' + getMetadata(login.metadata.LoginToken),
|
Authorization: 'Bearer ' + getMetadata(login.metadata.LoginToken),
|
||||||
@ -78,6 +99,10 @@
|
|||||||
msg
|
msg
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
if (channel !== undefined) {
|
||||||
|
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||||
|
}
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addContact (phone: string) {
|
async function addContact (phone: string) {
|
||||||
@ -137,6 +162,9 @@
|
|||||||
messages: convertMessages(selectedMessages, accounts)
|
messages: convertMessages(selectedMessages, accounts)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
if (channel !== undefined) {
|
||||||
|
await notificationClient.updateLastView(channel._id, channel._class, channel.modifiedOn, true)
|
||||||
|
}
|
||||||
clear()
|
clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +227,13 @@
|
|||||||
<Button label={telegram.string.Cancel} size={'small'} on:click={clear} />
|
<Button label={telegram.string.Cancel} size={'small'} on:click={clear} />
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-3">
|
<div class="ml-3">
|
||||||
<Button label={telegram.string.PublishSelected} size={'small'} primary disabled={!selected.size} on:click={share} />
|
<Button
|
||||||
|
label={telegram.string.PublishSelected}
|
||||||
|
size={'small'}
|
||||||
|
primary
|
||||||
|
disabled={!selected.size}
|
||||||
|
on:click={share}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,10 +16,23 @@
|
|||||||
import type { SharedTelegramMessage } from '@anticrm/telegram'
|
import type { SharedTelegramMessage } from '@anticrm/telegram'
|
||||||
|
|
||||||
export let message: SharedTelegramMessage
|
export let message: SharedTelegramMessage
|
||||||
|
|
||||||
|
const current = new Date()
|
||||||
|
const target = new Date(message.modifiedOn)
|
||||||
|
let options: Intl.DateTimeFormatOptions = {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'long'
|
||||||
|
}
|
||||||
|
if (current.getFullYear() !== target.getFullYear()) {
|
||||||
|
options = {
|
||||||
|
...options,
|
||||||
|
year: '2-digit'
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="datetime-container">
|
<div class="datetime-container">
|
||||||
{new Intl.DateTimeFormat('default', { day: 'numeric', month: 'long' }).format(message.modifiedOn)}
|
{new Intl.DateTimeFormat('default', options).format(message.modifiedOn)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@ -32,10 +32,10 @@
|
|||||||
|
|
||||||
export let _id: Ref<Doc>
|
export let _id: Ref<Doc>
|
||||||
export let _class: Ref<Class<Doc>>
|
export let _class: Ref<Class<Doc>>
|
||||||
|
export let rightSection: AnyComponent | undefined = undefined
|
||||||
let object: Doc
|
let object: Doc
|
||||||
let objectClass: Class<Doc>
|
let objectClass: Class<Doc>
|
||||||
let parentClass: Ref<Class<Doc>>
|
let parentClass: Ref<Class<Doc>>
|
||||||
let rightSection: AnyComponent | undefined
|
|
||||||
let fullSize: boolean = true
|
let fullSize: boolean = true
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
|
@ -30,7 +30,7 @@ import core, {
|
|||||||
} from '@anticrm/core'
|
} from '@anticrm/core'
|
||||||
import type { IntlString } from '@anticrm/platform'
|
import type { IntlString } from '@anticrm/platform'
|
||||||
import { getResource } from '@anticrm/platform'
|
import { getResource } from '@anticrm/platform'
|
||||||
import { Channels, getAttributePresenterClass } from '@anticrm/presentation'
|
import { getAttributePresenterClass } from '@anticrm/presentation'
|
||||||
import { ErrorPresenter, getPlatformColorForText } from '@anticrm/ui'
|
import { ErrorPresenter, getPlatformColorForText } from '@anticrm/ui'
|
||||||
import type { Action, ActionTarget, BuildModelOptions, ObjectDDParticipant } from '@anticrm/view'
|
import type { Action, ActionTarget, BuildModelOptions, ObjectDDParticipant } from '@anticrm/view'
|
||||||
import view, { AttributeModel, BuildModelKey } from '@anticrm/view'
|
import view, { AttributeModel, BuildModelKey } from '@anticrm/view'
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Employee, EmployeeAccount } from '@anticrm/contact'
|
import contact, { Employee, EmployeeAccount } from '@anticrm/contact'
|
||||||
import core, { Client, getCurrentAccount, Ref, Space } from '@anticrm/core'
|
import core, { Client, Doc, getCurrentAccount, Ref, Space } from '@anticrm/core'
|
||||||
import { Avatar, createQuery, setClient } from '@anticrm/presentation'
|
import { Avatar, createQuery, setClient } from '@anticrm/presentation'
|
||||||
import {
|
import {
|
||||||
AnyComponent,
|
AnyComponent,
|
||||||
@ -235,7 +235,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- <div class="aside"><Chat thread/></div> -->
|
<!-- <div class="aside"><Chat thread/></div> -->
|
||||||
</div>
|
</div>
|
||||||
<PanelInstance/>
|
<PanelInstance />
|
||||||
<Popup />
|
<Popup />
|
||||||
<TooltipInstance />
|
<TooltipInstance />
|
||||||
{:else}
|
{:else}
|
||||||
|
17
rush.json
17
rush.json
@ -985,6 +985,21 @@
|
|||||||
"packageName": "@anticrm/rekoni",
|
"packageName": "@anticrm/rekoni",
|
||||||
"projectFolder": "packages/rekoni",
|
"projectFolder": "packages/rekoni",
|
||||||
"shouldPublish": true
|
"shouldPublish": true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"packageName": "@anticrm/model-notification",
|
||||||
|
"projectFolder": "models/notification",
|
||||||
|
"shouldPublish": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"packageName": "@anticrm/notification",
|
||||||
|
"projectFolder": "plugins/notification",
|
||||||
|
"shouldPublish": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"packageName": "@anticrm/notification-resources",
|
||||||
|
"projectFolder": "plugins/notification-resources",
|
||||||
|
"shouldPublish": true
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,9 @@ class TServerStorage implements ServerStorage {
|
|||||||
const _class = txClass(tx)
|
const _class = txClass(tx)
|
||||||
const objClass = txObjectClass(tx)
|
const objClass = txObjectClass(tx)
|
||||||
return await ctx.with('tx', { _class, objClass }, async (ctx) => {
|
return await ctx.with('tx', { _class, objClass }, async (ctx) => {
|
||||||
await ctx.with('domain-tx', { _class, objClass }, async () => await this.getAdapter(DOMAIN_TX).tx(tx))
|
if (tx.objectSpace !== core.space.DerivedTx) {
|
||||||
|
await ctx.with('domain-tx', { _class, objClass }, async () => await this.getAdapter(DOMAIN_TX).tx(tx))
|
||||||
|
}
|
||||||
|
|
||||||
if (tx.objectSpace === core.space.Model) {
|
if (tx.objectSpace === core.space.Model) {
|
||||||
// maintain hiearachy and triggers
|
// maintain hiearachy and triggers
|
||||||
|
Loading…
Reference in New Issue
Block a user