import React, { useContext, useEffect, useMemo, useState } from "react"; import { AppCircle, AppCircleContainer, AppCircleLabel, AppDownloadButton, AppDownloadButtonText, AppInfoAppName, AppInfoSnippetContainer, AppInfoSnippetLeft, AppInfoSnippetMiddle, AppInfoSnippetRight, AppInfoUserName, AppLibrarySubTitle, AppPublishTagsContainer, AppsLibraryContainer, AppsParent, AppsWidthLimiter, PublishQAppCTAButton, PublishQAppChoseFile, PublishQAppInfo, } from "./Apps-styles"; import { Avatar, Box, ButtonBase, InputBase, InputLabel, MenuItem, Select, } from "@mui/material"; import { Select as BaseSelect, SelectProps, selectClasses, SelectRootSlotProps, } from "@mui/base/Select"; import { Option as BaseOption, optionClasses } from "@mui/base/Option"; import { styled } from "@mui/system"; import UnfoldMoreRoundedIcon from "@mui/icons-material/UnfoldMoreRounded"; import { Add } from "@mui/icons-material"; import { MyContext, getBaseApiReact, isMobile } from "../../App"; import LogoSelected from "../../assets/svgs/LogoSelected.svg"; import { Spacer } from "../../common/Spacer"; import { executeEvent } from "../../utils/events"; import { useDropzone } from "react-dropzone"; import { LoadingSnackbar } from "../Snackbar/LoadingSnackbar"; import { CustomizedSnackbars } from "../Snackbar/Snackbar"; import { getFee } from "../../background"; import { fileToBase64 } from "../../utils/fileReading"; const CustomSelect = styled(Select)({ border: "0.5px solid var(--50-white, #FFFFFF80)", padding: "0px 15px", borderRadius: "5px", height: "36px", width: "100%", maxWidth: "450px", "& .MuiSelect-select": { padding: "0px", }, "&:hover": { borderColor: "none", // Border color on hover }, "&.Mui-focused .MuiOutlinedInput-notchedOutline": { borderColor: "none", // Border color when focused }, "&.Mui-disabled": { opacity: 0.5, // Lower opacity when disabled }, "& .MuiSvgIcon-root": { color: "var(--50-white, #FFFFFF80)", }, }); const CustomMenuItem = styled(MenuItem)({ backgroundColor: "#1f1f1f", // Background for dropdown items color: "#ccc", "&:hover": { backgroundColor: "#333", // Darker background on hover }, }); export const AppPublish = ({ names, categories }) => { const [name, setName] = useState(""); const [title, setTitle] = useState(""); const [description, setDescription] = useState(""); const [category, setCategory] = useState(""); const [appType, setAppType] = useState("APP"); const [file, setFile] = useState(null); const { show } = useContext(MyContext); const [tag1, setTag1] = useState(""); const [tag2, setTag2] = useState(""); const [tag3, setTag3] = useState(""); const [tag4, setTag4] = useState(""); const [tag5, setTag5] = useState(""); const [openSnack, setOpenSnack] = useState(false); const [infoSnack, setInfoSnack] = useState(null); const [isLoading, setIsLoading] = useState(""); const maxFileSize = appType === "APP" ? 50 * 1024 * 1024 : 400 * 1024 * 1024; // 50MB or 400MB const { getRootProps, getInputProps } = useDropzone({ accept: { "application/zip": [".zip"], // Only accept zip files }, maxSize: maxFileSize, // Set the max size based on appType 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 getQapp = React.useCallback(async (name, appType) => { try { setIsLoading("Loading app information"); const url = `${getBaseApiReact()}/arbitrary/resources/search?service=${appType}&mode=ALL&name=${name}&includemetadata=true`; const response = await fetch(url, { method: "GET", headers: { "Content-Type": "application/json", }, }); if (!response?.ok) return; const responseData = await response.json(); if (responseData?.length > 0) { const myApp = responseData[0]; setTitle(myApp?.metadata?.title || ""); setDescription(myApp?.metadata?.description || ""); setCategory(myApp?.metadata?.category || ""); setTag1(myApp?.metadata?.tags[0] || ""); setTag2(myApp?.metadata?.tags[1] || ""); setTag3(myApp?.metadata?.tags[2] || ""); setTag4(myApp?.metadata?.tags[3] || ""); setTag5(myApp?.metadata?.tags[4] || ""); } } catch (error) { } finally { setIsLoading(""); } }, []); useEffect(() => { if (!name || !appType) return; getQapp(name, appType); }, [name, appType]); const publishApp = async () => { try { const data = { name, title, description, category, appType, file, }; const requiredFields = [ "name", "title", "description", "category", "appType", "file", ]; const missingFields: string[] = []; requiredFields.forEach((field) => { if (!data[field]) { missingFields.push(field); } }); if (missingFields.length > 0) { const missingFieldsString = missingFields.join(", "); const errorMsg = `Missing fields: ${missingFieldsString}`; throw new Error(errorMsg); } const fee = await getFee("ARBITRARY"); await show({ message: "Would you like to publish this app?", publishFee: fee.fee + " QORT", }); setIsLoading("Publishing... Please wait."); const fileBase64 = await fileToBase64(file); await new Promise((res, rej) => { chrome?.runtime?.sendMessage( { action: "publishOnQDN", payload: { data: fileBase64, service: appType, title, description, category, tag1, tag2, tag3, tag4, tag5, uploadType: 'zip' }, }, (response) => { if (!response?.error) { res(response); return; } rej(response.error); } ); }); setInfoSnack({ type: "success", message: "Successfully published. Please wait a couple minutes for the network to propogate the changes.", }); setOpenSnack(true); const dataObj = { name: name, service: appType, metadata: { title: title, description: description, category: category, }, created: Date.now(), }; executeEvent("addTab", { data: dataObj, }); } catch (error) { setInfoSnack({ type: "error", message: error?.message || "Unable to publish app", }); setOpenSnack(true); } finally { setIsLoading(""); } }; return ( Create Apps! Note: Currently, only one App and Website is allowed per Name. Name/App setName(event?.target.value)} > Select Name/App {" "} {/* This is the placeholder item */} {names.map((name) => { return {name}; })} App service type setAppType(event?.target.value)} > Select App Type {" "} {/* This is the placeholder item */} App Website Title setTitle(e.target.value)} sx={{ border: "0.5px solid var(--50-white, #FFFFFF80)", padding: "0px 15px", borderRadius: "5px", height: "36px", width: "100%", maxWidth: "450px", }} placeholder="Title" inputProps={{ "aria-label": "Title", fontSize: "14px", fontWeight: 400, }} /> Description setDescription(e.target.value)} sx={{ border: "0.5px solid var(--50-white, #FFFFFF80)", padding: "0px 15px", borderRadius: "5px", height: "36px", width: "100%", maxWidth: "450px", }} placeholder="Description" inputProps={{ "aria-label": "Description", fontSize: "14px", fontWeight: 400, }} /> Category setCategory(event?.target.value)} > Select Category {" "} {/* This is the placeholder item */} {categories?.map((category) => { return ( {category?.name} ); })} Tags setTag1(e.target.value)} sx={{ border: "0.5px solid var(--50-white, #FFFFFF80)", padding: "0px 15px", borderRadius: "5px", height: "36px", width: "100px", }} placeholder="Tag 1" inputProps={{ "aria-label": "Tag 1", fontSize: "14px", fontWeight: 400, }} /> setTag2(e.target.value)} sx={{ border: "0.5px solid var(--50-white, #FFFFFF80)", padding: "0px 15px", borderRadius: "5px", height: "36px", width: "100px", }} placeholder="Tag 2" inputProps={{ "aria-label": "Tag 2", fontSize: "14px", fontWeight: 400, }} /> setTag3(e.target.value)} sx={{ border: "0.5px solid var(--50-white, #FFFFFF80)", padding: "0px 15px", borderRadius: "5px", height: "36px", width: "100px", }} placeholder="Tag 3" inputProps={{ "aria-label": "Tag 3", fontSize: "14px", fontWeight: 400, }} /> setTag4(e.target.value)} sx={{ border: "0.5px solid var(--50-white, #FFFFFF80)", padding: "0px 15px", borderRadius: "5px", height: "36px", width: "100px", }} placeholder="Tag 4" inputProps={{ "aria-label": "Tag 4", fontSize: "14px", fontWeight: 400, }} /> setTag5(e.target.value)} sx={{ border: "0.5px solid var(--50-white, #FFFFFF80)", padding: "0px 15px", borderRadius: "5px", height: "36px", width: "100px", }} placeholder="Tag 5" inputProps={{ "aria-label": "Tag 5", fontSize: "14px", fontWeight: 400, }} /> Select .zip file containing static content:{" "} {`(${ appType === "APP" ? "50mb" : "400mb" } MB maximum)`} {file && ( <> {`Selected: (${file?.name})`} )} {" "} Choose File Publish ); };