From 9685a40e6a532714f371ab3e630c85ab74ff1e0c Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 19 Oct 2024 04:54:59 +0300 Subject: [PATCH] ability to publish apps --- src/assets/svgs/qappDevelopText.svg | 3 + src/assets/svgs/qappDots.svg | 4 + src/background.ts | 24 +- src/backgroundFunctions/encryption.ts | 37 +- src/common/Spacer.tsx | 7 +- src/components/Apps/AppInfo.tsx | 3 + src/components/Apps/AppPublish.tsx | 512 ++++++++++++++++++++++++++ src/components/Apps/AppRating.tsx | 14 + src/components/Apps/Apps-styles.tsx | 74 +++- src/components/Apps/Apps.tsx | 483 +++++++++++++----------- src/components/Apps/AppsHome.tsx | 98 ++++- src/components/Apps/AppsLibrary.tsx | 45 ++- src/components/Apps/AppsNavBar.tsx | 7 +- src/components/Group/Group.tsx | 2 +- src/components/Snackbar/Snackbar.tsx | 5 +- src/qdn/publish/pubish.ts | 39 +- 16 files changed, 1102 insertions(+), 255 deletions(-) create mode 100644 src/assets/svgs/qappDevelopText.svg create mode 100644 src/assets/svgs/qappDots.svg create mode 100644 src/components/Apps/AppPublish.tsx create mode 100644 src/components/Apps/AppRating.tsx diff --git a/src/assets/svgs/qappDevelopText.svg b/src/assets/svgs/qappDevelopText.svg new file mode 100644 index 0000000..3aa786a --- /dev/null +++ b/src/assets/svgs/qappDevelopText.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/qappDots.svg b/src/assets/svgs/qappDots.svg new file mode 100644 index 0000000..086de81 --- /dev/null +++ b/src/assets/svgs/qappDots.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/background.ts b/src/background.ts index 68443d3..8d3fdf0 100644 --- a/src/background.ts +++ b/src/background.ts @@ -3905,19 +3905,35 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => { break; } case "publishOnQDN": { - const { data, identifier, service } = request.payload; + const { data, identifier, service, title, + description, + category, + tag1, + tag2, + tag3, + tag4, + tag5, uploadType } = request.payload; publishOnQDN({ data, identifier, - service + service, + title, + description, + category, + tag1, + tag2, + tag3, + tag4, + tag5, + uploadType }) .then((data) => { sendResponse(data); }) .catch((error) => { - console.error(error.message); - sendResponse({ error: error.message }); + console.error(error?.message); + sendResponse({ error: error?.message || 'Unable to publish' }); }); return true; break; diff --git a/src/backgroundFunctions/encryption.ts b/src/backgroundFunctions/encryption.ts index 4456381..a78f479 100644 --- a/src/backgroundFunctions/encryption.ts +++ b/src/backgroundFunctions/encryption.ts @@ -152,23 +152,40 @@ export const publishGroupEncryptedResource = async ({encryptedData, identifier}) throw new Error(error.message); } } -export const publishOnQDN = async ({data, identifier, service}) => { - try { - - if(data && identifier && service){ +export const publishOnQDN = async ({data, identifier, service, title, + description, + category, + tag1, + tag2, + tag3, + tag4, + tag5, + uploadType = 'file' +}) => { + + if(data && service){ const registeredName = await getNameInfo() if(!registeredName) throw new Error('You need a name to publish') - const res = await publishData({ - registeredName, file: data, service, identifier, uploadType: 'file', isBase64: true, withFee: true + + const res = await publishData({ + registeredName, file: data, service, identifier, uploadType, isBase64: true, withFee: true, title, + description, + category, + tag1, + tag2, + tag3, + tag4, + tag5 + }) return res + + } else { - throw new Error('Cannot encrypt content') + throw new Error('Cannot publish content') } - } catch (error: any) { - throw new Error(error.message); - } + } export function uint8ArrayToBase64(uint8Array: any) { diff --git a/src/common/Spacer.tsx b/src/common/Spacer.tsx index 3a387a7..e8b2288 100644 --- a/src/common/Spacer.tsx +++ b/src/common/Spacer.tsx @@ -1,12 +1,13 @@ import { Box } from "@mui/material"; -export const Spacer = ({ height }: any) => { +export const Spacer = ({ height, width }: any) => { return ( ); diff --git a/src/components/Apps/AppInfo.tsx b/src/components/Apps/AppInfo.tsx index af4787f..fecbb80 100644 --- a/src/components/Apps/AppInfo.tsx +++ b/src/components/Apps/AppInfo.tsx @@ -13,6 +13,7 @@ import { AppInfoUserName, AppsLibraryContainer, AppsParent, + AppsWidthLimiter, } from "./Apps-styles"; import { Avatar, Box, ButtonBase, InputBase } from "@mui/material"; import { Add } from "@mui/icons-material"; @@ -28,6 +29,7 @@ export const AppInfo = ({ app }) => { const isInstalled = app?.status?.status === 'READY' return ( + { }}> {isInstalled ? 'Open' : 'Download'} + diff --git a/src/components/Apps/AppPublish.tsx b/src/components/Apps/AppPublish.tsx new file mode 100644 index 0000000..117aff2 --- /dev/null +++ b/src/components/Apps/AppPublish.tsx @@ -0,0 +1,512 @@ +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 } 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 + + + + + + ); +}; diff --git a/src/components/Apps/AppRating.tsx b/src/components/Apps/AppRating.tsx new file mode 100644 index 0000000..b063563 --- /dev/null +++ b/src/components/Apps/AppRating.tsx @@ -0,0 +1,14 @@ +import { Rating } from '@mui/material' +import React, { useState } from 'react' + +export const AppRating = () => { + const [value, setValue] = useState(0) + return ( +
+ { + + }} precision={0.1} /> +
+ ) +} diff --git a/src/components/Apps/Apps-styles.tsx b/src/components/Apps/Apps-styles.tsx index d219bcb..74b0526 100644 --- a/src/components/Apps/Apps-styles.tsx +++ b/src/components/Apps/Apps-styles.tsx @@ -40,6 +40,13 @@ import { })); export const AppsLibraryContainer = styled(Box)(({ theme }) => ({ + display: "flex", + width: "100%", + flexDirection: 'column', + justifyContent: 'flex-start', + alignItems: 'center', + })); + export const AppsWidthLimiter = styled(Box)(({ theme }) => ({ display: "flex", width: "90%", flexDirection: 'column', @@ -138,7 +145,8 @@ import { display: 'flex', justifyContent: 'center', alignItems: 'center', - borderRadius: '25px' + borderRadius: '25px', + alignSelf: 'center' })); export const AppDownloadButtonText = styled(Typography)(({ theme }) => ({ @@ -147,6 +155,13 @@ import { lineHeight: 1.2, })); + export const AppPublishTagsContainer = styled(Box)(({theme})=> ({ + gap: '10px', + flexWrap: 'wrap', + justifyContent: 'flex-start', + width: '100%', + display: 'flex' + })) export const AppInfoSnippetMiddle = styled(Box)(({ theme }) => ({ @@ -205,4 +220,59 @@ import { justifyContent: 'center' })); - \ No newline at end of file + export const PublishQAppCTAParent = styled(Box)(({ theme }) => ({ + display: "flex", + justifyContent: 'space-between', + alignItems: 'center', + width: '100%', + backgroundColor: '#181C23' + })); + + export const PublishQAppCTALeft = styled(Box)(({ theme }) => ({ + display: "flex", + justifyContent: 'flex-start', + alignItems: 'center', + })); + export const PublishQAppCTARight = styled(Box)(({ theme }) => ({ + display: "flex", + justifyContent: 'flex-end', + alignItems: 'center', + })); + + export const PublishQAppCTAButton = styled(ButtonBase)(({ theme }) => ({ + width: '101px', + height: '29px', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + borderRadius: '25px', + border: '1px solid #FFFFFF' + })); + export const PublishQAppDotsBG = styled(Box)(({ theme }) => ({ + display: "flex", + justifyContent: 'center', + alignItems: 'center', + width: '60px', + height: '60px', + backgroundColor: '#4BBCFE' + })); + + export const PublishQAppInfo = styled(Typography)(({ theme }) => ({ + fontSize: '10px', + fontWeight: 400, + lineHeight: 1.2, + fontStyle: 'italic' + })); + + export const PublishQAppChoseFile = styled(ButtonBase)(({ theme }) => ({ + width: '101px', + height: '30px', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + borderRadius: '5px', + backgroundColor: '#0091E1', + color: 'white', + fontWeight: 600, + fontSize: '10px' + })); \ No newline at end of file diff --git a/src/components/Apps/Apps.tsx b/src/components/Apps/Apps.tsx index f17033f..2ea6037 100644 --- a/src/components/Apps/Apps.tsx +++ b/src/components/Apps/Apps.tsx @@ -1,241 +1,290 @@ -import React, { useContext, useEffect, useRef, useState } from 'react' -import { AppsHome } from './AppsHome' -import { Spacer } from '../../common/Spacer' -import { MyContext, getBaseApiReact } from '../../App' -import { AppsLibrary } from './AppsLibrary' -import { AppInfo } from './AppInfo' -import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from '../../utils/events' -import { AppsNavBar } from './AppsNavBar' -import { AppsParent } from './Apps-styles' -import { AppViewer } from './AppViewer' -import AppViewerContainer from './AppViewerContainer' +import React, { useContext, useEffect, useRef, useState } from "react"; +import { AppsHome } from "./AppsHome"; +import { Spacer } from "../../common/Spacer"; +import { MyContext, getBaseApiReact } from "../../App"; +import { AppsLibrary } from "./AppsLibrary"; +import { AppInfo } from "./AppInfo"; +import { + executeEvent, + subscribeToEvent, + unsubscribeFromEvent, +} from "../../utils/events"; +import { AppsNavBar } from "./AppsNavBar"; +import { AppsParent } from "./Apps-styles"; +import { AppViewer } from "./AppViewer"; +import AppViewerContainer from "./AppViewerContainer"; import ShortUniqueId from "short-unique-id"; +import { AppPublish } from "./AppPublish"; const uid = new ShortUniqueId({ length: 8 }); +export const Apps = ({ mode, setMode, show , myName}) => { + const [availableQapps, setAvailableQapps] = useState([]); + const [downloadedQapps, setDownloadedQapps] = useState([]); + const [selectedAppInfo, setSelectedAppInfo] = useState(null); + const [tabs, setTabs] = useState([]); + const [selectedTab, setSelectedTab] = useState(null); + const [isNewTabWindow, setIsNewTabWindow] = useState(false); + const [categories, setCategories] = useState([]) + const [myApp, setMyApp] = useState(null) + const [myWebsite, setMyWebsite] = useState(null) -export const Apps = ({mode, setMode, show}) => { - const [availableQapps, setAvailableQapps] = useState([]) - const [downloadedQapps, setDownloadedQapps] = useState([]) - const [selectedAppInfo, setSelectedAppInfo] = useState(null) - const [tabs, setTabs] = useState([]) - const [selectedTab, setSelectedTab] = useState(null) - const [isNewTabWindow, setIsNewTabWindow] = useState(false) - - useEffect(()=> { - setTimeout(() => { - executeEvent('setTabsToNav', { - data: { - tabs: tabs, - selectedTab: selectedTab - } - }) - }, 100); - }, [show, tabs, selectedTab]) - - const getQapps = React.useCallback( - async () => { - try { - let apps = [] - let websites = [] - // dispatch(setIsLoadingGlobal(true)) - const url = `${getBaseApiReact()}/arbitrary/resources/search?service=APP&mode=ALL&includestatus=true&limit=0&includemetadata=true`; - - const response = await fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - if(!response?.ok) return - const responseData = await response.json(); - const urlWebsites = `${getBaseApiReact()}/arbitrary/resources/search?service=WEBSITE&mode=ALL&includestatus=true&limit=0&includemetadata=true`; - - const responseWebsites = await fetch(urlWebsites, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - if(!responseWebsites?.ok) return - const responseDataWebsites = await responseWebsites.json(); - apps = responseData - websites = responseDataWebsites - const combine = [...apps, ...websites] - setAvailableQapps(combine) - setDownloadedQapps(combine.filter((qapp)=> qapp?.status?.status === 'READY')) - } catch (error) { - } finally { - // dispatch(setIsLoadingGlobal(false)) - } + useEffect(() => { + setTimeout(() => { + executeEvent("setTabsToNav", { + data: { + tabs: tabs, + selectedTab: selectedTab, + isNewTabWindow: isNewTabWindow, }, - [] + }); + }, 100); + }, [show, tabs, selectedTab, isNewTabWindow]); + + const getCategories = React.useCallback(async () => { + try { + const url = `${getBaseApiReact()}/arbitrary/categories`; + + const response = await fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + if (!response?.ok) return; + const responseData = await response.json(); + + setCategories(responseData); + + } catch (error) { + } finally { + // dispatch(setIsLoadingGlobal(false)) + } + }, []); + + const getQapps = React.useCallback(async (myName) => { + try { + let apps = []; + let websites = []; + // dispatch(setIsLoadingGlobal(true)) + const url = `${getBaseApiReact()}/arbitrary/resources/search?service=APP&mode=ALL&includestatus=true&limit=0&includemetadata=true`; + + const response = await fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + if (!response?.ok) return; + const responseData = await response.json(); + const urlWebsites = `${getBaseApiReact()}/arbitrary/resources/search?service=WEBSITE&mode=ALL&includestatus=true&limit=0&includemetadata=true`; + + const responseWebsites = await fetch(urlWebsites, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + if (!responseWebsites?.ok) return; + const responseDataWebsites = await responseWebsites.json(); + const findMyWebsite = responseDataWebsites.find((web)=> web.name === myName) + if(findMyWebsite){ + setMyWebsite(findMyWebsite) + } + const findMyApp = responseData.find((web)=> web.name === myName) + console.log('findMyApp', findMyApp) + if(findMyApp){ + setMyWebsite(findMyApp) + } + apps = responseData; + websites = responseDataWebsites; + const combine = [...apps, ...websites]; + setAvailableQapps(combine); + setDownloadedQapps( + combine.filter((qapp) => qapp?.status?.status === "READY") ); - useEffect(()=> { - getQapps() - }, [getQapps]) + } catch (error) { + } finally { + // dispatch(setIsLoadingGlobal(false)) + } + }, []); + useEffect(() => { + getQapps(myName); + getCategories() + }, [getQapps, getCategories, myName]); + const selectedAppInfoFunc = (e) => { + const data = e.detail?.data; + setSelectedAppInfo(data); + setMode("appInfo"); + }; - const selectedAppInfoFunc = (e) => { - const data = e.detail?.data; - setSelectedAppInfo(data) - setMode('appInfo') - }; - - useEffect(() => { - subscribeToEvent("selectedAppInfo", selectedAppInfoFunc); - - return () => { - unsubscribeFromEvent("selectedAppInfo", selectedAppInfoFunc); - }; - }, []); + useEffect(() => { + subscribeToEvent("selectedAppInfo", selectedAppInfoFunc); - const navigateBackFunc = (e) => { - if(mode === 'appInfo'){ - setMode('library') - } else if(mode === 'library'){ - setMode('home') - } else { - const iframeId = `browser-iframe-${selectedTab?.tabId}` - const iframe = document.getElementById(iframeId); - console.log('iframe', iframe) -// Go Back in the iframe's history -if (iframe) { - console.log(iframe.contentWindow) - if (iframe && iframe.contentWindow) { - const iframeWindow = iframe.contentWindow; - if (iframeWindow && iframeWindow.history) { - iframeWindow.history.back(); + return () => { + unsubscribeFromEvent("selectedAppInfo", selectedAppInfoFunc); + }; + }, []); + + const navigateBackFunc = (e) => { + if (mode === "appInfo") { + setMode("library"); + } else if (mode === "library") { + if (isNewTabWindow) { + setMode("viewer"); + } else { + setMode("home"); + } + } else if(mode === 'publish'){ + setMode('library') + } else { + const iframeId = `browser-iframe-${selectedTab?.tabId}`; + const iframe = document.getElementById(iframeId); + console.log("iframe", iframe); + // Go Back in the iframe's history + if (iframe) { + console.log(iframe.contentWindow); + if (iframe && iframe.contentWindow) { + const iframeWindow = iframe.contentWindow; + if (iframeWindow && iframeWindow.history) { + iframeWindow.history.back(); + } } } - } + } + }; - } - }; - - useEffect(() => { - subscribeToEvent("navigateBack", navigateBackFunc); - - return () => { - unsubscribeFromEvent("navigateBack", navigateBackFunc); - }; - }, [mode, selectedTab]); + useEffect(() => { + subscribeToEvent("navigateBack", navigateBackFunc); + return () => { + unsubscribeFromEvent("navigateBack", navigateBackFunc); + }; + }, [mode, selectedTab]); - const addTabFunc = (e) => { - const data = e.detail?.data; - const newTab = { - ...data, - tabId: uid.rnd() - } - setTabs((prev)=> [...prev, newTab]) - setSelectedTab(newTab) - setMode('viewer') - - setIsNewTabWindow(false) - }; - - useEffect(() => { - subscribeToEvent("addTab", addTabFunc); - - return () => { - unsubscribeFromEvent("addTab", addTabFunc); - }; - }, [tabs]); + const addTabFunc = (e) => { + const data = e.detail?.data; + const newTab = { + ...data, + tabId: uid.rnd(), + }; + setTabs((prev) => [...prev, newTab]); + setSelectedTab(newTab); + setMode("viewer"); - const setSelectedTabFunc = (e) => { - const data = e.detail?.data; - - - setSelectedTab(data) - setTimeout(() => { - executeEvent('setTabsToNav', { - data: { - tabs: tabs, - selectedTab: data - } - }) - }, 100); - setIsNewTabWindow(false) - }; - - useEffect(() => { - subscribeToEvent("setSelectedTab", setSelectedTabFunc); - - return () => { - unsubscribeFromEvent("setSelectedTab", setSelectedTabFunc); - }; - }, [tabs]); + setIsNewTabWindow(false); + }; - const removeTabFunc = (e) => { - const data = e.detail?.data; - const copyTabs = [...tabs].filter((tab)=> tab?.tabId !== data?.tabId) - if(copyTabs?.length === 0){ - setMode('home') - } - else{ - setSelectedTab(copyTabs[0]) - } - setTabs(copyTabs) - setSelectedTab(copyTabs[0]) - setTimeout(() => { - executeEvent('setTabsToNav', { - data: { - tabs: copyTabs, - selectedTab: copyTabs[0] - } - }) - }, 400); - - }; - - useEffect(() => { - subscribeToEvent("removeTab", removeTabFunc); - - return () => { - unsubscribeFromEvent("removeTab", removeTabFunc); - }; - }, [tabs]); + useEffect(() => { + subscribeToEvent("addTab", addTabFunc); - const setNewTabWindowFunc = (e) => { - setIsNewTabWindow(true) - - }; - - useEffect(() => { - subscribeToEvent("newTabWindow", setNewTabWindowFunc); - - return () => { - unsubscribeFromEvent("newTabWindow", setNewTabWindowFunc); - }; - }, [tabs]); + return () => { + unsubscribeFromEvent("addTab", addTabFunc); + }; + }, [tabs]); - - + const setSelectedTabFunc = (e) => { + const data = e.detail?.data; + + setSelectedTab(data); + setTimeout(() => { + executeEvent("setTabsToNav", { + data: { + tabs: tabs, + selectedTab: data, + isNewTabWindow: isNewTabWindow, + }, + }); + }, 100); + setIsNewTabWindow(false); + }; + + useEffect(() => { + subscribeToEvent("setSelectedTab", setSelectedTabFunc); + + return () => { + unsubscribeFromEvent("setSelectedTab", setSelectedTabFunc); + }; + }, [tabs, isNewTabWindow]); + + const removeTabFunc = (e) => { + const data = e.detail?.data; + const copyTabs = [...tabs].filter((tab) => tab?.tabId !== data?.tabId); + if (copyTabs?.length === 0) { + setMode("home"); + } else { + setSelectedTab(copyTabs[0]); + } + setTabs(copyTabs); + setSelectedTab(copyTabs[0]); + setTimeout(() => { + executeEvent("setTabsToNav", { + data: { + tabs: copyTabs, + selectedTab: copyTabs[0], + }, + }); + }, 400); + }; + + useEffect(() => { + subscribeToEvent("removeTab", removeTabFunc); + + return () => { + unsubscribeFromEvent("removeTab", removeTabFunc); + }; + }, [tabs]); + + const setNewTabWindowFunc = (e) => { + setIsNewTabWindow(true); + }; + + useEffect(() => { + subscribeToEvent("newTabWindow", setNewTabWindowFunc); + + return () => { + unsubscribeFromEvent("newTabWindow", setNewTabWindowFunc); + }; + }, [tabs]); return ( - - {mode !== 'viewer' && ( - + + {mode !== "viewer" && } + {mode === "home" && ( + + )} + {mode === "library" && ( + + )} + {mode === "appInfo" && } + {mode === "publish" && } - )} - {mode === 'home' && } - {mode === 'library' && } - {mode === 'appInfo' && } - - {tabs.map((tab)=> { - return - })} - - {isNewTabWindow && ( - - )} - {mode !== 'viewer' && ( - + {tabs.map((tab) => { + return ( + + ); + })} - )} + {isNewTabWindow && mode === "viewer" && ( + <> + + + + )} + {mode !== "viewer" && } - ) -} + ); +}; diff --git a/src/components/Apps/AppsHome.tsx b/src/components/Apps/AppsHome.tsx index ab5dc31..d9142a1 100644 --- a/src/components/Apps/AppsHome.tsx +++ b/src/components/Apps/AppsHome.tsx @@ -12,7 +12,7 @@ import { getBaseApiReact } from "../../App"; import LogoSelected from "../../assets/svgs/LogoSelected.svg"; import { executeEvent } from "../../utils/events"; -export const AppsHome = ({ downloadedQapps, setMode }) => { +export const AppsHome = ({ downloadedQapps, setMode, myApp, myWebsite, myName }) => { return ( { Add - {downloadedQapps?.map((app) => { + {myApp &&( + { + executeEvent("addTab", { + data: myApp + }) + }} + > + + + + center-icon + + + + {myApp?.name} + + + + )} + {myWebsite &&( + { + executeEvent("addTab", { + data: myWebsite + }) + }} + > + + + + center-icon + + + + {myWebsite?.name} + + + + )} + {downloadedQapps?.filter((item)=> item?.name !== myName).map((app) => { return ( { +export const AppsLibrary = ({ downloadedQapps, availableQapps, setMode }) => { const [searchValue, setSearchValue] = useState(""); const virtuosoRef = useRef(); const { rootHeight } = useContext(MyContext); @@ -113,6 +122,7 @@ export const AppsLibrary = ({ downloadedQapps, availableQapps }) => { return ( + { + {searchedList?.length > 0 ? ( + { }} /> + ) : ( <> + Official Apps @@ -174,6 +188,11 @@ export const AppsLibrary = ({ downloadedQapps, availableQapps }) => { height: "80px", width: "60px", }} + onClick={()=> { + executeEvent("addTab", { + data: qapp + }) + }} > { ); })} + + Create Apps! - Featured + + + + + + + + + + { + setMode('publish') + }}> + + Publish + + + + + + Categories + )} diff --git a/src/components/Apps/AppsNavBar.tsx b/src/components/Apps/AppsNavBar.tsx index 2e1ec5c..09c4e22 100644 --- a/src/components/Apps/AppsNavBar.tsx +++ b/src/components/Apps/AppsNavBar.tsx @@ -14,7 +14,7 @@ import TabComponent from "./TabComponent"; export const AppsNavBar = () => { const [tabs, setTabs] = useState([]) const [selectedTab, setSelectedTab] = useState([]) - + const [isNewTabWindow, setIsNewTabWindow] = useState(false) const tabsRef = useRef(null); useEffect(() => { @@ -30,10 +30,11 @@ export const AppsNavBar = () => { }, [tabs.length]); // Dependency on the number of tabs const setTabsToNav = (e) => { - const {tabs, selectedTab} = e.detail?.data; + const {tabs, selectedTab, isNewTabWindow} = e.detail?.data; setTabs([...tabs]) setSelectedTab({...selectedTab}) + setIsNewTabWindow(isNewTabWindow) }; useEffect(() => { @@ -68,7 +69,7 @@ export const AppsNavBar = () => { {tabs?.map((tab) => ( } // Pass custom component + label={} // Pass custom component sx={{ "&.Mui-selected": { color: "white", diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 3a147b3..64e1adb 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -2737,7 +2737,7 @@ export const Group = ({ /> )} {isMobile && ( - + )} { !isMobile && !selectedGroup && diff --git a/src/components/Snackbar/Snackbar.tsx b/src/components/Snackbar/Snackbar.tsx index a909487..3e5fccb 100644 --- a/src/components/Snackbar/Snackbar.tsx +++ b/src/components/Snackbar/Snackbar.tsx @@ -3,7 +3,7 @@ import Button from '@mui/material/Button'; import Snackbar, { SnackbarCloseReason } from '@mui/material/Snackbar'; import Alert from '@mui/material/Alert'; -export const CustomizedSnackbars = ({open, setOpen, info, setInfo}) => { +export const CustomizedSnackbars = ({open, setOpen, info, setInfo, duration}) => { @@ -19,9 +19,10 @@ export const CustomizedSnackbars = ({open, setOpen, info, setInfo}) => { setInfo(null) }; + if(!open) return null return (
- + { - + console.log({ + registeredName, + file, + service, + identifier, + uploadType, + isBase64, + filename, + withFee, + title, + description, + category, + tag1, + tag2, + tag3, + tag4, + tag5, + feeAmount + }) const validateName = async (receiverName: string) => { return await reusableGet(`/names/${receiverName}`) } @@ -153,7 +171,7 @@ export const publishData = async ({ fee = feeAmount } else if (withFee) { const res = await getArbitraryFee() - + console.log('res', res) if (res.fee) { fee = res.fee } else { @@ -162,9 +180,9 @@ export const publishData = async ({ } let transactionBytes = await uploadData(registeredName, file, fee) - - if (transactionBytes.error) { - throw new Error(transactionBytes.message || 'Error when uploading') + console.log('transactionBytes', transactionBytes) + if (!transactionBytes || transactionBytes.error) { + throw new Error(transactionBytes?.message || 'Error when uploading') } else if (transactionBytes.includes('Error 500 Internal Server Error')) { throw new Error('Error when uploading') } @@ -183,7 +201,8 @@ export const publishData = async ({ } const uploadData = async (registeredName: string, file:any, fee: number) => { - if (identifier != null && identifier.trim().length > 0) { + console.log('uploadData', registeredName, file, fee) + let postBody = '' let urlSuffix = '' @@ -211,8 +230,8 @@ export const publishData = async ({ } let uploadDataUrl = `/arbitrary/${service}/${registeredName}${urlSuffix}` - - if (identifier.trim().length > 0) { + console.log('uploadDataUrl', uploadDataUrl) + if (identifier?.trim().length > 0) { uploadDataUrl = `/arbitrary/${service}/${registeredName}/${identifier}${urlSuffix}` } @@ -254,14 +273,16 @@ export const publishData = async ({ if (tag5 != null && tag5 != 'undefined') { uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag5) } + console.log('uploadDataUrl2', uploadDataUrl) return await reusablePost(uploadDataUrl, postBody) - } + } try { return await validate() } catch (error: any) { + console.log('error2', error) throw new Error(error?.message) } } \ No newline at end of file