import * as React from "react"; import * as Constants from "~/common/constants"; import * as SVG from "~/common/svg"; import { css } from "@emotion/core"; import { FileTypeIcon } from "~/components/core/FileTypeIcon"; import { Blurhash } from "react-blurhash"; import { isBlurhashValid } from "blurhash"; const STYLES_IMAGE_CONTAINER = css` background-color: ${Constants.system.foreground}; width: 100%; height: 100%; background-size: cover; background-position: 50% 50%; `; const STYLES_IMAGE = css` background-color: ${Constants.system.foreground}; display: block; max-width: 100%; pointer-events: none; transition: 200ms ease all; `; const STYLES_ENTITY = css` height: 100%; width: 100%; border: 1px solid ${Constants.system.gray}; background-color: ${Constants.system.white}; font-size: 24px; display: flex; flex-direction: column; align-items: center; justify-content: center; cursor: pointer; pointer-events: none; padding: 8px; font-size: 0.9rem; `; const STYLES_TITLE = css` width: 100%; text-align: center; margin-top: 8px; overflow: hidden; word-break: break-all; text-overflow: break-word; `; const STYLES_BLUR_CONTAINER = css` width: 100%; height: 100%; border-radius: 8px; overflow: hidden; `; let preload = (url) => new Promise((resolve, reject) => { const img = new Image(); img.onload = resolve(img); img.onerror = reject; img.src = url.replace("https://undefined", "https://"); }); export default class SlateMediaObjectPreview extends React.Component { static defaultProps = { charCap: 30, }; state = { showImage: false, error: false, }; componentDidMount = async () => { if (this.props.type && this.props.type.startsWith("image/")) { try { let img = await preload(this.props.url); if (!img.height && !img.width) { this.setState({ showImage: false, error: true }); } this.setState({ showImage: true, error: false }); } catch (error) { this.setState({ showImage: false, error: true }); } } }; render() { // NOTE(jim): // This is a hack to catch this undefined case I don't want to track down yet. const url = this.props.url.replace("https://undefined", "https://"); const title = this.props.title && this.props.title.length > this.props.charCap ? this.props.title.substring(0, this.props.charCap) + "..." : this.props.title; if (this.props.type && this.props.type.startsWith("image/")) { let blurhash = this.props.blurhash && isBlurhashValid(this.props.blurhash); if (this.props.centeredImage) { return ( <React.Fragment> {this.state.error ? ( <div css={STYLES_ENTITY} style={{ ...this.props.imageStyle, backgroundColor: Constants.system.foreground, }} > <SVG.FileNotFound height="24px" /> {this.props.iconOnly ? null : <div css={STYLES_TITLE}>File not found</div>} </div> ) : this.state.showImage ? ( <div css={STYLES_IMAGE_CONTAINER} style={{ backgroundImage: `url(${url})`, ...this.props.imageStyle, }} /> ) : blurhash ? ( <div css={STYLES_BLUR_CONTAINER}> <Blurhash hash={this.props.blurhash} style={{ height: "100%", width: "100%", ...this.props.imageStyle, }} resolutionX={32} resolutionY={32} punch={1} /> </div> ) : ( <div css={STYLES_IMAGE_CONTAINER} style={this.props.imageStyle} /> )} </React.Fragment> ); } return ( <React.Fragment> {this.state.error ? ( <div css={STYLES_ENTITY} style={{ ...this.props.imageStyle, backgroundColor: "#F2F2F2" }} > <SVG.FileNotFound height="24px" /> {this.props.iconOnly ? null : <div css={STYLES_TITLE}>File not found</div>} </div> ) : this.state.showImage ? ( <img css={STYLES_IMAGE} style={{ maxHeight: "100%", ...this.props.imageStyle }} src={url} /> ) : blurhash ? ( <Blurhash hash={this.props.blurhash} style={{ height: "100%", width: "100%", ...this.props.imageStyle, }} resolutionX={32} resolutionY={32} punch={1} /> ) : ( <div css={STYLES_IMAGE} style={{ maxHeight: "100%", ...this.props.imageStyle }} /> )} </React.Fragment> ); } let element = <FileTypeIcon type={this.props.type} height="24px" />; return ( <article css={STYLES_ENTITY} style={this.props.style}> <div>{element}</div> {this.props.title && !this.props.iconOnly ? <div css={STYLES_TITLE}>{title}</div> : null} </article> ); } }