From c236b90b6f7a50c3d5dda72b2b280b3f23d66100 Mon Sep 17 00:00:00 2001 From: Alexander Onnikov Date: Fri, 13 Sep 2024 15:39:11 +0700 Subject: [PATCH 01/12] UBERF-8079 Disable slash menu in codeblock (#6548) Signed-off-by: Alexander Onnikov --- .../src/components/extension/inlineCommands.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/text-editor-resources/src/components/extension/inlineCommands.ts b/plugins/text-editor-resources/src/components/extension/inlineCommands.ts index 8e02544e7a..0ff6cd7692 100644 --- a/plugins/text-editor-resources/src/components/extension/inlineCommands.ts +++ b/plugins/text-editor-resources/src/components/extension/inlineCommands.ts @@ -14,8 +14,8 @@ // import { Extension } from '@tiptap/core' -import Suggestion, { type SuggestionOptions } from './suggestion' import { PluginKey } from '@tiptap/pm/state' +import Suggestion, { type SuggestionOptions } from './suggestion' export interface InlineCommandsOptions { suggestion: Omit @@ -31,7 +31,11 @@ export const InlineCommandsExtension = Extension.create({ return { suggestion: { char: '/', - startOfLine: true + allow: ({ state }) => { + const { $anchor } = state.selection + const parent = $anchor.parent + return parent.type.name === 'paragraph' + } } } }, From 80dc1f5aeda20f2e2924293e5d9b5fbdb1235a0b Mon Sep 17 00:00:00 2001 From: Denis Bykhov Date: Fri, 13 Sep 2024 14:03:57 +0500 Subject: [PATCH 02/12] sql fixes (#6549) Signed-off-by: Denis Bykhov --- server/postgres/src/storage.ts | 12 +++++++++--- server/postgres/src/utils.ts | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/server/postgres/src/storage.ts b/server/postgres/src/storage.ts index 8c8a063577..5cb5eeceb1 100644 --- a/server/postgres/src/storage.ts +++ b/server/postgres/src/storage.ts @@ -753,12 +753,14 @@ abstract class PostgresAdapterBase implements DbAdapter { if (!isDataField(key)) return `"${key}"` const arr = key.split('.').filter((p) => p) let tKey = '' + let isNestedField = false for (let i = 0; i < arr.length; i++) { const element = arr[i] if (element === '$lookup') { tKey += arr[++i] + '_lookup' } else if (this.hierarchy.isMixin(element as Ref>)) { + isNestedField = true tKey += `${element}` if (i !== arr.length - 1) { tKey += "'->'" @@ -773,7 +775,7 @@ abstract class PostgresAdapterBase implements DbAdapter { tKey = this.checkMixinKey(tKey, _class, isDataArray) } - return isDataArray ? `data->'${tKey}'` : `data#>>'{${tKey}}'` + return isDataArray || isNestedField ? `data->'${tKey}'` : `data#>>'{${tKey}}'` } private checkMixinKey(key: string, _class: Ref>, isDataArray: boolean): string { @@ -1046,7 +1048,9 @@ abstract class PostgresAdapterBase implements DbAdapter { const vals = part .map((doc) => { const d = convertDoc(doc, this.workspaceId.name) - return `('${d._id}', '${d.workspaceId}', '${d._class}', '${d.createdBy ?? d.modifiedBy}', '${d.modifiedBy}', ${d.modifiedOn}, ${d.createdOn ?? d.modifiedOn}, '${d.space}', '${d.attachedTo ?? '[NULL]'}', '${escapeBackticks(JSON.stringify(d.data))}')` + return `('${d._id}', '${d.workspaceId}', '${d._class}', '${d.createdBy ?? d.modifiedBy}', '${d.modifiedBy}', ${d.modifiedOn}, ${d.createdOn ?? d.modifiedOn}, '${d.space}', ${ + d.attachedTo != null ? `'${d.attachedTo}'` : 'NULL' + }, '${escapeBackticks(JSON.stringify(d.data))}')` }) .join(', ') await client.query( @@ -1133,7 +1137,9 @@ abstract class PostgresAdapterBase implements DbAdapter { const vals = part .map((doc) => { const d = convertDoc(doc, this.workspaceId.name) - return `('${d._id}', '${d.workspaceId}', '${d._class}', '${d.createdBy ?? d.modifiedBy}', '${d.modifiedBy}', ${d.modifiedOn}, ${d.createdOn ?? d.modifiedOn}, '${d.space}', '${d.attachedTo ?? '[NULL]'}', '${escapeBackticks(JSON.stringify(d.data))}')` + return `('${d._id}', '${d.workspaceId}', '${d._class}', '${d.createdBy ?? d.modifiedBy}', '${d.modifiedBy}', ${d.modifiedOn}, ${d.createdOn ?? d.modifiedOn}, '${d.space}', ${ + d.attachedTo != null ? `'${d.attachedTo}'` : 'NULL' + }, '${escapeBackticks(JSON.stringify(d.data))}')` }) .join(', ') await client.query( diff --git a/server/postgres/src/utils.ts b/server/postgres/src/utils.ts index 904bfc344c..58091da343 100644 --- a/server/postgres/src/utils.ts +++ b/server/postgres/src/utils.ts @@ -290,7 +290,7 @@ export function translateDomain (domain: string): string { export function parseDocWithProjection (doc: DBDoc, projection: Projection | undefined): T { const { workspaceId, data, ...rest } = doc for (const key in rest) { - if ((rest as any)[key] === 'NULL') { + if ((rest as any)[key] === 'NULL' || (rest as any)[key] === null) { if (key === 'attachedTo') { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete rest[key] @@ -321,7 +321,7 @@ export function parseDocWithProjection (doc: DBDoc, projection: P export function parseDoc (doc: DBDoc): T { const { workspaceId, data, ...rest } = doc for (const key in rest) { - if ((rest as any)[key] === 'NULL') { + if ((rest as any)[key] === 'NULL' || (rest as any)[key] === null) { if (key === 'attachedTo') { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete rest[key] From 4eee28641f590fb9794ca98ae3ab77e9a94bf949 Mon Sep 17 00:00:00 2001 From: Alexander Onnikov Date: Fri, 13 Sep 2024 16:22:21 +0700 Subject: [PATCH 03/12] feat: hls video support (#6542) Signed-off-by: Alexander Onnikov --- common/config/rush/pnpm-lock.yaml | 15 ++++- packages/presentation/src/preview.ts | 63 +++++++++++++++++-- plugins/view-resources/package.json | 3 +- .../src/components/viewer/VideoViewer.svelte | 36 +++++++++-- server/front/readme.md | 11 ++-- 5 files changed, 106 insertions(+), 22 deletions(-) diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 6367e53b2d..36367f9c66 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -1586,6 +1586,9 @@ dependencies: highlight.js: specifier: ~11.8.0 version: 11.8.0 + hls.js: + specifier: ^1.5.15 + version: 1.5.15 html-to-text: specifier: ^9.0.3 version: 9.0.5 @@ -15598,6 +15601,10 @@ packages: requiresBuild: true dev: false + /hls.js@1.5.15: + resolution: {integrity: sha512-6cD7xN6bycBHaXz2WyPIaHn/iXFizE5au2yvY5q9aC4wfihxAr16C9fUy4nxh2a3wOw0fEgLRa9dN6wsYjlpNg==} + dev: false + /hogan.js@3.0.2: resolution: {integrity: sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg==} hasBin: true @@ -27291,7 +27298,7 @@ packages: dev: false file:projects/lead-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2): - resolution: {integrity: sha512-TJVh5S1o+GvRWeeNWwXveGOpMtsQTR0n5RMGjK3kXsuAUYCQRPzgMh3noJxe2n4vvijK8IUNjAR9+AjeSPo5kw==, tarball: file:projects/lead-resources.tgz} + resolution: {integrity: sha512-xg8Fq55+BYSO+pwIkFTJFDJGPu1CWGB8CiZ64+J2jqzbAHkRaiOCP0u3R4lOw/z6k1tnqhL0m2bvV9pCUCYTHA==, tarball: file:projects/lead-resources.tgz} id: file:projects/lead-resources.tgz name: '@rush-temp/lead-resources' version: 0.0.0 @@ -30482,6 +30489,7 @@ packages: eslint-plugin-promise: 6.1.1(eslint@8.56.0) eslint-plugin-svelte: 2.35.1(eslint@8.56.0)(svelte@4.2.12)(ts-node@10.9.2) fast-equals: 5.0.1 + hls.js: 1.5.15 jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) png-chunks-extract: 1.0.0 prettier: 3.2.5 @@ -34795,7 +34803,7 @@ packages: dev: false file:projects/tool.tgz(bufferutil@4.0.8)(utf-8-validate@6.0.4): - resolution: {integrity: sha512-LwQbmBaSOZ5IKwCHz2mULcIuEr9rZ2b/7tqUGICHCawUzexUlQVxv2Yt0oFf2aZu83Sittt7dZwnN3sXHX9t9g==, tarball: file:projects/tool.tgz} + resolution: {integrity: sha512-sZH5yB7zg/kTpuIhLSqPYh0wFgw4aOpsriMq4wad8ZHRzlHASseyJAbEylIP8ltfPbFFN4Yy1nXaUOXS49anHg==, tarball: file:projects/tool.tgz} id: file:projects/tool.tgz name: '@rush-temp/tool' version: 0.0.0 @@ -35311,7 +35319,7 @@ packages: dev: false file:projects/view-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2): - resolution: {integrity: sha512-l/K7osn3HZ3KIFeCyBe+rxQGUxvLvM+35if2HgylqgbWtD10Gk/rR+vuW1L54o8hT4ADMkbhvBW7VHE19isd+w==, tarball: file:projects/view-resources.tgz} + resolution: {integrity: sha512-g6op8hiY1zLsms7Sab4cAs29Ucbk6r20mx9hkZrhxn70uPW/VCLS+JW67cfWf85SyMwMloWuvY6ujfQfwNuScw==, tarball: file:projects/view-resources.tgz} id: file:projects/view-resources.tgz name: '@rush-temp/view-resources' version: 0.0.0 @@ -35326,6 +35334,7 @@ packages: eslint-plugin-promise: 6.1.1(eslint@8.56.0) eslint-plugin-svelte: 2.35.1(eslint@8.56.0)(svelte@4.2.12)(ts-node@10.9.2) fast-equals: 5.0.1 + hls.js: 1.5.15 jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) prettier: 3.2.5 prettier-plugin-svelte: 3.2.2(prettier@3.2.5)(svelte@4.2.12) diff --git a/packages/presentation/src/preview.ts b/packages/presentation/src/preview.ts index 1b5e2e5a1a..814d89b284 100644 --- a/packages/presentation/src/preview.ts +++ b/packages/presentation/src/preview.ts @@ -6,27 +6,56 @@ import { getFileUrl, getCurrentWorkspaceId } from './file' import presentation from './plugin' export interface PreviewConfig { - previewUrl: string + image: string + video: string } -const defaultPreview = (): string => `/files/${getCurrentWorkspaceId()}?file=:blobId&size=:size` +export interface VideoMeta { + status: 'ready' | 'error' | 'inprogress' | 'queued' | 'downloading' | 'pendingupload' + thumbnail: string + hls: string +} + +const defaultImagePreview = (): string => `/files/${getCurrentWorkspaceId()}?file=:blobId&size=:size` /** * * PREVIEW_CONFIG env variable format. - * previewUrl - an Url with :workspace, :blobId, :downloadFile, :size placeholders, they will be replaced in UI with an appropriate blob values. + * - image - an Url with :workspace, :blobId, :downloadFile, :size placeholders. + * - video - an Url with :workspace, :blobId placeholders. */ export function parsePreviewConfig (config?: string): PreviewConfig | undefined { if (config === undefined) { return } - return { previewUrl: config } + + const previewConfig = { image: defaultImagePreview(), video: '' } + + const configs = config.split(';') + for (const c of configs) { + if (c.includes('|')) { + const [key, value] = c.split('|') + if (key === 'image') { + previewConfig.image = value + } else if (key === 'video') { + previewConfig.video = value + } else { + throw new Error(`Unknown preview config key: ${key}`) + } + } else { + // fallback to image-only config for compatibility + previewConfig.image = c + } + } + + return Object.freeze(previewConfig) } export function getPreviewConfig (): PreviewConfig { return ( (getMetadata(presentation.metadata.PreviewConfig) as PreviewConfig) ?? { - previewUrl: defaultPreview() + image: defaultImagePreview(), + video: '' } ) } @@ -58,7 +87,7 @@ function blobToSrcSet (cfg: PreviewConfig, blob: Ref, width: number | unde return '' } - let url = cfg.previewUrl.replaceAll(':workspace', encodeURIComponent(getCurrentWorkspaceId())) + let url = cfg.image.replaceAll(':workspace', encodeURIComponent(getCurrentWorkspaceId())) const downloadUrl = getFileUrl(blob) const frontUrl = getMetadata(presentation.metadata.FrontUrl) ?? window.location.origin @@ -89,3 +118,25 @@ function blobToSrcSet (cfg: PreviewConfig, blob: Ref, width: number | unde export function getFileSrcSet (_blob: Ref, width?: number): string { return blobToSrcSet(getPreviewConfig(), _blob, width) } + +/** + * @public + */ +export async function getVideoMeta (file: string, filename?: string): Promise { + const cfg = getPreviewConfig() + + const url = cfg.video + .replaceAll(':workspace', encodeURIComponent(getCurrentWorkspaceId())) + .replaceAll(':blobId', encodeURIComponent(file)) + + if (url === '') { + return undefined + } + + try { + const response = await fetch(url) + if (response.ok) { + return (await response.json()) as VideoMeta + } + } catch {} +} diff --git a/plugins/view-resources/package.json b/plugins/view-resources/package.json index 5bb9ff4e37..ba3f0a27c8 100644 --- a/plugins/view-resources/package.json +++ b/plugins/view-resources/package.json @@ -57,6 +57,7 @@ "@hcengineering/text-editor-resources": "^0.6.0", "@hcengineering/analytics": "^0.6.0", "@hcengineering/query": "^0.6.12", - "fast-equals": "^5.0.1" + "fast-equals": "^5.0.1", + "hls.js": "^1.5.15" } } diff --git a/plugins/view-resources/src/components/viewer/VideoViewer.svelte b/plugins/view-resources/src/components/viewer/VideoViewer.svelte index e91194496d..fbed991d3f 100644 --- a/plugins/view-resources/src/components/viewer/VideoViewer.svelte +++ b/plugins/view-resources/src/components/viewer/VideoViewer.svelte @@ -14,20 +14,46 @@ --> -