diff --git a/packages/presentation/src/link-preview.ts b/packages/presentation/src/link-preview.ts index 8028c5eb20..3e229713a6 100644 --- a/packages/presentation/src/link-preview.ts +++ b/packages/presentation/src/link-preview.ts @@ -24,11 +24,18 @@ export interface LinkPreviewDetails { url?: string icon?: string image?: string + imageWidth?: number + imageHeight?: number charset?: string hostname?: string host?: string } +export type LinkPreviewAttachmentMetadata = Pick< +LinkPreviewDetails, +'title' | 'description' | 'image' | 'imageWidth' | 'imageHeight' +> + export function canDisplayLinkPreview (val: LinkPreviewDetails): boolean { if (isEmpty(val.host) && isEmpty(val.title)) { return false diff --git a/plugins/attachment-resources/src/components/AttachmentImagePreview.svelte b/plugins/attachment-resources/src/components/AttachmentImagePreview.svelte index ffe8932285..32cb79131d 100644 --- a/plugins/attachment-resources/src/components/AttachmentImagePreview.svelte +++ b/plugins/attachment-resources/src/components/AttachmentImagePreview.svelte @@ -20,6 +20,7 @@ import BrokenImage from './icons/BrokenImage.svelte' import { AttachmentImageSize } from '../types' + import { getImageDimensions } from '../utils' export let value: WithLookup | BlobType export let size: AttachmentImageSize = 'auto' @@ -33,64 +34,24 @@ const minSizeRem = 4 const maxSizeRem = 20 - const preferredWidthMap = { - 'x-large': 300 - } as const - let dimensions: Dimensions $: dimensions = getDimensions(value, size) function getDimensions (value: Attachment | BlobType, size: AttachmentImageSize): Dimensions { - if (size === 'auto' || size == null) { - return { - width: 300, - height: 300, - fit: 'contain' - } - } + const byDefault = { width: 300, height: 300, fit: 'contain' } as const + if (size === 'auto' || size == null) return byDefault - const preferredWidth = preferredWidthMap[size] const { metadata } = value + if (metadata === undefined) return byDefault - if (metadata === undefined) { - return { - width: preferredWidth, - height: preferredWidth, - fit: 'contain' - } - } - - const { originalWidth, originalHeight } = metadata - const fontSize = parseFloat(getComputedStyle(document.documentElement).fontSize) - const maxSize = maxSizeRem * fontSize - const minSize = minSizeRem * fontSize - - const width = Math.min(originalWidth, preferredWidth) - const ratio = originalHeight / originalWidth - const height = Math.ceil(width * ratio) - - const fit = width < minSize || height < minSize ? 'cover' : 'contain' - - if (height > maxSize) { - return { - width: maxSize / ratio, - height: maxSize, - fit - } - } else if (height < minSize) { - return { - width, - height: minSize, - fit - } - } else { - return { - width, - height, - fit - } - } + return getImageDimensions( + { + width: metadata.originalWidth, + height: metadata.originalHeight + }, + { maxWidth: maxSizeRem, minWidth: minSizeRem, maxHeight: maxSizeRem, minHeight: minSizeRem } + ) } function toStyle (size: 'auto' | number): string { @@ -131,8 +92,8 @@ blob={value.file} alt={value.name} fit={dimensions.fit} - width={Math.ceil(dimensions.width)} - height={Math.ceil(dimensions.height)} + width={dimensions.width} + height={dimensions.height} on:load={handleLoad} on:error={handleError} on:loadstart={handleLoadStart} diff --git a/plugins/attachment-resources/src/components/AttachmentRefInput.svelte b/plugins/attachment-resources/src/components/AttachmentRefInput.svelte index 9f26072d92..68d0376ed6 100644 --- a/plugins/attachment-resources/src/components/AttachmentRefInput.svelte +++ b/plugins/attachment-resources/src/components/AttachmentRefInput.svelte @@ -13,7 +13,7 @@ // limitations under the License. --> + +{#if src && !useDefaultIcon} + link-preview-icon { + useDefaultIcon = true + }} + /> +{:else} + +{/if} + + diff --git a/plugins/attachment-resources/src/components/LinkPreviewImage.svelte b/plugins/attachment-resources/src/components/LinkPreviewImage.svelte new file mode 100644 index 0000000000..508b73cd6c --- /dev/null +++ b/plugins/attachment-resources/src/components/LinkPreviewImage.svelte @@ -0,0 +1,83 @@ + + + + +{#if previewSrc} + {#if url} + + link-preview { + refreshPreviewSrc() + }} + /> + + {:else} + link-preview { + refreshPreviewSrc() + }} + /> + {/if} +{/if} + + diff --git a/plugins/attachment-resources/src/components/LinkPreviewPresenter.svelte b/plugins/attachment-resources/src/components/LinkPreviewPresenter.svelte index 6218c23ad9..7f11cab575 100644 --- a/plugins/attachment-resources/src/components/LinkPreviewPresenter.svelte +++ b/plugins/attachment-resources/src/components/LinkPreviewPresenter.svelte @@ -13,33 +13,29 @@ // limitations under the License. // --> -
- {#if viewModel} -
- {#if viewModel.icon !== undefined && !useDefaultIcon} - link-preview-icon { - useDefaultIcon = true - }} - /> - {:else} - - {/if} - {viewModel.hostname} - {#if isOwn} - - { - void onDelete() - }}> - {/if} -
-
- {#if viewModel.title?.toLowerCase() !== viewModel.hostname?.toLowerCase()} -
- {viewModel.title} -
- {/if} - - {#if viewModel.description} -
- {viewModel.description} -
- {/if} -
- {#if previewImageSrc} -
- - link-preview { - refreshPreviewImage() - }} - /> - + diff --git a/plugins/attachment-resources/src/utils.ts b/plugins/attachment-resources/src/utils.ts index 4a5b819990..cc329e41e2 100644 --- a/plugins/attachment-resources/src/utils.ts +++ b/plugins/attachment-resources/src/utils.ts @@ -213,3 +213,38 @@ export function showAttachmentPreviewPopup (value: WithLookup | Blob getPreviewAlignment(value.type ?? '') ) } + +interface ImageDimensions { + width: number + height: number + fit: 'cover' | 'contain' +} + +export function getImageDimensions ( + size: { width: number, height: number }, + maxRem: { maxWidth: number, minWidth: number, maxHeight: number, minHeight: number } +): ImageDimensions { + const originalWidth = size.width + const originalHeight = size.height + const fontSize = parseFloat(getComputedStyle(document.documentElement).fontSize) + const maxWidthPx = maxRem.maxWidth * fontSize + const minWidthPx = maxRem.minWidth * fontSize + const maxHeightPx = maxRem.maxHeight * fontSize + const minHeightPx = maxRem.minHeight * fontSize + + const ratio = originalHeight / originalWidth + + let width = Math.min(originalWidth, maxWidthPx) + let height = Math.ceil(width * ratio) + + const fit = width < minWidthPx || height < minHeightPx ? 'cover' : 'contain' + + if (height > maxHeightPx) { + width = maxHeightPx / ratio + height = maxHeightPx + } else if (height < minHeightPx) { + height = minHeightPx + } + + return { width: Math.round(width), height: Math.round(height), fit } +}