metaforecast/src/web/questions/components/CaptureQuestion.tsx
2022-05-07 22:49:14 +04:00

156 lines
4.4 KiB
TypeScript

import domtoimage from "dom-to-image"; // https://github.com/tsayen/dom-to-image
import { useEffect, useRef, useState } from "react";
import { Button } from "../../common/Button";
import { CopyParagraph } from "../../common/CopyParagraph";
import { QuestionFragment } from "../../fragments.generated";
import { uploadToImgur } from "../../worker/uploadToImgur";
import { QuestionCard } from "./QuestionCard";
const domToImageWrapper = async (node: HTMLDivElement) => {
const scale = 3; // Increase for better quality
const style = {
transform: "scale(" + scale + ")",
transformOrigin: "top left",
width: node.offsetWidth + "px",
height: node.offsetHeight + "px",
};
const param = {
height: node.offsetHeight * scale,
width: node.offsetWidth * scale,
quality: 1,
style,
};
const image = await domtoimage.toPng(node, param);
return image;
};
const ImageSource: React.FC<{ question: QuestionFragment; imgSrc: string }> = ({
question,
imgSrc,
}) => {
if (!imgSrc) {
return null;
}
const html = `<a href="${question.url}" target="_blank"><img src="${imgSrc}" alt="Metaforecast.org snapshot of ''${question.title}'', from ${question.platform.label}"></a>`;
const markdown = `[![](${imgSrc})](${question.url})`;
return (
<div className="space-y-4">
<CopyParagraph text={markdown} buttonText="Copy markdown" />
<CopyParagraph text={html} buttonText="Copy HTML" />
</div>
);
};
const generateMetaculusIframeURL = (question: QuestionFragment) => {
let parts = question.url.replace("questions", "questions/embed").split("/");
parts.pop();
parts.pop();
const iframeURL = parts.join("/");
return iframeURL;
};
const generateMetaculusIframeHTML = (question: QuestionFragment) => {
const iframeURL = generateMetaculusIframeURL(question);
return `<iframe src="${iframeURL}" height="400" width="600"/>`;
};
const MetaculusEmbed: React.FC<{ question: QuestionFragment }> = ({
question,
}) => {
if (question.platform.id !== "metaculus") return null;
const iframeURL = generateMetaculusIframeURL(question);
return <iframe className="w-full h-80" src={iframeURL} />;
};
const MetaculusSource: React.FC<{
question: QuestionFragment;
}> = ({ question }) => {
if (question.platform.id !== "metaculus") return null;
return (
<CopyParagraph
text={generateMetaculusIframeHTML(question)}
buttonText="Copy HTML"
/>
);
};
interface Props {
question: QuestionFragment;
}
export const CaptureQuestion: React.FC<Props> = ({ question }) => {
const [imgSrc, setImgSrc] = useState<string | null>(null);
useEffect(() => {
setImgSrc(null);
}, [question]);
const containerRef = useRef<HTMLDivElement | null>(null);
const initialMainButtonText = "Capture image and generate code";
const [mainButtonText, setMainButtonText] = useState(initialMainButtonText);
const exportAsPictureAndCode = async () => {
if (!containerRef.current) {
return;
}
try {
const dataUrl = await domToImageWrapper(containerRef.current);
const imgurUrl = await uploadToImgur(dataUrl);
setImgSrc(imgurUrl);
setMainButtonText("Done!");
setTimeout(async () => {
setMainButtonText(initialMainButtonText);
}, 2000);
} catch (error) {
console.error("oops, something went wrong!", error);
}
};
const onCaptureButtonClick = async () => {
setMainButtonText("Processing...");
setImgSrc(null);
await exportAsPictureAndCode();
};
return (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 place-items-center">
<div ref={containerRef}>
<QuestionCard
question={question}
showTimeStamp={true}
showExpandButton={false}
expandFooterToFullWidth={true}
/>
</div>
<div>
<Button onClick={onCaptureButtonClick}>{mainButtonText}</Button>
</div>
{imgSrc ? (
<>
<div>
<img src={imgSrc} />
</div>
<div>
<ImageSource question={question} imgSrc={imgSrc} />
</div>
<div className="justify-self-stretch">
<MetaculusEmbed question={question} />
</div>
<div>
<MetaculusSource question={question} />
</div>
</>
) : null}
</div>
);
};
// Note: https://stackoverflow.com/questions/66016033/can-no-longer-upload-images-to-imgur-from-localhost
// Use: http://imgurtester:3000/embed for testing.