import styles from './text.module.css' import { useState, useEffect, useMemo, useCallback } from 'react' import { IMGPROXY_URL_REGEXP } from '../lib/url' import { useShowModal } from './modal' import { useMe } from './me' import { Dropdown } from 'react-bootstrap' export function decodeOriginalUrl (imgproxyUrl) { const parts = imgproxyUrl.split('/') // base64url is not a known encoding in browsers // so we need to replace the invalid chars const b64Url = parts[parts.length - 1].replace(/-/g, '+').replace(/_/, '/') const originalUrl = Buffer.from(b64Url, 'base64').toString('utf-8') return originalUrl } function ImageOriginal ({ src, topLevel, nofollow, tab, children, onClick, ...props }) { const me = useMe() const [showImage, setShowImage] = useState(false) useEffect(() => { if (me?.imgproxyOnly && tab !== 'preview') return // make sure it's not a false negative by trying to load URL as const img = new window.Image() img.onload = () => setShowImage(true) img.src = src return () => { img.onload = null img.src = '' } }, [src, showImage]) if (showImage) { return ( onClick(src)} onError={() => setShowImage(false)} /> ) } else { // user is not okay with loading original url automatically or there was an error loading the image // If element parsed by markdown is a raw URL, we use src as the text to not mislead users. // This will not be the case if [text](url) format is used. Then we will show what was chosen as text. const isRawURL = /^https?:\/\//.test(children?.[0]) return ( {isRawURL ? src : children} ) } } function ImageProxy ({ src, srcSet: srcSetObj, onClick, topLevel, onError, ...props }) { const srcSet = useMemo(() => { if (!srcSetObj) return undefined // srcSetObj shape: { [widthDescriptor]: , ... } return Object.entries(srcSetObj).reduce((acc, [wDescriptor, url], i, arr) => { return acc + `${url} ${wDescriptor}` + (i < arr.length - 1 ? ', ' : '') }, '') }, [srcSetObj]) const sizes = srcSet ? `${(topLevel ? 100 : 66)}vw` : undefined // get source url in best resolution const bestResSrc = useMemo(() => { if (!srcSetObj) return src return Object.entries(srcSetObj).reduce((acc, [wDescriptor, url]) => { const w = Number(wDescriptor.replace(/w$/, '')) return w > acc.w ? { w, url } : acc }, { w: 0, url: undefined }).url }, [srcSetObj]) return ( onClick(bestResSrc)} onError={onError} /> ) } export default function ZoomableImage ({ src, srcSet, ...props }) { const showModal = useShowModal() // if `srcSet` is falsy, it means the image was not processed by worker yet const [imgproxy, setImgproxy] = useState(!!srcSet || IMGPROXY_URL_REGEXP.test(src)) // backwards compatibility: // src may already be imgproxy url since we used to replace image urls with imgproxy urls const originalUrl = IMGPROXY_URL_REGEXP.test(src) ? decodeOriginalUrl(src) : src const handleClick = useCallback((src) => showModal(close => { return (
) }, { fullScreen: true, overflow: ( open original ) }), [showModal, originalUrl, styles]) if (!src) return null if (imgproxy) { return ( setImgproxy(false)} {...props} /> ) } return }