UNPKG

profile-plus

Version:

### IOS

306 lines 16.5 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProfilePictureUploader = void 0; const camera_1 = require("@capacitor/camera"); const core_1 = require("@capacitor/core"); const useAuth_1 = require("lincd-auth/hooks/useAuth"); const Button_1 = require("lincd-mui-base/components/Button"); const Modal_1 = require("lincd-mui-base/components/Modal"); const ImageObject_1 = require("lincd-schema/shapes/ImageObject"); const ImageResize_1 = require("lincd-server-utils/utils/ImageResize"); const Server_1 = require("lincd-server-utils/utils/Server"); const ClassNames_1 = require("lincd/utils/ClassNames"); const react_1 = __importStar(require("react")); const package_js_1 = require("../package.js"); const Person_js_1 = require("../shapes/Person.js"); const helper_js_1 = require("../utils/helper.js"); const ProfilePictureUploader_module_css_1 = __importDefault(require("./ProfilePictureUploader.module.css")); const Spinner_js_1 = __importDefault(require("./Spinner.js")); const Query = Person_js_1.Person.query((p) => [p.profilePicture.cropped.contentUrl]); exports.ProfilePictureUploader = (0, package_js_1.linkedComponent)(Query, ({ profilePicture, thumbnailHeight, componentHeight, thumbnailWidth, aspectRatio = 3 / 5, className, property = 'profilePicture', confirmText = 'Save', renderAction, uploadIcon, onUpdate, }) => { var _a; let auth = (0, useAuth_1.useAuth)(); let user = auth.user; // crop state const [cropModalOpen, setCropModalOpen] = (0, react_1.useState)(false); const [crop, setCrop] = (0, react_1.useState)({ x: 0, y: 0 }); const [zoom, setZoom] = (0, react_1.useState)(1); const [cropArea, setCropArea] = (0, react_1.useState)(null); const [cropLoading, setCropLoading] = (0, react_1.useState)(false); const [Cropper, setCropper] = (0, react_1.useState)(null); // uploader state const [loadingImage, setLoadingImage] = (0, react_1.useState)(false); const [image, setImage] = (0, react_1.useState)(); const [imageWebPath, setImageWebPath] = (0, react_1.useState)(); const [fileName, setFileName] = (0, react_1.useState)(''); const [file, setFile] = (0, react_1.useState)(); // state for FileTransfer const [uploadError, setUploadError] = (0, react_1.useState)(false); // dynamically construct the property name based on property const croppedImageUrl = (_a = profilePicture === null || profilePicture === void 0 ? void 0 : profilePicture.cropped) === null || _a === void 0 ? void 0 : _a.contentUrl; const [croppedImage, setCroppedImage] = (0, react_1.useState)(croppedImageUrl || ''); // android development path fix let croppedImageSrc = (0, ImageResize_1.getResizedImagePath)((0, helper_js_1.replaceLocalhostWithSiteRoot)(croppedImage), thumbnailWidth ? thumbnailWidth * 2 : NaN, thumbnailHeight ? thumbnailHeight * 2 : thumbnailWidth ? NaN : 300); // Dynamically load react-easy-crop when modal opens to avoid import cycle (0, react_1.useEffect)(() => { if (cropModalOpen && !Cropper) { Promise.resolve().then(() => __importStar(require('react-easy-crop'))).then((module) => { setCropper(() => module.default); }) .catch((error) => { console.error('Failed to load react-easy-crop:', error); }); } }, [cropModalOpen, Cropper]); // close crop modal const handleCloseModal = () => { setCropModalOpen(false); }; // set user profilePicture property const onCancel = () => { user[property] = null; }; // handle cancel crop button const onCropCancel = () => { setImage(null); handleCloseModal(); setLoadingImage(false); if (onCancel) { onCancel(); } }; // handle crop when completed const onCropComplete = (0, react_1.useCallback)((croppedAreaPercentage, croppedArea) => { setCropArea(croppedArea); }, []); // handle capacitor camera const handleCapacitorCamera = () => __awaiter(void 0, void 0, void 0, function* () { try { const imageOptions = { quality: 90, allowEditing: false, width: 1024, height: 1024, resultType: core_1.Capacitor.isNativePlatform() ? camera_1.CameraResultType.Uri : camera_1.CameraResultType.DataUrl, source: core_1.Capacitor.isNativePlatform() ? camera_1.CameraSource.Prompt : camera_1.CameraSource.Photos, }; return yield camera_1.Camera.getPhoto(imageOptions); } catch (error) { if (error.message !== 'User cancelled photos app') { console.error('error in handleCapacitorCamera:', error); } } }); // handle the web platform image processing const handleWebImageUpload = (file) => __awaiter(void 0, void 0, void 0, function* () { try { const dataUrl = file.dataUrl; // set the image in the state setImage(dataUrl); // show crop image modal setCropModalOpen(true); // generate a random filename with the correct format const filename = (0, helper_js_1.generateRandomName)(file.format); setFileName(filename); // create an ImageObject and save it const imageObj = yield ImageObject_1.ImageObject.fromDataURL(dataUrl, filename); // update profile picture's image reference if (imageObj) { //the backend may rename the image during upload, so we need the updated file name setFileName(imageObj.contentUrl.split('/').pop()); const personUpdate = yield Person_js_1.Person.update(user, { profilePicture: { image: imageObj, }, }); } else { console.error('Something went wrong uploading image to the server.'); } } catch (err) { console.error('Error in handleWebImage:', err); // show the alert error message setUploadError(true); alert(`Hmm, things are a little crowded right now. We were not able to upload the image. Please try again in a moment.`); // hide the crop modal setCropModalOpen(false); } finally { setLoadingImage(false); } }); // handle the native platform image uploading const handleNativeImageUpload = (file) => { setCropModalOpen(true); setImageWebPath(file.webPath); setFile(file); }; // upload image using Cordova FileTransfer only for Native Platform const onFileTransferUpload = () => __awaiter(void 0, void 0, void 0, function* () { const fileTransfer = new FileTransfer(); const token = yield auth.getAccessToken(); // generate a unique file name for the image // example: originalName.jpg -> originalName_1633024800000_abcd12.jpg const filename = (0, helper_js_1.generateUniqueFileName)(file.path.substring(file.path.lastIndexOf('/') + 1)); setFileName(filename); console.log(`filename: ${filename}`); const options = { fileKey: 'file', fileName: filename, mimeType: 'image/jpeg', chunkedMode: false, // Change this based on your requirements headers: { Authorization: 'Bearer ' + token, }, }; const uploadUrl = `${process.env.SITE_ROOT}/call/${package_js_1.packageName}/uploadImage?property=${property}`; return new Promise((resolve, reject) => { fileTransfer.upload(file.path, encodeURI(uploadUrl), (success) => { const imageUrl = JSON.parse(success.response); resolve(imageUrl); }, (error) => { reject(new Error('Upload error: ' + JSON.stringify(error))); }, options); }); }); // handle cropping image call from backend const onCropUploadImage = (uploadImage) => { // set featureImage from uploadImage param or image state // image state use for web platform and uploadImage param use for native platform const featuredImage = uploadImage || image; // before crop, we need to make sure featuredImage is exist if (!featuredImage) { throw new Error('Please upload your image before crop'); } // cropped image from backend) Server_1.Server.call(package_js_1.packageName, 'cropImage', featuredImage, cropArea, property, fileName).then((croppedImageUrl) => { setLoadingImage(false); setCroppedImage(croppedImageUrl); if (onUpdate) { onUpdate(croppedImageUrl); } }); }; // main function to handle file change uploader const handleFileChange = () => __awaiter(void 0, void 0, void 0, function* () { setUploadError(false); try { const file = yield handleCapacitorCamera(); // start loading when upload image setLoadingImage(true); // make sure file is already exist before do upload if (!file) { console.error('Photo is not defined. User may have cancelled.'); return; } // check use which platform for upload the image if (core_1.Capacitor.getPlatform() === 'web') { yield handleWebImageUpload(file); } else { handleNativeImageUpload(file); } } catch (err) { console.error('Error in handleFileChange:', err); } finally { setLoadingImage(false); } }); // handle crop image for native app const handleCropImage = () => { // setCropLoading(true); setLoadingImage(true); handleCloseModal(); // check crop image base on platform // for native apps, we upload image use Cordova FileTransfer first and then crop // for web, on this function only do cropping image if (core_1.Capacitor.isNativePlatform()) { onFileTransferUpload() .then((imageUpload) => { // make sure image already success upload to server before crop if (!imageUpload) { throw new Error('Upload image failed'); } // start to crop image onCropUploadImage(imageUpload); }) .catch((err) => { setUploadError(true); alert(`Hmm, things are a little crowded right now. We were not able to upload the image. Please try again in a moment.`); throw err; }) .finally(() => { setLoadingImage(false); }); } else { // start to crop image for web platform onCropUploadImage(); } }; const renderActionView = () => { return react_1.default.createElement("div", { onClick: handleFileChange }, renderAction); }; return (react_1.default.createElement("div", { className: ProfilePictureUploader_module_css_1.default.Root }, react_1.default.createElement(Modal_1.Modal, { isOpen: cropModalOpen, backdrop: "rgba(255, 255, 255, 1)", onClose: handleCloseModal, renderContent: react_1.default.createElement("div", { className: ProfilePictureUploader_module_css_1.default.modalWrapper }, react_1.default.createElement("div", null, Cropper ? (react_1.default.createElement(Cropper, { image: core_1.Capacitor.isNativePlatform() ? imageWebPath : image, crop: crop, zoom: zoom, aspect: aspectRatio, onCropChange: setCrop, onZoomChange: setZoom, onCropComplete: onCropComplete })) : (react_1.default.createElement(Spinner_js_1.default, null))), react_1.default.createElement("div", { className: ProfilePictureUploader_module_css_1.default.modalAction }, react_1.default.createElement(Button_1.Button, { color: "secondary", onClick: onCropCancel }, "Cancel"), react_1.default.createElement(Button_1.Button, { color: "primary", onClick: handleCropImage, disabled: !Cropper }, cropLoading ? `Uploading...` : confirmText))) }), react_1.default.createElement("div", { className: (0, ClassNames_1.cl)(ProfilePictureUploader_module_css_1.default.uploaderContainer, className), style: { aspectRatio: aspectRatio ? aspectRatio : 'inherit', height: thumbnailHeight ? thumbnailHeight + 'px' : 'auto', width: thumbnailWidth ? thumbnailWidth + 'px' : 'auto', } }, renderAction ? (renderActionView()) : (react_1.default.createElement(react_1.default.Fragment, null, !loadingImage && croppedImage ? (react_1.default.createElement("img", { src: croppedImageSrc, alt: fileName, className: (0, ClassNames_1.cl)(ProfilePictureUploader_module_css_1.default.imageUpload), style: { height: thumbnailHeight ? thumbnailHeight + 'px' : 'auto', aspectRatio: aspectRatio ? aspectRatio : 'inherit', }, onClick: handleFileChange })) : (react_1.default.createElement("div", { className: (0, ClassNames_1.cl)(ProfilePictureUploader_module_css_1.default.uploadButton), onClick: handleFileChange, style: { aspectRatio: aspectRatio ? aspectRatio : 'inherit', } }, uploadError ? (react_1.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", fill: "none", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", className: "lucide lucide-circle-alert-icon lucide-circle-alert", viewBox: "0 0 24 24" }, react_1.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), react_1.default.createElement("path", { d: "M12 8v4M12 16h.01" }))) : loadingImage ? (react_1.default.createElement(Spinner_js_1.default, null)) : uploadIcon ? (uploadIcon) : (react_1.default.createElement("svg", { viewBox: "0 -960 960 960", height: "48", width: "48" }, react_1.default.createElement("path", { fill: "currentColor", d: "M450-450H200v-60h250v-250h60v250h250v60H510v250h-60v-250Z" })))))))))); }); //# sourceMappingURL=ProfilePictureUploader.js.map