mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-07-22 20:26:50 +00:00
added pdf viewer and optimizations
This commit is contained in:
52
src/App.tsx
52
src/App.tsx
@@ -133,6 +133,7 @@ import { BuyQortInformation } from "./components/BuyQortInformation";
|
||||
import { QortPayment } from "./components/QortPayment";
|
||||
import { GeneralNotifications } from "./components/GeneralNotifications";
|
||||
import ThemeSelector from "./components/Theme/ThemeSelector";
|
||||
import { PdfViewer } from "./common/PdfViewer";
|
||||
|
||||
type extStates =
|
||||
| "not-authenticated"
|
||||
@@ -2019,32 +2020,31 @@ function App() {
|
||||
// backgroundRepeat: desktopViewMode === "apps" && "no-repeat",
|
||||
}}
|
||||
>
|
||||
<GlobalContext.Provider
|
||||
value={{
|
||||
showTutorial,
|
||||
openTutorialModal,
|
||||
setOpenTutorialModal,
|
||||
downloadResource,
|
||||
hasSeenGettingStarted,
|
||||
}}
|
||||
>
|
||||
<Tutorials />
|
||||
{extState === "not-authenticated" && (
|
||||
<NotAuthenticated
|
||||
getRootProps={getRootProps}
|
||||
getInputProps={getInputProps}
|
||||
setExtstate={setExtstate}
|
||||
apiKey={apiKey}
|
||||
globalApiKey={globalApiKey}
|
||||
setApiKey={setApiKey}
|
||||
handleSetGlobalApikey={handleSetGlobalApikey}
|
||||
currentNode={currentNode}
|
||||
setCurrentNode={setCurrentNode}
|
||||
setUseLocalNode={setUseLocalNode}
|
||||
useLocalNode={useLocalNode}
|
||||
/>
|
||||
)}
|
||||
{/* {extState !== "not-authenticated" && (
|
||||
<PdfViewer />
|
||||
<GlobalContext.Provider value={{
|
||||
showTutorial,
|
||||
openTutorialModal,
|
||||
setOpenTutorialModal,
|
||||
downloadResource,
|
||||
hasSeenGettingStarted
|
||||
}}>
|
||||
<Tutorials />
|
||||
{extState === "not-authenticated" && (
|
||||
<NotAuthenticated
|
||||
getRootProps={getRootProps}
|
||||
getInputProps={getInputProps}
|
||||
setExtstate={setExtstate}
|
||||
apiKey={apiKey}
|
||||
globalApiKey={globalApiKey}
|
||||
setApiKey={setApiKey}
|
||||
handleSetGlobalApikey={handleSetGlobalApikey}
|
||||
currentNode={currentNode}
|
||||
setCurrentNode={setCurrentNode}
|
||||
setUseLocalNode={setUseLocalNode}
|
||||
useLocalNode={useLocalNode}
|
||||
/>
|
||||
)}
|
||||
{/* {extState !== "not-authenticated" && (
|
||||
<button onClick={logoutFunc}>logout</button>
|
||||
)} */}
|
||||
{extState === "authenticated" && isMainWindow && (
|
||||
|
80
src/common/PdfViewer.tsx
Normal file
80
src/common/PdfViewer.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { subscribeToEvent, unsubscribeFromEvent } from "../utils/events";
|
||||
import { Box, Button } from "@mui/material";
|
||||
|
||||
export const PdfViewer = () => {
|
||||
const [pdfUrl, setPdfUrl] = useState("");
|
||||
|
||||
const openPdf = (e) => {
|
||||
try {
|
||||
const blob = e.detail?.blob;
|
||||
if (blob) {
|
||||
// Create Object URL
|
||||
const url = URL.createObjectURL(blob);
|
||||
setPdfUrl(url);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (pdfUrl) {
|
||||
URL.revokeObjectURL(pdfUrl);
|
||||
}
|
||||
};
|
||||
}, [pdfUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("openPdf", openPdf);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("openPdf", openPdf);
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (!pdfUrl) return null;
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: "fixed",
|
||||
height: "100vh",
|
||||
width: "100vw",
|
||||
backgroundColor: 'var(--Mail-Background)',
|
||||
zIndex: 990000000
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
height: "50px",
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
padding: '10px'
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setPdfUrl("");
|
||||
}}
|
||||
variant="contained"
|
||||
>
|
||||
Exit
|
||||
</Button>
|
||||
</Box>
|
||||
<iframe
|
||||
title="PDFViewer"
|
||||
src={`/pdfjs/web/viewer.html?file=${pdfUrl}`}
|
||||
style={{
|
||||
width: "100vw",
|
||||
height: "calc(100vh - 50px)",
|
||||
display: "fixed",
|
||||
left: 0,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
@@ -254,7 +254,8 @@ export const listOfAllQortalRequests = [
|
||||
'GET_USER_WALLET_TRANSACTIONS',
|
||||
'GET_NODE_INFO',
|
||||
'GET_NODE_STATUS',
|
||||
'GET_ARRR_SYNC_STATUS'
|
||||
'GET_ARRR_SYNC_STATUS',
|
||||
'SHOW_PDF_READER'
|
||||
]
|
||||
|
||||
export const UIQortalRequests = [
|
||||
@@ -309,7 +310,8 @@ export const UIQortalRequests = [
|
||||
'GET_USER_WALLET_TRANSACTIONS',
|
||||
'GET_NODE_INFO',
|
||||
'GET_NODE_STATUS',
|
||||
'GET_ARRR_SYNC_STATUS'
|
||||
'GET_ARRR_SYNC_STATUS',
|
||||
'SHOW_PDF_READER'
|
||||
];
|
||||
|
||||
|
||||
|
@@ -572,11 +572,17 @@ const WatchComponent = ({onSeen, isLast, children})=> {
|
||||
const { ref, inView } = useInView({
|
||||
threshold: 0.7, // Fully visible
|
||||
triggerOnce: true, // Only trigger once when it becomes visible
|
||||
delay: 100,
|
||||
trackVisibility: false,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (inView && isLast && onSeen) {
|
||||
|
||||
setTimeout(() => {
|
||||
onSeen();
|
||||
}, 100);
|
||||
|
||||
}
|
||||
}, [inView, isLast, onSeen]);
|
||||
|
||||
|
@@ -17,7 +17,7 @@ import { InviteMember } from "./InviteMember";
|
||||
import { ListOfInvites } from "./ListOfInvites";
|
||||
import { ListOfBans } from "./ListOfBans";
|
||||
import { ListOfJoinRequests } from "./ListOfJoinRequests";
|
||||
import { Box, Card, Tab, Tabs } from "@mui/material";
|
||||
import { Box, ButtonBase, Card, Tab, Tabs } from "@mui/material";
|
||||
import { CustomizedSnackbars } from "../Snackbar/Snackbar";
|
||||
import { MyContext, getBaseApiReact, isMobile } from "../../App";
|
||||
import { getGroupMembers, getNames } from "./Group";
|
||||
@@ -26,7 +26,7 @@ import { getFee } from "../../background";
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
import { subscribeToEvent, unsubscribeFromEvent } from "../../utils/events";
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
|
||||
import InsertLinkIcon from '@mui/icons-material/InsertLink';
|
||||
function a11yProps(index: number) {
|
||||
return {
|
||||
id: `simple-tab-${index}`,
|
||||
@@ -268,6 +268,12 @@ export const ManageMembers = ({
|
||||
<Typography>GroupId: {groupInfo?.groupId}</Typography>
|
||||
<Typography>GroupName: {groupInfo?.groupName}</Typography>
|
||||
<Typography>Number of members: {groupInfo?.memberCount}</Typography>
|
||||
<ButtonBase sx={{
|
||||
gap: '10px'
|
||||
}} onClick={async ()=> {
|
||||
const link = `qortal://use-group/action-join/groupid-${groupInfo?.groupId}`
|
||||
await navigator.clipboard.writeText(link);
|
||||
}}><InsertLinkIcon /> <Typography>Join Group Link</Typography></ButtonBase>
|
||||
</Box>
|
||||
<Spacer height="20px" />
|
||||
{selectedGroup?.groupId && !isOwner && (
|
||||
|
@@ -2,6 +2,7 @@ import { gateways, getApiKeyFromStorage } from "./background";
|
||||
import { listOfAllQortalRequests } from "./components/Apps/useQortalMessageListener";
|
||||
import { addForeignServer, addGroupAdminRequest, addListItems, adminAction, banFromGroupRequest, cancelGroupBanRequest, cancelGroupInviteRequest, cancelSellOrder, createAndCopyEmbedLink, createBuyOrder, createGroupRequest, createPoll, createSellOrder, decryptAESGCMRequest, decryptData, decryptDataWithSharingKey, decryptQortalGroupData, deleteHostedData, deleteListItems, deployAt, encryptData, encryptDataWithSharingKey, encryptQortalGroupData, getCrossChainServerInfo, getDaySummary, getNodeInfo, getNodeStatus, getForeignFee, getHostedData, getListItems, getServerConnectionHistory, getTxActivitySummary, getUserAccount, getUserWallet, getUserWalletInfo, getUserWalletTransactions, getWalletBalance, inviteToGroupRequest, joinGroup, kickFromGroupRequest, leaveGroupRequest, openNewTab, publishMultipleQDNResources, publishQDNResource, registerNameRequest, removeForeignServer, removeGroupAdminRequest, saveFile, sendChatMessage, sendCoin, setCurrentForeignServer, signTransaction, updateForeignFee, updateNameRequest, voteOnPoll, getArrrSyncStatus } from "./qortalRequests/get";
|
||||
import { getData, storeData } from "./utils/chromeStorage";
|
||||
import { executeEvent } from "./utils/events";
|
||||
|
||||
function getLocalStorage(key) {
|
||||
return getData(key).catch((error) => {
|
||||
@@ -1214,7 +1215,29 @@ export const isRunningGateway = async ()=> {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "SHOW_PDF_READER" : {
|
||||
try {
|
||||
if(!request.payload?.blob){
|
||||
throw new Error('Missing blob')
|
||||
}
|
||||
if(request.payload?.blob?.type !== "application/pdf") throw new Error('blob type must be application/pdf')
|
||||
executeEvent("openPdf", { blob: request.payload?.blob});
|
||||
event.source.postMessage({
|
||||
requestId: request.requestId,
|
||||
action: request.action,
|
||||
payload: true,
|
||||
type: "backgroundMessageResponse",
|
||||
}, event.origin);
|
||||
} catch (error) {
|
||||
event.source.postMessage({
|
||||
requestId: request.requestId,
|
||||
action: request.action,
|
||||
error: error?.message,
|
||||
type: "backgroundMessageResponse",
|
||||
}, event.origin);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@@ -106,6 +106,30 @@ const dogeFeePerByte = 0.00001;
|
||||
const dgbFeePerByte = 0.0000001;
|
||||
const rvnFeePerByte = 0.00001125;
|
||||
|
||||
const MAX_RETRIES = 3; // Set max number of retries
|
||||
|
||||
|
||||
export async function retryTransaction(fn, args, throwError, retries = MAX_RETRIES) {
|
||||
let attempt = 0;
|
||||
while (attempt < retries) {
|
||||
try {
|
||||
return await fn(...args);
|
||||
} catch (error) {
|
||||
console.error(`Attempt ${attempt + 1} failed: ${error.message}`);
|
||||
attempt++;
|
||||
if (attempt === retries) {
|
||||
console.error("Max retries reached. Skipping transaction.");
|
||||
if(throwError){
|
||||
throw new Error(error?.message || "Unable to process transaction")
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
await new Promise(res => setTimeout(res, 10000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function roundUpToDecimals(number, decimals = 8) {
|
||||
const factor = Math.pow(10, decimals); // Create a factor based on the number of decimals
|
||||
return Math.ceil(+number * factor) / factor;
|
||||
@@ -1395,8 +1419,9 @@ export const publishMultipleQDNResources = async (
|
||||
}
|
||||
|
||||
try {
|
||||
await publishData({
|
||||
registeredName: encodeURIComponent(name),
|
||||
await retryTransaction(publishData, [
|
||||
{
|
||||
registeredName: encodeURIComponent(name),
|
||||
file: data64,
|
||||
service: service,
|
||||
identifier: encodeURIComponent(identifier),
|
||||
@@ -1413,7 +1438,8 @@ export const publishMultipleQDNResources = async (
|
||||
tag5,
|
||||
apiVersion: 2,
|
||||
withFee: true,
|
||||
});
|
||||
},
|
||||
], false);
|
||||
await new Promise((res) => {
|
||||
setTimeout(() => {
|
||||
res();
|
||||
|
Reference in New Issue
Block a user