try some new aproach to handel mobile image upload issue

This commit is contained in:
Anton Tranelis 2024-07-31 21:09:06 +02:00
parent 930a4d6ceb
commit 52eb8e29db

View File

@ -1,14 +1,16 @@
import * as React from "react"; import * as React from "react";
import { useState } from "react"; import { useState, useCallback, useRef } from "react";
import ReactCrop, { Crop, centerCrop, makeAspectCrop } from 'react-image-crop'; import ReactCrop, { Crop, centerCrop, makeAspectCrop } from 'react-image-crop';
import { useAssetApi } from '../../AppShell/hooks/useAssets'; import { useAssetApi } from '../../AppShell/hooks/useAssets';
import 'react-image-crop/dist/ReactCrop.css';
import DialogModal from "../../Templates/DialogModal"; import DialogModal from "../../Templates/DialogModal";
import 'react-image-crop/dist/ReactCrop.css'
export const AvatarWidget = ({avatar, setAvatar}:{avatar:string, setAvatar : React.Dispatch<React.SetStateAction<any>>}) => {
interface AvatarWidgetProps {
avatar: string;
setAvatar: React.Dispatch<React.SetStateAction<any>>;
}
export const AvatarWidget: React.FC<AvatarWidgetProps> = ({ avatar, setAvatar }) => {
const [crop, setCrop] = useState<Crop>(); const [crop, setCrop] = useState<Crop>();
const [image, setImage] = useState<string>(""); const [image, setImage] = useState<string>("");
const [cropModalOpen, setCropModalOpen] = useState<boolean>(false); const [cropModalOpen, setCropModalOpen] = useState<boolean>(false);
@ -16,31 +18,38 @@ export const AvatarWidget = ({avatar, setAvatar}:{avatar:string, setAvatar : Rea
const assetsApi = useAssetApi(); const assetsApi = useAssetApi();
const imgRef = useRef<HTMLImageElement>(null);
const onImageChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files && event.target.files[0];
if (file) {
const validFormats = ["image/jpeg", "image/png"];
const maxSizeMB = 10;
const maxSizeBytes = maxSizeMB * 1024 * 1024;
const imgRef = React.useRef<HTMLImageElement>(null) if (!validFormats.includes(file.type)) {
alert("Unsupported file format. Please upload a JPEG or PNG image.");
const onImageChange = (event) => { return;
if (event.target.files && event.target.files[0]) {
setImage(URL.createObjectURL(event.target.files[0]));
} }
if (file.size > maxSizeBytes) {
alert(`File size exceeds ${maxSizeMB}MB. Please upload a smaller image.`);
return;
}
setImage(URL.createObjectURL(file));
setCropModalOpen(true); setCropModalOpen(true);
} else {
alert("No file selected or an error occurred while selecting the file.");
} }
}, []);
function onImageLoad(e: React.SyntheticEvent<HTMLImageElement>) { const onImageLoad = useCallback((e: React.SyntheticEvent<HTMLImageElement>) => {
const { width, height } = e.currentTarget const { width, height } = e.currentTarget;
setCrop(centerAspectCrop(width, height, 1));
}, []);
setCrop(centerAspectCrop(width, height, 1)) const centerAspectCrop = (mediaWidth: number, mediaHeight: number, aspect: number) => {
}
// This is to demonstate how to make and center a % aspect crop
// which is a bit trickier so we use some helper functions.
function centerAspectCrop(
mediaWidth: number,
mediaHeight: number,
aspect: number,
) {
return centerCrop( return centerCrop(
makeAspectCrop( makeAspectCrop(
{ {
@ -53,22 +62,50 @@ export const AvatarWidget = ({avatar, setAvatar}:{avatar:string, setAvatar : Rea
), ),
mediaWidth, mediaWidth,
mediaHeight, mediaHeight,
) );
};
async function resizeImage(image: HTMLImageElement, maxWidth: number, maxHeight: number): Promise<HTMLImageElement> {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
let width = image.width;
let height = image.height;
if (width > maxWidth) {
height *= maxWidth / width;
width = maxWidth;
}
if (height > maxHeight) {
width *= maxHeight / height;
height = maxHeight;
} }
async function renderCrop() { canvas.width = width;
// get the image element canvas.height = height;
if (ctx) {
ctx.drawImage(image, 0, 0, width, height);
}
const resizedImage = new Image();
resizedImage.src = canvas.toDataURL();
await resizedImage.decode();
return resizedImage;
}
const renderCrop = useCallback(async () => {
const image = imgRef.current; const image = imgRef.current;
if (crop && image) { if (crop && image) {
const resizedImage = await resizeImage(image, 1024, 1024); // Bildgröße vor dem Zuschneiden reduzieren
const scaleX = resizedImage.naturalWidth / resizedImage.width;
const scaleY = resizedImage.naturalHeight / resizedImage.height;
const scaleX = image.naturalWidth / image.width
const scaleY = image.naturalHeight / image.height
// create a canvas element to draw the cropped image
const canvas = new OffscreenCanvas( const canvas = new OffscreenCanvas(
crop.width * scaleX, crop.width * scaleX,
crop.height * scaleY, crop.height * scaleY
) );
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
const pixelRatio = window.devicePixelRatio; const pixelRatio = window.devicePixelRatio;
canvas.width = crop.width * pixelRatio * scaleX; canvas.width = crop.width * pixelRatio * scaleX;
@ -76,9 +113,8 @@ export const AvatarWidget = ({avatar, setAvatar}:{avatar:string, setAvatar : Rea
if (ctx) { if (ctx) {
ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0); ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
ctx.drawImage( ctx.drawImage(
image, resizedImage,
crop.x * scaleX, crop.x * scaleX,
crop.y * scaleY, crop.y * scaleY,
crop.width * scaleX, crop.width * scaleX,
@ -89,28 +125,27 @@ export const AvatarWidget = ({avatar, setAvatar}:{avatar:string, setAvatar : Rea
crop.height * scaleY crop.height * scaleY
); );
} }
const blob = await canvas.convertToBlob(); const blob = await canvas.convertToBlob();
await resizeBlob(blob); await resizeBlob(blob);
setCropping(false); setCropping(false);
setImage(""); setImage("");
} }
} }, [crop]);
async function resizeBlob(blob) { const resizeBlob = useCallback(async (blob: Blob) => {
var img = new Image(); const img = new Image();
img.src = URL.createObjectURL(blob); img.src = URL.createObjectURL(blob);
await img.decode(); await img.decode();
const canvas = new OffscreenCanvas(
400,
400
)
var ctx = canvas.getContext("2d");
ctx?.drawImage(img, 0, 0, 400, 400);
const resizedBlob = await canvas.convertToBlob()
const asset = await assetsApi.upload(resizedBlob, "test");
setAvatar(asset.id)
}
const canvas = new OffscreenCanvas(400, 400);
const ctx = canvas.getContext("2d");
ctx?.drawImage(img, 0, 0, 400, 400);
const resizedBlob = await canvas.convertToBlob();
const asset = await assetsApi.upload(resizedBlob, "avatar");
setAvatar(asset.id);
}, [assetsApi, setAvatar]);
return ( return (
<> <>
@ -135,11 +170,9 @@ export const AvatarWidget = ({avatar, setAvatar}:{avatar:string, setAvatar : Rea
</div> </div>
} }
</label> </label>
: <div className='tw-w-20 tw-flex tw-items-center tw-justify-center'> : <div className='tw-w-20 tw-flex tw-items-center tw-justify-center'>
<span className="tw-loading tw-loading-spinner"></span> <span className="tw-loading tw-loading-spinner"></span>
</div> </div>
} }
<DialogModal <DialogModal
title="" title=""
@ -159,5 +192,5 @@ export const AvatarWidget = ({avatar, setAvatar}:{avatar:string, setAvatar : Rea
}}>Select</button> }}>Select</button>
</DialogModal> </DialogModal>
</> </>
) );
} };