mirror of
https://github.com/Qortal/q-shop.git
synced 2025-02-11 17:55:48 +00:00
added multi-publish
This commit is contained in:
parent
6131c89f7c
commit
f8c678b740
23
src/assets/svgs/CircleSVG.tsx
Normal file
23
src/assets/svgs/CircleSVG.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { IconTypes } from "./IconTypes";
|
||||||
|
|
||||||
|
export const CircleSVG: React.FC<IconTypes> = ({
|
||||||
|
color,
|
||||||
|
height,
|
||||||
|
width,
|
||||||
|
className,
|
||||||
|
onClickFunc,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
onClick={onClickFunc}
|
||||||
|
className={className}
|
||||||
|
fill={color}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
height={height}
|
||||||
|
viewBox="0 -960 960 960"
|
||||||
|
width={width}
|
||||||
|
>
|
||||||
|
<path d="m424-296 282-282-56-56-226 226-114-114-56 56 170 170Zm56 216q-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>
|
||||||
|
);
|
||||||
|
};
|
23
src/assets/svgs/EmptyCircleSVG.tsx
Normal file
23
src/assets/svgs/EmptyCircleSVG.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { IconTypes } from "./IconTypes";
|
||||||
|
|
||||||
|
export const EmptyCircleSVG: React.FC<IconTypes> = ({
|
||||||
|
color,
|
||||||
|
height,
|
||||||
|
width,
|
||||||
|
className,
|
||||||
|
onClickFunc,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
|
||||||
|
<svg onClick={onClickFunc}
|
||||||
|
className={className}
|
||||||
|
fill={color}
|
||||||
|
|
||||||
|
height={height}
|
||||||
|
|
||||||
|
width={width} xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" ><path d="M480-80q-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>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
214
src/components/common/MultiplePublish/MultiplePublish.tsx
Normal file
214
src/components/common/MultiplePublish/MultiplePublish.tsx
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
CircularProgress,
|
||||||
|
Modal,
|
||||||
|
Typography,
|
||||||
|
useTheme,
|
||||||
|
} from "@mui/material";
|
||||||
|
import React, { useCallback, useEffect, useState, useRef } from "react";
|
||||||
|
import { CircleSVG } from "../../../assets/svgs/CircleSVG";
|
||||||
|
import { EmptyCircleSVG } from "../../../assets/svgs/EmptyCircleSVG";
|
||||||
|
import { styled } from "@mui/system";
|
||||||
|
|
||||||
|
interface Publish {
|
||||||
|
resources: any[];
|
||||||
|
action: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MultiplePublishProps {
|
||||||
|
publishes: Publish;
|
||||||
|
isOpen: boolean;
|
||||||
|
onSubmit: ()=> void
|
||||||
|
onError: (message?: string)=> void
|
||||||
|
}
|
||||||
|
export const MultiplePublish = ({ publishes, isOpen, onSubmit, onError}: MultiplePublishProps) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const listOfSuccessfulPublishesRef = useRef([])
|
||||||
|
const [listOfSuccessfulPublishes, setListOfSuccessfulPublishes] = useState<
|
||||||
|
any[]
|
||||||
|
>([]);
|
||||||
|
const [listOfUnsuccessfulPublishes, setListOfUnSuccessfulPublishes] = useState<
|
||||||
|
any[]
|
||||||
|
>([]);
|
||||||
|
const [currentlyInPublish, setCurrentlyInPublish] = useState(null);
|
||||||
|
const hasStarted = useRef(false);
|
||||||
|
const publish = useCallback(async (pub: any) => {
|
||||||
|
const lengthOfResources = pub?.resources?.length
|
||||||
|
const lengthOfTimeout = lengthOfResources * 30000
|
||||||
|
return await qortalRequestWithTimeout(pub, lengthOfTimeout);
|
||||||
|
}, []);
|
||||||
|
const [isPublishing, setIsPublishing] = useState(true)
|
||||||
|
|
||||||
|
const handlePublish = useCallback(
|
||||||
|
async (pub: any) => {
|
||||||
|
try {
|
||||||
|
setCurrentlyInPublish(pub?.identifier);
|
||||||
|
setIsPublishing(true)
|
||||||
|
const res = await publish(pub);
|
||||||
|
|
||||||
|
onSubmit()
|
||||||
|
setListOfUnSuccessfulPublishes([])
|
||||||
|
|
||||||
|
} catch (error: any) {
|
||||||
|
const unsuccessfulPublishes = error?.error?.unsuccessfulPublishes || []
|
||||||
|
if(error?.error === 'User declined request'){
|
||||||
|
onError()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(error?.error === 'The request timed out'){
|
||||||
|
onError("The request timed out")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(unsuccessfulPublishes?.length > 0){
|
||||||
|
setListOfUnSuccessfulPublishes(unsuccessfulPublishes)
|
||||||
|
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
|
||||||
|
setIsPublishing(false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[publish]
|
||||||
|
);
|
||||||
|
|
||||||
|
const retry = ()=> {
|
||||||
|
let newlistOfMultiplePublishes: any[] = [];
|
||||||
|
listOfUnsuccessfulPublishes?.forEach((item)=> {
|
||||||
|
const findPub = publishes?.resources.find((res: any)=> res?.identifier === item.identifier)
|
||||||
|
if(findPub){
|
||||||
|
newlistOfMultiplePublishes.push(findPub)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const multiplePublish = {
|
||||||
|
...publishes,
|
||||||
|
resources: newlistOfMultiplePublishes
|
||||||
|
};
|
||||||
|
handlePublish(multiplePublish)
|
||||||
|
}
|
||||||
|
|
||||||
|
const startPublish = useCallback(
|
||||||
|
async (pubs: any) => {
|
||||||
|
await handlePublish(pubs);
|
||||||
|
},
|
||||||
|
[handlePublish, onSubmit, listOfSuccessfulPublishes, publishes]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (publishes && !hasStarted.current) {
|
||||||
|
hasStarted.current = true;
|
||||||
|
startPublish(publishes);
|
||||||
|
}
|
||||||
|
}, [startPublish, publishes, listOfSuccessfulPublishes]);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={isOpen}
|
||||||
|
aria-labelledby="modal-title"
|
||||||
|
aria-describedby="modal-description"
|
||||||
|
sx={{
|
||||||
|
zIndex: 10001
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ModalBody
|
||||||
|
sx={{
|
||||||
|
minHeight: "50vh",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{publishes?.resources?.map((publish: any) => {
|
||||||
|
const unpublished = listOfUnsuccessfulPublishes.map(item => item?.identifier)
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
gap: "20px",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography>{publish?.identifier}</Typography>
|
||||||
|
{!isPublishing && hasStarted.current ? (
|
||||||
|
<>
|
||||||
|
{!unpublished.includes(publish.identifier) ? (
|
||||||
|
<CircleSVG
|
||||||
|
color={theme.palette.text.primary}
|
||||||
|
height="24px"
|
||||||
|
width="24px"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<EmptyCircleSVG
|
||||||
|
color={theme.palette.text.primary}
|
||||||
|
height="24px"
|
||||||
|
width="24px"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
): <CircularProgress size={16} color="secondary"/>}
|
||||||
|
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{!isPublishing && listOfUnsuccessfulPublishes.length > 0 && (
|
||||||
|
<>
|
||||||
|
<Typography sx={{
|
||||||
|
marginTop: '20px',
|
||||||
|
fontSize: '16px'
|
||||||
|
}}>Some files were not published. Please try again. It's important that all the files get published. Maybe wait a couple minutes if the error keeps occurring</Typography>
|
||||||
|
<Button variant="contained" onClick={()=> {
|
||||||
|
retry()
|
||||||
|
}}>Try again</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
</ModalBody>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const ModalBody = styled(Box)(({ theme }) => ({
|
||||||
|
position: "absolute",
|
||||||
|
backgroundColor: theme.palette.background.default,
|
||||||
|
borderRadius: "4px",
|
||||||
|
top: "50%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
width: "75%",
|
||||||
|
maxWidth: "900px",
|
||||||
|
padding: "15px 35px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: "17px",
|
||||||
|
overflowY: "auto",
|
||||||
|
maxHeight: "95vh",
|
||||||
|
boxShadow:
|
||||||
|
theme.palette.mode === "dark"
|
||||||
|
? "0px 4px 5px 0px hsla(0,0%,0%,0.14), 0px 1px 10px 0px hsla(0,0%,0%,0.12), 0px 2px 4px -1px hsla(0,0%,0%,0.2)"
|
||||||
|
: "rgba(99, 99, 99, 0.2) 0px 2px 8px 0px",
|
||||||
|
"&::-webkit-scrollbar-track": {
|
||||||
|
backgroundColor: theme.palette.background.paper,
|
||||||
|
},
|
||||||
|
"&::-webkit-scrollbar-track:hover": {
|
||||||
|
backgroundColor: theme.palette.background.paper,
|
||||||
|
},
|
||||||
|
"&::-webkit-scrollbar": {
|
||||||
|
width: "16px",
|
||||||
|
height: "10px",
|
||||||
|
backgroundColor: theme.palette.mode === "light" ? "#f6f8fa" : "#292d3e",
|
||||||
|
},
|
||||||
|
"&::-webkit-scrollbar-thumb": {
|
||||||
|
backgroundColor: theme.palette.mode === "light" ? "#d3d9e1" : "#575757",
|
||||||
|
borderRadius: "8px",
|
||||||
|
backgroundClip: "content-box",
|
||||||
|
border: "4px solid transparent",
|
||||||
|
},
|
||||||
|
"&::-webkit-scrollbar-thumb:hover": {
|
||||||
|
backgroundColor: theme.palette.mode === "light" ? "#b7bcc4" : "#474646",
|
||||||
|
},
|
||||||
|
}));
|
46
src/components/common/useModal.tsx
Normal file
46
src/components/common/useModal.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { useRef, useState } from 'react';
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
isShow: boolean;
|
||||||
|
}
|
||||||
|
export const useModal = () => {
|
||||||
|
const [state, setState] = useState<State>({
|
||||||
|
isShow: false,
|
||||||
|
});
|
||||||
|
const promiseConfig = useRef<any>(null);
|
||||||
|
const show = async () => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
promiseConfig.current = {
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
};
|
||||||
|
setState({
|
||||||
|
isShow: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const hide = () => {
|
||||||
|
setState({
|
||||||
|
isShow: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onOk = (payload?:any) => {
|
||||||
|
const { resolve } = promiseConfig.current;
|
||||||
|
hide();
|
||||||
|
resolve(payload);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCancel = () => {
|
||||||
|
const { reject } = promiseConfig.current;
|
||||||
|
hide();
|
||||||
|
reject();
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
show,
|
||||||
|
onOk,
|
||||||
|
onCancel,
|
||||||
|
isShow: state.isShow
|
||||||
|
};
|
||||||
|
};
|
@ -140,8 +140,9 @@ const MyModal: React.FC<MyModalProps> = ({ open, onClose, onPublish }) => {
|
|||||||
setLogo(currentStore?.logo || null);
|
setLogo(currentStore?.logo || null);
|
||||||
setLocation(currentStore?.location || "");
|
setLocation(currentStore?.location || "");
|
||||||
setShipsTo(currentStore?.shipsTo || "");
|
setShipsTo(currentStore?.shipsTo || "");
|
||||||
setSupportedCoinsSelected(currentStore?.supportedCoins || ["QORT"]);
|
const selectedCoinsList = [...new Set([...(currentStore?.supportedCoins || []), 'QORT'])];
|
||||||
setArrrWalletAddress(currentStore?.foreignCoins?.ARRR || "");
|
setSupportedCoinsSelected(selectedCoinsList)
|
||||||
|
setArrrWalletAddress(currentStore?.foreignCoins?.ARRR || "")
|
||||||
}
|
}
|
||||||
}, [currentStore, storeId, open]);
|
}, [currentStore, storeId, open]);
|
||||||
|
|
||||||
@ -233,15 +234,12 @@ const MyModal: React.FC<MyModalProps> = ({ open, onClose, onPublish }) => {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
navigate("/");
|
|
||||||
dispatch(
|
dispatch(
|
||||||
setNotification({
|
setNotification({
|
||||||
msg: "Error when creating the data container. Please try again!",
|
msg: "Error when creating the data container. Please try again!",
|
||||||
alertType: "error",
|
alertType: "error",
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
dispatch(updateRecentlyVisitedStoreId(""));
|
|
||||||
dispatch(clearDataCotainer());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -81,6 +81,8 @@ import { AcceptedCoin } from "../StoreList/StoreList-styles";
|
|||||||
import { ARRRSVG } from "../../assets/svgs/ARRRSVG";
|
import { ARRRSVG } from "../../assets/svgs/ARRRSVG";
|
||||||
import { setPreferredCoin } from "../../state/features/storeSlice";
|
import { setPreferredCoin } from "../../state/features/storeSlice";
|
||||||
import { CoinFilter } from "../Store/Store/Store";
|
import { CoinFilter } from "../Store/Store/Store";
|
||||||
|
import { useModal } from "../../components/common/useModal";
|
||||||
|
import { MultiplePublish } from "../../components/common/MultiplePublish/MultiplePublish";
|
||||||
|
|
||||||
/* Currency must be replaced in the order confirmation email by proper currency */
|
/* Currency must be replaced in the order confirmation email by proper currency */
|
||||||
|
|
||||||
@ -92,7 +94,8 @@ interface CountryProps {
|
|||||||
export const Cart = () => {
|
export const Cart = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
|
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
|
||||||
|
const {isShow, onCancel, onOk, show} = useModal()
|
||||||
|
const [publishes, setPublishes] = useState<any>(null);
|
||||||
const uid = new ShortUniqueId({
|
const uid = new ShortUniqueId({
|
||||||
length: 10,
|
length: 10,
|
||||||
});
|
});
|
||||||
@ -754,7 +757,9 @@ export const Cart = () => {
|
|||||||
encrypt: true,
|
encrypt: true,
|
||||||
publicKeys: [resAddress.publicKey, usernamePublicKey],
|
publicKeys: [resAddress.publicKey, usernamePublicKey],
|
||||||
};
|
};
|
||||||
await qortalRequest(multiplePublish);
|
setPublishes(multiplePublish)
|
||||||
|
await show()
|
||||||
|
// await qortalRequest(multiplePublish);
|
||||||
// Clear this cart state from global carts redux
|
// Clear this cart state from global carts redux
|
||||||
dispatch(removeCartFromCarts({ storeId }));
|
dispatch(removeCartFromCarts({ storeId }));
|
||||||
// Clear cart local state
|
// Clear cart local state
|
||||||
@ -787,6 +792,7 @@ export const Cart = () => {
|
|||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
dispatch(setIsLoadingGlobal(false));
|
dispatch(setIsLoadingGlobal(false));
|
||||||
|
setPublishes(null)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -828,6 +834,18 @@ export const Cart = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{isShow && (
|
||||||
|
<MultiplePublish
|
||||||
|
isOpen={isShow}
|
||||||
|
onError={(messageNotification)=> {
|
||||||
|
onCancel()
|
||||||
|
}}
|
||||||
|
onSubmit={() => {
|
||||||
|
onOk()
|
||||||
|
}}
|
||||||
|
publishes={publishes}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<ReusableModal
|
<ReusableModal
|
||||||
open={isOpen}
|
open={isOpen}
|
||||||
customStyles={{
|
customStyles={{
|
||||||
|
@ -57,10 +57,15 @@ import {
|
|||||||
STORE_BASE,
|
STORE_BASE,
|
||||||
} from "../../constants/identifiers";
|
} from "../../constants/identifiers";
|
||||||
import { resetOrders } from "../../state/features/orderSlice";
|
import { resetOrders } from "../../state/features/orderSlice";
|
||||||
|
import { useModal } from "../../components/common/useModal";
|
||||||
|
import { MultiplePublish } from "../../components/common/MultiplePublish/MultiplePublish";
|
||||||
|
|
||||||
const uid = new ShortUniqueId({ length: 10 });
|
const uid = new ShortUniqueId({ length: 10 });
|
||||||
|
|
||||||
export const ProductManager = () => {
|
export const ProductManager = () => {
|
||||||
|
const {isShow, onCancel, onOk, show} = useModal()
|
||||||
|
const [publishes, setPublishes] = useState<any>(null);
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -362,7 +367,9 @@ export const ProductManager = () => {
|
|||||||
action: "PUBLISH_MULTIPLE_QDN_RESOURCES",
|
action: "PUBLISH_MULTIPLE_QDN_RESOURCES",
|
||||||
resources: [...publishMultipleCatalogues, publishDataContainer],
|
resources: [...publishMultipleCatalogues, publishDataContainer],
|
||||||
};
|
};
|
||||||
await qortalRequest(multiplePublish);
|
setPublishes(multiplePublish)
|
||||||
|
await show()
|
||||||
|
// await qortalRequest(multiplePublish);
|
||||||
|
|
||||||
// Clear productsToSave from Redux store
|
// Clear productsToSave from Redux store
|
||||||
dispatch(clearAllProductsToSave());
|
dispatch(clearAllProductsToSave());
|
||||||
@ -431,6 +438,8 @@ export const ProductManager = () => {
|
|||||||
dispatch(setNotification(notificationObj));
|
dispatch(setNotification(notificationObj));
|
||||||
|
|
||||||
throw new Error("Failed to send message");
|
throw new Error("Failed to send message");
|
||||||
|
} finally {
|
||||||
|
setPublishes(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -656,6 +665,18 @@ export const ProductManager = () => {
|
|||||||
|
|
||||||
{/* Confirm Remove Product from productsToSave in global state */}
|
{/* Confirm Remove Product from productsToSave in global state */}
|
||||||
<Modal />
|
<Modal />
|
||||||
|
{isShow && (
|
||||||
|
<MultiplePublish
|
||||||
|
isOpen={isShow}
|
||||||
|
onError={(messageNotification)=> {
|
||||||
|
onCancel()
|
||||||
|
}}
|
||||||
|
onSubmit={() => {
|
||||||
|
onOk()
|
||||||
|
}}
|
||||||
|
publishes={publishes}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</ProductManagerContainer>
|
</ProductManagerContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -48,6 +48,8 @@ import {
|
|||||||
import QortalLogo from "../assets/img/Q-AppsLogo.webp";
|
import QortalLogo from "../assets/img/Q-AppsLogo.webp";
|
||||||
import { DownloadCircleSVG } from "../assets/svgs/DownloadCircleSVG";
|
import { DownloadCircleSVG } from "../assets/svgs/DownloadCircleSVG";
|
||||||
import { UAParser } from "ua-parser-js";
|
import { UAParser } from "ua-parser-js";
|
||||||
|
import { useModal } from "../components/common/useModal";
|
||||||
|
import { MultiplePublish } from "../components/common/MultiplePublish/MultiplePublish";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@ -65,7 +67,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
|||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const [isCreatingShop, setIsCreatingShop] = useState(false)
|
||||||
// Determine which OS they're on
|
// Determine which OS they're on
|
||||||
const parser = new UAParser();
|
const parser = new UAParser();
|
||||||
|
|
||||||
@ -109,7 +111,8 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
|||||||
useState<boolean>(false);
|
useState<boolean>(false);
|
||||||
const [retryDataContainer, setRetryDataContainer] = useState<boolean>(false);
|
const [retryDataContainer, setRetryDataContainer] = useState<boolean>(false);
|
||||||
const [showDownloadModal, setShowDownloadModal] = useState<boolean>(false);
|
const [showDownloadModal, setShowDownloadModal] = useState<boolean>(false);
|
||||||
|
const {isShow, onCancel, onOk, show} = useModal()
|
||||||
|
const [publishes, setPublishes] = useState<any>(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!user?.name) return;
|
if (!user?.name) return;
|
||||||
|
|
||||||
@ -269,104 +272,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handlePublishDataContainer = React.useCallback(async () => {
|
|
||||||
try {
|
|
||||||
const dataContainerToBase64 = await objectToBase64(storedDataContainer);
|
|
||||||
// Publish Data Container to QDN
|
|
||||||
const resourceResponse = await qortalRequest({
|
|
||||||
action: "PUBLISH_QDN_RESOURCE",
|
|
||||||
name: storedDataContainer?.owner,
|
|
||||||
service: "DOCUMENT",
|
|
||||||
data64: dataContainerToBase64,
|
|
||||||
identifier: `${storedDataContainer?.storeId}-${DATA_CONTAINER_BASE}`,
|
|
||||||
filename: "datacontainer.json",
|
|
||||||
});
|
|
||||||
if (isSuccessful(resourceResponse)) {
|
|
||||||
await new Promise<void>((res, rej) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
res();
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
dispatch(
|
|
||||||
setDataContainer({
|
|
||||||
...storedDataContainer,
|
|
||||||
id: `${storedDataContainer?.storeId}-${DATA_CONTAINER_BASE}`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
dispatch(
|
|
||||||
setNotification({
|
|
||||||
msg: "Shop successfully created",
|
|
||||||
alertType: "success",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
setCloseCreateStoreModal(true);
|
|
||||||
setRetryDataContainer(false);
|
|
||||||
setOpenDataContainer(false);
|
|
||||||
} else {
|
|
||||||
setOpenDataContainer(true);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
dispatch(
|
|
||||||
setNotification({
|
|
||||||
msg: "You must create a data container in order to create a shop!",
|
|
||||||
alertType: "error",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
// Try again after 8 seconds automatically
|
|
||||||
setOpenDataContainer(true);
|
|
||||||
let interval: number | undefined = undefined;
|
|
||||||
const dataContainerToBase64 = await objectToBase64(storedDataContainer);
|
|
||||||
interval = window.setInterval(async () => {
|
|
||||||
try {
|
|
||||||
const resourceResponse = await qortalRequest({
|
|
||||||
action: "PUBLISH_QDN_RESOURCE",
|
|
||||||
name: storedDataContainer?.owner,
|
|
||||||
service: "DOCUMENT",
|
|
||||||
data64: dataContainerToBase64,
|
|
||||||
identifier: `${storedDataContainer?.storeId}-${DATA_CONTAINER_BASE}`,
|
|
||||||
filename: "datacontainer.json",
|
|
||||||
});
|
|
||||||
if (isSuccessful(resourceResponse)) {
|
|
||||||
await new Promise<void>((res, rej) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
res();
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
dispatch(
|
|
||||||
setDataContainer({
|
|
||||||
...storedDataContainer,
|
|
||||||
id: `${storedDataContainer?.storeId}-${DATA_CONTAINER_BASE}`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
dispatch(
|
|
||||||
setNotification({
|
|
||||||
msg: "Shop successfully created",
|
|
||||||
alertType: "success",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
setCloseCreateStoreModal(true);
|
|
||||||
setRetryDataContainer(false);
|
|
||||||
setOpenDataContainer(false);
|
|
||||||
clearInterval(interval);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
setRetryDataContainer(false);
|
|
||||||
// clear interval
|
|
||||||
if (interval) {
|
|
||||||
clearInterval(interval);
|
|
||||||
}
|
|
||||||
dispatch(
|
|
||||||
setNotification({
|
|
||||||
msg: "You must create a data container in order to create a shop!",
|
|
||||||
alertType: "error",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, 8000);
|
|
||||||
}
|
|
||||||
}, [storedDataContainer]);
|
|
||||||
|
|
||||||
// If they successfully create a store but not a data container, keep the data-container information in the state.
|
// If they successfully create a store but not a data container, keep the data-container information in the state.
|
||||||
// Wait 8 seconds and try again automatically. If it fails again, then tell them to republish again.
|
// Wait 8 seconds and try again automatically. If it fails again, then tell them to republish again.
|
||||||
@ -382,6 +288,8 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
|||||||
foreignCoins,
|
foreignCoins,
|
||||||
supportedCoins,
|
supportedCoins,
|
||||||
}: onPublishParam) => {
|
}: onPublishParam) => {
|
||||||
|
if(isCreatingShop) return
|
||||||
|
setIsCreatingShop(true)
|
||||||
if (!user || !user.name)
|
if (!user || !user.name)
|
||||||
throw new Error("Cannot publish: You do not have a Qortal name");
|
throw new Error("Cannot publish: You do not have a Qortal name");
|
||||||
if (!title) throw new Error("A title is required");
|
if (!title) throw new Error("A title is required");
|
||||||
@ -429,7 +337,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
|||||||
`**coins:QORTtrue,ARRR${supportedCoins.includes("ARRR")}**` +
|
`**coins:QORTtrue,ARRR${supportedCoins.includes("ARRR")}**` +
|
||||||
description.slice(0, 180);
|
description.slice(0, 180);
|
||||||
|
|
||||||
const resourceResponse = await qortalRequest({
|
const resourceStore = {
|
||||||
action: "PUBLISH_QDN_RESOURCE",
|
action: "PUBLISH_QDN_RESOURCE",
|
||||||
name: name,
|
name: name,
|
||||||
service: "STORE",
|
service: "STORE",
|
||||||
@ -438,13 +346,9 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
|||||||
title,
|
title,
|
||||||
description: metadescription,
|
description: metadescription,
|
||||||
identifier: identifier,
|
identifier: identifier,
|
||||||
});
|
}
|
||||||
if (isSuccessful(resourceResponse)) {
|
|
||||||
await new Promise<void>((res, rej) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
res();
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
const createdAt = Date.now();
|
const createdAt = Date.now();
|
||||||
const dataContainer = {
|
const dataContainer = {
|
||||||
storeId: identifier,
|
storeId: identifier,
|
||||||
@ -472,15 +376,43 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
|||||||
updated: createdAt,
|
updated: createdAt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const dataContainerToBase64 = await objectToBase64(dataContainer);
|
||||||
|
// Publish Data Container to QDN
|
||||||
|
const resourceDatacontainer = {
|
||||||
|
action: "PUBLISH_QDN_RESOURCE",
|
||||||
|
name: name,
|
||||||
|
service: "DOCUMENT",
|
||||||
|
data64: dataContainerToBase64,
|
||||||
|
identifier: `${identifier}-${DATA_CONTAINER_BASE}`,
|
||||||
|
filename: "datacontainer.json",
|
||||||
|
}
|
||||||
|
|
||||||
|
const multiplePublish = {
|
||||||
|
action: "PUBLISH_MULTIPLE_QDN_RESOURCES",
|
||||||
|
resources: [resourceStore, resourceDatacontainer],
|
||||||
|
};
|
||||||
|
setPublishes(multiplePublish)
|
||||||
|
await show()
|
||||||
dispatch(setCurrentStore(storefullObj));
|
dispatch(setCurrentStore(storefullObj));
|
||||||
dispatch(addToHashMapStores(storefullObj));
|
dispatch(addToHashMapStores(storefullObj));
|
||||||
dispatch(addToStores(storefullObj));
|
dispatch(addToStores(storefullObj));
|
||||||
dispatch(addToAllMyStores(storeData));
|
dispatch(addToAllMyStores(storeData));
|
||||||
setStoredDataContainer(dataContainer);
|
dispatch(
|
||||||
setRetryDataContainer(true);
|
setDataContainer({
|
||||||
} else {
|
...dataContainer,
|
||||||
throw new Error("Failed to create store");
|
id: `${identifier}-${DATA_CONTAINER_BASE}`,
|
||||||
}
|
})
|
||||||
|
);
|
||||||
|
dispatch(
|
||||||
|
setNotification({
|
||||||
|
msg: "Shop successfully created",
|
||||||
|
alertType: "success",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setCloseCreateStoreModal(true);
|
||||||
|
setOpenDataContainer(false);
|
||||||
|
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
let notificationObj: any = null;
|
let notificationObj: any = null;
|
||||||
if (typeof error === "string") {
|
if (typeof error === "string") {
|
||||||
@ -507,6 +439,8 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
|||||||
console.error(error);
|
console.error(error);
|
||||||
throw new Error("An unknown error occurred");
|
throw new Error("An unknown error occurred");
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
setIsCreatingShop(false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[user]
|
[user]
|
||||||
@ -821,24 +755,22 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
|||||||
}
|
}
|
||||||
}, [recentlyVisitedStoreId, myStores, userOwnDataContainer]);
|
}, [recentlyVisitedStoreId, myStores, userOwnDataContainer]);
|
||||||
|
|
||||||
// Handle publishing of data container when creating a store, or if it fails too (Need this to be able to call it from the reusable modal)
|
|
||||||
useEffect(() => {
|
|
||||||
const publishDataContainer = async () => {
|
|
||||||
// Publish Data Container to QDN here
|
|
||||||
await handlePublishDataContainer();
|
|
||||||
};
|
|
||||||
if (
|
|
||||||
retryDataContainer &&
|
|
||||||
storedDataContainer &&
|
|
||||||
Object.keys(storedDataContainer).length > 0
|
|
||||||
) {
|
|
||||||
publishDataContainer();
|
|
||||||
}
|
|
||||||
// We only want to run this when retryDataContainer changes, or else storedDataContainer will be cleared beforehand.
|
|
||||||
}, [retryDataContainer]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{isShow && (
|
||||||
|
<MultiplePublish
|
||||||
|
isOpen={isShow}
|
||||||
|
onError={()=> {
|
||||||
|
onCancel()
|
||||||
|
}}
|
||||||
|
onSubmit={() => {
|
||||||
|
onOk()
|
||||||
|
}}
|
||||||
|
publishes={publishes}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{isLoadingGlobal && <PageLoader />}
|
{isLoadingGlobal && <PageLoader />}
|
||||||
{isOpenCreateStoreModal && user?.name && (
|
{isOpenCreateStoreModal && user?.name && (
|
||||||
<CreateStoreModal
|
<CreateStoreModal
|
||||||
|
Loading…
x
Reference in New Issue
Block a user