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
+ })
+ }}
+ >
+
+
+
+
+
+
+
+ {myApp?.name}
+
+
+
+ )}
+ {myWebsite &&(
+ {
+ executeEvent("addTab", {
+ data: myWebsite
+ })
+ }}
+ >
+
+
+
+
+
+
+
+ {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