import React, { useContext, useMemo, useState } from "react"; import { Avatar, Box, Button, ButtonBase, Dialog, DialogActions, DialogContent, DialogTitle, Input, MenuItem, Select, Tab, Tabs, Typography, } from "@mui/material"; import { useDropzone } from "react-dropzone"; import { useHandlePrivateApps } from "./useHandlePrivateApps"; import { useRecoilState, useSetRecoilState } from "recoil"; import { groupsPropertiesAtom, myGroupsWhereIAmAdminAtom } from "../../atoms/global"; import { Label } from "../Group/AddGroup"; import { Spacer } from "../../common/Spacer"; import { Add, AppCircle, AppCircleContainer, AppCircleLabel, PublishQAppChoseFile, PublishQAppInfo, } from "./Apps-styles"; import ImageUploader from "../../common/ImageUploader"; import { isMobile, MyContext } from "../../App"; import { fileToBase64 } from "../../utils/fileReading"; import { objectToBase64 } from "../../qdn/encryption/group-encryption"; import { getFee } from "../../background"; const maxFileSize = 50 * 1024 * 1024; // 50MB export const AppsPrivate = ({myName}) => { const { openApp } = useHandlePrivateApps(); const [file, setFile] = useState(null); const [logo, setLogo] = useState(null); const [qortalUrl, setQortalUrl] = useState(""); const [selectedGroup, setSelectedGroup] = useState(0); const [groupsProperties] = useRecoilState(groupsPropertiesAtom) const [valueTabPrivateApp, setValueTabPrivateApp] = useState(0); const [myGroupsWhereIAmAdminFromGlobal] = useRecoilState( myGroupsWhereIAmAdminAtom ); const myGroupsWhereIAmAdmin = useMemo(()=> { return myGroupsWhereIAmAdminFromGlobal?.filter((group)=> groupsProperties[group?.groupId]?.isOpen === false) }, [myGroupsWhereIAmAdminFromGlobal, groupsProperties]) const [isOpenPrivateModal, setIsOpenPrivateModal] = useState(false); const { show, setInfoSnackCustom, setOpenSnackGlobal, memberGroups } = useContext(MyContext); const myGroupsPrivate = useMemo(()=> { return memberGroups?.filter((group)=> groupsProperties[group?.groupId]?.isOpen === false) }, [memberGroups, groupsProperties]) const [privateAppValues, setPrivateAppValues] = useState({ name: "", service: "DOCUMENT", identifier: "", groupId: 0, }); const [newPrivateAppValues, setNewPrivateAppValues] = useState({ service: "DOCUMENT", identifier: "", name: "", }); const { getRootProps, getInputProps } = useDropzone({ accept: { "application/zip": [".zip"], // Only accept zip files }, maxSize: maxFileSize, multiple: false, // Disable multiple file uploads onDrop: (acceptedFiles) => { if (acceptedFiles.length > 0) { setFile(acceptedFiles[0]); // Set the file name } }, onDropRejected: (fileRejections) => { fileRejections.forEach(({ file, errors }) => { errors.forEach((error) => { if (error.code === "file-too-large") { console.error( `File ${file.name} is too large. Max size allowed is ${ maxFileSize / (1024 * 1024) } MB.` ); } }); }); }, }); const addPrivateApp = async () => { try { if (privateAppValues?.groupId === 0) return; await openApp(privateAppValues, true); } catch (error) { console.error(error) } }; const clearFields = () => { setPrivateAppValues({ name: "", service: "DOCUMENT", identifier: "", groupId: 0, }); setNewPrivateAppValues({ service: "DOCUMENT", identifier: "", name: "", }); setFile(null); setValueTabPrivateApp(0); setSelectedGroup(0); setLogo(null); }; const publishPrivateApp = async () => { try { if (selectedGroup === 0) return; if (!logo) throw new Error("Please select an image for a logo"); if (!myName) throw new Error("You need a Qortal name to publish"); if (!newPrivateAppValues?.name) throw new Error("Your app needs a name"); const base64Logo = await fileToBase64(logo); const base64App = await fileToBase64(file); const objectToSave = { app: base64App, logo: base64Logo, name: newPrivateAppValues.name, }; const object64 = await objectToBase64(objectToSave); const decryptedData = await window.sendMessage( "ENCRYPT_QORTAL_GROUP_DATA", { base64: object64, groupId: selectedGroup, } ); if (decryptedData?.error) { throw new Error( decryptedData?.error || "Unable to encrypt app. App not published" ); } const fee = await getFee("ARBITRARY"); await show({ message: "Would you like to publish this app?", publishFee: fee.fee + " QORT", }); await new Promise((res, rej) => { window .sendMessage("publishOnQDN", { data: decryptedData, identifier: newPrivateAppValues?.identifier, service: newPrivateAppValues?.service, }) .then((response) => { if (!response?.error) { res(response); return; } rej(response.error); }) .catch((error) => { rej(error.message || "An error occurred"); }); }); openApp( { identifier: newPrivateAppValues?.identifier, service: newPrivateAppValues?.service, name: myName, groupId: selectedGroup, }, true ); clearFields(); } catch (error) { setOpenSnackGlobal(true) setInfoSnackCustom({ type: "error", message: error?.message || "Unable to publish app", }); } }; const handleChange = (event: React.SyntheticEvent, newValue: number) => { setValueTabPrivateApp(newValue); }; function a11yProps(index: number) { return { id: `simple-tab-${index}`, "aria-controls": `simple-tabpanel-${index}`, }; } return ( <> { setIsOpenPrivateModal(true); }} sx={{ width: "80px", }} > + Private {isOpenPrivateModal && ( { if (e.key === "Enter") { if (valueTabPrivateApp === 0) { if ( !privateAppValues.name || !privateAppValues.service || !privateAppValues.identifier || !privateAppValues?.groupId ) return; addPrivateApp(); } } }} maxWidth="md" fullWidth={true} > {valueTabPrivateApp === 0 ? "Access private app" : "Publish private app"} {valueTabPrivateApp === 0 && ( <> setPrivateAppValues((prev) => { return { ...prev, name: e.target.value, }; }) } /> setPrivateAppValues((prev) => { return { ...prev, identifier: e.target.value, }; }) } /> )} {valueTabPrivateApp === 1 && ( <> Select .zip file containing static content:{" "} {` 50mb MB maximum`} {file && ( <> {`Selected: (${file?.name})`} )} {" "} {file ? "Change" : "Choose"} File setNewPrivateAppValues((prev) => { return { ...prev, identifier: e.target.value, }; }) } /> setNewPrivateAppValues((prev) => { return { ...prev, name: e.target.value, }; }) } /> setLogo(file)}> {logo?.name} )} )} ); };