Skip to content
Snippets Groups Projects
Embed.tsx 5.22 KiB
Newer Older
insert's avatar
insert committed
import classNames from 'classnames';
import EmbedMedia from './EmbedMedia';
import styles from "./Embed.module.scss";
import { useContext } from 'preact/hooks';
import { Embed as EmbedRJS } from "revolt.js/dist/api/objects";
import { useIntermediate } from '../../../../context/intermediate/Intermediate';
import { MessageAreaWidthContext } from '../../../../pages/channels/messaging/MessageArea';

interface Props {
    embed: EmbedRJS;
}

const MAX_EMBED_WIDTH = 480;
const MAX_EMBED_HEIGHT = 640;
const CONTAINER_PADDING = 24;
const MAX_PREVIEW_SIZE = 150;

export default function Embed({ embed }: Props) {
    // ! FIXME: temp code
    // ! add proxy function to client
    function proxyImage(url: string) {
        return 'https://jan.revolt.chat/proxy?url=' + encodeURIComponent(url);
    }

    const { openScreen } = useIntermediate();
    const maxWidth = Math.min(useContext(MessageAreaWidthContext) - CONTAINER_PADDING, MAX_EMBED_WIDTH);

    function calculateSize(w: number, h: number): { width: number, height: number } {
        let limitingWidth = Math.min(
            maxWidth,
            w
        );

        let limitingHeight = Math.min(
            MAX_EMBED_HEIGHT,
            h
        );

        // Calculate smallest possible WxH.
        let width = Math.min(
            limitingWidth,
            limitingHeight * (w / h)
        );

        let height = Math.min(
            limitingHeight,
            limitingWidth * (h / w)
        );

        return { width, height };
    }

    switch (embed.type) {
        case 'Website': {
            // ! FIXME: move this to january
            /*if (embed.url && YOUTUBE_RE.test(embed.url)) {
                embed.color = '#FF424F';
            }

            if (embed.url && TWITCH_RE.test(embed.url)) {
                embed.color = '#7B68EE';
            }

            if (embed.url && SPOTIFY_RE.test(embed.url)) {
                embed.color = '#1ABC9C';
            }

            if (embed.url && SOUNDCLOUD_RE.test(embed.url)) {
                embed.color = '#FF7F50';
            }*/

            // Determine special embed size.
            let mw, mh;
            let largeMedia = (embed.special && embed.special.type !== 'None') || embed.image?.size === 'Large';
            switch (embed.special?.type) {
                case 'YouTube':
                case 'Bandcamp': {
                    mw = embed.video?.width ?? 1280;
                    mh = embed.video?.height ?? 720;
                    break;
                }
                case 'Twitch': {
                    mw = 1280;
                    mh = 720;
                    break;
                }
                default: {
                    if (embed.image?.size === 'Preview') {
                        mw = MAX_EMBED_WIDTH;
                        mh = Math.min(embed.image.height ?? 0, MAX_PREVIEW_SIZE);
                    } else {
                        mw = embed.image?.width ?? MAX_EMBED_WIDTH;
                        mh = embed.image?.height ?? 0;
                    }
                }
            }

            let { width, height } = calculateSize(mw, mh);
            return (
                <div
                    className={classNames(styles.embed, styles.website)}
                    style={{
                        borderInlineStartColor: embed.color ?? 'var(--tertiary-background)',
                        width: width + CONTAINER_PADDING
                    }}>
                    <div>
                        { embed.site_name && <div className={styles.siteinfo}>
                            { embed.icon_url && <img className={styles.favicon} src={proxyImage(embed.icon_url)} draggable={false} onError={e => e.currentTarget.style.display = 'none'} /> }
                            <div className={styles.site}>{ embed.site_name } </div>
                        </div> }

                        {/*<span><a href={embed.url} target={"_blank"} className={styles.author}>Author</a></span>*/}
                        { embed.title && <span><a href={embed.url} target={"_blank"} className={styles.title}>{ embed.title }</a></span> }
                        { embed.description && <div className={styles.description}>{ embed.description }</div> }

                        { largeMedia && <EmbedMedia embed={embed} height={height} /> }
                    </div>
                    {
                        !largeMedia && <div>
                            <EmbedMedia embed={embed} width={height * ((embed.image?.width ?? 0) / (embed.image?.height ?? 0))} height={height} />
                        </div>
                    }
                </div>
            )
        }
        case 'Image': {
            return (
                <img className={classNames(styles.embed, styles.image)}
                    style={calculateSize(embed.width, embed.height)}
                    src={proxyImage(embed.url)}
                    type="text/html"
                    frameBorder="0"
                    onClick={() =>
                        openScreen({ id: "image_viewer", embed })
                    }
                    onMouseDown={ev =>
                        ev.button === 1 &&
                        window.open(embed.url, "_blank")
                    }
                />
            )
        }
        default: return null;
    }
}