mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-28 23:27:38 +03:00
UBERF-6353: Extensible preview (#5264)
This commit is contained in:
parent
e363ca4aa4
commit
dd70ea43ff
@ -38,6 +38,7 @@
|
|||||||
"@hcengineering/model-view": "^0.6.0",
|
"@hcengineering/model-view": "^0.6.0",
|
||||||
"@hcengineering/platform": "^0.6.9",
|
"@hcengineering/platform": "^0.6.9",
|
||||||
"@hcengineering/ui": "^0.6.11",
|
"@hcengineering/ui": "^0.6.11",
|
||||||
"@hcengineering/view": "^0.6.9"
|
"@hcengineering/view": "^0.6.9",
|
||||||
|
"@hcengineering/model-presentation": "^0.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,14 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import activity from '@hcengineering/activity'
|
import activity from '@hcengineering/activity'
|
||||||
import type { Attachment, AttachmentMetadata, Photo, SavedAttachments } from '@hcengineering/attachment'
|
import type {
|
||||||
|
Attachment,
|
||||||
|
AttachmentMetadata,
|
||||||
|
Photo,
|
||||||
|
SavedAttachments,
|
||||||
|
AttachmentPreviewExtension
|
||||||
|
} from '@hcengineering/attachment'
|
||||||
|
import presentation, { TComponentPointExtension } from '@hcengineering/model-presentation'
|
||||||
import { type Domain, IndexKind, type Ref } from '@hcengineering/core'
|
import { type Domain, IndexKind, type Ref } from '@hcengineering/core'
|
||||||
import {
|
import {
|
||||||
type Builder,
|
type Builder,
|
||||||
@ -31,6 +38,7 @@ import {
|
|||||||
import core, { TAttachedDoc } from '@hcengineering/model-core'
|
import core, { TAttachedDoc } from '@hcengineering/model-core'
|
||||||
import preference, { TPreference } from '@hcengineering/model-preference'
|
import preference, { TPreference } from '@hcengineering/model-preference'
|
||||||
import view, { createAction } from '@hcengineering/model-view'
|
import view, { createAction } from '@hcengineering/model-view'
|
||||||
|
import { type Resource } from '@hcengineering/platform'
|
||||||
|
|
||||||
import attachment from './plugin'
|
import attachment from './plugin'
|
||||||
|
|
||||||
@ -78,8 +86,17 @@ export class TSavedAttachments extends TPreference implements SavedAttachments {
|
|||||||
declare attachedTo: Ref<Attachment>
|
declare attachedTo: Ref<Attachment>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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<boolean>>
|
||||||
|
}
|
||||||
|
|
||||||
export function createModel (builder: Builder): void {
|
export function createModel (builder: Builder): void {
|
||||||
builder.createModel(TAttachment, TPhoto, TSavedAttachments)
|
builder.createModel(TAttachment, TPhoto, TSavedAttachments, TAttachmentPreviewExtension)
|
||||||
|
|
||||||
builder.mixin(attachment.class.Attachment, core.class.Class, view.mixin.ObjectPresenter, {
|
builder.mixin(attachment.class.Attachment, core.class.Class, view.mixin.ObjectPresenter, {
|
||||||
presenter: attachment.component.AttachmentPresenter
|
presenter: attachment.component.AttachmentPresenter
|
||||||
@ -164,6 +181,42 @@ export function createModel (builder: Builder): void {
|
|||||||
attachment.category.Attachments
|
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: 'float',
|
||||||
|
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, {
|
createAction(builder, {
|
||||||
action: view.actionImpl.ShowEditor,
|
action: view.actionImpl.ShowEditor,
|
||||||
actionProps: {
|
actionProps: {
|
||||||
|
@ -40,7 +40,8 @@ export default mergeIds(attachmentId, attachment, {
|
|||||||
PinAttachment: '' as IntlString,
|
PinAttachment: '' as IntlString,
|
||||||
UnPinAttachment: '' as IntlString,
|
UnPinAttachment: '' as IntlString,
|
||||||
FilterAttachments: '' as IntlString,
|
FilterAttachments: '' as IntlString,
|
||||||
RemovedAttachment: '' as IntlString
|
RemovedAttachment: '' as IntlString,
|
||||||
|
ContentType: '' as IntlString
|
||||||
},
|
},
|
||||||
ids: {
|
ids: {
|
||||||
TxAttachmentCreate: '' as Ref<TxViewlet>,
|
TxAttachmentCreate: '' as Ref<TxViewlet>,
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
export let showIcon = true
|
export let showIcon = true
|
||||||
export let fullSize = false
|
export let fullSize = false
|
||||||
export let isLoading = false
|
export let isLoading = false
|
||||||
|
export let css: string | undefined = undefined
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
@ -45,6 +46,19 @@
|
|||||||
let download: HTMLAnchorElement
|
let download: HTMLAnchorElement
|
||||||
$: src = file === undefined ? '' : getFileUrl(file, 'full', name)
|
$: src = file === undefined ? '' : getFileUrl(file, 'full', name)
|
||||||
$: isImage = contentType !== undefined && contentType.startsWith('image/')
|
$: isImage = contentType !== undefined && contentType.startsWith('image/')
|
||||||
|
|
||||||
|
let frame: HTMLIFrameElement | undefined = undefined
|
||||||
|
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
|
||||||
|
$: if (css !== undefined && frame !== undefined && frame !== null) {
|
||||||
|
frame.onload = () => {
|
||||||
|
const head = frame?.contentDocument?.querySelector('head')
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
|
||||||
|
if (css !== undefined && head !== undefined && head !== null) {
|
||||||
|
head.appendChild(document.createElement('style')).textContent = css
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ActionContext context={{ mode: 'browser' }} />
|
<ActionContext context={{ mode: 'browser' }} />
|
||||||
@ -69,7 +83,7 @@
|
|||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
|
||||||
<svelte:fragment slot="utils">
|
<svelte:fragment slot="utils">
|
||||||
{#if !isLoading && isImage && src !== ''}
|
{#if !isLoading && src !== ''}
|
||||||
<a class="no-line" href={src} download={name} bind:this={download}>
|
<a class="no-line" href={src} download={name} bind:this={download}>
|
||||||
<Button
|
<Button
|
||||||
icon={Download}
|
icon={Download}
|
||||||
@ -93,7 +107,7 @@
|
|||||||
<img class="img-fit" {src} alt="" />
|
<img class="img-fit" {src} alt="" />
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<iframe class="pdfviewer-content" src={src + '#view=FitH&navpanes=0'} title="" />
|
<iframe bind:this={frame} class="pdfviewer-content" src={src + '#view=FitH&navpanes=0'} title="" />
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<div class="centered">
|
<div class="centered">
|
||||||
@ -119,9 +133,7 @@
|
|||||||
.pdfviewer-content {
|
.pdfviewer-content {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
border-style: none;
|
border: none;
|
||||||
border-radius: 0.5rem;
|
|
||||||
background-color: var(--theme-bg-color);
|
|
||||||
|
|
||||||
&.img {
|
&.img {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -51,7 +51,8 @@
|
|||||||
"PinAttachment": "Mark important",
|
"PinAttachment": "Mark important",
|
||||||
"UnPinAttachment": "Mark less important",
|
"UnPinAttachment": "Mark less important",
|
||||||
"FilterAttachments": "Attachments",
|
"FilterAttachments": "Attachments",
|
||||||
"RemovedAttachment": "Removed attachment"
|
"RemovedAttachment": "Removed attachment",
|
||||||
|
"ContentType": "Content type"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"FileTooLarge": "File too large"
|
"FileTooLarge": "File too large"
|
||||||
|
@ -51,7 +51,8 @@
|
|||||||
"PinAttachment": "Marcar como importante",
|
"PinAttachment": "Marcar como importante",
|
||||||
"UnPinAttachment": "Marcar como menos importante",
|
"UnPinAttachment": "Marcar como menos importante",
|
||||||
"FilterAttachments": "Adjuntos",
|
"FilterAttachments": "Adjuntos",
|
||||||
"RemovedAttachment": "Adjunto eliminado"
|
"RemovedAttachment": "Adjunto eliminado",
|
||||||
|
"ContentType": "Tipo de contenido"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"FileTooLarge": "Archivo demasiado grande"
|
"FileTooLarge": "Archivo demasiado grande"
|
||||||
|
@ -51,7 +51,8 @@
|
|||||||
"PinAttachment": "Marcar como importante",
|
"PinAttachment": "Marcar como importante",
|
||||||
"UnPinAttachment": "Marcar como menos importante",
|
"UnPinAttachment": "Marcar como menos importante",
|
||||||
"FilterAttachments": "Anexos",
|
"FilterAttachments": "Anexos",
|
||||||
"RemovedAttachment": "Anexo removido"
|
"RemovedAttachment": "Anexo removido",
|
||||||
|
"ContentType": "Tipo de conteúdo"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"FileTooLarge": "Ficheiro demasiado grande"
|
"FileTooLarge": "Ficheiro demasiado grande"
|
||||||
|
@ -51,7 +51,8 @@
|
|||||||
"PinAttachment": "Пометить как важное",
|
"PinAttachment": "Пометить как важное",
|
||||||
"UnPinAttachment": "Убрать пометку важное",
|
"UnPinAttachment": "Убрать пометку важное",
|
||||||
"FilterAttachments": "Вложения",
|
"FilterAttachments": "Вложения",
|
||||||
"RemovedAttachment": "Удалил(а) вложение"
|
"RemovedAttachment": "Удалил(а) вложение",
|
||||||
|
"ContentType": "Тип контента"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"FileTooLarge": "Файл слишком большой"
|
"FileTooLarge": "Файл слишком большой"
|
||||||
|
@ -13,13 +13,22 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Attachment } from '@hcengineering/attachment'
|
import { AttachmentPreviewExtension, type Attachment } from '@hcengineering/attachment'
|
||||||
import { getResource } from '@hcengineering/platform'
|
import { getResource } from '@hcengineering/platform'
|
||||||
import { PDFViewer, getFileUrl } from '@hcengineering/presentation'
|
import { getFileUrl } from '@hcengineering/presentation'
|
||||||
import { Action as UIAction, ActionIcon, IconMoreH, IconOpen, Menu, closeTooltip, showPopup } from '@hcengineering/ui'
|
import {
|
||||||
|
Action as UIAction,
|
||||||
|
ActionIcon,
|
||||||
|
IconMoreH,
|
||||||
|
IconOpen,
|
||||||
|
Menu,
|
||||||
|
closeTooltip,
|
||||||
|
showPopup,
|
||||||
|
PopupAlignment
|
||||||
|
} from '@hcengineering/ui'
|
||||||
import view, { Action } from '@hcengineering/view'
|
import view, { Action } from '@hcengineering/view'
|
||||||
import MediaViewer from './MediaViewer.svelte'
|
|
||||||
|
|
||||||
|
import { previewTypes, getPreviewType, isOpenable } from '../utils'
|
||||||
import attachmentPlugin from '../plugin'
|
import attachmentPlugin from '../plugin'
|
||||||
import FileDownload from './icons/FileDownload.svelte'
|
import FileDownload from './icons/FileDownload.svelte'
|
||||||
|
|
||||||
@ -30,10 +39,27 @@
|
|||||||
let download: HTMLAnchorElement
|
let download: HTMLAnchorElement
|
||||||
|
|
||||||
$: contentType = attachment?.type ?? ''
|
$: contentType = attachment?.type ?? ''
|
||||||
$: openable =
|
let openable = false
|
||||||
contentType.includes('application/pdf') || contentType.startsWith('image/') || contentType.startsWith('video/')
|
$: {
|
||||||
|
void isOpenable(contentType, $previewTypes).then((res) => {
|
||||||
|
openable = res
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let previewType: AttachmentPreviewExtension | undefined = undefined
|
||||||
|
$: if (openable) {
|
||||||
|
void getPreviewType(contentType, $previewTypes).then((res) => {
|
||||||
|
previewType = res
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
previewType = undefined
|
||||||
|
}
|
||||||
|
|
||||||
function showPreview (e: MouseEvent): void {
|
function showPreview (e: MouseEvent): void {
|
||||||
|
if (!openable || previewType === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
if (e.metaKey || e.ctrlKey) {
|
if (e.metaKey || e.ctrlKey) {
|
||||||
@ -41,10 +67,11 @@
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
closeTooltip()
|
closeTooltip()
|
||||||
|
|
||||||
showPopup(
|
showPopup(
|
||||||
contentType.startsWith('video/') ? MediaViewer : PDFViewer,
|
previewType.component,
|
||||||
{ file: attachment.file, name: attachment.name, contentType, value: attachment },
|
{ ...(previewType.props ?? {}), file: attachment.file, name: attachment.name, contentType, value: attachment },
|
||||||
contentType.startsWith('image/') ? 'centered' : 'float'
|
(previewType.alignment ?? 'center') as PopupAlignment
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,14 +15,13 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import type { Attachment } from '@hcengineering/attachment'
|
import type { Attachment, AttachmentPreviewExtension } from '@hcengineering/attachment'
|
||||||
import { showPopup, closeTooltip, Label, getIconSize2x, Loading } from '@hcengineering/ui'
|
import { showPopup, closeTooltip, Label, getIconSize2x, Loading, PopupAlignment } from '@hcengineering/ui'
|
||||||
import presentation, { PDFViewer, getFileUrl } from '@hcengineering/presentation'
|
import presentation, { getFileUrl } from '@hcengineering/presentation'
|
||||||
import filesize from 'filesize'
|
import filesize from 'filesize'
|
||||||
import core from '@hcengineering/core'
|
import core from '@hcengineering/core'
|
||||||
import { permissionsStore } from '@hcengineering/view-resources'
|
import { permissionsStore } from '@hcengineering/view-resources'
|
||||||
import MediaViewer from './MediaViewer.svelte'
|
import { getType, isOpenable, previewTypes, getPreviewType } from '../utils'
|
||||||
import { getType } from '../utils'
|
|
||||||
|
|
||||||
import AttachmentName from './AttachmentName.svelte'
|
import AttachmentName from './AttachmentName.svelte'
|
||||||
|
|
||||||
@ -54,18 +53,29 @@
|
|||||||
function isImage (contentType: string): boolean {
|
function isImage (contentType: string): boolean {
|
||||||
return getType(contentType) === 'image'
|
return getType(contentType) === 'image'
|
||||||
}
|
}
|
||||||
function isPlayable (contentType: string) {
|
|
||||||
const type = getType(contentType)
|
let openable = false
|
||||||
return type === 'video' || type === 'audio'
|
$: if (value !== undefined) {
|
||||||
|
void isOpenable(value.type, $previewTypes).then((res) => {
|
||||||
|
openable = res
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
openable = false
|
||||||
}
|
}
|
||||||
function openEmbedded (contentType: string): boolean {
|
|
||||||
return getType(contentType) !== 'other'
|
let previewType: AttachmentPreviewExtension | undefined = undefined
|
||||||
|
$: if (openable && value !== undefined) {
|
||||||
|
void getPreviewType(value.type, $previewTypes).then((res) => {
|
||||||
|
previewType = res
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
previewType = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
function clickHandler (e: MouseEvent): void {
|
function clickHandler (e: MouseEvent): void {
|
||||||
if (value === undefined) return
|
if (value === undefined) return
|
||||||
|
|
||||||
if (!openEmbedded(value.type)) return
|
if (!openable || previewType === undefined) return
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
if (e.metaKey || e.ctrlKey) {
|
if (e.metaKey || e.ctrlKey) {
|
||||||
@ -74,9 +84,9 @@
|
|||||||
}
|
}
|
||||||
closeTooltip()
|
closeTooltip()
|
||||||
showPopup(
|
showPopup(
|
||||||
isPlayable(value.type) ? MediaViewer : PDFViewer,
|
previewType.component,
|
||||||
{ file: value.file, name: value.name, contentType: value.type, value },
|
{ ...(previewType.props ?? {}), file: value.file, name: value.name, contentType: value.type, value },
|
||||||
isImage(value.type) ? 'centered' : 'float'
|
(previewType.alignment ?? 'center') as PopupAlignment
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ import attachment, { type Attachment } from '@hcengineering/attachment'
|
|||||||
import { type ObjQueryType, SortingOrder, type SortingQuery, type Markup } from '@hcengineering/core'
|
import { type ObjQueryType, SortingOrder, type SortingQuery, type Markup } from '@hcengineering/core'
|
||||||
import { type IntlString, type Resources } from '@hcengineering/platform'
|
import { type IntlString, type Resources } from '@hcengineering/platform'
|
||||||
import preference from '@hcengineering/preference'
|
import preference from '@hcengineering/preference'
|
||||||
import { getClient } from '@hcengineering/presentation'
|
import { getClient, PDFViewer } from '@hcengineering/presentation'
|
||||||
import activity, { type ActivityMessage, type DocUpdateMessage } from '@hcengineering/activity'
|
import activity, { type ActivityMessage, type DocUpdateMessage } from '@hcengineering/activity'
|
||||||
|
|
||||||
import TxAttachmentCreate from './components/activity/TxAttachmentCreate.svelte'
|
import TxAttachmentCreate from './components/activity/TxAttachmentCreate.svelte'
|
||||||
@ -42,6 +42,7 @@ import IconAttachment from './components/icons/Attachment.svelte'
|
|||||||
import AttachmentPreview from './components/AttachmentPreview.svelte'
|
import AttachmentPreview from './components/AttachmentPreview.svelte'
|
||||||
import AttachmentsUpdatedMessage from './components/activity/AttachmentsUpdatedMessage.svelte'
|
import AttachmentsUpdatedMessage from './components/activity/AttachmentsUpdatedMessage.svelte'
|
||||||
import AttachmentsTooltip from './components/AttachmentsTooltip.svelte'
|
import AttachmentsTooltip from './components/AttachmentsTooltip.svelte'
|
||||||
|
import MediaViewer from './components/MediaViewer.svelte'
|
||||||
import { deleteFile, uploadFile } from './utils'
|
import { deleteFile, uploadFile } from './utils'
|
||||||
|
|
||||||
export * from './types'
|
export * from './types'
|
||||||
@ -258,7 +259,9 @@ export default async (): Promise<Resources> => ({
|
|||||||
AttachmentGalleryPresenter,
|
AttachmentGalleryPresenter,
|
||||||
Attachments,
|
Attachments,
|
||||||
FileBrowser,
|
FileBrowser,
|
||||||
Photos
|
Photos,
|
||||||
|
PDFViewer,
|
||||||
|
MediaViewer
|
||||||
},
|
},
|
||||||
activity: {
|
activity: {
|
||||||
TxAttachmentCreate,
|
TxAttachmentCreate,
|
||||||
|
@ -14,7 +14,12 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { type Attachment, type AttachmentMetadata } from '@hcengineering/attachment'
|
import { writable } from 'svelte/store'
|
||||||
|
import attachments, {
|
||||||
|
type AttachmentPreviewExtension,
|
||||||
|
type Attachment,
|
||||||
|
type AttachmentMetadata
|
||||||
|
} from '@hcengineering/attachment'
|
||||||
import {
|
import {
|
||||||
type Class,
|
type Class,
|
||||||
concatLink,
|
concatLink,
|
||||||
@ -24,8 +29,16 @@ import {
|
|||||||
type Space,
|
type Space,
|
||||||
type TxOperations as Client
|
type TxOperations as Client
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import presentation, { getFileUrl, getImageSize } from '@hcengineering/presentation'
|
import presentation, { createQuery, getFileUrl, getImageSize } from '@hcengineering/presentation'
|
||||||
import { PlatformError, Severity, Status, getMetadata, setPlatformStatus, unknownError } from '@hcengineering/platform'
|
import {
|
||||||
|
PlatformError,
|
||||||
|
Severity,
|
||||||
|
Status,
|
||||||
|
getMetadata,
|
||||||
|
getResource,
|
||||||
|
setPlatformStatus,
|
||||||
|
unknownError
|
||||||
|
} from '@hcengineering/platform'
|
||||||
|
|
||||||
import attachment from './plugin'
|
import attachment from './plugin'
|
||||||
|
|
||||||
@ -174,3 +187,67 @@ async function getVideoSize (uuid: string): Promise<{ width: number, height: num
|
|||||||
|
|
||||||
return await promise
|
return await promise
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const previewTypes = writable<AttachmentPreviewExtension[]>([])
|
||||||
|
const previewTypesQuery = createQuery(true)
|
||||||
|
previewTypesQuery.query(attachments.class.AttachmentPreviewExtension, {}, (result) => {
|
||||||
|
previewTypes.set(result)
|
||||||
|
})
|
||||||
|
|
||||||
|
function getPreviewTypeRegExp (type: string): RegExp {
|
||||||
|
return new RegExp(type.replace(/\//g, '\\/'))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export async function isOpenable (contentType: string, _previewTypes: AttachmentPreviewExtension[]): Promise<boolean> {
|
||||||
|
for (const previewType of _previewTypes) {
|
||||||
|
if (await isApplicableType(previewType, contentType)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
async function isApplicableType (
|
||||||
|
{ contentType, availabilityChecker }: AttachmentPreviewExtension,
|
||||||
|
_contentType: string
|
||||||
|
): Promise<boolean> {
|
||||||
|
const checkAvailability = availabilityChecker !== undefined ? await getResource(availabilityChecker) : undefined
|
||||||
|
const isAvailable: boolean = checkAvailability === undefined || (await checkAvailability())
|
||||||
|
|
||||||
|
return (
|
||||||
|
isAvailable &&
|
||||||
|
(Array.isArray(contentType) ? contentType : [contentType]).some((type) =>
|
||||||
|
getPreviewTypeRegExp(type).test(_contentType)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function comparePreviewTypes (a: AttachmentPreviewExtension, b: AttachmentPreviewExtension): number {
|
||||||
|
if (a.order === undefined && b.order === undefined) {
|
||||||
|
return 0
|
||||||
|
} else if (a.order === undefined) {
|
||||||
|
return -1
|
||||||
|
} else if (b.order === undefined) {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return a.order - b.order
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPreviewType (
|
||||||
|
contentType: string,
|
||||||
|
_previewTypes: AttachmentPreviewExtension[]
|
||||||
|
): Promise<AttachmentPreviewExtension | undefined> {
|
||||||
|
const applicableTypes: AttachmentPreviewExtension[] = []
|
||||||
|
for (const previewType of _previewTypes) {
|
||||||
|
if (await isApplicableType(previewType, contentType)) {
|
||||||
|
applicableTypes.push(previewType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return applicableTypes.sort(comparePreviewTypes)[0]
|
||||||
|
}
|
||||||
|
@ -35,7 +35,8 @@
|
|||||||
"@hcengineering/platform": "^0.6.9",
|
"@hcengineering/platform": "^0.6.9",
|
||||||
"@hcengineering/ui": "^0.6.11",
|
"@hcengineering/ui": "^0.6.11",
|
||||||
"@hcengineering/core": "^0.6.28",
|
"@hcengineering/core": "^0.6.28",
|
||||||
"@hcengineering/preference": "^0.6.9"
|
"@hcengineering/preference": "^0.6.9",
|
||||||
|
"@hcengineering/presentation": "^0.6.2"
|
||||||
},
|
},
|
||||||
"repository": "https://github.com/hcengineering/anticrm",
|
"repository": "https://github.com/hcengineering/anticrm",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
@ -18,7 +18,8 @@ import type { AttachedDoc, Class, Ref } from '@hcengineering/core'
|
|||||||
import type { Asset, Plugin } from '@hcengineering/platform'
|
import type { Asset, Plugin } from '@hcengineering/platform'
|
||||||
import { IntlString, plugin, Resource } from '@hcengineering/platform'
|
import { IntlString, plugin, Resource } from '@hcengineering/platform'
|
||||||
import type { Preference } from '@hcengineering/preference'
|
import type { Preference } from '@hcengineering/preference'
|
||||||
import { AnyComponent } from '@hcengineering/ui'
|
import { AnyComponent, ComponentExtensionId } from '@hcengineering/ui'
|
||||||
|
import { type ComponentPointExtension } from '@hcengineering/presentation'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
@ -71,6 +72,16 @@ export interface SavedAttachments extends Preference {
|
|||||||
attachedTo: Ref<Attachment>
|
attachedTo: Ref<Attachment>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface AttachmentPreviewExtension extends ComponentPointExtension {
|
||||||
|
contentType: string | string[]
|
||||||
|
alignment?: string
|
||||||
|
// Extension is only available if this checker returns true
|
||||||
|
availabilityChecker?: Resource<() => Promise<boolean>>
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
@ -81,7 +92,9 @@ export default plugin(attachmentId, {
|
|||||||
Attachments: '' as AnyComponent,
|
Attachments: '' as AnyComponent,
|
||||||
Photos: '' as AnyComponent,
|
Photos: '' as AnyComponent,
|
||||||
AttachmentsPresenter: '' as AnyComponent,
|
AttachmentsPresenter: '' as AnyComponent,
|
||||||
FileBrowser: '' as AnyComponent
|
FileBrowser: '' as AnyComponent,
|
||||||
|
PDFViewer: '' as AnyComponent,
|
||||||
|
MediaViewer: '' as AnyComponent
|
||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
Attachment: '' as Asset,
|
Attachment: '' as Asset,
|
||||||
@ -90,7 +103,8 @@ export default plugin(attachmentId, {
|
|||||||
class: {
|
class: {
|
||||||
Attachment: '' as Ref<Class<Attachment>>,
|
Attachment: '' as Ref<Class<Attachment>>,
|
||||||
Photo: '' as Ref<Class<Photo>>,
|
Photo: '' as Ref<Class<Photo>>,
|
||||||
SavedAttachments: '' as Ref<Class<SavedAttachments>>
|
SavedAttachments: '' as Ref<Class<SavedAttachments>>,
|
||||||
|
AttachmentPreviewExtension: '' as Ref<Class<AttachmentPreviewExtension>>
|
||||||
},
|
},
|
||||||
helper: {
|
helper: {
|
||||||
UploadFile: '' as Resource<(file: File) => Promise<string>>,
|
UploadFile: '' as Resource<(file: File) => Promise<string>>,
|
||||||
@ -116,5 +130,13 @@ export default plugin(attachmentId, {
|
|||||||
DeleteFile: '' as IntlString,
|
DeleteFile: '' as IntlString,
|
||||||
Attachments: '' as IntlString,
|
Attachments: '' as IntlString,
|
||||||
FileBrowser: '' as IntlString
|
FileBrowser: '' as IntlString
|
||||||
|
},
|
||||||
|
previewExtension: {
|
||||||
|
Image: '' as Ref<AttachmentPreviewExtension>,
|
||||||
|
Media: '' as Ref<AttachmentPreviewExtension>,
|
||||||
|
PDF: '' as Ref<AttachmentPreviewExtension>
|
||||||
|
},
|
||||||
|
extension: {
|
||||||
|
AttachmentPreview: '' as ComponentExtensionId
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user