mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 02:51: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-inventory': file:./projects/model-inventory.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-recruit': file:./projects/model-recruit.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-workbench': file:./projects/model-workbench.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/platform': file:./projects/platform.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-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-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-recruit': file:projects/model-recruit.tgz_typescript@4.5.4
|
||||
'@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-workbench': file:projects/model-workbench.tgz_typescript@4.5.4
|
||||
'@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/platform': file:projects/platform.tgz
|
||||
'@rush-temp/platform-rig': file:projects/platform-rig.tgz_37f79b97d0d86442e45d380c86f520c5
|
||||
@ -12094,7 +12100,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
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
|
||||
name: '@rush-temp/gmail-resources'
|
||||
version: 0.0.0
|
||||
@ -12425,7 +12431,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
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
|
||||
name: '@rush-temp/model-all'
|
||||
version: 0.0.0
|
||||
@ -12617,6 +12623,27 @@ packages:
|
||||
- typescript
|
||||
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:
|
||||
resolution: {integrity: sha512-cBkfqfxIgaViuDI4bj0NDuVg1NnzoSjL2mxvRRRn1Dnqkq+2K00cKyx67Pil9dHwH636h2HEYKoM4VNsT3Os1w==, tarball: file:projects/model-presentation.tgz}
|
||||
id: file:projects/model-presentation.tgz
|
||||
@ -12931,6 +12958,59 @@ packages:
|
||||
- supports-color
|
||||
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:
|
||||
resolution: {integrity: sha512-RxiNEW/PUGlF6/JrOvYWaUVfmC4kVnrd1tUQBpUM9tcRQebTwjPIUF1+V19xT63Rer6sRmz4SWo9v6/BtRAJlw==, tarball: file:projects/panel.tgz}
|
||||
id: file:projects/panel.tgz
|
||||
@ -13047,7 +13127,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
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
|
||||
name: '@rush-temp/presentation'
|
||||
version: 0.0.0
|
||||
@ -13083,7 +13163,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
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
|
||||
name: '@rush-temp/prod'
|
||||
version: 0.0.0
|
||||
@ -13157,7 +13237,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
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
|
||||
name: '@rush-temp/recruit-resources'
|
||||
version: 0.0.0
|
||||
@ -13611,7 +13691,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
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
|
||||
name: '@rush-temp/telegram-resources'
|
||||
version: 0.0.0
|
||||
|
@ -97,6 +97,8 @@
|
||||
"@anticrm/templates": "~0.6.0",
|
||||
"@anticrm/templates-assets": "~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/rekoni": "~0.6.0"
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import { gmailId } from '@anticrm/gmail'
|
||||
import { imageCropperId } from '@anticrm/image-cropper'
|
||||
import { inventoryId } from '@anticrm/inventory'
|
||||
import { templatesId } from '@anticrm/templates'
|
||||
import { notificationId } from '@anticrm/notification'
|
||||
import rekoni from '@anticrm/rekoni'
|
||||
|
||||
import '@anticrm/login-assets'
|
||||
@ -86,4 +87,6 @@ export async function configurePlatform() {
|
||||
addLocation(imageCropperId, () => import(/* webpackChunkName: "image-cropper" */ '@anticrm/image-cropper-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(notificationId, () => import(/* webpackChunkName: "notification" */ '@anticrm/notification-resources'))
|
||||
}
|
||||
|
@ -50,6 +50,7 @@
|
||||
"@anticrm/model-inventory": "~0.6.0",
|
||||
"@anticrm/model-presentation": "~0.6.0",
|
||||
"@anticrm/model-templates": "~0.6.0",
|
||||
"@anticrm/model-notification": "~0.6.0",
|
||||
"@anticrm/model-text-editor": "~0.6.0",
|
||||
"@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 viewModel } from '@anticrm/model-view'
|
||||
import { createModel as workbenchModel } from '@anticrm/model-workbench'
|
||||
import { createModel as notificationModel } from '@anticrm/model-notification'
|
||||
|
||||
const builder = new Builder()
|
||||
|
||||
@ -57,7 +58,7 @@ const builders = [
|
||||
presentationModel,
|
||||
templatesModel,
|
||||
textEditorModel,
|
||||
|
||||
notificationModel,
|
||||
serverCoreModel,
|
||||
serverAttachmentModel,
|
||||
serverContactModel,
|
||||
|
@ -70,6 +70,8 @@ export class TChannel extends TAttachedDoc implements Channel {
|
||||
|
||||
@Prop(TypeString(), 'Value' as IntlString)
|
||||
value!: string
|
||||
|
||||
items?: number
|
||||
}
|
||||
|
||||
@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: {
|
||||
Tx: '' as Ref<Space>,
|
||||
DerivedTx: '' as Ref<Space>,
|
||||
Model: '' as Ref<Space>
|
||||
},
|
||||
account: {
|
||||
|
@ -30,7 +30,6 @@ export { default as AttributesBar } from './components/AttributesBar.svelte'
|
||||
export { default as AttributeBarEditor } from './components/AttributeBarEditor.svelte'
|
||||
export { default as AttributeEditor } from './components/AttributeEditor.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 MessageBox } from './components/MessageBox.svelte'
|
||||
export { default as SpaceCreateCard } from './components/SpaceCreateCard.svelte'
|
||||
|
@ -22,11 +22,9 @@ import login from '@anticrm/login'
|
||||
import { getMetadata } from '@anticrm/platform'
|
||||
import { LiveQuery as LQ } from '@anticrm/query'
|
||||
import { onDestroy } from 'svelte'
|
||||
import contact, { ChannelProvider } from '@anticrm/contact'
|
||||
|
||||
let liveQuery: LQ
|
||||
let client: TxOperations
|
||||
let channelProviders: Promise<ChannelProvider[]> | undefined
|
||||
|
||||
class UIClient extends TxOperations implements Client {
|
||||
constructor (client: Client, private readonly liveQuery: LQ) {
|
||||
@ -49,7 +47,6 @@ export function setClient (_client: Client): void {
|
||||
_client.notify = (tx: Tx) => {
|
||||
liveQuery.tx(tx).catch((err) => console.log(err))
|
||||
}
|
||||
channelProviders = client.findAll(contact.class.ChannelProvider, {})
|
||||
}
|
||||
|
||||
export class LiveQuery {
|
||||
@ -113,12 +110,3 @@ export function getAttributePresenterClass (attribute: AnyAttribute): Ref<Class<
|
||||
}
|
||||
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}
|
||||
_id={props._id}
|
||||
_class={props._class}
|
||||
rightSection={props.rightSection}
|
||||
on:update={fitPopup}
|
||||
on:close={_close}
|
||||
/>
|
||||
|
@ -7,6 +7,7 @@ export interface PanelProps {
|
||||
_id: string
|
||||
_class: string
|
||||
element?: PopupAlignment
|
||||
rightSection?: AnyComponent
|
||||
}
|
||||
|
||||
export const panelstore = writable < {panel?: PanelProps|undefined}>({ panel: undefined })
|
||||
@ -23,7 +24,8 @@ export function showPanel (
|
||||
component: AnyComponent,
|
||||
_id: string,
|
||||
_class: string,
|
||||
element?: PopupAlignment
|
||||
element?: PopupAlignment,
|
||||
rightSection?: AnyComponent
|
||||
): void {
|
||||
const newLoc = encodeURIComponent([component, _id, _class].join('|'))
|
||||
if (currentLocation === newLoc) {
|
||||
@ -31,7 +33,7 @@ export function showPanel (
|
||||
}
|
||||
currentLocation = newLoc
|
||||
panelstore.update(() => {
|
||||
return { panel: { component, _id, _class, element } }
|
||||
return { panel: { component, _id, _class, element, rightSection } }
|
||||
})
|
||||
const location = getCurrentLocation()
|
||||
if (location.fragment !== currentLocation) {
|
||||
|
@ -39,6 +39,7 @@
|
||||
"svelte": "^3.37.0",
|
||||
"@anticrm/text-editor": "~0.6.0",
|
||||
"@anticrm/contact": "~0.6.2",
|
||||
"@anticrm/contact-resources": "~0.6.0",
|
||||
"@anticrm/view-resources": "~0.6.0",
|
||||
"@anticrm/view": "~0.6.0"
|
||||
}
|
||||
|
@ -40,6 +40,7 @@
|
||||
"@anticrm/core": "~0.6.11",
|
||||
"@anticrm/view": "~0.6.0",
|
||||
"@anticrm/attachment-resources": "~0.6.0",
|
||||
"@anticrm/notification-resources": "~0.6.0",
|
||||
"@anticrm/panel": "~0.6.0",
|
||||
"@anticrm/view-resources": "~0.6.0",
|
||||
"@anticrm/attachment": "~0.6.1"
|
||||
|
@ -16,9 +16,10 @@
|
||||
<script lang="ts">
|
||||
import { Channel } from '@anticrm/contact'
|
||||
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 { createEventDispatcher } from 'svelte'
|
||||
import ChannelsView from './ChannelsView.svelte'
|
||||
import contact from '../plugin'
|
||||
|
||||
export let integrations: Set<Ref<Doc>> | undefined = undefined
|
||||
@ -40,7 +41,7 @@
|
||||
/>
|
||||
<span><Label label={presentation.string.AddSocialLinks} /></span>
|
||||
{:else}
|
||||
<Channels value={channels} size={'small'} {integrations} on:click />
|
||||
<ChannelsView value={channels} size={'small'} {integrations} on:click />
|
||||
<div class="ml-1">
|
||||
<CircleButton
|
||||
icon={contact.icon.Edit}
|
||||
|
@ -14,7 +14,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<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 { ChannelProvider, Channel } from '@anticrm/contact'
|
||||
@ -35,40 +35,72 @@
|
||||
}
|
||||
|
||||
const query = createQuery()
|
||||
query.query(contact.class.Channel, {
|
||||
attachedTo: attachedTo
|
||||
}, (res) => {
|
||||
channels = res
|
||||
})
|
||||
query.query(
|
||||
contact.class.Channel,
|
||||
{
|
||||
attachedTo: attachedTo
|
||||
},
|
||||
(res) => {
|
||||
channels = res
|
||||
}
|
||||
)
|
||||
|
||||
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 promises = []
|
||||
for (const value of newValues) {
|
||||
const oldChannel = findValue(value.provider)
|
||||
if (oldChannel === undefined) {
|
||||
if (oldChannel === undefined) {
|
||||
if (value.value.length === 0) continue
|
||||
promises.push(client.addCollection(contact.class.Channel, contact.space.Contacts, attachedTo, attachedClass, 'channels', {
|
||||
value: value.value,
|
||||
provider: value.provider
|
||||
}))
|
||||
promises.push(
|
||||
client.addCollection(contact.class.Channel, contact.space.Contacts, attachedTo, attachedClass, 'channels', {
|
||||
value: value.value,
|
||||
provider: value.provider
|
||||
})
|
||||
)
|
||||
} else {
|
||||
currentProviders.delete(value.provider)
|
||||
if (value.value === oldChannel.value) continue
|
||||
promises.push(client.updateCollection(oldChannel._class, oldChannel.space, oldChannel._id, oldChannel.attachedTo, oldChannel.attachedToClass, oldChannel.collection, {
|
||||
value: value.value
|
||||
}))
|
||||
promises.push(
|
||||
client.updateCollection(
|
||||
oldChannel._class,
|
||||
oldChannel.space,
|
||||
oldChannel._id,
|
||||
oldChannel.attachedTo,
|
||||
oldChannel.attachedToClass,
|
||||
oldChannel.collection,
|
||||
{
|
||||
value: value.value
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
for (const value of currentProviders) {
|
||||
const oldChannel = findValue(value)
|
||||
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)
|
||||
}
|
||||
</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">
|
||||
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
|
||||
|
||||
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>
|
||||
|
||||
<Channels {value} size={'small'} reverse />
|
||||
<ChannelsView {value} size={'small'} reverse on:click={click} />
|
||||
|
@ -15,19 +15,21 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
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 { AnyComponent } from '@anticrm/ui'
|
||||
import { CircleButton, Tooltip } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { getClient } from '..'
|
||||
import { getChannelProviders } from '../utils'
|
||||
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 reverse: boolean = false
|
||||
export let integrations: Set<Ref<Doc>> = new Set<Ref<Doc>>()
|
||||
const notificationClient = NotificationClient.getClient()
|
||||
const lastViews = notificationClient.getLastViews()
|
||||
|
||||
interface Item {
|
||||
label: IntlString
|
||||
@ -35,19 +37,25 @@
|
||||
value: string
|
||||
presenter?: AnyComponent
|
||||
integration: boolean
|
||||
notification: boolean
|
||||
}
|
||||
|
||||
const client = getClient()
|
||||
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)
|
||||
if (provider) {
|
||||
const notification = (item as Channel)._id !== undefined ? isNew((item as Channel), lastViews) : false
|
||||
return {
|
||||
label: provider.label as IntlString,
|
||||
icon: provider.icon as Asset,
|
||||
value: item.value,
|
||||
presenter: provider.presenter,
|
||||
notification,
|
||||
integration: provider.integrationType !== undefined ? integrations.has(provider.integrationType) : false
|
||||
}
|
||||
} 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 map = await getChannelProviders()
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) {
|
||||
const provider = getProvider(item, map)
|
||||
const provider = getProvider(item, map, lastViews)
|
||||
if (provider !== undefined) {
|
||||
result.push(provider)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const provider = getProvider(value, map)
|
||||
const provider = getProvider(value, map, lastViews)
|
||||
if (provider !== undefined) {
|
||||
result.push(provider)
|
||||
}
|
||||
@ -74,7 +91,7 @@
|
||||
displayItems = result
|
||||
}
|
||||
|
||||
$: if (value) update(value)
|
||||
$: if (value) update(value, $lastViews)
|
||||
|
||||
let displayItems: Item[] = []
|
||||
let divHTML: HTMLElement
|
||||
@ -94,7 +111,7 @@
|
||||
}}
|
||||
>
|
||||
<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>
|
||||
</div>
|
||||
{/each}
|
@ -23,7 +23,7 @@
|
||||
import { Channel, Organization } from '@anticrm/contact'
|
||||
import contact from '../plugin'
|
||||
import Company from './icons/Company.svelte'
|
||||
import { generateId } from '@anticrm/core'
|
||||
import { AttachedData, generateId } from '@anticrm/core'
|
||||
import Channels from './Channels.svelte'
|
||||
|
||||
export function canClose (): boolean {
|
||||
@ -51,7 +51,7 @@
|
||||
dispatch('close')
|
||||
}
|
||||
|
||||
let channels: Channel[] = []
|
||||
let channels: AttachedData<Channel>[] = []
|
||||
</script>
|
||||
|
||||
<Card
|
||||
|
@ -15,7 +15,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Data, generateId } from '@anticrm/core'
|
||||
import { AttachedData, Data, generateId } from '@anticrm/core'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
|
||||
import { getClient, Card, EditableAvatar } from '@anticrm/presentation'
|
||||
@ -72,7 +72,7 @@
|
||||
dispatch('close')
|
||||
}
|
||||
|
||||
let channels: Channel[] = []
|
||||
let channels: AttachedData<Channel>[] = []
|
||||
</script>
|
||||
|
||||
<Card
|
||||
|
@ -14,7 +14,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { AttachedData, Ref } from '@anticrm/core'
|
||||
import { AttachedData, Ref } from '@anticrm/core'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { EditBox, Button, ScrollBox } from '@anticrm/ui'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
@ -42,14 +42,14 @@
|
||||
for (const provider of providers) {
|
||||
const i = findValue(provider._id)
|
||||
if (i !== -1) {
|
||||
newValues.push({ provider: provider._id, value: values[i].value })
|
||||
newValues.push(values[i])
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
</script>
|
||||
|
@ -21,6 +21,7 @@ import { Avatar, ObjectSearchResult, UserInfo } from '@anticrm/presentation'
|
||||
import ChannelsEditor from './components/ChannelsEditor.svelte'
|
||||
import Channels from './components/Channels.svelte'
|
||||
import ChannelsPresenter from './components/ChannelsPresenter.svelte'
|
||||
import ChannelsView from './components/ChannelsView.svelte'
|
||||
import ContactPresenter from './components/ContactPresenter.svelte'
|
||||
import Contacts from './components/Contacts.svelte'
|
||||
import CreateOrganization from './components/CreateOrganization.svelte'
|
||||
@ -34,7 +35,7 @@ import PersonPresenter from './components/PersonPresenter.svelte'
|
||||
import SocialEditor from './components/SocialEditor.svelte'
|
||||
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[]> {
|
||||
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 {
|
||||
provider: Ref<ChannelProvider>
|
||||
value: string
|
||||
items?: number
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -41,6 +41,7 @@
|
||||
"@anticrm/setting": "~0.6.0",
|
||||
"@anticrm/chunter": "~0.6.0",
|
||||
"@anticrm/chunter-resources": "~0.6.0",
|
||||
"@anticrm/notification-resources": "~0.6.0",
|
||||
"@anticrm/login": "~0.6.1",
|
||||
"@anticrm/core": "~0.6.11"
|
||||
}
|
||||
|
@ -13,7 +13,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { Message, SharedMessage } from '@anticrm/gmail'
|
||||
@ -25,12 +24,14 @@
|
||||
import setting from '@anticrm/setting'
|
||||
import Connect from './Connect.svelte'
|
||||
import Messages from './Messages.svelte'
|
||||
import { NotificationClient } from '@anticrm/notification-resources'
|
||||
|
||||
export let object: Contact
|
||||
export let channel: Channel
|
||||
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 accounts: EmployeeAccount[] = []
|
||||
@ -42,22 +43,31 @@
|
||||
const accauntsQuery = createQuery()
|
||||
const settingsQuery = createQuery()
|
||||
const accountId = getCurrentAccount()._id
|
||||
const notificationClient = NotificationClient.getClient()
|
||||
|
||||
$: messagesQuery.query(
|
||||
gmail.class.Message,
|
||||
{ attachedTo: channel._id },
|
||||
(res) => {
|
||||
messages = res
|
||||
},
|
||||
{ sort: { modifiedOn: SortingOrder.Descending } }
|
||||
)
|
||||
function updateMessagesQuery (channelId: Ref<Channel>): void {
|
||||
messagesQuery.query(
|
||||
gmail.class.Message,
|
||||
{ attachedTo: channelId },
|
||||
(res) => {
|
||||
messages = res
|
||||
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>)
|
||||
$: accauntsQuery.query(contact.class.EmployeeAccount, { _id: { $in: accountsIds }}, (result) => {
|
||||
accounts = result
|
||||
})
|
||||
$: updateMessagesQuery(channel._id)
|
||||
|
||||
$: 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,
|
||||
{ type: gmail.integrationType.Gmail, space: accountId as string as Ref<Space> },
|
||||
(res) => {
|
||||
@ -68,9 +78,17 @@
|
||||
|
||||
async function share (): Promise<void> {
|
||||
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', {
|
||||
messages: convertMessages(selectedMessages, accounts)
|
||||
})
|
||||
await client.addCollection(
|
||||
gmail.class.SharedMessages,
|
||||
object.space,
|
||||
object._id,
|
||||
object._class,
|
||||
'gmailSharedMessages',
|
||||
{
|
||||
messages: convertMessages(selectedMessages, accounts)
|
||||
}
|
||||
)
|
||||
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||
clear()
|
||||
}
|
||||
|
||||
@ -91,7 +109,6 @@
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function getName (message: Message, accounts: EmployeeAccount[], sender: boolean): string {
|
||||
if (message.incoming === sender) {
|
||||
return `${formatName(object.name)} (${channel.value})`
|
||||
|
@ -66,7 +66,7 @@
|
||||
{#if currentMessage.copy?.length}
|
||||
<Label label={gmail.string.Copy} />: {currentMessage.copy.join(', ')}
|
||||
{/if}
|
||||
<div class="flex-col clear-mins mt-9">
|
||||
<div class="flex-col h-full clear-mins mt-9">
|
||||
<FullMessageContent content={currentMessage.content} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -15,20 +15,18 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
export let content: string
|
||||
|
||||
let editor: HTMLDivElement
|
||||
$: if (editor) editor.innerHTML = content
|
||||
</script>
|
||||
|
||||
<div bind:this={editor} class="input clear-mins" />
|
||||
<iframe srcdoc={content} title="e-mail" />
|
||||
|
||||
<style lang="scss">
|
||||
.input {
|
||||
iframe {
|
||||
overflow: auto;
|
||||
padding: 1rem;
|
||||
border: none;
|
||||
border-radius: 0.5rem;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
color: #1f212b;
|
||||
border-radius: .5rem;
|
||||
|
||||
:global(a) {
|
||||
font: inherit;
|
||||
|
@ -40,7 +40,7 @@
|
||||
{#if message.copy?.length}
|
||||
<Label label={gmail.string.Copy} />: {message.copy.join(', ')}
|
||||
{/if}
|
||||
<div class="flex-col clear-mins mt-5">
|
||||
<div class="flex-col h-full clear-mins mt-5">
|
||||
<FullMessageContent content={message.content} />
|
||||
</div>
|
||||
</div>
|
||||
@ -51,7 +51,7 @@
|
||||
max-height: 500px;
|
||||
background-color: var(--theme-button-bg-focused);
|
||||
border: 1px solid var(--theme-button-border-enabled);
|
||||
border-radius: .75rem;
|
||||
box-shadow: 0px 10px 20px rgba(0, 0, 0, .2);
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
</style>
|
||||
|
@ -20,18 +20,24 @@
|
||||
import FullMessage from './FullMessage.svelte'
|
||||
import Chats from './Chats.svelte'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { NotificationClient } from '@anticrm/notification-resources'
|
||||
|
||||
export let object: Contact
|
||||
let newMessage: boolean = false
|
||||
let currentMessage: SharedMessage | undefined = undefined
|
||||
let channel: Channel | undefined = undefined
|
||||
const notificationClient = NotificationClient.getClient()
|
||||
|
||||
const client = getClient()
|
||||
|
||||
client.findOne(contact.class.Channel, {
|
||||
attachedTo: object._id,
|
||||
provider: contact.channelProvider.Email
|
||||
}).then((res) => channel = res)
|
||||
client
|
||||
.findOne(contact.class.Channel, {
|
||||
attachedTo: object._id,
|
||||
provider: contact.channelProvider.Email
|
||||
})
|
||||
.then((res) => {
|
||||
channel = res
|
||||
})
|
||||
|
||||
function back () {
|
||||
if (newMessage) {
|
||||
@ -40,8 +46,11 @@
|
||||
return (currentMessage = undefined)
|
||||
}
|
||||
|
||||
function selectHandler (e: CustomEvent) {
|
||||
async function selectHandler (e: CustomEvent): Promise<void> {
|
||||
currentMessage = e.detail
|
||||
if (channel !== undefined) {
|
||||
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -15,9 +15,10 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { SharedMessage } from '@anticrm/gmail'
|
||||
import { CheckBox } from '@anticrm/ui'
|
||||
import { CheckBox, Label } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { getTime } from '../utils'
|
||||
import gmail from '../plugin'
|
||||
|
||||
export let message: SharedMessage
|
||||
export let selected: boolean = false
|
||||
@ -37,10 +38,14 @@
|
||||
{/if}
|
||||
<div class="flex-col message" class:selected>
|
||||
<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>
|
||||
<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">
|
||||
{message.subject}
|
||||
</div>
|
||||
@ -60,10 +65,12 @@
|
||||
padding: 1rem;
|
||||
min-width: 0;
|
||||
background-color: var(--theme-incoming-msg);
|
||||
border-radius: .75rem;
|
||||
border-radius: 0.75rem;
|
||||
white-space: nowrap;
|
||||
flex-grow: 1;
|
||||
|
||||
&.selected { background-color: var(--primary-button-enabled); }
|
||||
&.selected {
|
||||
background-color: var(--primary-button-enabled);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -24,10 +24,12 @@
|
||||
import { Channel, Contact, formatName } from '@anticrm/contact'
|
||||
import { TextEditor } from '@anticrm/text-editor'
|
||||
import plugin from '../plugin'
|
||||
import { NotificationClient } from '@anticrm/notification-resources'
|
||||
|
||||
export let object: Contact
|
||||
export let channel: Channel
|
||||
export let currentMessage: SharedMessage | undefined
|
||||
const notificationClient = NotificationClient.getClient()
|
||||
|
||||
let editor: TextEditor
|
||||
let copy: string = ''
|
||||
@ -42,7 +44,7 @@
|
||||
const url = getMetadata(login.metadata.GmailUrl) ?? ''
|
||||
|
||||
async function sendMsg () {
|
||||
fetch(url + '/send', {
|
||||
await fetch(url + '/send', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + getMetadata(login.metadata.LoginToken),
|
||||
@ -53,6 +55,7 @@
|
||||
copy: copy.split(',').map((m) => m.trim())
|
||||
})
|
||||
})
|
||||
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||
dispatch('close')
|
||||
}
|
||||
|
||||
@ -130,7 +133,7 @@
|
||||
background-color: #fff;
|
||||
color: #1f212b;
|
||||
height: 100%;
|
||||
border-radius: .5rem;
|
||||
border-radius: 0.5rem;
|
||||
|
||||
:global(.ProseMirror) {
|
||||
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">
|
||||
import { Avatar } from '@anticrm/presentation'
|
||||
import { Avatar, createQuery } from '@anticrm/presentation'
|
||||
import type { Candidate } from '@anticrm/recruit'
|
||||
import { Channels } from '@anticrm/presentation'
|
||||
import { formatName } from '@anticrm/contact'
|
||||
import { ChannelsView } from '@anticrm/contact-resources'
|
||||
import contact, { Channel, formatName } from '@anticrm/contact'
|
||||
|
||||
export let candidate: Candidate
|
||||
|
||||
let channels: Channel[] = []
|
||||
const channelsQuery = createQuery()
|
||||
channelsQuery.query(
|
||||
contact.class.Channel,
|
||||
{
|
||||
attachedTo: candidate._id
|
||||
},
|
||||
(res) => {
|
||||
channels = res
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class="flex-col h-full card-container">
|
||||
@ -29,7 +41,7 @@
|
||||
<div class="name">{formatName(candidate.name)}</div>
|
||||
<div class="description">{candidate.title ?? ''}</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}
|
||||
</div>
|
||||
|
||||
|
@ -14,16 +14,15 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
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 type { AttachedData, Data, MixinData, Ref } from '@anticrm/core'
|
||||
import { generateId } from '@anticrm/core'
|
||||
import { AttachedData, Data,generateId,MixinData,Ref } from '@anticrm/core'
|
||||
import login from '@anticrm/login'
|
||||
import { getMetadata, getResource, setPlatformStatus, unknownError } from '@anticrm/platform'
|
||||
import { Card, EditableAvatar, getClient, getFileUrl, PDFViewer } from '@anticrm/presentation'
|
||||
import { getMetadata,getResource,setPlatformStatus,unknownError } from '@anticrm/platform'
|
||||
import { Card,EditableAvatar,getClient,getFileUrl,PDFViewer } from '@anticrm/presentation'
|
||||
import type { Candidate } from '@anticrm/recruit'
|
||||
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 recruit from '../plugin'
|
||||
import FileUpload from './icons/FileUpload.svelte'
|
||||
|
@ -51,7 +51,7 @@
|
||||
|
||||
{#if object !== undefined && candidate !== undefined}
|
||||
<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="card"><VacancyCard {vacancy} /></div>
|
||||
</div>
|
||||
|
@ -40,6 +40,7 @@
|
||||
"@anticrm/setting": "~0.6.0",
|
||||
"@anticrm/chunter": "~0.6.0",
|
||||
"@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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact,{ Channel,Contact,EmployeeAccount,formatName } from '@anticrm/contact'
|
||||
import { getCurrentAccount,Ref,SortingOrder,Space } from '@anticrm/core'
|
||||
import contact, { Channel, Contact, EmployeeAccount, formatName } from '@anticrm/contact'
|
||||
import { getCurrentAccount, Ref, SortingOrder, Space } from '@anticrm/core'
|
||||
import login from '@anticrm/login'
|
||||
import { getMetadata } from '@anticrm/platform'
|
||||
import { createQuery,getClient } from '@anticrm/presentation'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import setting from '@anticrm/setting'
|
||||
import type { SharedTelegramMessage, TelegramMessage } from '@anticrm/telegram'
|
||||
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 Connect from './Connect.svelte'
|
||||
import TelegramIcon from './icons/Telegram.svelte'
|
||||
import Messages from './Messages.svelte'
|
||||
import { NotificationClient } from '@anticrm/notification-resources'
|
||||
|
||||
export let object: Contact
|
||||
let channel: Channel | undefined = undefined
|
||||
const client = getClient()
|
||||
const notificationClient = NotificationClient.getClient()
|
||||
|
||||
client.findOne(contact.class.Channel, {
|
||||
attachedTo: object._id,
|
||||
provider: contact.channelProvider.Telegram
|
||||
}).then((res) => channel = res)
|
||||
client
|
||||
.findOne(contact.class.Channel, {
|
||||
attachedTo: object._id,
|
||||
provider: contact.channelProvider.Telegram
|
||||
})
|
||||
.then((res) => {
|
||||
channel = res
|
||||
})
|
||||
|
||||
let messages: TelegramMessage[] = []
|
||||
let accounts: EmployeeAccount[] = []
|
||||
@ -49,16 +55,31 @@
|
||||
const settingsQuery = createQuery()
|
||||
const accountId = getCurrentAccount()._id
|
||||
|
||||
$: channel && messagesQuery.query(telegram.class.Message, { modifiedBy: accountId, attachedTo: channel._id }, (res) => {
|
||||
messages = res
|
||||
}, { sort: { modifiedOn: SortingOrder.Ascending }})
|
||||
function updateMessagesQuery (channelId: Ref<Channel>): void {
|
||||
messagesQuery.query(
|
||||
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>)
|
||||
$: accauntsQuery.query(contact.class.EmployeeAccount, { _id: { $in: accountsIds }}, (result) => {
|
||||
accounts = result
|
||||
})
|
||||
$: channel && updateMessagesQuery(channel._id)
|
||||
|
||||
$: 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,
|
||||
{ type: telegram.integrationType.Telegram, space: accountId as string as Ref<Space> },
|
||||
(res) => {
|
||||
@ -67,7 +88,7 @@
|
||||
)
|
||||
|
||||
async function sendMsg (to: string, msg: string) {
|
||||
return await fetch(url + '/send-msg', {
|
||||
const res = await fetch(url + '/send-msg', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + getMetadata(login.metadata.LoginToken),
|
||||
@ -78,6 +99,10 @@
|
||||
msg
|
||||
})
|
||||
})
|
||||
if (channel !== undefined) {
|
||||
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
async function addContact (phone: string) {
|
||||
@ -137,6 +162,9 @@
|
||||
messages: convertMessages(selectedMessages, accounts)
|
||||
}
|
||||
)
|
||||
if (channel !== undefined) {
|
||||
await notificationClient.updateLastView(channel._id, channel._class, channel.modifiedOn, true)
|
||||
}
|
||||
clear()
|
||||
}
|
||||
|
||||
@ -199,7 +227,13 @@
|
||||
<Button label={telegram.string.Cancel} size={'small'} on:click={clear} />
|
||||
</div>
|
||||
<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>
|
||||
|
@ -16,10 +16,23 @@
|
||||
import type { SharedTelegramMessage } from '@anticrm/telegram'
|
||||
|
||||
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>
|
||||
|
||||
<div class="datetime-container">
|
||||
{new Intl.DateTimeFormat('default', { day: 'numeric', month: 'long' }).format(message.modifiedOn)}
|
||||
{new Intl.DateTimeFormat('default', options).format(message.modifiedOn)}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
|
@ -32,10 +32,10 @@
|
||||
|
||||
export let _id: Ref<Doc>
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let rightSection: AnyComponent | undefined = undefined
|
||||
let object: Doc
|
||||
let objectClass: Class<Doc>
|
||||
let parentClass: Ref<Class<Doc>>
|
||||
let rightSection: AnyComponent | undefined
|
||||
let fullSize: boolean = true
|
||||
|
||||
const client = getClient()
|
||||
|
@ -30,7 +30,7 @@ import core, {
|
||||
} from '@anticrm/core'
|
||||
import type { IntlString } 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 type { Action, ActionTarget, BuildModelOptions, ObjectDDParticipant } from '@anticrm/view'
|
||||
import view, { AttributeModel, BuildModelKey } from '@anticrm/view'
|
||||
|
@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
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 {
|
||||
AnyComponent,
|
||||
@ -235,7 +235,7 @@
|
||||
</div>
|
||||
<!-- <div class="aside"><Chat thread/></div> -->
|
||||
</div>
|
||||
<PanelInstance/>
|
||||
<PanelInstance />
|
||||
<Popup />
|
||||
<TooltipInstance />
|
||||
{:else}
|
||||
|
17
rush.json
17
rush.json
@ -985,6 +985,21 @@
|
||||
"packageName": "@anticrm/rekoni",
|
||||
"projectFolder": "packages/rekoni",
|
||||
"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 objClass = txObjectClass(tx)
|
||||
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) {
|
||||
// maintain hiearachy and triggers
|
||||
|
Loading…
Reference in New Issue
Block a user