Browse Source

Added download for gateway link

feature/new-gateway-modal
Justin Ferrari 9 months ago
parent
commit
701c6da4fe
  1. 43
      package-lock.json
  2. 4
      package.json
  3. BIN
      src/assets/img/AbstractTechArt.png
  4. 22
      src/assets/svgs/DownloadCircleSVG.tsx
  5. 13
      src/components/layout/Navbar/Navbar.tsx
  6. 17
      src/components/modals/ReusableModal-styles.tsx
  7. 5
      src/components/modals/ReusableModal.tsx
  8. 5
      src/index.css
  9. BIN
      src/styles/fonts/Figtree.ttf
  10. 1
      src/styles/theme.tsx
  11. 59
      src/wrappers/GlobalWrapper-styles.tsx
  12. 240
      src/wrappers/GlobalWrapper.tsx

43
package-lock.json generated

@ -37,13 +37,15 @@
"short-unique-id": "^4.4.4",
"slate": "^0.91.4",
"slate-history": "^0.86.0",
"slate-react": "^0.91.11"
"slate-react": "^0.91.11",
"ua-parser-js": "^1.0.37"
},
"devDependencies": {
"@mui/types": "^7.2.3",
"@types/react": "^18.0.28",
"@types/react-copy-to-clipboard": "^5.0.4",
"@types/react-dom": "^18.0.11",
"@types/ua-parser-js": "^0.7.39",
"@vitejs/plugin-react-swc": "^3.2.0",
"prettier": "^2.8.6",
"typescript": "^4.9.3",
@ -1764,6 +1766,12 @@
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
},
"node_modules/@types/ua-parser-js": {
"version": "0.7.39",
"resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.39.tgz",
"integrity": "sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==",
"dev": true
},
"node_modules/@types/use-sync-external-store": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
@ -3874,6 +3882,28 @@
"node": ">=4.2.0"
}
},
"node_modules/ua-parser-js": {
"version": "1.0.37",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz",
"integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/ua-parser-js"
},
{
"type": "paypal",
"url": "https://paypal.me/faisalman"
},
{
"type": "github",
"url": "https://github.com/sponsors/faisalman"
}
],
"engines": {
"node": "*"
}
},
"node_modules/update-browserslist-db": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
@ -5190,6 +5220,12 @@
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
},
"@types/ua-parser-js": {
"version": "0.7.39",
"resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.39.tgz",
"integrity": "sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==",
"dev": true
},
"@types/use-sync-external-store": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
@ -6781,6 +6817,11 @@
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"dev": true
},
"ua-parser-js": {
"version": "1.0.37",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz",
"integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ=="
},
"update-browserslist-db": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",

4
package.json

@ -38,13 +38,15 @@
"short-unique-id": "^4.4.4",
"slate": "^0.91.4",
"slate-history": "^0.86.0",
"slate-react": "^0.91.11"
"slate-react": "^0.91.11",
"ua-parser-js": "^1.0.37"
},
"devDependencies": {
"@mui/types": "^7.2.3",
"@types/react": "^18.0.28",
"@types/react-copy-to-clipboard": "^5.0.4",
"@types/react-dom": "^18.0.11",
"@types/ua-parser-js": "^0.7.39",
"@vitejs/plugin-react-swc": "^3.2.0",
"prettier": "^2.8.6",
"typescript": "^4.9.3",

BIN
src/assets/img/AbstractTechArt.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

22
src/assets/svgs/DownloadCircleSVG.tsx

@ -0,0 +1,22 @@
import { IconTypes } from "./IconTypes";
export const DownloadCircleSVG: React.FC<IconTypes> = ({
color,
height,
width,
className,
onClickFunc,
}) => {
return (
<svg
fill={color}
className={className}
xmlns="http://www.w3.org/2000/svg"
height={height}
viewBox="0 -960 960 960"
width={width}
>
<path d="M280-280h400v-80H280v80Zm200-120 160-160-56-56-64 62v-166h-80v166l-64-62-56 56 160 160Zm0 320q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z" />
</svg>
);
};

13
src/components/layout/Navbar/Navbar.tsx

@ -41,6 +41,7 @@ interface Props {
authenticate: () => void;
hasAttemptedToFetchShopInitial: boolean;
setTheme: (val: string) => void;
displayDownloadGatewayModalFunc: () => void;
}
const NavBar: React.FC<Props> = ({
@ -49,7 +50,8 @@ const NavBar: React.FC<Props> = ({
userAvatar,
authenticate,
hasAttemptedToFetchShopInitial,
setTheme
setTheme,
displayDownloadGatewayModalFunc
}) => {
const navigate = useNavigate();
const dispatch = useDispatch();
@ -128,7 +130,14 @@ const NavBar: React.FC<Props> = ({
}}
>
{!isAuthenticated && (
<AuthenticateButton onClick={authenticate}>
<AuthenticateButton onClick={() => {
const isGateWay = window.location.href.includes("https");
if (isGateWay) {
displayDownloadGatewayModalFunc();
return;
}
authenticate();
}}>
<ExitToAppIcon />
Authenticate
</AuthenticateButton>

17
src/components/modals/ReusableModal-styles.tsx

@ -1,5 +1,6 @@
import { styled } from "@mui/system";
import { Box } from "@mui/material";
import AbstractTechArt from "../../assets/img/AbstractTechArt.png";
export const ReusableModalBody = styled(Box)(({ theme }) => ({
position: "absolute",
@ -35,5 +36,21 @@ export const ReusableModalBody = styled(Box)(({ theme }) => ({
},
"&::-webkit-scrollbar-thumb:hover": {
backgroundColor: theme.palette.mode === "light" ? "#b7bcc4" : "#474646"
},
"&.download-qortal-modal": {
"&::before": {
content: "''",
position: "absolute",
top: "0",
left: "0",
width: "100%",
height: "100%",
backgroundImage: `url(${AbstractTechArt})`,
backgroundSize: "cover",
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
filter: "blur(2px) contrast(0.3)",
zIndex: -1
}
}
}));

5
src/components/modals/ReusableModal.tsx

@ -9,6 +9,7 @@ interface MyModalProps {
children: any;
customStyles?: any;
id?: string;
className?: string;
}
export const ReusableModal: React.FC<MyModalProps> = ({
@ -17,7 +18,8 @@ export const ReusableModal: React.FC<MyModalProps> = ({
onClose,
// onSubmit,
children,
customStyles = {}
customStyles = {},
className
}) => {
const theme = useTheme();
return (
@ -32,6 +34,7 @@ export const ReusableModal: React.FC<MyModalProps> = ({
sx={{
...customStyles
}}
className={className}
>
{children}
</ReusableModalBody>

5
src/index.css

@ -43,6 +43,11 @@
src: url("./styles/fonts/Montserrat.ttf") format("truetype");
}
@font-face {
font-family: "Figtree";
src: url("./styles/fonts/Figtree.ttf") format("truetype");
}
:root {
padding: 0px;
margin: 0px;

BIN
src/styles/fonts/Figtree.ttf

Binary file not shown.

1
src/styles/theme.tsx

@ -13,6 +13,7 @@ const commonThemeOptions = {
"Catamaran",
"Cairo",
"Arial",
"Figtree"
].join(","),
h1: {
fontSize: "2rem",

59
src/wrappers/GlobalWrapper-styles.tsx

@ -1,5 +1,5 @@
import { styled } from "@mui/system";
import { Button, Typography } from "@mui/material";
import { Button, Stack, Typography } from "@mui/material";
export const CustomModalTitle = styled(Typography)({
textAlign: "center",
@ -24,3 +24,60 @@ export const CustomModalButton = styled(Button)(({ theme }) => ({
cursor: "pointer"
}
}));
export const DownloadQortalCol = styled(Stack)(({theme}) => ({
display: "flex",
flexDirection: "column",
justifyContent: "flex-start",
alignItems: "center",
gap: "15px",
textAlign: "center",
width: "100%",
height: "100%",
}))
export const QortalIcon = styled("img")({
width: "120px",
height: "120px",
userSelect: "none"
})
export const DownloadQortalFont = styled(Typography)(({theme}) => ({
fontFamily: "Figtree",
letterSpacing: "2.2px",
lineHeight: '52px',
fontSize: "50px",
fontWeight: 600,
color: theme.palette.text.primary
}))
export const DownloadQortalSubFont = styled(Typography)(({theme}) => ({
fontFamily: "Raleway",
fontSize: "25px",
lineHeight: "38px",
fontWeight: 500,
color: theme.palette.text.primary,
userSelect: "none"
}))
export const DownloadNowButton = styled(Button)(({theme}) => ({
fontFamily: "Montserrat",
fontSize: "22px",
marginTop: "20px",
fontWeight: 500,
width: "90%",
padding: "12px 20px",
gap: "10px",
backgroundColor: theme.palette.secondary.main,
color: "#fff",
transition: "all 0.3s ease-in-out",
borderRadius: "10px",
boxShadow:
"rgba(50, 50, 93, 0.25) 0px 2px 5px -1px, rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;",
"&:hover": {
cursor: "pointer",
backgroundColor: theme.palette.secondary.dark,
boxShadow:
"rgba(50, 50, 93, 0.35) 0px 3px 5px -1px, rgba(0, 0, 0, 0.4) 0px 2px 3px -1px;"
}
}))

240
src/wrappers/GlobalWrapper.tsx

@ -3,23 +3,20 @@ import { useDispatch, useSelector } from "react-redux";
import { addUser } from "../state/features/authSlice";
import { RootState } from "../state/store";
import CreateStoreModal, {
onPublishParam
onPublishParam,
} from "../components/modals/CreateStoreModal";
import EditStoreModal, {
onPublishParamEdit
onPublishParamEdit,
} from "../components/modals/EditStoreModal";
import {
setCurrentStore,
setDataContainer,
toggleEditStoreModal,
toggleCreateStoreModal,
setIsLoadingGlobal,
resetProducts,
resetListProducts,
DataContainer,
ProductDataContainer,
updateRecentlyVisitedStoreId,
clearDataCotainer
clearDataCotainer,
} from "../state/features/globalSlice";
import NavBar from "../components/layout/Navbar/Navbar";
import PageLoader from "../components/common/PageLoader";
@ -32,14 +29,26 @@ import {
addToAllMyStores,
addToHashMapStores,
addToStores,
setAllMyStores
setAllMyStores,
} from "../state/features/storeSlice";
import { useFetchStores } from "../hooks/useFetchStores";
import { DATA_CONTAINER_BASE, STORE_BASE } from "../constants/identifiers";
import { ReusableModal } from "../components/modals/ReusableModal";
import { Box, Button, Stack, Typography } from "@mui/material";
import { Stack, Typography, useTheme } from "@mui/material";
import { useNavigate } from "react-router-dom";
import { CustomModalButton, CustomModalTitle } from "./GlobalWrapper-styles";
import {
CustomModalButton,
CustomModalTitle,
DownloadNowButton,
DownloadQortalCol,
DownloadQortalFont,
DownloadQortalSubFont,
QortalIcon,
} from "./GlobalWrapper-styles";
import QortalLogo from "../assets/img/Q-AppsLogo.webp";
import { DownloadCircleSVG } from "../assets/svgs/DownloadCircleSVG";
import { UAParser } from "ua-parser-js";
interface Props {
children: React.ReactNode;
setTheme: (val: string) => void;
@ -54,6 +63,10 @@ interface ShortDataContainer {
const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
const dispatch = useDispatch();
const navigate = useNavigate();
const theme = useTheme();
// Determine which OS they're on
const parser = new UAParser();
// Get user from auth
const user = useSelector((state: RootState) => state.auth.user);
@ -94,6 +107,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
const [openDataContainerModal, setOpenDataContainer] =
useState<boolean>(false);
const [retryDataContainer, setRetryDataContainer] = useState<boolean>(false);
const [showDownloadModal, setShowDownloadModal] = useState<boolean>(false);
useEffect(() => {
if (!user?.name) return;
@ -107,7 +121,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
action: "GET_QDN_RESOURCE_URL",
name: user?.name,
service: "THUMBNAIL",
identifier: "qortal_avatar"
identifier: "qortal_avatar",
});
if (url === "Resource does not exist") return;
@ -142,8 +156,8 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
const responseBlogs = await fetch(url2, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
"Content-Type": "application/json",
},
});
const dataMetadata = await responseBlogs.json();
if (dataMetadata.length === 0) {
@ -157,8 +171,8 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
const responseBlogs = await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
"Content-Type": "application/json",
},
});
const responseDataBlogs = await responseBlogs.json();
const filterOut = responseDataBlogs.filter((blog: any) =>
@ -173,8 +187,8 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
const response = await fetch(urlBlog, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
"Content-Type": "application/json",
},
});
const responseData = await response.json();
@ -182,8 +196,8 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
const response2 = await fetch(urlDataContainer, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
"Content-Type": "application/json",
},
});
const responseData2 = await response2.json();
// Set currentStore in the Redux global state
@ -200,7 +214,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
logo: responseData?.logo || "",
shortStoreId: responseData?.shortStoreId,
supportedCoins: responseData?.supportedCoins || [],
foreignCoins: responseData?.foreignCoins || {}
foreignCoins: responseData?.foreignCoins || {},
})
);
// Set listProducts in the Redux global state
@ -208,7 +222,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
dispatch(
setDataContainer({
...responseData2,
id: `${store.identifier}-${DATA_CONTAINER_BASE}`
id: `${store.identifier}-${DATA_CONTAINER_BASE}`,
})
);
} else {
@ -218,7 +232,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
storeId: store.identifier,
shortStoreId: shortStoreId,
owner: store.name,
products: {}
products: {},
};
const dataContainerToBase64 = await objectToBase64(dataContainer);
@ -227,15 +241,21 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
name: store.name,
service: "DOCUMENT",
data64: dataContainerToBase64,
identifier: `${store.identifier}-${DATA_CONTAINER_BASE}`
identifier: `${store.identifier}-${DATA_CONTAINER_BASE}`,
});
}
}
// Only called when user clicks authenticate from inside a gateway
const displayDownloadQortalGatewayModalFunc = () => {
setShowDownloadModal(true);
};
const askForAccountInformation = React.useCallback(async () => {
try {
let account = await qortalRequest({
action: "GET_USER_ACCOUNT"
action: "GET_USER_ACCOUNT",
});
const name = await getNameInfo(account.address);
@ -258,7 +278,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
service: "DOCUMENT",
data64: dataContainerToBase64,
identifier: `${storedDataContainer?.storeId}-${DATA_CONTAINER_BASE}`,
filename: "datacontainer.json"
filename: "datacontainer.json",
});
if (isSuccessful(resourceResponse)) {
await new Promise<void>((res, rej) => {
@ -269,13 +289,13 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
dispatch(
setDataContainer({
...storedDataContainer,
id: `${storedDataContainer?.storeId}-${DATA_CONTAINER_BASE}`
id: `${storedDataContainer?.storeId}-${DATA_CONTAINER_BASE}`,
})
);
dispatch(
setNotification({
msg: "Shop successfully created",
alertType: "success"
alertType: "success",
})
);
setCloseCreateStoreModal(true);
@ -289,7 +309,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
dispatch(
setNotification({
msg: "You must create a data container in order to create a shop!",
alertType: "error"
alertType: "error",
})
);
// Try again after 8 seconds automatically
@ -304,7 +324,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
service: "DOCUMENT",
data64: dataContainerToBase64,
identifier: `${storedDataContainer?.storeId}-${DATA_CONTAINER_BASE}`,
filename: "datacontainer.json"
filename: "datacontainer.json",
});
if (isSuccessful(resourceResponse)) {
await new Promise<void>((res, rej) => {
@ -315,13 +335,13 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
dispatch(
setDataContainer({
...storedDataContainer,
id: `${storedDataContainer?.storeId}-${DATA_CONTAINER_BASE}`
id: `${storedDataContainer?.storeId}-${DATA_CONTAINER_BASE}`,
})
);
dispatch(
setNotification({
msg: "Shop successfully created",
alertType: "success"
alertType: "success",
})
);
setCloseCreateStoreModal(true);
@ -339,7 +359,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
dispatch(
setNotification({
msg: "You must create a data container in order to create a shop!",
alertType: "error"
alertType: "error",
})
);
}
@ -359,7 +379,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
storeIdentifier,
logo,
foreignCoins,
supportedCoins
supportedCoins,
}: onPublishParam) => {
if (!user || !user.name)
throw new Error("Cannot publish: You do not have a Qortal name");
@ -394,18 +414,19 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
shortStoreId: formatStoreIdentifier,
logo,
foreignCoins,
supportedCoins
supportedCoins,
};
if (!storeObj.shortStoreId) {
throw new Error("Please insert a valid store id");
}
// Store Data Container to send to QDN (this will allow easier querying of products afterwards. Think of it as a database in the redux global state for the current store. Max 1 per store). At first there's no products, but they will be added later. We store this in the state so we can reuse it easily if our data container fails to publish.
try {
const storeToBase64 = await objectToBase64(storeObj);
// Publish Store to QDN
let metadescription = `**coins:QORTtrue,ARRR${supportedCoins.includes('ARRR')}**` + description.slice(0,180)
let metadescription =
`**coins:QORTtrue,ARRR${supportedCoins.includes("ARRR")}**` +
description.slice(0, 180);
const resourceResponse = await qortalRequest({
action: "PUBLISH_QDN_RESOURCE",
@ -415,7 +436,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
filename: "store.json",
title,
description: metadescription,
identifier: identifier
identifier: identifier,
});
if (isSuccessful(resourceResponse)) {
await new Promise<void>((res, rej) => {
@ -428,7 +449,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
storeId: identifier,
shortStoreId: formatStoreIdentifier,
owner: name,
products: {}
products: {},
};
// Store data (other than the raw data or metadata) to add to Redux
const storeData = {
@ -438,7 +459,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
owner: name,
id: storeIdentifier,
shortStoreId: formatStoreIdentifier,
logo: logo
logo: logo,
};
// Store Full Object to send to redux hashMapStores
const storefullObj = {
@ -447,7 +468,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
isValid: true,
owner: name,
created: createdAt,
updated: createdAt
updated: createdAt,
};
dispatch(setCurrentStore(storefullObj));
@ -464,17 +485,17 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
if (typeof error === "string") {
notificationObj = {
msg: error || "Failed to create store",
alertType: "error"
alertType: "error",
};
} else if (typeof error?.error === "string") {
notificationObj = {
msg: error?.error || "Failed to create store",
alertType: "error"
alertType: "error",
};
} else {
notificationObj = {
msg: error?.message || "Failed to create store",
alertType: "error"
alertType: "error",
};
}
if (!notificationObj) return;
@ -498,7 +519,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
shipsTo,
logo,
foreignCoins,
supportedCoins
supportedCoins,
}: onPublishParamEdit) => {
if (!user || !user.name || !currentStore)
throw new Error("Cannot publish: You do not have a Qortal name");
@ -522,13 +543,15 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
logo,
shortStoreId: currentStore.shortStoreId ?? shortStoreId,
foreignCoins,
supportedCoins
supportedCoins,
};
try {
const storeToBase64 = await objectToBase64(storeObj);
let metadescription = `**coins:QORTtrue,ARRR${supportedCoins.includes('ARRR')}**` + description.slice(0,180)
let metadescription =
`**coins:QORTtrue,ARRR${supportedCoins.includes("ARRR")}**` +
description.slice(0, 180);
const resourceResponse = await qortalRequest({
action: "PUBLISH_QDN_RESOURCE",
name: name,
@ -537,7 +560,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
filename: "store.json",
title,
description: metadescription,
identifier: currentStore.id
identifier: currentStore.id,
});
await new Promise<void>((res, rej) => {
@ -550,7 +573,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
dispatch(
setNotification({
msg: "Shop successfully updated",
alertType: "success"
alertType: "success",
})
);
} catch (error: any) {
@ -558,17 +581,17 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
if (typeof error === "string") {
notificationObj = {
msg: error || "Failed to update blog",
alertType: "error"
alertType: "error",
};
} else if (typeof error?.error === "string") {
notificationObj = {
msg: error?.error || "Failed to update blog",
alertType: "error"
alertType: "error",
};
} else {
notificationObj = {
msg: error?.message || "Failed to update blog",
alertType: "error"
alertType: "error",
};
}
if (!notificationObj) return;
@ -597,8 +620,8 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
const response = await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
"Content-Type": "application/json",
},
});
const responseData = await response.json();
// Data returned from that endpoint of the API
@ -612,7 +635,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
created: storeItem.created,
updated: storeItem.updated,
owner: storeItem.name,
id: storeItem.identifier
id: storeItem.identifier,
};
});
@ -624,7 +647,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
if (content.owner && content.id) {
const res = checkAndUpdateResource({
id: content.id,
updated: content.updated
updated: content.updated,
});
if (res) {
getStore(content.owner, content.id, content);
@ -668,8 +691,8 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
const shopData = await fetch(urlShop, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
"Content-Type": "application/json",
},
});
const shopResource = await shopData.json();
// Clear product list from redux global state
@ -687,7 +710,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
logo: shopResource?.logo,
shortStoreId: shopResource?.shortStoreId,
supportedCoins: shopResource?.supportedCoins || [],
foreignCoins: shopResource?.foreignCoins || {}
foreignCoins: shopResource?.foreignCoins || {},
})
);
// Fetch data container data on QDN (product resources)
@ -695,8 +718,8 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
const response = await fetch(urlDataContainer, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
"Content-Type": "application/json",
},
});
const responseData2 = await response.json();
if (
@ -707,7 +730,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
dispatch(
setDataContainer({
...responseData2,
id: `${myStoreFound.id}-${DATA_CONTAINER_BASE}`
id: `${myStoreFound.id}-${DATA_CONTAINER_BASE}`,
})
);
} else if (user?.name && recentlyVisitedStoreId) {
@ -722,7 +745,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
limit: 0,
offset: 0,
reverse: true,
mode: "ALL"
mode: "ALL",
});
if (dataContainerExists?.length === 0) {
// Publish Data Container to QDN
@ -737,7 +760,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
storeId: recentlyVisitedStoreId,
shortStoreId: formatStoreIdentifier,
owner: user?.name,
products: {}
products: {},
};
const dataContainerToBase64 = await objectToBase64(
dataContainer
@ -749,20 +772,20 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
service: "DOCUMENT",
data64: dataContainerToBase64,
identifier: `${recentlyVisitedStoreId}-${DATA_CONTAINER_BASE}`,
filename: "datacontainer.json"
filename: "datacontainer.json",
});
if (dataContainerCreated && !dataContainerCreated.error) {
dispatch(
setDataContainer({
...dataContainer,
id: `${recentlyVisitedStoreId}-${DATA_CONTAINER_BASE}`
id: `${recentlyVisitedStoreId}-${DATA_CONTAINER_BASE}`,
})
);
}
dispatch(
setNotification({
msg: "Data Container Created!",
alertType: "success"
alertType: "success",
})
);
} catch (error) {
@ -770,7 +793,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
dispatch(
setNotification({
msg: "Error when creating the data container. Please try again!",
alertType: "error"
alertType: "error",
})
);
dispatch(updateRecentlyVisitedStoreId(""));
@ -780,7 +803,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
dispatch(
setNotification({
msg: "Error when fetching store data. Please try again!",
alertType: "error"
alertType: "error",
})
);
navigate("/");
@ -862,11 +885,94 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
userName={user?.name || ""}
userAvatar={userAvatar}
authenticate={askForAccountInformation}
displayDownloadGatewayModalFunc={displayDownloadQortalGatewayModalFunc}
hasAttemptedToFetchShopInitial={hasAttemptedToFetchShopInitial}
/>
<ConsentModal />
{/* Cart opens when setIsOpen action is dispatched to Redux Global State */}
<Cart />
{showDownloadModal && (
<ReusableModal
open={showDownloadModal}
onClose={() => {
setShowDownloadModal(false);
}}
customStyles={{
width: "370px",
height: "80%",
backgroundColor:
theme.palette.mode === "light" ? "#e8e8e8" : "#030d1a",
position: "relative",
padding: "15px 20px",
borderRadius: "3px",
overflowY: "auto",
overflowX: "hidden",
maxHeight: "90vh",
}}
className="download-qortal-modal"
>
<DownloadQortalCol>
<QortalIcon src={QortalLogo} alt="qortal-icon" />
<DownloadQortalFont>Download Qortal</DownloadQortalFont>
<DownloadQortalSubFont>
Experience a new internet paradigm, and start your Qortal
experience today by downloading and installing the Qortal
software.
</DownloadQortalSubFont>
<DownloadNowButton
onClick={() => {
const userOS = parser.getOS().name;
if (userOS?.includes("Android" || "iOS")) {
dispatch(
setNotification({
msg: "Qortal is not available on mobile devices yet. Please download on a desktop or laptop.",
alertType: "error",
})
);
return;
} else if (userOS?.includes("Mac")) {
window.location.href =
"https://github.com/Qortal/qortal-ui/releases/latest/download/Qortal-Setup-macOS.dmg";
dispatch(
setNotification({
msg: "Download successful!",
alertType: "success",
})
);
return;
} else if (userOS?.includes("Windows")) {
window.location.href =
"https://github.com/Qortal/qortal-ui/releases/latest/download/Qortal-Setup-win64.exe";
dispatch(
setNotification({
msg: "Download successful!",
alertType: "success",
})
);
return;
} else if (userOS?.includes("Linux")) {
window.location.href =
"https://github.com/Qortal/qortal-ui/releases/latest/download/Qortal-Setup-amd64.AppImage";
dispatch(
setNotification({
msg: "Download successful!",
alertType: "success",
})
);
return;
}
}}
>
Download Now{" "}
<DownloadCircleSVG
color={theme.palette.text.primary}
height="30"
width="30"
/>
</DownloadNowButton>
</DownloadQortalCol>
</ReusableModal>
)}
{children}
</>
);

Loading…
Cancel
Save