diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml
index 407fb10602..63d60d1f90 100644
--- a/common/config/rush/pnpm-lock.yaml
+++ b/common/config/rush/pnpm-lock.yaml
@@ -34,6 +34,8 @@ specifiers:
'@rush-temp/gmail': file:./projects/gmail.tgz
'@rush-temp/gmail-assets': file:./projects/gmail-assets.tgz
'@rush-temp/gmail-resources': file:./projects/gmail-resources.tgz
+ '@rush-temp/image-cropper': file:./projects/image-cropper.tgz
+ '@rush-temp/image-cropper-resources': file:./projects/image-cropper-resources.tgz
'@rush-temp/lead': file:./projects/lead.tgz
'@rush-temp/lead-assets': file:./projects/lead-assets.tgz
'@rush-temp/lead-resources': file:./projects/lead-resources.tgz
@@ -127,6 +129,7 @@ specifiers:
commander: ^8.1.0
compression-webpack-plugin: ~9.0.0
cors: ^2.8.5
+ cropperjs: ~1.5.12
cross-env: ^7.0.3
css-loader: ^5.2.1
deep-equal: ^2.0.5
@@ -163,6 +166,7 @@ specifiers:
sass: ^1.37.5
sass-loader: ^12.1.0
simplytyped: ^3.3.0
+ smartcrop: ~2.0.5
style-loader: ^3.2.1
svelte-check: ^2.2.10
svelte-preprocess: ^4.7.4
@@ -211,6 +215,8 @@ dependencies:
'@rush-temp/gmail': file:projects/gmail.tgz
'@rush-temp/gmail-assets': file:projects/gmail-assets.tgz
'@rush-temp/gmail-resources': file:projects/gmail-resources.tgz_096c09b0b673a57c275d9767a12070b1
+ '@rush-temp/image-cropper': file:projects/image-cropper.tgz
+ '@rush-temp/image-cropper-resources': file:projects/image-cropper-resources.tgz_096c09b0b673a57c275d9767a12070b1
'@rush-temp/lead': file:projects/lead.tgz
'@rush-temp/lead-assets': file:projects/lead-assets.tgz
'@rush-temp/lead-resources': file:projects/lead-resources.tgz_096c09b0b673a57c275d9767a12070b1
@@ -304,6 +310,7 @@ dependencies:
commander: 8.3.0
compression-webpack-plugin: 9.0.1_webpack@5.65.0
cors: 2.8.5
+ cropperjs: 1.5.12
cross-env: 7.0.3
css-loader: 5.2.7_webpack@5.65.0
deep-equal: 2.0.5
@@ -340,6 +347,7 @@ dependencies:
sass: 1.45.0
sass-loader: 12.4.0_sass@1.45.0+webpack@5.65.0
simplytyped: 3.3.0_typescript@4.5.4
+ smartcrop: 2.0.5
style-loader: 3.3.1_webpack@5.65.0
svelte-check: 2.2.11_ac194b5590200ebf8338e0f86ec190f4
svelte-preprocess: 4.10.1_3ae2e5fc7d8fb60bbcea513ad0b15c0f
@@ -3530,6 +3538,10 @@ packages:
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
dev: false
+ /cropperjs/1.5.12:
+ resolution: {integrity: sha512-re7UdjE5UnwdrovyhNzZ6gathI4Rs3KGCBSc8HCIjUo5hO42CtzyblmWLj6QWVw7huHyDMfpKxhiO2II77nhDw==}
+ dev: false
+
/cross-env/7.0.3:
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
@@ -8877,6 +8889,10 @@ packages:
is-fullwidth-code-point: 3.0.0
dev: false
+ /smartcrop/2.0.5:
+ resolution: {integrity: sha512-aXoHTM8XlC51g96kgZkYxZ2mx09/ibOrIVLiUNOFozV/MHmFSgEr1/5CKVBoFD5vd+re2wSy0xra21CyjRITzA==}
+ dev: false
+
/snapdragon-node/2.1.1:
resolution: {integrity: sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==}
engines: {node: '>=0.10.0'}
@@ -10926,7 +10942,7 @@ packages:
dev: false
file:projects/contact-resources.tgz_096c09b0b673a57c275d9767a12070b1:
- resolution: {integrity: sha512-5c2Hkvtnj+3t/r1Z+RFQuoxDwYm6ndTPCxFns6PK3SqCs/KfWREism8tiswJXrB2FNSQkb2CE7yvsftKZFcbPQ==, tarball: file:projects/contact-resources.tgz}
+ resolution: {integrity: sha512-5m/Rr4eGcqsTpvxL8nYdwl7Vn74QrZXdcgIDGbOJaRWh8uXuQBsDIUgE4wCrL49sgOJQEq/dUIfJVA9ndBOd5A==, tarball: file:projects/contact-resources.tgz}
id: file:projects/contact-resources.tgz
name: '@rush-temp/contact-resources'
version: 0.0.0
@@ -11363,6 +11379,63 @@ packages:
- supports-color
dev: false
+ file:projects/image-cropper-resources.tgz_096c09b0b673a57c275d9767a12070b1:
+ resolution: {integrity: sha512-ljTYKZOYl34hx76ETCFNAcExK+MP6WSS09tbRRixWxD7SYc7NmBeMntGyZTy3qNu4/jAwNaDAIH52kXQg9ktXQ==, tarball: file:projects/image-cropper-resources.tgz}
+ id: file:projects/image-cropper-resources.tgz
+ name: '@rush-temp/image-cropper-resources'
+ version: 0.0.0
+ dependencies:
+ '@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
+ '@typescript-eslint/parser': 5.7.0_eslint@7.32.0+typescript@4.5.4
+ cropperjs: 1.5.12
+ 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
+ eslint-plugin-svelte3: 3.2.1_eslint@7.32.0+svelte@3.44.3
+ prettier: 2.5.1
+ prettier-plugin-svelte: 2.5.1_prettier@2.5.1+svelte@3.44.3
+ sass: 1.45.0
+ smartcrop: 2.0.5
+ svelte: 3.44.3
+ svelte-check: 2.2.11_4374c622c67ed7479ff0e44c29d09bce
+ svelte-loader: 3.1.2_svelte@3.44.3
+ svelte-preprocess: 4.10.1_14d64cad431e31f100de7363af24a44f
+ typescript: 4.5.4
+ transitivePeerDependencies:
+ - '@babel/core'
+ - coffeescript
+ - less
+ - node-sass
+ - postcss
+ - postcss-load-config
+ - pug
+ - stylus
+ - sugarss
+ - supports-color
+ dev: false
+
+ file:projects/image-cropper.tgz:
+ resolution: {integrity: sha512-7Fj5//tMmR0uKUwcnFrk1KHmI+N+CLNmOiNPApLUtdK7q6clzFT8LMTDZapnbh93NdTCVXa+DxVLwzNGaeqsQA==, tarball: file:projects/image-cropper.tgz}
+ name: '@rush-temp/image-cropper'
+ 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/lead-assets.tgz:
resolution: {integrity: sha512-cRYB8PutP6HmaJjoEMLIEyMQEhKAQaCu0w2NJMF5TUW9vokia/22TXsHo1+xEGI1rx2epywbGXet/fL40tdbDw==, tarball: file:projects/lead-assets.tgz}
name: '@rush-temp/lead-assets'
@@ -12045,7 +12118,7 @@ packages:
dev: false
file:projects/presentation.tgz_096c09b0b673a57c275d9767a12070b1:
- resolution: {integrity: sha512-tpa5gk8H/quPYXkpBhp5jS0bp/E+Jk7mTGhfE54q4RTr5LOYGTCg2HKmxZWqRV1N3g1Jk7nKAZHWMxMSGHCuKw==, tarball: file:projects/presentation.tgz}
+ resolution: {integrity: sha512-QhfoHLyegkl3JsleXaZkSWsVQgikJM9jdqkla2mRW3kHyQAYWPBITUnVVWu1qBADViJupNZdYmYEcm7oeMF5og==, tarball: file:projects/presentation.tgz}
id: file:projects/presentation.tgz
name: '@rush-temp/presentation'
version: 0.0.0
@@ -12081,7 +12154,7 @@ packages:
dev: false
file:projects/prod.tgz_sass@1.45.0+typescript@4.5.4:
- resolution: {integrity: sha512-XgKxpfDD6oNTIijCj64CrOXM6sYgZhDuDoy5wWIrr/4Y5QFn7TWpOnSjIgmIXMtLUCGk4L55CAZBK+okRGX25Q==, tarball: file:projects/prod.tgz}
+ resolution: {integrity: sha512-7OeW4OKQlYw/FrF1rSmbwacpTXSVzNnIPCb/mIP/Q4Kc+uxroItvCRRYUqVH/V9u8W7nyTE7CjDM+KrADL/BAg==, tarball: file:projects/prod.tgz}
id: file:projects/prod.tgz
name: '@rush-temp/prod'
version: 0.0.0
diff --git a/dev/prod/package.json b/dev/prod/package.json
index 7d23dd5ea3..4d2068e871 100644
--- a/dev/prod/package.json
+++ b/dev/prod/package.json
@@ -88,6 +88,8 @@
"@anticrm/lead-resources": "~0.6.0",
"@anticrm/gmail": "~0.6.0",
"@anticrm/gmail-assets": "~0.6.0",
- "@anticrm/gmail-resources": "~0.6.0"
+ "@anticrm/gmail-resources": "~0.6.0",
+ "@anticrm/image-cropper": "~0.6.0",
+ "@anticrm/image-cropper-resources": "~0.6.0"
}
}
diff --git a/dev/prod/src/platform.ts b/dev/prod/src/platform.ts
index 812de966af..4fd7bba820 100644
--- a/dev/prod/src/platform.ts
+++ b/dev/prod/src/platform.ts
@@ -29,6 +29,7 @@ import { attachmentId } from '@anticrm/attachment'
import { leadId } from '@anticrm/lead'
import { clientId } from '@anticrm/client'
import { gmailId } from '@anticrm/gmail'
+import { imageCropperId } from '@anticrm/image-cropper'
import '@anticrm/login-assets'
import '@anticrm/task-assets'
@@ -71,4 +72,5 @@ export async function configurePlatform() {
addLocation(telegramId, () => import(/* webpackChunkName: "telegram" */ '@anticrm/telegram-resources'))
addLocation(attachmentId, () => import(/* webpackChunkName: "attachment" */ '@anticrm/attachment-resources'))
addLocation(gmailId, () => import(/* webpackChunkName: "gmail" */ '@anticrm/gmail-resources'))
+ addLocation(imageCropperId, () => import(/* webpackChunkName: "image-cropper" */ '@anticrm/image-cropper-resources'))
}
diff --git a/packages/presentation/package.json b/packages/presentation/package.json
index 200256cc5d..c236163542 100644
--- a/packages/presentation/package.json
+++ b/packages/presentation/package.json
@@ -37,6 +37,7 @@
"@anticrm/view": "~0.6.0",
"svelte": "^3.37.0",
"@anticrm/contact": "~0.6.2",
- "@anticrm/login": "~0.6.1"
+ "@anticrm/login": "~0.6.1",
+ "@anticrm/image-cropper": "~0.6.0"
}
}
diff --git a/packages/presentation/src/components/Avatar.svelte b/packages/presentation/src/components/Avatar.svelte
index 4209ef5a2c..5b2a42427c 100644
--- a/packages/presentation/src/components/Avatar.svelte
+++ b/packages/presentation/src/components/Avatar.svelte
@@ -16,12 +16,22 @@
diff --git a/packages/presentation/src/components/EditAvatarPopup.svelte b/packages/presentation/src/components/EditAvatarPopup.svelte
new file mode 100644
index 0000000000..127464da22
--- /dev/null
+++ b/packages/presentation/src/components/EditAvatarPopup.svelte
@@ -0,0 +1,88 @@
+
+
+
+
{ dispatch('close') }} />
+
+ {#await CropperP then Cropper}
+
+
+
+
+ {/await}
+
+
+
diff --git a/packages/presentation/src/components/EditableAvatar.svelte b/packages/presentation/src/components/EditableAvatar.svelte
new file mode 100644
index 0000000000..79a2d4d19a
--- /dev/null
+++ b/packages/presentation/src/components/EditableAvatar.svelte
@@ -0,0 +1,58 @@
+
+
+
+
+
diff --git a/packages/presentation/src/index.ts b/packages/presentation/src/index.ts
index 338db59d99..20b0f02de0 100644
--- a/packages/presentation/src/index.ts
+++ b/packages/presentation/src/index.ts
@@ -23,6 +23,7 @@ export * from './attributes'
export { default as UserBox } from './components/UserBox.svelte'
export { default as UserInfo } from './components/UserInfo.svelte'
export { default as Avatar } from './components/Avatar.svelte'
+export { default as EditableAvatar } from './components/EditableAvatar.svelte'
export { default as MessageViewer } from './components/MessageViewer.svelte'
export { default as AttributesBar } from './components/AttributesBar.svelte'
export { default as AttributeBarEditor } from './components/AttributeBarEditor.svelte'
diff --git a/packages/presentation/src/utils.ts b/packages/presentation/src/utils.ts
index db1a8d6082..a9b8e5d2df 100644
--- a/packages/presentation/src/utils.ts
+++ b/packages/presentation/src/utils.ts
@@ -114,6 +114,15 @@ export function getFileUrl (file: string): string {
return url
}
+export async function getBlobURL (blob: Blob): Promise
{
+ return await new Promise((resolve) => {
+ const reader = new FileReader()
+
+ reader.addEventListener('load', () => resolve(reader.result as string), false)
+ reader.readAsDataURL(blob)
+ })
+}
+
/**
* @public
*/
diff --git a/plugins/attachment-resources/src/components/Attachments.svelte b/plugins/attachment-resources/src/components/Attachments.svelte
index 98c4268a7a..4d9e91a375 100644
--- a/plugins/attachment-resources/src/components/Attachments.svelte
+++ b/plugins/attachment-resources/src/components/Attachments.svelte
@@ -38,7 +38,7 @@
async function createAttachment (file: File) {
loading++
try {
- const uuid = await uploadFile(space, file, objectId)
+ const uuid = await uploadFile(file, space, objectId)
console.log('uploaded file uuid', uuid)
client.addCollection(attachment.class.Attachment, space, objectId, _class, 'attachments', {
name: file.name,
diff --git a/plugins/attachment-resources/src/index.ts b/plugins/attachment-resources/src/index.ts
index cb140ad7d4..86ddc95acd 100644
--- a/plugins/attachment-resources/src/index.ts
+++ b/plugins/attachment-resources/src/index.ts
@@ -17,6 +17,7 @@ import AttachmentsPresenter from './components/AttachmentsPresenter.svelte'
import AttachmentPresenter from './components/AttachmentPresenter.svelte'
import TxAttachmentCreate from './components/activity/TxAttachmentCreate.svelte'
import Attachments from './components/Attachments.svelte'
+import { uploadFile } from './utils'
export { Attachments, AttachmentsPresenter }
@@ -28,5 +29,8 @@ export default async () => ({
},
activity: {
TxAttachmentCreate
+ },
+ helper: {
+ UploadFile: uploadFile
}
})
diff --git a/plugins/attachment-resources/src/utils.ts b/plugins/attachment-resources/src/utils.ts
index f61a3d32d9..787fb3eddc 100644
--- a/plugins/attachment-resources/src/utils.ts
+++ b/plugins/attachment-resources/src/utils.ts
@@ -18,14 +18,19 @@ import type { Doc, Ref, Space } from '@anticrm/core'
import login from '@anticrm/login'
import { getMetadata } from '@anticrm/platform'
-export async function uploadFile (space: Ref, file: File, attachedTo: Ref): Promise {
+export async function uploadFile (file: File, space?: Ref, attachedTo?: Ref): Promise {
console.log(file)
const uploadUrl = getMetadata(login.metadata.UploadUrl)
const data = new FormData()
data.append('file', file)
- const url = `${uploadUrl as string}?space=${space}&name=${encodeURIComponent(file.name)}&attachedTo=${attachedTo}`
+ const params = [['space', space], ['attachedTo', attachedTo]]
+ .filter((x): x is [string, Ref] => x[1] !== undefined)
+ .map(([name, value]) => `${name}=${value}`)
+ .join('&')
+
+ const url = `${uploadUrl as string}?name=${encodeURIComponent(file.name)}&${params}`
const resp = await fetch(url, {
method: 'POST',
headers: {
diff --git a/plugins/attachment/src/index.ts b/plugins/attachment/src/index.ts
index 3e41f1953d..677790c096 100644
--- a/plugins/attachment/src/index.ts
+++ b/plugins/attachment/src/index.ts
@@ -14,8 +14,8 @@
// limitations under the License.
//
-import type { Ref, Class, AttachedDoc } from '@anticrm/core'
-import { plugin } from '@anticrm/platform'
+import type { Ref, Class, AttachedDoc, Space, Doc } from '@anticrm/core'
+import { plugin, Resource } from '@anticrm/platform'
import type { Asset, Plugin } from '@anticrm/platform'
import { AnyComponent } from '@anticrm/ui'
@@ -44,5 +44,8 @@ export default plugin(attachmentId, {
},
class: {
Attachment: '' as Ref>
+ },
+ helper: {
+ UploadFile: '' as Resource<(file: File, space?: Ref, attachedTo?: Ref) => Promise>
}
})
diff --git a/plugins/contact-resources/package.json b/plugins/contact-resources/package.json
index c2c426aeaf..963e6a49ba 100644
--- a/plugins/contact-resources/package.json
+++ b/plugins/contact-resources/package.json
@@ -41,6 +41,7 @@
"@anticrm/view": "~0.6.0",
"@anticrm/attachment-resources": "~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"
}
}
diff --git a/plugins/contact-resources/src/components/CreatePerson.svelte b/plugins/contact-resources/src/components/CreatePerson.svelte
index 7096f5c715..adcc808b08 100644
--- a/plugins/contact-resources/src/components/CreatePerson.svelte
+++ b/plugins/contact-resources/src/components/CreatePerson.svelte
@@ -16,9 +16,11 @@
{#if object !== undefined}
diff --git a/plugins/image-cropper-resources/.eslintrc.js b/plugins/image-cropper-resources/.eslintrc.js
new file mode 100644
index 0000000000..a327214967
--- /dev/null
+++ b/plugins/image-cropper-resources/.eslintrc.js
@@ -0,0 +1,7 @@
+module.exports = {
+ extends: ['./node_modules/@anticrm/platform-rig/profiles/ui/config/eslint.config.json'],
+ parserOptions: { tsconfigRootDir: __dirname },
+ settings: {
+ 'svelte3/ignore-styles': () => true
+ }
+}
diff --git a/plugins/image-cropper-resources/package.json b/plugins/image-cropper-resources/package.json
new file mode 100644
index 0000000000..622e8d46ca
--- /dev/null
+++ b/plugins/image-cropper-resources/package.json
@@ -0,0 +1,38 @@
+{
+ "name": "@anticrm/image-cropper-resources",
+ "version": "0.6.0",
+ "main": "src/index.ts",
+ "author": "Anticrm Platform Contributors",
+ "license": "EPL-2.0",
+ "scripts": {
+ "build": "echo 'no build for ui'",
+ "build:docs": "api-extractor run --local",
+ "lint": "svelte-check && eslint",
+ "lint:fix": "eslint --fix src",
+ "format": "prettier --write --plugin-search-dir=. src && eslint --fix src"
+ },
+ "devDependencies": {
+ "@anticrm/platform-rig": "~0.6.0",
+ "svelte-loader": "^3.1.2",
+ "sass": "^1.37.5",
+ "svelte-preprocess": "^4.7.4",
+ "@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",
+ "eslint-plugin-svelte3": "~3.2.1",
+ "prettier-plugin-svelte": "^2.2.0",
+ "prettier": "^2.4.1",
+ "svelte-check": "^2.2.10",
+ "typescript": "^4.3.5"
+ },
+ "dependencies": {
+ "svelte": "^3.37.0",
+ "@anticrm/platform": "~0.6.5",
+ "cropperjs": "~1.5.12",
+ "smartcrop": "~2.0.5"
+ }
+}
diff --git a/plugins/image-cropper-resources/postcss.config.js b/plugins/image-cropper-resources/postcss.config.js
new file mode 100644
index 0000000000..88752c6cb0
--- /dev/null
+++ b/plugins/image-cropper-resources/postcss.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+ plugins: [
+ require('autoprefixer')
+ ]
+}
diff --git a/plugins/image-cropper-resources/src/components/Cropper.svelte b/plugins/image-cropper-resources/src/components/Cropper.svelte
new file mode 100644
index 0000000000..2593fd3964
--- /dev/null
+++ b/plugins/image-cropper-resources/src/components/Cropper.svelte
@@ -0,0 +1,102 @@
+
+
+
+
+
+ {#await init()}
+ Waiting...
+ {/await}
+
+
+
\ No newline at end of file
diff --git a/plugins/image-cropper-resources/src/index.ts b/plugins/image-cropper-resources/src/index.ts
new file mode 100644
index 0000000000..e995c43b7a
--- /dev/null
+++ b/plugins/image-cropper-resources/src/index.ts
@@ -0,0 +1,25 @@
+//
+// 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 { Resources } from '@anticrm/platform'
+
+import Cropper from './components/Cropper.svelte'
+
+export default async (): Promise
=> ({
+ component: {
+ Cropper
+ }
+})
diff --git a/plugins/image-cropper-resources/svelte.config.js b/plugins/image-cropper-resources/svelte.config.js
new file mode 100644
index 0000000000..944a06f73e
--- /dev/null
+++ b/plugins/image-cropper-resources/svelte.config.js
@@ -0,0 +1,5 @@
+const sveltePreprocess = require('svelte-preprocess')
+
+module.exports = {
+ preprocess: sveltePreprocess()
+};
\ No newline at end of file
diff --git a/plugins/image-cropper-resources/tsconfig.json b/plugins/image-cropper-resources/tsconfig.json
new file mode 100644
index 0000000000..cabe5aefad
--- /dev/null
+++ b/plugins/image-cropper-resources/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "moduleResolution": "node",
+ "target": "esnext",
+ "module": "esnext",
+ "declaration": true,
+ "outDir": "./lib",
+ "strict": true,
+ "esModuleInterop": true,
+ "lib": [
+ "esnext",
+ "dom"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/plugins/image-cropper/.eslintrc.js b/plugins/image-cropper/.eslintrc.js
new file mode 100644
index 0000000000..5da5872d4a
--- /dev/null
+++ b/plugins/image-cropper/.eslintrc.js
@@ -0,0 +1,7 @@
+module.exports = {
+ extends: ['./node_modules/@anticrm/platform-rig/profiles/default/config/eslint.config.json'],
+ parserOptions: {
+ tsconfigRootDir: __dirname,
+ project: './tsconfig.json'
+ }
+}
diff --git a/plugins/image-cropper/.npmignore b/plugins/image-cropper/.npmignore
new file mode 100644
index 0000000000..e3ec093c38
--- /dev/null
+++ b/plugins/image-cropper/.npmignore
@@ -0,0 +1,4 @@
+*
+!/lib/**
+!CHANGELOG.md
+/lib/**/__tests__/
diff --git a/plugins/image-cropper/config/rig.json b/plugins/image-cropper/config/rig.json
new file mode 100644
index 0000000000..af1257a896
--- /dev/null
+++ b/plugins/image-cropper/config/rig.json
@@ -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"
+}
diff --git a/plugins/image-cropper/package.json b/plugins/image-cropper/package.json
new file mode 100644
index 0000000000..bc4b6c55b2
--- /dev/null
+++ b/plugins/image-cropper/package.json
@@ -0,0 +1,32 @@
+{
+ "name": "@anticrm/image-cropper",
+ "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/ui": "~0.6.0"
+ }
+}
diff --git a/plugins/image-cropper/src/index.ts b/plugins/image-cropper/src/index.ts
new file mode 100644
index 0000000000..b70c2167ea
--- /dev/null
+++ b/plugins/image-cropper/src/index.ts
@@ -0,0 +1,29 @@
+//
+// Copyright © 2020, 2021 Anticrm Platform Contributors.
+//
+// Licensed under the Eclipse Public License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License. You may
+// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import { plugin } from '@anticrm/platform'
+import type { Plugin } from '@anticrm/platform'
+import type { AnyComponent } from '@anticrm/ui'
+
+/**
+ * @public
+ */
+export const imageCropperId = 'image-cropper' as Plugin
+
+export default plugin(imageCropperId, {
+ component: {
+ Cropper: '' as AnyComponent
+ }
+})
diff --git a/plugins/image-cropper/tsconfig.json b/plugins/image-cropper/tsconfig.json
new file mode 100644
index 0000000000..32045300ce
--- /dev/null
+++ b/plugins/image-cropper/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "./node_modules/@anticrm/platform-rig/profiles/default/tsconfig.json",
+
+ "compilerOptions": {
+ "rootDir": "./src",
+ "outDir": "./lib",
+ "lib": ["esnext", "dom"]
+ }
+}
\ No newline at end of file
diff --git a/plugins/recruit-resources/src/components/CreateCandidate.svelte b/plugins/recruit-resources/src/components/CreateCandidate.svelte
index 3f91478468..3dac469a8a 100644
--- a/plugins/recruit-resources/src/components/CreateCandidate.svelte
+++ b/plugins/recruit-resources/src/components/CreateCandidate.svelte
@@ -18,13 +18,12 @@
import contact, { combineName, Person } from '@anticrm/contact'
import type { Data, MixinData, Ref } from '@anticrm/core'
import { generateId } from '@anticrm/core'
- import { setPlatformStatus, unknownError } from '@anticrm/platform'
- import { Avatar, Card, Channels, getClient, PDFViewer } from '@anticrm/presentation'
+ import { getResource, setPlatformStatus, unknownError } from '@anticrm/platform'
+ import { EditableAvatar, Card, Channels, getClient, PDFViewer } from '@anticrm/presentation'
import type { Candidate } from '@anticrm/recruit'
import { CircleButton, EditBox, IconAdd, IconFile as FileIcon, Label, Link, showPopup, Spinner } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte'
import recruit from '../plugin'
- import { uploadFile } from '../utils'
import FileUpload from './icons/FileUpload.svelte'
import YesNo from './YesNo.svelte'
@@ -50,10 +49,15 @@
const candidateId = generateId()
async function createCandidate () {
+ const uploadFile = await getResource(attachment.helper.UploadFile)
+ const avatarUUID = avatar !== undefined
+ ? await uploadFile(avatar)
+ : undefined
const candidate: Data = {
name: combineName(firstName, lastName),
city: object.city,
- channels: object.channels
+ channels: object.channels,
+ avatar: avatarUUID
}
const candidateData: MixinData = {
title: object.title,
@@ -86,7 +90,9 @@
async function createAttachment (file: File) {
loading = true
try {
- resume.uuid = await uploadFile(space, file, candidateId)
+ const uploadFile = await getResource(attachment.helper.UploadFile)
+
+ resume.uuid = await uploadFile(file, space, candidateId)
resume.name = file.name
resume.size = file.size
resume.type = file.type
@@ -111,6 +117,15 @@
const file = inputFile.files?.[0]
if (file !== undefined) { createAttachment(file) }
}
+
+ let avatar: File | undefined
+
+ function onAvatarDone (e: any) {
+ const { file } = e.detail
+
+ avatar = file
+ }
+
@@ -124,7 +139,7 @@
diff --git a/plugins/recruit-resources/src/utils.ts b/plugins/recruit-resources/src/utils.ts
deleted file mode 100644
index 02b83892dc..0000000000
--- a/plugins/recruit-resources/src/utils.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-// Copyright © 2020, 2021 Anticrm Platform Contributors.
-// Copyright © 2021 Hardcore Engineering Inc.
-//
-// Licensed under the Eclipse Public License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License. You may
-// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-import type { Ref, Doc, Space } from '@anticrm/core'
-import { getMetadata, PlatformError } from '@anticrm/platform'
-
-import login from '@anticrm/login'
-
-export async function uploadFile(space: Ref
, file: File, attachedTo: Ref): Promise {
- console.log(file)
- const uploadUrl = getMetadata(login.metadata.UploadUrl)
-
- const data = new FormData()
- data.append('file', file)
-
- const url = `${uploadUrl}?space=${space}&name=${encodeURIComponent(file.name)}&attachedTo=${attachedTo}`
- const resp = await fetch(url, {
- method: 'POST',
- headers: {
- 'Authorization': 'Bearer ' + getMetadata(login.metadata.LoginToken)
- },
- body: data
- })
- if (resp.status !== 200) {
- throw new Error('Can\'t upload file.')
- }
- const uuid = await resp.text()
- console.log(uuid)
- return uuid
-}
diff --git a/rush.json b/rush.json
index f06c3451c5..4857fdb943 100644
--- a/rush.json
+++ b/rush.json
@@ -691,6 +691,16 @@
"projectFolder": "models/demo",
"shouldPublish": true
},
+ {
+ "packageName": "@anticrm/image-cropper",
+ "projectFolder": "plugins/image-cropper",
+ "shouldPublish": true
+ },
+ {
+ "packageName": "@anticrm/image-cropper-resources",
+ "projectFolder": "plugins/image-cropper-resources",
+ "shouldPublish": true
+ },
{
"packageName": "@anticrm/dev-server-chunter-resources",
"projectFolder": "dev/server-chunter-resources",
diff --git a/server/front/src/app.ts b/server/front/src/app.ts
index 404a039b08..4bfb4dbf35 100644
--- a/server/front/src/app.ts
+++ b/server/front/src/app.ts
@@ -159,9 +159,9 @@ export function start (config: { transactorEndpoint: string, elasticUrl: string,
const uuid = await minioUpload(config.minio, payload.workspace, file)
console.log('uploaded uuid', uuid)
- const name = req.query.name as string
- const space = req.query.space as Ref
- const attachedTo = req.query.attachedTo as Ref
+ const name = req.query.name as string | undefined
+ const space = req.query.space as Ref | undefined
+ const attachedTo = req.query.attachedTo as Ref | undefined
// const name = req.query.name as string
// await createAttachment(
@@ -175,20 +175,22 @@ export function start (config: { transactorEndpoint: string, elasticUrl: string,
// fileId
// )
- const elastic = await createElasticAdapter(config.elasticUrl, payload.workspace)
+ if (name !== undefined && space !== undefined && attachedTo !== undefined) {
+ const elastic = await createElasticAdapter(config.elasticUrl, payload.workspace)
- const indexedDoc: IndexedDoc = {
- id: generateId() + '/attachments/' + name as Ref,
- _class: attachment.class.Attachment,
- space,
- modifiedOn: Date.now(),
- modifiedBy: 'core:account:System' as Ref,
- attachedTo,
- data: file.data.toString('base64')
+ const indexedDoc: IndexedDoc = {
+ id: generateId() + '/attachments/' + name as Ref,
+ _class: attachment.class.Attachment,
+ space,
+ modifiedOn: Date.now(),
+ modifiedBy: 'core:account:System' as Ref,
+ attachedTo,
+ data: file.data.toString('base64')
+ }
+
+ await elastic.index(indexedDoc)
}
- await elastic.index(indexedDoc)
-
res.status(200).send(uuid)
} catch (error) {
console.log(error)