Hey is a decentralized and permissionless social media app built with Lens Protocol 馃尶
at main 114 lines 3.0 kB view raw
1import { 2 DEFAULT_AVATAR, 3 STATIC_IMAGES_URL, 4 type TRANSFORMS 5} from "@hey/data/constants"; 6import { ERRORS } from "@hey/data/errors"; 7import imageKit from "@hey/helpers/imageKit"; 8import sanitizeDStorageUrl from "@hey/helpers/sanitizeDStorageUrl"; 9import type { ApolloClientError } from "@hey/types/errors"; 10import type { ChangeEvent } from "react"; 11import { useCallback, useState } from "react"; 12import type { Area } from "react-easy-crop"; 13import { toast } from "sonner"; 14import uploadCroppedImage, { readFile } from "@/helpers/accountPictureUtils"; 15import getCroppedImg from "@/helpers/cropUtils"; 16import errorToast from "@/helpers/errorToast"; 17 18interface UseImageCropUploadProps { 19 src: string; 20 setSrc: (src: string) => void; 21 aspect: number; 22 transform: (typeof TRANSFORMS)[keyof typeof TRANSFORMS]; 23 label: "avatar" | "cover"; 24} 25 26const useImageCropUpload = ({ 27 src, 28 setSrc, 29 aspect, 30 transform, 31 label 32}: UseImageCropUploadProps) => { 33 const [pictureSrc, setPictureSrc] = useState(src); 34 const [showModal, setShowModal] = useState(false); 35 const [uploadedPicture, setUploadedPicture] = useState(""); 36 const [uploading, setUploading] = useState(false); 37 const [area, setArea] = useState<Area | null>(null); 38 const [crop, setCrop] = useState({ x: 0, y: 0 }); 39 const [zoom, setZoom] = useState(1); 40 41 const onError = useCallback((error: ApolloClientError) => { 42 errorToast(error); 43 }, []); 44 45 const handleUploadAndSave = async () => { 46 try { 47 setUploading(true); 48 const croppedImage = await getCroppedImg(pictureSrc, area); 49 50 if (!croppedImage) { 51 return toast.error(ERRORS.SomethingWentWrong); 52 } 53 54 const decentralizedUrl = await uploadCroppedImage(croppedImage); 55 const dataUrl = croppedImage.toDataURL("image/png"); 56 57 setSrc(decentralizedUrl); 58 setUploadedPicture(dataUrl); 59 } catch (error) { 60 onError(error); 61 } finally { 62 setArea(null); 63 setCrop({ x: 0, y: 0 }); 64 setZoom(1); 65 setShowModal(false); 66 setUploading(false); 67 } 68 }; 69 70 const onFileChange = async (evt: ChangeEvent<HTMLInputElement>) => { 71 const file = evt.target.files?.[0]; 72 if (file) { 73 setPictureSrc(await readFile(file)); 74 setShowModal(true); 75 } 76 }; 77 78 const onCropComplete = (_: Area, croppedAreaPixels: Area) => { 79 setArea(croppedAreaPixels); 80 }; 81 82 const pictureUrl = 83 pictureSrc || 84 (label === "avatar" 85 ? DEFAULT_AVATAR 86 : `${STATIC_IMAGES_URL}/patterns/2.svg`); 87 const renderPictureUrl = pictureUrl 88 ? imageKit(sanitizeDStorageUrl(pictureUrl), transform) 89 : ""; 90 91 const handleModalClose = () => { 92 setPictureSrc(""); 93 setShowModal(false); 94 }; 95 96 return { 97 aspect, 98 crop, 99 handleModalClose, 100 handleUploadAndSave, 101 onCropComplete, 102 onFileChange, 103 pictureSrc, 104 renderPictureUrl, 105 setCrop, 106 setZoom, 107 showModal, 108 uploadedPicture, 109 uploading, 110 zoom 111 }; 112}; 113 114export default useImageCropUpload;