mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 05:25:40 +09:00
fix(embed): correct link thumbnail placement and harden unfurl parsing (#1672)
This commit is contained in:
1 parent
7a45d5844d
commit
039bd1a7df
13 files changed
+1124
-196
No files matched your search
@@ -20,11 +20,6 @@ export const EMBED_LEFT_BORDER_WIDTH = 4;
|
||||
export const EMBED_RIGHT_BORDER_WIDTH = 1;
|
||||
export const EMBED_MEDIA_CHROME_WIDTH = EMBED_PADDING_X * 2 + EMBED_LEFT_BORDER_WIDTH + EMBED_RIGHT_BORDER_WIDTH;
|
||||
export const EMBED_MEDIA_MAX_WIDTH = 432;
|
||||
export const EMBED_MEDIA_CONTENT_WIDTH = EMBED_MEDIA_MAX_WIDTH - EMBED_MEDIA_CHROME_WIDTH;
|
||||
export const EMBED_MEDIA_FILL_CONSTRAINTS = {
|
||||
maxWidth: EMBED_MEDIA_CONTENT_WIDTH,
|
||||
maxHeight: EMBED_MEDIA_CONTENT_WIDTH,
|
||||
} as const;
|
||||
|
||||
export interface EmbedProps {
|
||||
embed: MessageEmbed;
|
||||
@@ -88,11 +83,6 @@ export const SAVES_DESCRIPTOR = msg({
|
||||
message: 'saves',
|
||||
comment: 'Plural external-post stat label on a social post embed. Lowercase to appear after the count.',
|
||||
});
|
||||
const thumbnailCalculator = createCalculator({
|
||||
maxWidth: THUMBNAIL_SIZE,
|
||||
maxHeight: THUMBNAIL_SIZE,
|
||||
forceScale: true,
|
||||
});
|
||||
const URL_CACHE_CAPACITY = 4096;
|
||||
const normalizedUrlCache = new Map<string, string | null>();
|
||||
const hostnameCache = new Map<string, string | null>();
|
||||
@@ -138,19 +128,6 @@ export const calculateMediaDimensions = (media: Required<EmbedMedia>): MediaDime
|
||||
const {dimensions} = mediaCalculator.calculate({width: media.width, height: media.height});
|
||||
return dimensions;
|
||||
};
|
||||
export const calculateEmbedImageDimensions = (media: Required<EmbedMedia>): MediaDimensions => {
|
||||
const naturalWidth = media.width > 0 ? media.width : 1;
|
||||
const naturalHeight = media.height > 0 ? media.height : 1;
|
||||
const scale = Math.min(
|
||||
1,
|
||||
EMBED_MEDIA_FILL_CONSTRAINTS.maxWidth / naturalWidth,
|
||||
EMBED_MEDIA_FILL_CONSTRAINTS.maxHeight / naturalHeight,
|
||||
);
|
||||
return {
|
||||
width: Math.max(1, Math.round(naturalWidth * scale)),
|
||||
height: Math.max(1, Math.round(naturalHeight * scale)),
|
||||
};
|
||||
};
|
||||
export const getOptimizedMediaURL = (proxyURL: string, width: number, height: number, contentType?: string): string => {
|
||||
const targetWidth = Math.round(width * 2);
|
||||
const targetHeight = Math.round(height * 2);
|
||||
@@ -199,13 +176,6 @@ export const mediaPropsEqual = <
|
||||
if (prev.isPreview !== next.isPreview) return false;
|
||||
return embedMediaSignature(prev.embed) === embedMediaSignature(next.embed);
|
||||
};
|
||||
export const shouldRenderAsInlineThumbnail = (media?: EmbedMedia): boolean => {
|
||||
if (!isValidMedia(media)) return false;
|
||||
const {dimensions: thumbnailDimensions} = thumbnailCalculator.calculate({width: media.width, height: media.height});
|
||||
const thumbnailWidth = thumbnailDimensions.width;
|
||||
const {width: fullWidth} = calculateEmbedImageDimensions(media);
|
||||
return fullWidth < 300 && thumbnailWidth >= 40;
|
||||
};
|
||||
export const isMediaMatureContent = (media?: EmbedMedia): boolean => {
|
||||
if (!media) return false;
|
||||
return Boolean(media.nsfw || ((media.flags ?? 0) & MessageAttachmentFlags.CONTAINS_EXPLICIT_MEDIA) !== 0);
|
||||
|
||||
+5
-4
@@ -8,6 +8,7 @@ import {
|
||||
isMediaMatureContent,
|
||||
isValidMedia,
|
||||
mediaPropsEqual,
|
||||
THUMBNAIL_SIZE,
|
||||
} from '@app/features/channel/components/embeds/channel_embed/ChannelEmbedShared';
|
||||
import {EmbedGif} from '@app/features/channel/components/embeds/media/EmbedGifv';
|
||||
import {EmbedImage} from '@app/features/channel/components/embeds/media/EmbedImage';
|
||||
@@ -203,7 +204,7 @@ const InlineThumbnailRendererInner: FC<EmbedMediaRendererProps> = observer(
|
||||
({embed, message, embedIndex, onDelete, isPreview}) => {
|
||||
if (!embed.thumbnail || !isValidMedia(embed.thumbnail)) return null;
|
||||
const thumbnail = embed.thumbnail;
|
||||
const width = Math.min(80, Math.round((80 * thumbnail.width) / thumbnail.height));
|
||||
const width = Math.min(THUMBNAIL_SIZE, Math.round((THUMBNAIL_SIZE * thumbnail.width) / thumbnail.height));
|
||||
const thumbnailIsAnimated =
|
||||
thumbnail.content_type === 'image/gif' ||
|
||||
(thumbnail.flags & MessageAttachmentFlags.IS_ANIMATED) === MessageAttachmentFlags.IS_ANIMATED;
|
||||
@@ -216,14 +217,14 @@ const InlineThumbnailRendererInner: FC<EmbedMediaRendererProps> = observer(
|
||||
<EmbedImage
|
||||
src={
|
||||
thumbnail.content_type === 'image/gif'
|
||||
? buildAnimatedImageProxyURL(thumbnail.proxy_url, width * 2, 160)
|
||||
: getOptimizedMediaURL(thumbnail.proxy_url, width, 80, thumbnail.content_type)
|
||||
? buildAnimatedImageProxyURL(thumbnail.proxy_url, width * 2, THUMBNAIL_SIZE * 2)
|
||||
: getOptimizedMediaURL(thumbnail.proxy_url, width, THUMBNAIL_SIZE, thumbnail.content_type)
|
||||
}
|
||||
originalSrc={thumbnail.url}
|
||||
naturalWidth={thumbnail.width}
|
||||
naturalHeight={thumbnail.height}
|
||||
width={width}
|
||||
height={80}
|
||||
height={THUMBNAIL_SIZE}
|
||||
placeholder={thumbnail.placeholder}
|
||||
constrain={true}
|
||||
isInline={true}
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
getBorderColor,
|
||||
getUrlHostname,
|
||||
isValidMedia,
|
||||
shouldRenderAsInlineThumbnail,
|
||||
} from '@app/features/channel/components/embeds/channel_embed/ChannelEmbedShared';
|
||||
import {
|
||||
EmbedMediaRenderer,
|
||||
@@ -48,9 +47,8 @@ export const RichEmbed: FC<EmbedProps> = observer(
|
||||
[galleryImages, embed, embedIndex, showGallery],
|
||||
);
|
||||
const shouldRenderMedia = hasAnyMedia || showGallery;
|
||||
const isRichType = embed.type === MessageEmbedTypes.RICH;
|
||||
const isInlineThumbnail =
|
||||
!hasVideo && hasThumbnail && !hasImage && (isRichType || shouldRenderAsInlineThumbnail(embed.thumbnail));
|
||||
const promotesThumbnailToImage = embed.type === MessageEmbedTypes.ARTICLE || embed.type === MessageEmbedTypes.IMAGE;
|
||||
const isInlineThumbnail = !hasVideo && hasThumbnail && !hasImage && !promotesThumbnailToImage;
|
||||
const shouldRenderInlineThumbnail = isInlineThumbnail && !showGallery;
|
||||
const isYouTubeEmbed = getUrlHostname(embed.provider?.url) === 'www.youtube.com';
|
||||
const useNarrowWidth = shouldRenderMedia && !shouldRenderInlineThumbnail;
|
||||
|
||||
Reference in new issue
Block a user