From 762483d7d50bcfbc4762d21e1f7f65b827614d2b Mon Sep 17 00:00:00 2001 From: Alexander Onnikov Date: Wed, 22 May 2024 22:14:24 +0700 Subject: [PATCH] UBERF-4946 Drive files preview (#5638) Signed-off-by: Alexander Onnikov --- models/attachment/src/index.ts | 57 +----- models/drive/src/index.ts | 25 ++- models/drive/src/plugin.ts | 1 + models/presentation/src/index.ts | 19 +- models/view/src/index.ts | 50 +++++ models/view/src/plugin.ts | 19 +- packages/presentation/lang/en.json | 7 +- packages/presentation/lang/es.json | 7 +- packages/presentation/lang/pt.json | 7 +- packages/presentation/lang/ru.json | 7 +- .../src/components/FilePreviewPopup.svelte | 151 ++++++++++++++ .../src/components/PDFViewer.svelte | 2 +- packages/presentation/src/file.ts | 179 +++++++++++++++++ packages/presentation/src/index.ts | 2 + packages/presentation/src/plugin.ts | 17 +- packages/presentation/src/types.ts | 17 ++ .../src/components/ImageStyleToolbar.svelte | 4 +- .../src/components/extension/fileExt.ts | 7 +- .../src/components/extension/imageExt.ts | 7 +- plugins/attachment-assets/lang/en.json | 3 - plugins/attachment-assets/lang/es.json | 3 - plugins/attachment-assets/lang/pt.json | 3 - plugins/attachment-assets/lang/ru.json | 3 - .../src/components/AttachmentActions.svelte | 53 +++-- .../AttachmentGalleryPresenter.svelte | 23 ++- .../src/components/AttachmentPopup.svelte | 5 +- .../src/components/AttachmentPresenter.svelte | 47 ++--- .../src/components/AttachmentPreview.svelte | 4 +- .../src/components/AttachmentRefInput.svelte | 13 +- .../AttachmentStyleBoxCollabEditor.svelte | 3 +- .../src/components/AttachmentStyledBox.svelte | 13 +- .../src/components/MediaViewer.svelte | 111 ---------- .../src/components/Photos.svelte | 5 +- plugins/attachment-resources/src/index.ts | 7 +- plugins/attachment-resources/src/plugin.ts | 5 +- plugins/attachment-resources/src/utils.ts | 189 +----------------- plugins/attachment/src/index.ts | 47 +---- .../src/components/EditableAvatar.svelte | 1 + plugins/drive-resources/package.json | 1 - .../src/components/FilePresenter.svelte | 33 ++- plugins/drive-resources/src/utils.ts | 14 +- plugins/drive/src/types.ts | 1 + .../src/components/CreateCandidate.svelte | 12 +- .../src/components/SubIssues.svelte | 3 +- plugins/view-resources/src/blob.ts | 58 ++++++ .../src/components/icons/Pause.svelte | 43 ++++ .../src/components/viewer/AudioPlayer.svelte | 67 +++++++ .../src/components/viewer/AudioViewer.svelte | 41 ++++ .../src/components/viewer/ImageViewer.svelte | 40 ++++ .../src/components/viewer/PDFViewer.svelte | 35 ++++ .../src/components/viewer/VideoViewer.svelte | 32 +++ plugins/view-resources/src/index.ts | 16 +- 52 files changed, 979 insertions(+), 540 deletions(-) create mode 100644 packages/presentation/src/components/FilePreviewPopup.svelte create mode 100644 packages/presentation/src/file.ts delete mode 100644 plugins/attachment-resources/src/components/MediaViewer.svelte create mode 100644 plugins/view-resources/src/blob.ts create mode 100644 plugins/view-resources/src/components/icons/Pause.svelte create mode 100644 plugins/view-resources/src/components/viewer/AudioPlayer.svelte create mode 100644 plugins/view-resources/src/components/viewer/AudioViewer.svelte create mode 100644 plugins/view-resources/src/components/viewer/ImageViewer.svelte create mode 100644 plugins/view-resources/src/components/viewer/PDFViewer.svelte create mode 100644 plugins/view-resources/src/components/viewer/VideoViewer.svelte diff --git a/models/attachment/src/index.ts b/models/attachment/src/index.ts index 88294fb223..89e3e59da8 100644 --- a/models/attachment/src/index.ts +++ b/models/attachment/src/index.ts @@ -14,14 +14,7 @@ // import activity from '@hcengineering/activity' -import type { - Attachment, - AttachmentMetadata, - Photo, - SavedAttachments, - AttachmentPreviewExtension -} from '@hcengineering/attachment' -import presentation, { TComponentPointExtension } from '@hcengineering/model-presentation' +import type { Attachment, AttachmentMetadata, Photo, SavedAttachments } from '@hcengineering/attachment' import { type Domain, IndexKind, type Ref } from '@hcengineering/core' import { type Builder, @@ -38,7 +31,6 @@ import { import core, { TAttachedDoc } from '@hcengineering/model-core' import preference, { TPreference } from '@hcengineering/model-preference' import view, { createAction } from '@hcengineering/model-view' -import { type Resource } from '@hcengineering/platform' import attachment from './plugin' @@ -86,17 +78,8 @@ export class TSavedAttachments extends TPreference implements SavedAttachments { declare attachedTo: Ref } -@Model(attachment.class.AttachmentPreviewExtension, presentation.class.ComponentPointExtension) -export class TAttachmentPreviewExtension extends TComponentPointExtension implements AttachmentPreviewExtension { - @Prop(TypeString(), attachment.string.ContentType) - contentType!: string | string[] - - alignment?: string - availabilityChecker?: Resource<() => Promise> -} - export function createModel (builder: Builder): void { - builder.createModel(TAttachment, TPhoto, TSavedAttachments, TAttachmentPreviewExtension) + builder.createModel(TAttachment, TPhoto, TSavedAttachments) builder.mixin(attachment.class.Attachment, core.class.Class, view.mixin.ObjectPresenter, { presenter: attachment.component.AttachmentPresenter @@ -181,42 +164,6 @@ export function createModel (builder: Builder): void { attachment.category.Attachments ) - builder.createDoc( - attachment.class.AttachmentPreviewExtension, - core.space.Model, - { - contentType: 'image/*', - alignment: 'centered', - component: attachment.component.PDFViewer, - extension: attachment.extension.AttachmentPreview - }, - attachment.previewExtension.Image - ) - - builder.createDoc( - attachment.class.AttachmentPreviewExtension, - core.space.Model, - { - contentType: ['video/*', 'audio/*'], - alignment: 'centered', - component: attachment.component.MediaViewer, - extension: attachment.extension.AttachmentPreview - }, - attachment.previewExtension.Media - ) - - builder.createDoc( - attachment.class.AttachmentPreviewExtension, - core.space.Model, - { - contentType: ['application/pdf', 'application/json', 'text/*'], - alignment: 'float', - component: attachment.component.PDFViewer, - extension: attachment.extension.AttachmentPreview - }, - attachment.previewExtension.PDF - ) - createAction(builder, { action: view.actionImpl.ShowEditor, actionProps: { diff --git a/models/drive/src/index.ts b/models/drive/src/index.ts index ec470a8314..5781269186 100644 --- a/models/drive/src/index.ts +++ b/models/drive/src/index.ts @@ -14,19 +14,31 @@ // import core, { + type Blob, type Domain, type Role, type RolesAssignment, type Type, Account, AccountRole, - Ref, IndexKind, - DOMAIN_MODEL, - type Blob + Ref, + DOMAIN_MODEL } from '@hcengineering/core' import { type Drive, type File, type Folder, type Resource, driveId } from '@hcengineering/drive' -import { type Builder, Model, UX, Mixin, Prop, TypeString, Index, TypeRef, ReadOnly } from '@hcengineering/model' +import { + type Builder, + Hidden, + Index, + Mixin, + Model, + Prop, + ReadOnly, + TypeRecord, + TypeRef, + TypeString, + UX +} from '@hcengineering/model' import { TDoc, TType, TTypedSpace } from '@hcengineering/model-core' import tracker from '@hcengineering/model-tracker' import view, { type Viewlet, classPresenter, createAction } from '@hcengineering/model-view' @@ -103,6 +115,11 @@ export class TFile extends TResource implements File { @ReadOnly() declare file: Ref + @Prop(TypeRecord(), drive.string.Metadata) + @ReadOnly() + @Hidden() + metadata?: Record + @Prop(TypeRef(drive.class.Folder), drive.string.Parent) @Index(IndexKind.Indexed) @ReadOnly() diff --git a/models/drive/src/plugin.ts b/models/drive/src/plugin.ts index 4d85ad9fbf..3ce59411c2 100644 --- a/models/drive/src/plugin.ts +++ b/models/drive/src/plugin.ts @@ -72,6 +72,7 @@ export default mergeIds(driveId, drive, { Description: '' as IntlString, Size: '' as IntlString, Type: '' as IntlString, + Metadata: '' as IntlString, LastModified: '' as IntlString, Parent: '' as IntlString, Path: '' as IntlString, diff --git a/models/presentation/src/index.ts b/models/presentation/src/index.ts index 5c37ac1ef2..9b900f2461 100644 --- a/models/presentation/src/index.ts +++ b/models/presentation/src/index.ts @@ -13,8 +13,8 @@ // limitations under the License. // -import { type Class, DOMAIN_MODEL, type Doc, type Ref } from '@hcengineering/core' -import { type Builder, Model, Prop, TypeRef } from '@hcengineering/model' +import { type Blob, type Class, type Doc, type Ref, DOMAIN_MODEL } from '@hcengineering/core' +import { type Builder, Model, Prop, TypeRef, TypeString } from '@hcengineering/model' import core, { TDoc } from '@hcengineering/model-core' import { type Asset, type IntlString, type Resource } from '@hcengineering/platform' // Import types to prevent .svelte components to being exposed to type typescript. @@ -23,12 +23,14 @@ import { type PresentationMiddlewareFactory } from '@hcengineering/presentation/src/pipeline' import { + type BlobMetadata, type ComponentPointExtension, type CreateExtensionKind, type DocAttributeRule, type DocRules, type DocCreateExtension, type DocCreateFunction, + type FilePreviewExtension, type ObjectSearchContext, type ObjectSearchCategory, type ObjectSearchFactory @@ -82,12 +84,23 @@ export class TDocRules extends TDoc implements DocRules { fieldRules!: DocAttributeRule[] } +@Model(presentation.class.FilePreviewExtension, presentation.class.ComponentPointExtension) +export class TFilePreviewExtension extends TComponentPointExtension implements FilePreviewExtension { + @Prop(TypeString(), presentation.string.ContentType) + contentType!: string | string[] + + alignment?: string + metadataProvider?: Resource<(file: File, blob: Ref) => Promise> + availabilityChecker?: Resource<() => Promise> +} + export function createModel (builder: Builder): void { builder.createModel( TObjectSearchCategory, TPresentationMiddlewareFactory, TComponentPointExtension, TDocCreateExtension, - TDocRules + TDocRules, + TFilePreviewExtension ) } diff --git a/models/view/src/index.ts b/models/view/src/index.ts index d2720fdc5e..3852b2d3b6 100644 --- a/models/view/src/index.ts +++ b/models/view/src/index.ts @@ -598,6 +598,56 @@ export function createModel (builder: Builder): void { view.pipeline.AnalyticsMiddleware ) + builder.createDoc( + presentation.class.FilePreviewExtension, + core.space.Model, + { + contentType: ['audio/*'], + alignment: 'centered', + component: view.component.AudioViewer, + extension: presentation.extension.FilePreviewExtension + }, + view.extension.Audio + ) + + builder.createDoc( + presentation.class.FilePreviewExtension, + core.space.Model, + { + contentType: 'image/*', + alignment: 'centered', + component: view.component.ImageViewer, + metadataProvider: view.function.BlobImageMetadata, + extension: presentation.extension.FilePreviewExtension + }, + view.extension.Image + ) + + builder.createDoc( + presentation.class.FilePreviewExtension, + core.space.Model, + { + contentType: ['video/*'], + alignment: 'centered', + component: view.component.VideoViewer, + metadataProvider: view.function.BlobVideoMetadata, + extension: presentation.extension.FilePreviewExtension + }, + view.extension.Video + ) + + builder.createDoc( + presentation.class.FilePreviewExtension, + core.space.Model, + { + contentType: ['application/pdf', 'application/json', 'text/*'], + alignment: 'float', + component: view.component.PDFViewer, + extension: presentation.extension.FilePreviewExtension + }, + view.extension.PDF + ) + createAction( builder, { diff --git a/models/view/src/plugin.ts b/models/view/src/plugin.ts index 70ec3889c6..a26003dda8 100644 --- a/models/view/src/plugin.ts +++ b/models/view/src/plugin.ts @@ -13,10 +13,11 @@ // limitations under the License. // -import { type Doc, type Ref } from '@hcengineering/core' +import { type Blob, type Doc, type Ref } from '@hcengineering/core' import { type IntlString, mergeIds, type Resource } from '@hcengineering/platform' import { type AnyComponent } from '@hcengineering/ui/src/types' import { type FilterFunction, type ViewAction, type ViewCategoryAction, viewId } from '@hcengineering/view' +import { type BlobMetadata, type FilePreviewExtension } from '@hcengineering/presentation' import { type PresentationMiddlewareFactory } from '@hcengineering/presentation/src/pipeline' import view from '@hcengineering/view-resources/src/plugin' @@ -82,7 +83,11 @@ export default mergeIds(viewId, view, { StatusPresenter: '' as AnyComponent, StatusRefPresenter: '' as AnyComponent, DateFilterPresenter: '' as AnyComponent, - StringFilterPresenter: '' as AnyComponent + StringFilterPresenter: '' as AnyComponent, + AudioViewer: '' as AnyComponent, + ImageViewer: '' as AnyComponent, + VideoViewer: '' as AnyComponent, + PDFViewer: '' as AnyComponent }, string: { Table: '' as IntlString, @@ -132,10 +137,18 @@ export default mergeIds(viewId, view, { CanArchiveSpace: '' as Resource<(doc?: Doc | Doc[]) => Promise>, CanDeleteSpace: '' as Resource<(doc?: Doc | Doc[]) => Promise>, CanJoinSpace: '' as Resource<(doc?: Doc | Doc[]) => Promise>, - CanLeaveSpace: '' as Resource<(doc?: Doc | Doc[]) => Promise> + CanLeaveSpace: '' as Resource<(doc?: Doc | Doc[]) => Promise>, + BlobImageMetadata: '' as Resource<(file: File, blob: Ref) => Promise>, + BlobVideoMetadata: '' as Resource<(file: File, blob: Ref) => Promise> }, pipeline: { PresentationMiddleware: '' as Ref, AnalyticsMiddleware: '' as Ref + }, + extension: { + Audio: '' as Ref, + Image: '' as Ref, + Video: '' as Ref, + PDF: '' as Ref } }) diff --git a/packages/presentation/lang/en.json b/packages/presentation/lang/en.json index 4b4296b6f4..0ab47a9b24 100644 --- a/packages/presentation/lang/en.json +++ b/packages/presentation/lang/en.json @@ -31,6 +31,11 @@ "Created": "Created", "NoResults": "No results to show", "Next": "Next", - "FailedToPreview": "Failed to preview" + "FailedToPreview": "Failed to preview", + "ContentType": "Content type", + "ContentTypeNotSupported": "Content type not supported" + }, + "status": { + "FileTooLarge": "File too large" } } diff --git a/packages/presentation/lang/es.json b/packages/presentation/lang/es.json index 1f0d61ceb7..b708e6949b 100644 --- a/packages/presentation/lang/es.json +++ b/packages/presentation/lang/es.json @@ -31,6 +31,11 @@ "Created": "Creado", "NoResults": "No hay resultados para mostrar", "Next": "Siguiente", - "FailedToPreview": "Error al previsualizar" + "FailedToPreview": "Error al previsualizar", + "ContentType": "Tipo de contenido", + "ContentTypeNotSupported": "Tipo de contenido no admitido" + }, + "status": { + "FileTooLarge": "Archivo demasiado grande" } } \ No newline at end of file diff --git a/packages/presentation/lang/pt.json b/packages/presentation/lang/pt.json index 594ec66c3a..f647ad35f5 100644 --- a/packages/presentation/lang/pt.json +++ b/packages/presentation/lang/pt.json @@ -31,6 +31,11 @@ "Created": "Criado", "NoResults": "Sem resultados para mostrar", "Next": "Seguinte", - "FailedToPreview": "Falha ao pré-visualizar" + "FailedToPreview": "Falha ao pré-visualizar", + "ContentType": "Tipo de conteúdo", + "ContentTypeNotSupported": "Tipo de conteúdo não suportado" + }, + "status": { + "FileTooLarge": "Ficheiro demasiado grande" } } \ No newline at end of file diff --git a/packages/presentation/lang/ru.json b/packages/presentation/lang/ru.json index 7737ffe33e..f4cbc3e7d2 100644 --- a/packages/presentation/lang/ru.json +++ b/packages/presentation/lang/ru.json @@ -31,6 +31,11 @@ "Created": "Созданные", "NoResults": "Нет результатов", "Next": "Далее", - "FailedToPreview": "Ошибка предпросмотра" + "FailedToPreview": "Ошибка предпросмотра", + "ContentType": "Тип контента", + "ContentTypeNotSupported": "Тип контента не поддерживается" + }, + "status": { + "FileTooLarge": "Файл слишком большой" } } diff --git a/packages/presentation/src/components/FilePreviewPopup.svelte b/packages/presentation/src/components/FilePreviewPopup.svelte new file mode 100644 index 0000000000..e6e6e2b82f --- /dev/null +++ b/packages/presentation/src/components/FilePreviewPopup.svelte @@ -0,0 +1,151 @@ + + + + + { + dispatch('close') + }} +> + +
+ {#if showIcon} +
+
+ {iconLabel(name)} +
+
+ {/if} + {name} +
+
+ + + {#if src !== ''} + +
+ + diff --git a/packages/presentation/src/components/PDFViewer.svelte b/packages/presentation/src/components/PDFViewer.svelte index 6cb20468e2..9fc83270e7 100644 --- a/packages/presentation/src/components/PDFViewer.svelte +++ b/packages/presentation/src/components/PDFViewer.svelte @@ -1,5 +1,5 @@ - - - { - dispatch('close') - }} -> - -
- {#if showIcon} -
-
- {iconLabel(value.name)} -
-
- {/if} - {value.name} -
-
- - - -