diff --git a/fluxer_app/src/features/channel/components/embeds/channel_embed/ChannelEmbedShared.ts b/fluxer_app/src/features/channel/components/embeds/channel_embed/ChannelEmbedShared.ts index 2040975e6..322b090b6 100644 --- a/fluxer_app/src/features/channel/components/embeds/channel_embed/ChannelEmbedShared.ts +++ b/fluxer_app/src/features/channel/components/embeds/channel_embed/ChannelEmbedShared.ts @@ -2,7 +2,13 @@ import type {Message} from '@app/features/messaging/models/MessagingMessage'; import {getEmbedMediaDimensions} from '@app/features/messaging/utils/MediaDimensionConfig'; -import {buildMediaProxyURL, resolvePreferredImageFormat} from '@app/features/messaging/utils/MediaProxyUtils'; +import { + buildAnimatedImageProxyURL, + buildMediaProxyURL, + mediaDevicePixelRatio, + resolvePreferredImageFormat, + stripMediaProxyParams, +} from '@app/features/messaging/utils/MediaProxyUtils'; import {Logger} from '@app/features/platform/utils/AppLogger'; import * as ColorUtils from '@app/features/theme/utils/ColorUtils'; import {createCalculator, type MediaDimensionCalculator} from '@app/features/ui/utils/DimensionUtils'; @@ -128,15 +134,40 @@ export const calculateMediaDimensions = (media: Required): MediaDime const {dimensions} = mediaCalculator.calculate({width: media.width, height: media.height}); return dimensions; }; +const toEmbedMediaPixels = (layoutWidth: number, layoutHeight: number): MediaDimensions => { + const ratio = mediaDevicePixelRatio(); + return {width: Math.round(layoutWidth * ratio), height: Math.round(layoutHeight * ratio)}; +}; export const getOptimizedMediaURL = (proxyURL: string, width: number, height: number, contentType?: string): string => { - const targetWidth = Math.round(width * 2); - const targetHeight = Math.round(height * 2); + const target = toEmbedMediaPixels(width, height); return buildMediaProxyURL(proxyURL, { format: resolvePreferredImageFormat(contentType), - width: targetWidth, - height: targetHeight, + width: target.width, + height: target.height, }); }; +export const getOptimizedAnimatedMediaURL = (proxyURL: string, width: number, height: number): string => { + if (!proxyURL) return proxyURL; + const target = toEmbedMediaPixels(width, height); + return buildAnimatedImageProxyURL(stripMediaProxyParams(proxyURL), target.width, target.height); +}; +export const isAnimatedEmbedMedia = (media: Pick): boolean => { + if (media.content_type === 'image/gif') return true; + return ((media.flags ?? 0) & MessageAttachmentFlags.IS_ANIMATED) === MessageAttachmentFlags.IS_ANIMATED; +}; +export const resolveEmbedImageSource = ( + media: Pick & {proxy_url: string}, + width: number, + height: number, +): {src: string; animated: boolean} => { + const animated = isAnimatedEmbedMedia(media); + return { + src: animated + ? getOptimizedAnimatedMediaURL(media.proxy_url, width, height) + : getOptimizedMediaURL(media.proxy_url, width, height, media.content_type), + animated, + }; +}; export const mediaIdentityKey = (media?: EmbedMedia): string => { if (!media) return ''; return [ diff --git a/fluxer_app/src/features/channel/components/embeds/channel_embed/EmbedMediaRenderer.tsx b/fluxer_app/src/features/channel/components/embeds/channel_embed/EmbedMediaRenderer.tsx index e3db1d52e..a8e40d0b2 100644 --- a/fluxer_app/src/features/channel/components/embeds/channel_embed/EmbedMediaRenderer.tsx +++ b/fluxer_app/src/features/channel/components/embeds/channel_embed/EmbedMediaRenderer.tsx @@ -3,11 +3,11 @@ import { calculateMediaDimensions, type EmbedMediaRendererProps, - getOptimizedMediaURL, getUrlHostname, isMediaMatureContent, isValidMedia, mediaPropsEqual, + resolveEmbedImageSource, THUMBNAIL_SIZE, } from '@app/features/channel/components/embeds/channel_embed/ChannelEmbedShared'; import {EmbedGif} from '@app/features/channel/components/embeds/media/EmbedGifv'; @@ -16,10 +16,9 @@ import EmbedVideo from '@app/features/channel/components/embeds/media/EmbedVideo import {EmbedYouTube} from '@app/features/channel/components/embeds/media/EmbedYouTube'; import {getInlineVideoLayoutConstraints} from '@app/features/channel/components/embeds/media/VideoDimensionUtils'; import {getEmbedMediaDimensions} from '@app/features/messaging/utils/MediaDimensionConfig'; -import {buildAnimatedImageProxyURL, buildMediaProxyURL} from '@app/features/messaging/utils/MediaProxyUtils'; +import {buildMediaProxyURL} from '@app/features/messaging/utils/MediaProxyUtils'; import messageStyles from '@app/features/theme/styles/Message.module.css'; import FocusRing from '@app/features/ui/focus_ring/FocusRing'; -import {MessageAttachmentFlags} from '@fluxer/constants/src/ChannelConstants'; import {observer} from 'mobx-react-lite'; import {type FC, memo} from 'react'; @@ -76,7 +75,6 @@ const EmbedMediaRendererInner: FC = observer( if (isValidMedia(image)) { const {width, height} = calculateMediaDimensions(image); const isGif = image.content_type === 'image/gif' || image.url.toLowerCase().endsWith('.gif'); - const imageIsAnimated = (image.flags & MessageAttachmentFlags.IS_ANIMATED) === MessageAttachmentFlags.IS_ANIMATED; if (isGif) { return ( = observer( ); } + const resolvedImageSource = resolveEmbedImageSource(image, width, height); return ( = observer( data-flx="channel.embeds.embed.embed-media-renderer-inner.focus-ring--4" > = observer( embedIndex={embedIndex} onDelete={onDelete} isPreview={isPreview} - animated={imageIsAnimated} + animated={resolvedImageSource.animated} alt={image.description ?? undefined} data-flx="channel.embeds.embed.embed-media-renderer-inner.embed-image" /> @@ -138,8 +137,6 @@ const EmbedMediaRendererInner: FC = observer( if (isValidMedia(thumbnail)) { const {width, height} = calculateMediaDimensions(thumbnail); const isGif = thumbnail.content_type === 'image/gif' || thumbnail.url.toLowerCase().endsWith('.gif'); - const thumbnailIsAnimated = - (thumbnail.flags & MessageAttachmentFlags.IS_ANIMATED) === MessageAttachmentFlags.IS_ANIMATED; if (isGif) { return ( = observer( ); } + const thumbnailSource = resolveEmbedImageSource(thumbnail, width, height); return ( = observer( data-flx="channel.embeds.embed.embed-media-renderer-inner.focus-ring--6" > = observer( contentHash={thumbnail.content_hash} embedIndex={embedIndex} onDelete={onDelete} - animated={thumbnailIsAnimated} + animated={thumbnailSource.animated} alt={thumbnail.description ?? undefined} data-flx="channel.embeds.embed.embed-media-renderer-inner.embed-image--2" /> @@ -205,9 +203,7 @@ const InlineThumbnailRendererInner: FC = observer( if (!embed.thumbnail || !isValidMedia(embed.thumbnail)) return null; const thumbnail = embed.thumbnail; 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; + const thumbnailSource = resolveEmbedImageSource(thumbnail, width, THUMBNAIL_SIZE); return ( = observer( data-flx="channel.embeds.embed.inline-thumbnail-renderer-inner.focus-ring" > = observer( embedIndex={embedIndex} onDelete={onDelete} isPreview={isPreview} - animated={thumbnailIsAnimated} + animated={thumbnailSource.animated} alt={thumbnail.description ?? undefined} data-flx="channel.embeds.embed.inline-thumbnail-renderer-inner.embed-image" />