From c381b41e6b2da37849f60c91424e6226d557a84d Mon Sep 17 00:00:00 2001 From: mytonwalletorg Date: Fri, 24 Jan 2025 00:01:10 +0100 Subject: [PATCH] v3.2.10 --- changelogs/3.2.10.txt | 1 + package-lock.json | 4 +- package.json | 2 +- public/version.txt | 2 +- src/api/chains/ton/util/metadata.ts | 20 +++-- src/api/types/misc.ts | 3 +- .../main/sections/Content/Nft.module.scss | 6 +- src/components/main/sections/Content/Nft.tsx | 77 +++++++++++++++---- .../main/sections/Content/NftImage.tsx | 34 -------- src/components/main/sections/Content/Nfts.tsx | 50 ++++++++++-- src/components/mediaViewer/Media.tsx | 23 +++++- .../mediaViewer/MediaViewer.module.scss | 3 + src/components/mediaViewer/MediaViewer.tsx | 2 +- .../mediaViewer/helpers/ghostAnimation.ts | 14 ++-- src/components/ui/AnimatedIconWithPreview.tsx | 18 ++++- src/components/ui/AnimatedSticker.tsx | 8 +- src/components/ui/Image.tsx | 19 ----- src/lib/rlottie/RLottie.ts | 16 ++-- src/lib/rlottie/rlottie.worker.ts | 3 +- 19 files changed, 190 insertions(+), 115 deletions(-) create mode 100644 changelogs/3.2.10.txt delete mode 100644 src/components/main/sections/Content/NftImage.tsx diff --git a/changelogs/3.2.10.txt b/changelogs/3.2.10.txt new file mode 100644 index 00000000..619f4cd5 --- /dev/null +++ b/changelogs/3.2.10.txt @@ -0,0 +1 @@ +Bug fixes and performance improvements diff --git a/package-lock.json b/package-lock.json index 5cdad20b..9fa09da1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mytonwallet", - "version": "3.2.9", + "version": "3.2.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mytonwallet", - "version": "3.2.9", + "version": "3.2.10", "license": "GPL-3.0-or-later", "dependencies": { "@awesome-cordova-plugins/core": "6.9.0", diff --git a/package.json b/package.json index 72bc65fc..0950c429 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mytonwallet", - "version": "3.2.9", + "version": "3.2.10", "description": "The most feature-rich web wallet and browser extension for TON – with support of multi-accounts, tokens (jettons), NFT, TON DNS, TON Sites, TON Proxy, and TON Magic.", "main": "index.js", "scripts": { diff --git a/public/version.txt b/public/version.txt index e650c01d..f15386a5 100644 --- a/public/version.txt +++ b/public/version.txt @@ -1 +1 @@ -3.2.9 +3.2.10 diff --git a/src/api/chains/ton/util/metadata.ts b/src/api/chains/ton/util/metadata.ts index 1443ec72..eb071746 100644 --- a/src/api/chains/ton/util/metadata.ts +++ b/src/api/chains/ton/util/metadata.ts @@ -13,16 +13,14 @@ import type { ApiNetwork, ApiNft, ApiNftMetadata, - ApiParsedPayload, ApiTransactionType, + ApiParsedPayload, + ApiTransactionType, } from '../../../types'; import type { DnsCategory } from '../constants'; import type { ApiTransactionExtra, JettonMetadata } from '../types'; import { - DEBUG, - LIQUID_JETTON, - MTW_CARDS_COLLECTION, - NFT_FRAGMENT_COLLECTIONS, + DEBUG, LIQUID_JETTON, MTW_CARDS_COLLECTION, NFT_FRAGMENT_COLLECTIONS, } from '../../../../config'; import { omitUndefined, pick, range } from '../../../../util/iteratees'; import { logDebugError } from '../../../../util/logs'; @@ -650,7 +648,7 @@ export function readSnakeBytes(slice: Slice) { return buffer; } -function buildNftMetadata(metadata: Record): ApiNftMetadata | undefined { +function buildMtwCardsNftMetadata(metadata: Record): ApiNftMetadata | undefined { const { image, id } = metadata as { image?: string; id?: number }; let mtwCardType: ApiMtwCardType | undefined; @@ -708,12 +706,13 @@ export function buildNft(network: ApiNetwork, rawNft: NftItem): ApiNft | undefin } = rawNft; const { - name, image, description, render_type: renderType, + name, image, description, render_type: renderType, lottie, } = rawMetadata as { name?: string; image?: string; description?: string; render_type?: string; + lottie?: string; }; const collectionAddress = collection && toBase64Address(collection.address, true, network); @@ -727,10 +726,15 @@ export function buildNft(network: ApiNetwork, rawNft: NftItem): ApiNft | undefin } } + const isWhitelisted = trust === 'whitelist'; const isScam = hasScamLink || description === 'SCAM' || trust === 'blacklist'; const isHidden = renderType === 'hidden' || isScam; const imageFromPreview = previews!.find((x) => x.resolution === '1500x1500')!.url; - const metadata = collectionAddress === MTW_CARDS_COLLECTION ? buildNftMetadata(rawMetadata) : undefined; + + const metadata = { + ...(isWhitelisted && { lottie }), + ...(collectionAddress === MTW_CARDS_COLLECTION && buildMtwCardsNftMetadata(rawMetadata)), + }; return omitUndefined({ index, diff --git a/src/api/types/misc.ts b/src/api/types/misc.ts index f58d4b8e..845b341b 100644 --- a/src/api/types/misc.ts +++ b/src/api/types/misc.ts @@ -88,6 +88,7 @@ export type ApiMtwCardTextType = 'light' | 'dark'; export type ApiMtwCardBorderShineType = 'up' | 'down' | 'left' | 'right' | 'radioactive'; export interface ApiNftMetadata { + lottie?: string; imageUrl?: string; mtwCardId?: number; mtwCardType?: ApiMtwCardType; @@ -109,7 +110,7 @@ export interface ApiNft { isHidden?: boolean; isOnFragment?: boolean; isScam?: boolean; - metadata?: ApiNftMetadata; + metadata: ApiNftMetadata; } export type ApiHistoryList = Array<[number, number]>; diff --git a/src/components/main/sections/Content/Nft.module.scss b/src/components/main/sections/Content/Nft.module.scss index 1874e42a..f71e94de 100644 --- a/src/components/main/sections/Content/Nft.module.scss +++ b/src/components/main/sections/Content/Nft.module.scss @@ -177,7 +177,6 @@ .imageWrapper { /* Fix for border-radius missing during transform on Safari. See https://stackoverflow.com/a/58283449 */ isolation: isolate; - position: relative; display: block !important; @@ -192,11 +191,10 @@ } .image { - position: absolute; - top: 0; - left: 0; transform-origin: center; + overflow: hidden; + width: 100%; height: 100%; diff --git a/src/components/main/sections/Content/Nft.tsx b/src/components/main/sections/Content/Nft.tsx index f5c8caf4..99aba29f 100644 --- a/src/components/main/sections/Content/Nft.tsx +++ b/src/components/main/sections/Content/Nft.tsx @@ -4,17 +4,20 @@ import React, { import { getActions } from '../../../../global'; import type { ApiNft } from '../../../../api/types'; +import type { ObserveFn } from '../../../../hooks/useIntersectionObserver'; import { type IAnchorPosition, MediaType } from '../../../../global/types'; import buildClassName from '../../../../util/buildClassName'; import { vibrate } from '../../../../util/capacitor'; -import { preloadImage } from '../../../../util/preloadImage'; import { shortenAddress } from '../../../../util/shortenAddress'; +import useFlag from '../../../../hooks/useFlag'; +import { useIsIntersecting } from '../../../../hooks/useIntersectionObserver'; import useLang from '../../../../hooks/useLang'; import useLastCallback from '../../../../hooks/useLastCallback'; import useShowTransition from '../../../../hooks/useShowTransition'; +import AnimatedIconWithPreview from '../../../ui/AnimatedIconWithPreview'; import Image from '../../../ui/Image'; import Radio from '../../../ui/Radio'; import NftMenu from './NftMenu'; @@ -24,14 +27,21 @@ import styles from './Nft.module.scss'; interface OwnProps { nft: ApiNft; selectedAddresses?: string[]; + observeIntersection: ObserveFn; } -function Nft({ nft, selectedAddresses }: OwnProps) { +function Nft({ nft, selectedAddresses, observeIntersection }: OwnProps) { const { openMediaViewer, selectNfts, clearNftSelection } = getActions(); const lang = useLang(); + // eslint-disable-next-line no-null/no-null const ref = useRef(null); + + const { + isLottie, shouldPlay, noLoop, markHover, unmarkHover, + } = useLottie(nft, ref, observeIntersection); + const [menuPosition, setMenuPosition] = useState(); const isSelectionEnabled = !!selectedAddresses && selectedAddresses.length > 0; const isSelected = useMemo(() => selectedAddresses?.includes(nft.address), [selectedAddresses, nft.address]); @@ -40,6 +50,7 @@ function Nft({ nft, selectedAddresses }: OwnProps) { shouldRender: shouldRenderWarning, transitionClassNames: warningTransitionClassNames, } = useShowTransition(isSelectionEnabled && nft.isOnSale); + const fullClassName = buildClassName( styles.item, !isSelectionEnabled && nft.isOnSale && styles.item_onSale, @@ -70,18 +81,15 @@ function Nft({ nft, selectedAddresses }: OwnProps) { setMenuPosition(undefined); }); - const handleIntersect = useLastCallback(() => { - preloadImage(nft.image).catch(() => { - }); - }); - return (
{isSelectionEnabled && !nft.isOnSale && ( )} - + {isLottie ? ( +
+ +
+ ) : ( + + )} {shouldRenderWarning && (
{lang('For sale. Cannot be sent and burned')} @@ -119,3 +142,29 @@ function Nft({ nft, selectedAddresses }: OwnProps) { } export default memo(Nft); + +function useLottie( + nft: ApiNft, + ref: React.RefObject, + observeIntersection: ObserveFn, +) { + const isLottie = Boolean(nft.metadata?.lottie); + + const isIntersecting = useIsIntersecting(ref, isLottie ? observeIntersection : undefined); + const [isHover, markHover, unmarkHover] = useFlag(); + + if (!isLottie) { + return { isLottie }; + } + + const shouldPlay = isIntersecting || isHover; + const noLoop = !isHover; + + return { + isLottie, + shouldPlay, + noLoop, + markHover, + unmarkHover, + }; +} diff --git a/src/components/main/sections/Content/NftImage.tsx b/src/components/main/sections/Content/NftImage.tsx deleted file mode 100644 index 5b7c758f..00000000 --- a/src/components/main/sections/Content/NftImage.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, { memo } from '../../../../lib/teact/teact'; - -import type { ApiNft } from '../../../../api/types'; - -import { preloadImage } from '../../../../util/preloadImage'; - -import useLastCallback from '../../../../hooks/useLastCallback'; - -import Image from '../../../ui/Image'; - -interface OwnProps { - nft: ApiNft; - className?: string; - imageClassName?: string; -} - -function NftImage({ - nft, className, imageClassName, -}: OwnProps) { - const handleIntersect = useLastCallback(() => { - preloadImage(nft.image).catch(() => {}); - }); - - return ( - - ); -} - -export default memo(NftImage); diff --git a/src/components/main/sections/Content/Nfts.tsx b/src/components/main/sections/Content/Nfts.tsx index 14c4863c..e334e055 100644 --- a/src/components/main/sections/Content/Nfts.tsx +++ b/src/components/main/sections/Content/Nfts.tsx @@ -1,13 +1,11 @@ -import React, { memo, useEffect, useMemo } from '../../../../lib/teact/teact'; +import React, { + memo, useEffect, useMemo, useRef, +} from '../../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../../global'; import type { ApiNft } from '../../../../api/types'; -import { - ANIMATED_STICKER_BIG_SIZE_PX, - NOTCOIN_VOUCHERS_ADDRESS, - TON_DIAMONDS_URL, -} from '../../../../config'; +import { ANIMATED_STICKER_BIG_SIZE_PX, NOTCOIN_VOUCHERS_ADDRESS, TON_DIAMONDS_URL } from '../../../../config'; import renderText from '../../../../global/helpers/renderText'; import { selectCurrentAccountState } from '../../../../global/selectors'; import buildClassName from '../../../../util/buildClassName'; @@ -15,6 +13,7 @@ import captureEscKeyListener from '../../../../util/captureEscKeyListener'; import { ANIMATED_STICKERS_PATHS } from '../../../ui/helpers/animatedAssets'; import { useDeviceScreen } from '../../../../hooks/useDeviceScreen'; +import { useIntersectionObserver } from '../../../../hooks/useIntersectionObserver'; import useLang from '../../../../hooks/useLang'; import useLastCallback from '../../../../hooks/useLastCallback'; @@ -40,6 +39,8 @@ interface StateProps { isNftBuyingDisabled?: boolean; } +const INTERSECTION_THROTTLE = 200; + function Nfts({ isActive, orderedAddresses, @@ -81,6 +82,14 @@ function Nfts({ byAddress, currentCollectionAddress, orderedAddresses, blacklistedNftAddresses, whitelistedNftAddresses, ]); + // eslint-disable-next-line no-null/no-null + const containerRef = useRef(null); + const { observe: observeIntersection } = useIntersectionObserver({ + rootRef: containerRef, + throttleMs: INTERSECTION_THROTTLE, + isDisabled: !nfts?.length, + }); + const handleBurnNotcoinVouchersClick = useLastCallback(() => { burnNfts({ nfts: nfts! }); }); @@ -127,22 +136,47 @@ function Nfts({ onClick={handleBurnNotcoinVouchersClick} > {/* eslint-disable-next-line max-len */} - + + + {lang('Burn NOT Vouchers')} )}
- {nfts.map((nft) => )} + {nfts.map((nft) => ( + + ))}
); } + export default memo( withGlobal( (global): StateProps => { diff --git a/src/components/mediaViewer/Media.tsx b/src/components/mediaViewer/Media.tsx index de3e644b..90564318 100644 --- a/src/components/mediaViewer/Media.tsx +++ b/src/components/mediaViewer/Media.tsx @@ -12,6 +12,8 @@ import buildClassName from '../../util/buildClassName'; import { useDeviceScreen } from '../../hooks/useDeviceScreen'; import useLang from '../../hooks/useLang'; +import AnimatedIconWithPreview from '../ui/AnimatedIconWithPreview'; + import styles from './MediaViewer.module.scss'; import scamImg from '../../assets/scam.svg'; @@ -25,13 +27,16 @@ interface StateProps { alt?: string; thumbnail?: string; image?: string; + lottie?: string; description?: string; isScam?: boolean; whitelistedMediaIds?: string[]; } +const ANIMATED_ICON_SIZE = 250; // Preview size (500px) / 2 + function Media({ - mediaId, alt, thumbnail, image, description, isScam, whitelistedMediaIds, + mediaId, alt, thumbnail, image, lottie, description, isScam, whitelistedMediaIds, }: OwnProps & StateProps) { const lang = useLang(); const src = image || thumbnail; @@ -54,7 +59,20 @@ function Media({ return (
- {alt} + {lottie ? ( + + ) : ( + {alt} + )}
{description && (
@@ -84,6 +102,7 @@ export default memo(withGlobal((global, { mediaId }): StateProps => { alt: nft.name, thumbnail: nft.thumbnail, image: nft.image, + lottie: nft.metadata?.lottie, description: nft.description, isScam: nft.isScam, whitelistedMediaIds: whitelistedNftAddresses, diff --git a/src/components/mediaViewer/MediaViewer.module.scss b/src/components/mediaViewer/MediaViewer.module.scss index 39133c87..759ab510 100644 --- a/src/components/mediaViewer/MediaViewer.module.scss +++ b/src/components/mediaViewer/MediaViewer.module.scss @@ -236,6 +236,9 @@ } .image { + overflow: hidden; + + aspect-ratio: 1; max-width: 100vw; max-height: calc(100vh - 14rem); diff --git a/src/components/mediaViewer/MediaViewer.tsx b/src/components/mediaViewer/MediaViewer.tsx index 9e71ba80..6a9f8d33 100644 --- a/src/components/mediaViewer/MediaViewer.tsx +++ b/src/components/mediaViewer/MediaViewer.tsx @@ -146,7 +146,7 @@ export default memo(withGlobal((global): StateProps => { const { orderedAddresses, byAddress } = accountState?.nfts || {}; const { blacklistedNftAddresses } = accountState || {}; const nft = byAddress?.[mediaId!]; - mediaUrl = nft?.image || nft?.thumbnail; + mediaUrl = (!nft?.metadata?.lottie && nft?.image) || nft?.thumbnail; mediaIds = orderedAddresses || MEMO_EMPTY_ARRAY; mediaByIds = byAddress; if (blacklistedNftAddresses?.length) { diff --git a/src/components/mediaViewer/helpers/ghostAnimation.ts b/src/components/mediaViewer/helpers/ghostAnimation.ts index 3c0aa4d4..5ef292f4 100644 --- a/src/components/mediaViewer/helpers/ghostAnimation.ts +++ b/src/components/mediaViewer/helpers/ghostAnimation.ts @@ -83,7 +83,9 @@ export function animateOpening( export function animateClosing(type: MediaType, mediaId: string, txId?: string, hiddenNfts?: 'user' | 'scam') { const { container, image: toImage } = getNode(type, mediaId, txId, hiddenNfts); - const fromImage = document.querySelector(`.${styles.slide_active} img`); + const fromImage = document.querySelector( + `.${styles.slide_active} img, .${styles.slide_active} canvas`, + ); if (!fromImage || !toImage) { return; } @@ -168,7 +170,7 @@ export function animateClosing(type: MediaType, mediaId: string, txId?: string, } function getNode(type: MediaType, mediaId: string, txId?: string, hiddenNfts?: 'user' | 'scam') { - let image: HTMLImageElement | undefined; + let image: HTMLImageElement | HTMLCanvasElement | undefined; let container: HTMLElement | undefined; if (type === MediaType.Nft) { container = document.querySelector( @@ -178,12 +180,12 @@ function getNode(type: MediaType, mediaId: string, txId?: string, hiddenNfts?: ' ? `.hidden-nfts-${hiddenNfts} [data-nft-address="${mediaId}"]` : `.nfts-container > .Transition_slide-active [data-nft-address="${mediaId}"]`, ) as HTMLElement; - image = container?.querySelector('img') as HTMLImageElement; + image = container?.querySelector('img, canvas') as HTMLImageElement | HTMLCanvasElement; } return { container, image }; } -function createGhost(source: HTMLImageElement) { +function createGhost(source: HTMLImageElement | HTMLCanvasElement) { const ghost = document.createElement('div'); ghost.classList.add(styles.ghost); @@ -191,7 +193,9 @@ function createGhost(source: HTMLImageElement) { img.classList.add(styles.ghostImage); img.draggable = false; img.oncontextmenu = stopEvent; - img.src = source.src; + img.src = source instanceof HTMLImageElement + ? source.src + : source.parentElement?.parentElement?.dataset.previewUrl || ''; ghost.appendChild(img); diff --git a/src/components/ui/AnimatedIconWithPreview.tsx b/src/components/ui/AnimatedIconWithPreview.tsx index 10c401db..00f4c263 100644 --- a/src/components/ui/AnimatedIconWithPreview.tsx +++ b/src/components/ui/AnimatedIconWithPreview.tsx @@ -33,13 +33,20 @@ const DEFAULT_SIZE = 150; function AnimatedIconWithPreview(props: OwnProps & StateProps) { const { - size = DEFAULT_SIZE, previewUrl, iconPreviewClass, thumbDataUri, className, noAnimation, ...otherProps + size = DEFAULT_SIZE, + previewUrl, + iconPreviewClass, + thumbDataUri, + className, + noAnimation, + noPreviewTransition, + ...otherProps } = props; const [isPreviewLoaded, markPreviewLoaded] = useFlag( Boolean(iconPreviewClass) || Boolean(thumbDataUri) || loadedPreviewUrls.has(previewUrl), ); - const transitionClassNames = useMediaTransition(isPreviewLoaded); + const transitionClassNames = useMediaTransition(isPreviewLoaded && !noPreviewTransition); const [isAnimationReady, markAnimationReady, markAnimationNotReady] = useFlag(false); useEffect(() => { @@ -55,8 +62,11 @@ function AnimatedIconWithPreview(props: OwnProps & StateProps) { return (
{thumbDataUri && !previewUrl && !isAnimationReady && ( // eslint-disable-next-line jsx-a11y/alt-text diff --git a/src/components/ui/AnimatedSticker.tsx b/src/components/ui/AnimatedSticker.tsx index a0190a71..d1887d04 100644 --- a/src/components/ui/AnimatedSticker.tsx +++ b/src/components/ui/AnimatedSticker.tsx @@ -1,8 +1,7 @@ import type { RefObject } from 'react'; import type { FC } from '../../lib/teact/teact'; import React, { - getIsHeavyAnimating, - memo, useEffect, useRef, useState, + getIsHeavyAnimating, memo, useEffect, useRef, useState, } from '../../lib/teact/teact'; import type RLottieInstance from '../../lib/rlottie/RLottie'; @@ -36,6 +35,7 @@ export type OwnProps = { speed?: number; noLoop?: boolean; size: number; + shouldStretch?: boolean; quality?: number; color?: string; isLowPriority?: boolean; @@ -62,6 +62,7 @@ const AnimatedSticker: FC = ({ speed, noLoop, size, + shouldStretch, quality, isLowPriority, color, @@ -129,6 +130,7 @@ const AnimatedSticker: FC = ({ renderId || generateUniqueId(), { size, + shouldStretch, noLoop, quality, isLowPriority, @@ -251,7 +253,7 @@ const AnimatedSticker: FC = ({ ref={containerRef} className={className} style={buildStyle( - size !== undefined && `width: ${size}px; height: ${size}px;`, + size !== undefined && !shouldStretch && `width: ${size}px; height: ${size}px;`, onClick && !IS_ELECTRON && 'cursor: pointer', style, )} diff --git a/src/components/ui/Image.tsx b/src/components/ui/Image.tsx index c0215ee0..1652ba95 100644 --- a/src/components/ui/Image.tsx +++ b/src/components/ui/Image.tsx @@ -4,7 +4,6 @@ import buildClassName from '../../util/buildClassName'; import { preloadedImageUrls } from '../../util/preloadImage'; import useFlag from '../../hooks/useFlag'; -import { useIntersectionObserver, useOnIntersect } from '../../hooks/useIntersectionObserver'; import useMediaTransition from '../../hooks/useMediaTransition'; interface OwnProps { @@ -13,37 +12,19 @@ interface OwnProps { loading?: 'lazy' | 'eager'; className?: string; imageClassName?: string; - onIntersect?: VoidFunction; } -const INTERSECTION_THROTTLE = 200; - function ImageComponent({ url, alt, loading, className, imageClassName, - onIntersect, }: OwnProps) { - // eslint-disable-next-line no-null/no-null - const containerRef = useRef(null); // eslint-disable-next-line no-null/no-null const ref = useRef(null); const [isLoaded, markIsLoaded] = useFlag(preloadedImageUrls.has(url)); - const { observe } = useIntersectionObserver({ - rootRef: containerRef, - throttleMs: INTERSECTION_THROTTLE, - isDisabled: !onIntersect, - }); - - useOnIntersect(ref, observe, (entry) => { - if (onIntersect && entry.isIntersecting) { - onIntersect(); - } - }); - const handleLoad = () => { markIsLoaded(); preloadedImageUrls.add(url); diff --git a/src/lib/rlottie/RLottie.ts b/src/lib/rlottie/RLottie.ts index 647de55d..dbf0c12e 100644 --- a/src/lib/rlottie/RLottie.ts +++ b/src/lib/rlottie/RLottie.ts @@ -3,13 +3,12 @@ import cycleRestrict from '../../util/cycleRestrict'; import Deferred from '../../util/Deferred'; import generateUniqueId from '../../util/generateUniqueId'; import launchMediaWorkers, { MAX_WORKERS } from '../../util/launchMediaWorkers'; -import { - IS_ANDROID, IS_IOS, IS_SAFARI, -} from '../../util/windowEnvironment'; +import { IS_ANDROID, IS_IOS, IS_SAFARI } from '../../util/windowEnvironment'; import { requestMeasure, requestMutation } from '../fasterdom/fasterdom'; interface Params { size: number; + shouldStretch?: boolean; noLoop?: boolean; quality?: number; isLowPriority?: boolean; @@ -268,7 +267,7 @@ class RLottie { throw new Error('[RLottie] Container is not mounted'); } - const { size } = this.params; + const { size, shouldStretch } = this.params; imgSize = Math.round(size * sizeFactor); @@ -281,8 +280,13 @@ class RLottie { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d')!; - canvas.style.width = `${size}px`; - canvas.style.height = `${size}px`; + if (shouldStretch) { + canvas.style.width = '100%'; + } else { + canvas.style.width = `${size}px`; + canvas.style.height = `${size}px`; + } + canvas.style.display = 'block'; canvas.width = imgSize; diff --git a/src/lib/rlottie/rlottie.worker.ts b/src/lib/rlottie/rlottie.worker.ts index 75389212..68506934 100644 --- a/src/lib/rlottie/rlottie.worker.ts +++ b/src/lib/rlottie/rlottie.worker.ts @@ -99,8 +99,7 @@ async function extractJson(tgsUrl: string) { const response = await fetch(tgsUrl); const contentType = response.headers.get('Content-Type'); - // Support deprecated JSON format cached locally - if (contentType?.startsWith('text/')) { + if (contentType === 'application/json') { return response.text(); }