slate/components/core/SlateMediaObjectPreview.js

199 lines
5.4 KiB
JavaScript
Raw Normal View History

2020-08-09 01:19:35 +03:00
import * as React from "react";
import * as Constants from "~/common/constants";
2020-09-01 02:09:57 +03:00
import * as SVG from "~/common/svg";
2020-08-09 01:19:35 +03:00
import { css } from "@emotion/react";
2020-09-18 07:28:32 +03:00
import { FileTypeIcon } from "~/components/core/FileTypeIcon";
2020-10-05 00:30:28 +03:00
import { Blurhash } from "react-blurhash";
import { isBlurhashValid } from "blurhash";
2020-08-09 01:19:35 +03:00
2020-10-01 03:41:53 +03:00
const STYLES_IMAGE_CONTAINER = css`
background-color: ${Constants.system.foreground};
width: 100%;
height: 100%;
background-size: cover;
background-position: 50% 50%;
`;
2020-08-09 01:19:35 +03:00
const STYLES_IMAGE = css`
2020-09-10 06:28:48 +03:00
background-color: ${Constants.system.foreground};
2020-08-09 01:19:35 +03:00
display: block;
max-width: 100%;
2020-08-22 20:30:03 +03:00
pointer-events: none;
2020-08-27 07:24:49 +03:00
transition: 200ms ease all;
2020-08-09 01:19:35 +03:00
`;
2020-08-22 12:32:40 +03:00
const STYLES_ENTITY = css`
2020-08-09 01:19:35 +03:00
height: 100%;
width: 100%;
2020-10-05 00:30:28 +03:00
border: 1px solid ${Constants.system.gray};
background-color: ${Constants.system.white};
2020-08-09 01:19:35 +03:00
font-size: 24px;
display: flex;
2020-09-01 02:09:57 +03:00
flex-direction: column;
2020-08-09 01:19:35 +03:00
align-items: center;
justify-content: center;
cursor: pointer;
2020-08-22 20:30:03 +03:00
pointer-events: none;
2020-09-01 02:09:57 +03:00
padding: 8px;
2020-09-04 07:16:19 +03:00
font-size: 0.9rem;
2020-09-01 02:09:57 +03:00
`;
const STYLES_TITLE = css`
width: 100%;
text-align: center;
margin-top: 8px;
overflow: hidden;
word-break: break-all;
2020-09-05 07:45:50 +03:00
text-overflow: break-word;
2020-08-09 01:19:35 +03:00
`;
2020-10-05 00:30:28 +03:00
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://");
});
2020-08-09 01:19:35 +03:00
export default class SlateMediaObjectPreview extends React.Component {
2020-09-05 07:45:50 +03:00
static defaultProps = {
charCap: 30,
};
2020-10-05 00:30:28 +03:00
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 });
}
}
};
2020-08-09 01:19:35 +03:00
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://");
2020-10-05 00:30:28 +03:00
2020-09-01 02:09:57 +03:00
const title =
2020-09-05 07:45:50 +03:00
this.props.title && this.props.title.length > this.props.charCap
? this.props.title.substring(0, this.props.charCap) + "..."
2020-09-01 02:09:57 +03:00
: this.props.title;
2020-09-18 06:40:10 +03:00
if (this.props.type && this.props.type.startsWith("image/")) {
2020-10-05 00:30:28 +03:00
let blurhash =
this.props.blurhash && isBlurhashValid(this.props.blurhash);
2020-10-01 03:41:53 +03:00
if (this.props.centeredImage) {
return (
2020-10-05 00:30:28 +03:00
<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>
2020-10-01 03:41:53 +03:00
);
}
2020-10-05 00:30:28 +03:00
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>
);
2020-09-18 06:40:10 +03:00
}
2020-09-18 07:28:32 +03:00
let element = <FileTypeIcon type={this.props.type} height="24px" />;
2020-09-18 06:40:10 +03:00
return (
<article css={STYLES_ENTITY} style={this.props.style}>
2020-09-18 06:40:10 +03:00
<div>{element}</div>
2020-10-05 00:30:28 +03:00
{this.props.title && !this.props.iconOnly ? (
2020-09-01 02:09:57 +03:00
<div css={STYLES_TITLE}>{title}</div>
) : null}
</article>
);
2020-08-09 01:19:35 +03:00
}
}