import React, { ChangeEventHandler, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import ReactCrop, { Crop } from 'react-image-crop'; import { useDispatch } from 'react-redux'; import { useGetSessionQuery } from '../../services/session'; import { showModal } from '../../store/slices/screen'; import 'react-image-crop/dist/ReactCrop.css'; import { useTitle } from '../../hooks/useTitle'; export const NekomimiPage: React.VFC = () => { const {t} = useTranslation(); const dispatch = useDispatch(); const [blobUrl, setBlobUrl] = useState(null); const [fileName, setFileName] = useState(null); const [percentage, setPercentage] = useState(0); const [isUploading, setUploading] = useState(false); const [image, setImage] = useState(null); const [crop, setCrop] = useState>({unit: '%', width: 100, aspect: 1 / 1}); const [completedCrop, setCompletedCrop] = useState(); useTitle('catAdjuster'); const previewCanvasRef = useRef(null); const {data} = useGetSessionQuery(undefined); const beginUpload = async () => { if (!previewCanvasRef.current) return; if (!data) return; const canvas = previewCanvasRef.current; const blob = await new Promise(res => canvas.toBlob(res)); if (!blob) return; const formData = new FormData(); formData.append('i', data.token); formData.append('force', 'true'); formData.append('file', blob); formData.append('name', `(Cropped) ${fileName ?? 'File'}`); await new Promise((res, rej) => { const xhr = new XMLHttpRequest(); xhr.open('POST', `https://${data.host}/api/drive/files/create`, true); xhr.onload = () => { setPercentage(100); const {id: avatarId} = JSON.parse(xhr.responseText); fetch(`https://${data.host}/api/i/update`, { method: 'POST', body: JSON.stringify({ i: data.token, avatarId }), }).then(() => res()).catch(rej); }; xhr.onerror = rej; xhr.upload.onprogress = e => { setPercentage(Math.floor(e.loaded / e.total * 100)); }; xhr.send(formData); }); dispatch(showModal({ type: 'dialog', icon: 'info', message: t('saved'), })); }; const onChangeFile: ChangeEventHandler = (e) => { if (e.target.files === null || e.target.files.length === 0) return; const file = e.target.files[0]; const reader = new FileReader(); setFileName(file.name); reader.addEventListener('load', () => setBlobUrl(reader.result as string)); reader.readAsDataURL(file); setCrop({unit: '%', width: 100, aspect: 1 / 1}); }; useEffect(() => { if (!completedCrop || !previewCanvasRef.current || !image) { return; } const canvas = previewCanvasRef.current; const crop = completedCrop; const scaleX = image.naturalWidth / image.width; const scaleY = image.naturalHeight / image.height; const ctx = canvas.getContext('2d'); if (!ctx) return; const pixelRatio = window.devicePixelRatio; canvas.width = crop.width * pixelRatio * scaleX; canvas.height = crop.height * pixelRatio * scaleY; ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0); ctx.imageSmoothingQuality = 'high'; ctx.drawImage( image, crop.x * scaleX, crop.y * scaleY, crop.width * scaleX, crop.height * scaleY, 0, 0, crop.width * scaleX, crop.height * scaleY ); }, [completedCrop]); const onClickUpload = async () => { setUploading(true); setPercentage(0); try { await beginUpload(); } finally { setUploading(false); } }; return (

{t('catAdjuster')}

{blobUrl && (
setImage(i)} onChange={(c) => setCrop(c)} onComplete={(c) => setCompletedCrop(c)} />

{t('preview')}

)}
); };