added wallets embed

This commit is contained in:
PhilReact 2025-03-08 20:10:09 +02:00
parent 2ada0fd3d4
commit 8c8c6e512b
19 changed files with 360 additions and 55 deletions

View File

@ -20,6 +20,7 @@ import {
DialogContent, DialogContent,
DialogContentText, DialogContentText,
DialogTitle, DialogTitle,
Divider,
Input, Input,
InputLabel, InputLabel,
Popover, Popover,
@ -27,6 +28,8 @@ import {
Typography, Typography,
} from "@mui/material"; } from "@mui/material";
import { decryptStoredWallet } from "./utils/decryptWallet"; import { decryptStoredWallet } from "./utils/decryptWallet";
import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet';
import { JsonView, allExpanded, darkStyles } from 'react-json-view-lite'; import { JsonView, allExpanded, darkStyles } from 'react-json-view-lite';
import 'react-json-view-lite/dist/index.css'; import 'react-json-view-lite/dist/index.css';
import { CountdownCircleTimer } from "react-countdown-circle-timer"; import { CountdownCircleTimer } from "react-countdown-circle-timer";
@ -1659,7 +1662,7 @@ function App() {
REGISTER NAME REGISTER NAME
</TextP> </TextP>
)} )}
<Spacer height="20px" /> {/* <Spacer height="20px" /> */}
<CustomButton <CustomButton
onClick={() => { onClick={() => {
setIsOpenSendQort(true); setIsOpenSendQort(true);
@ -1669,14 +1672,46 @@ function App() {
> >
Transfer QORT Transfer QORT
</CustomButton> </CustomButton>
<Spacer height="20px" />
<ButtonBase
sx={{
"&:hover": { backgroundColor: "secondary.main" },
transition: "all 0.1s ease-in-out",
padding: "5px",
borderRadius: "5px",
gap: "5px",
}}
onClick={async () => {
setIsOpenDrawerProfile(false);
executeEvent("openWalletsApp", {
});
}}
>
<AccountBalanceWalletIcon
sx={{
color: "white",
}}
/>
<Typography
sx={{
fontSize: "1rem",
}}
>
See Wallets
</Typography>
</ButtonBase>
<Divider />
<Spacer height="10px" />
<AddressQRCode targetAddress={rawWallet?.address0} /> <AddressQRCode targetAddress={rawWallet?.address0} />
</> </>
)} )}
<Spacer height="10px" />
<TextP <TextP
sx={{ sx={{
textAlign: "center", textAlign: "center",
lineHeight: "24px", lineHeight: "24px",
fontSize: "12px", fontSize: "14px",
fontWeight: 500, fontWeight: 500,
cursor: "pointer", cursor: "pointer",
marginTop: "10px", marginTop: "10px",

View File

@ -37,6 +37,10 @@ export const sortablePinnedAppsAtom = atom({
{ {
name: 'Q-Mintership', name: 'Q-Mintership',
service: 'APP' service: 'APP'
},
{
name: 'Q-Wallets',
service: 'APP'
} }
], ],
}); });

View File

@ -1,36 +1,72 @@
import React, { useState } from "react";
import React from 'react'; import QRCode from "react-qr-code";
import QRCode from 'react-qr-code'; import { TextP } from "../App-styles";
import { TextP } from '../App-styles'; import { Box, Typography } from "@mui/material";
import { Box } from '@mui/material';
export const AddressQRCode = ({ targetAddress }) => { export const AddressQRCode = ({ targetAddress }) => {
const [open, setOpen] = useState(false);
return ( return (
<Box sx={{ <Box
display: 'flex', sx={{
gap: '10px', display: "flex",
width: '100%', gap: "10px",
alignItems: 'center', alignItems: "center",
flexDirection: 'column', flexDirection: "column",
marginTop: '20px' marginTop: '10px'
}}> }}
<TextP >
sx={{ <Typography
textAlign: "center", sx={{
lineHeight: 1.2, cursor: "pointer",
fontSize: "16px", fontSize: "14px",
fontWeight: 500, }}
}} onClick={() => {
> setOpen((prev)=> !prev);
Your address }}
</TextP> >
<QRCode {open ? 'Hide QR code' :'See QR code'}
value={targetAddress} // Your address here </Typography>
size={180} // Adjust size as needed
level="M" // Error correction level (L, M, Q, H) {open && (
bgColor="#FFFFFF" // Background color (white) <Box
fgColor="#000000" // Foreground color (black) sx={{
/> display: "flex",
gap: "10px",
alignItems: "center",
justifyContent: "center",
width: "100%",
}}
>
<Box
sx={{
display: "flex",
gap: "10px",
width: "100%",
alignItems: "center",
flexDirection: "column",
marginTop: "20px",
}}
>
<TextP
sx={{
textAlign: "center",
lineHeight: 1.2,
fontSize: "16px",
fontWeight: 500,
}}
>
Your address
</TextP>
<QRCode
value={targetAddress} // Your address here
size={150} // Adjust size as needed
level="M" // Error correction level (L, M, Q, H)
bgColor="#FFFFFF" // Background color (white)
fgColor="#000000" // Foreground color (black)
/>
</Box>
</Box>
)}
</Box> </Box>
); );
}; };

View File

@ -11,11 +11,11 @@ import { useQortalMessageListener } from "./useQortalMessageListener";
export const AppViewer = React.forwardRef(({ app , hide, isDevMode}, iframeRef) => { export const AppViewer = React.forwardRef(({ app , hide, isDevMode, skipAuth}, iframeRef) => {
const { rootHeight } = useContext(MyContext); const { rootHeight } = useContext(MyContext);
// const iframeRef = useRef(null); // const iframeRef = useRef(null);
const { document, window: frameWindow } = useFrame(); const { document, window: frameWindow } = useFrame();
const {path, history, changeCurrentIndex, resetHistory} = useQortalMessageListener(frameWindow, iframeRef, app?.tabId, isDevMode, app?.name, app?.service) const {path, history, changeCurrentIndex, resetHistory} = useQortalMessageListener(frameWindow, iframeRef, app?.tabId, isDevMode, app?.name, app?.service, skipAuth)
const [url, setUrl] = useState('') const [url, setUrl] = useState('')

View File

@ -3,7 +3,7 @@ import { AppViewer } from './AppViewer';
import Frame from 'react-frame-component'; import Frame from 'react-frame-component';
import { MyContext, isMobile } from '../../App'; import { MyContext, isMobile } from '../../App';
const AppViewerContainer = React.forwardRef(({ app, isSelected, hide, customHeight, isDevMode }, ref) => { const AppViewerContainer = React.forwardRef(({ app, isSelected, hide, customHeight, isDevMode, skipAuth }, ref) => {
const { rootHeight } = useContext(MyContext); const { rootHeight } = useContext(MyContext);
@ -42,7 +42,7 @@ const AppViewerContainer = React.forwardRef(({ app, isSelected, hide, customHeig
overflow: 'hidden', overflow: 'hidden',
}} }}
> >
<AppViewer app={app} ref={ref} hide={!isSelected || hide} isDevMode={isDevMode} /> <AppViewer skipAuth={skipAuth} app={app} ref={ref} hide={!isSelected || hide} isDevMode={isDevMode} />
</Frame> </Frame>
); );
}); });

View File

@ -40,7 +40,8 @@ const officialAppList = [
"q-shop", "q-shop",
"q-trade", "q-trade",
"q-support", "q-support",
"q-manager" "q-manager",
"q-wallets"
]; ];
const ScrollerStyled = styled('div')({ const ScrollerStyled = styled('div')({

View File

@ -46,7 +46,8 @@ const officialAppList = [
"q-mail", "q-mail",
"q-fund", "q-fund",
"q-shop", "q-shop",
"q-manager" "q-manager",
"q-wallets"
]; ];
const ScrollerStyled = styled("div")({ const ScrollerStyled = styled("div")({

View File

@ -44,7 +44,8 @@ const officialAppList = [
"q-trade", "q-trade",
"q-support", "q-support",
"q-manager", "q-manager",
"q-mintership" "q-mintership",
"q-wallets"
]; ];
const ScrollerStyled = styled('div')({ const ScrollerStyled = styled('div')({

View File

@ -55,7 +55,8 @@ const officialAppList = [
"q-fund", "q-fund",
"q-shop", "q-shop",
"q-manager", "q-manager",
"q-mintership" "q-mintership",
"q-wallets"
]; ];
const ScrollerStyled = styled("div")({ const ScrollerStyled = styled("div")({

View File

@ -486,7 +486,7 @@ const UIQortalRequests = [
return obj; // Updated object with references to stored files return obj; // Updated object with references to stored files
} }
export const useQortalMessageListener = (frameWindow, iframeRef, tabId, appName, appService) => { export const useQortalMessageListener = (frameWindow, iframeRef, tabId, isDevMode, appName, appService, skipAuth) => {
const [path, setPath] = useState('') const [path, setPath] = useState('')
const [history, setHistory] = useState({ const [history, setHistory] = useState({
customQDNHistoryPaths: [], customQDNHistoryPaths: [],
@ -542,7 +542,7 @@ isDOMContentLoaded: false
const sendMessageToRuntime = (message, eventPort) => { const sendMessageToRuntime = (message, eventPort) => {
window.sendMessage(message.action, message.payload, 300000, message.isExtension, { window.sendMessage(message.action, message.payload, 300000, message.isExtension, {
name: appName, service: appService name: appName, service: appService
}) }, skipAuth)
.then((response) => { .then((response) => {
if (response.error) { if (response.error) {
eventPort.postMessage({ eventPort.postMessage({
@ -636,7 +636,9 @@ isDOMContentLoaded: false
window.sendMessage("addEnteredQmailTimestamp").catch((error) => { window.sendMessage("addEnteredQmailTimestamp").catch((error) => {
// error // error
}); });
} } else if(appName?.toLowerCase() === 'q-wallets'){
executeEvent('setLastEnteredTimestampPaymentEvent', {})
}
} else if(event?.data?.action === 'NAVIGATION_HISTORY'){ } else if(event?.data?.action === 'NAVIGATION_HISTORY'){
if(event?.data?.payload?.isDOMContentLoaded){ if(event?.data?.payload?.isDOMContentLoaded){
setHistory((prev)=> { setHistory((prev)=> {

View File

@ -4,6 +4,9 @@ import ChatIcon from "@mui/icons-material/Chat";
import qTradeLogo from "../../assets/Icons/q-trade-logo.webp"; import qTradeLogo from "../../assets/Icons/q-trade-logo.webp";
import AppsIcon from "@mui/icons-material/Apps"; import AppsIcon from "@mui/icons-material/Apps";
import { executeEvent } from "../../utils/events"; import { executeEvent } from "../../utils/events";
import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet';
export const Explore = ({setMobileViewMode}) => { export const Explore = ({setMobileViewMode}) => {
return ( return (
<Box <Box
@ -11,7 +14,8 @@ export const Explore = ({setMobileViewMode}) => {
display: "flex", display: "flex",
gap: "20px", gap: "20px",
flexWrap: "wrap", flexWrap: "wrap",
justifyContent: 'center' justifyContent: 'center',
padding: '10px'
}} }}
> >
<ButtonBase <ButtonBase
@ -97,6 +101,33 @@ export const Explore = ({setMobileViewMode}) => {
General Chat General Chat
</Typography> </Typography>
</ButtonBase> </ButtonBase>
<ButtonBase
sx={{
"&:hover": { backgroundColor: "secondary.main" },
transition: "all 0.1s ease-in-out",
padding: "5px",
borderRadius: "5px",
gap: "5px",
}}
onClick={async () => {
executeEvent("openWalletsApp", {
});
}}
>
<AccountBalanceWalletIcon
sx={{
color: "white",
}}
/>
<Typography
sx={{
fontSize: "1rem",
}}
>
Wallets
</Typography>
</ButtonBase>
</Box> </Box>
); );
}; };

View File

@ -109,7 +109,13 @@ export const GlobalTouchMenu = () => {
executeEvent('openUserProfile',{}) executeEvent('openUserProfile',{})
handleClose() handleClose()
}}> }}>
<Typography variant="inherit">Wallet</Typography> <Typography variant="inherit">My Account</Typography>
</MenuItem>
<MenuItem onClick={()=> {
executeEvent('openWalletsApp', {})
handleClose()
}}>
<Typography variant="inherit">Wallets</Typography>
</MenuItem> </MenuItem>
</CustomStyledMenu> </CustomStyledMenu>
); );

View File

@ -20,6 +20,7 @@ import React, {
useState, useState,
} from "react"; } from "react";
import BlockIcon from '@mui/icons-material/Block'; import BlockIcon from '@mui/icons-material/Block';
import { WalletsAppWrapper } from "./WalletsAppWrapper";
import SettingsIcon from "@mui/icons-material/Settings"; import SettingsIcon from "@mui/icons-material/Settings";
import { ChatGroup } from "../Chat/ChatGroup"; import { ChatGroup } from "../Chat/ChatGroup";
@ -2226,7 +2227,9 @@ export const Group = ({
setNewChat={setNewChat} setNewChat={setNewChat}
/> />
)} )}
<WalletsAppWrapper />
<div <div
style={{ style={{

View File

@ -0,0 +1,163 @@
import { Box, ButtonBase, Divider, Typography } from "@mui/material";
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import CloseIcon from "@mui/icons-material/Close";
import AppViewerContainer from "../Apps/AppViewerContainer";
import {
executeEvent,
subscribeToEvent,
unsubscribeFromEvent,
} from "../../utils/events";
import { useRecoilState } from "recoil";
import { navigationControllerAtom } from "../../atoms/global";
import { AppsNavBarLeft, AppsNavBarParent } from "../Apps/Apps-styles";
import NavBack from "../../assets/svgs/NavBack.svg";
import RefreshIcon from "@mui/icons-material/Refresh";
export const WalletsAppWrapper = () => {
const iframeRef = useRef(null);
const [isOpen, setIsOpen] = useState(false);
const [navigationController, setNavigationController] = useRecoilState(
navigationControllerAtom
);
const [selectedTab, setSelectedTab] = useState({
tabId: "5558589",
name: "Q-Wallets",
service: "APP",
path: '/qortal'
});
const isDisableBackButton = useMemo(() => {
if (selectedTab && navigationController[selectedTab?.tabId]?.hasBack)
return false;
if (selectedTab && !navigationController[selectedTab?.tabId]?.hasBack)
return true;
return false;
}, [navigationController, selectedTab]);
const openWalletsAppFunc = useCallback(
(e) => {
setIsOpen(true);
},
[setIsOpen]
);
useEffect(() => {
subscribeToEvent("openWalletsApp", openWalletsAppFunc);
return () => {
unsubscribeFromEvent("openWalletsApp", openWalletsAppFunc);
};
}, [openWalletsAppFunc]);
const handleClose = ()=> {
setIsOpen(false);
iframeRef.current = null
}
return (
<>
{isOpen && (
<Box
sx={{
position: "fixed",
height: "1000px",
maxHeight: "100vh",
width: "1200px",
maxWidth: "100vw",
backgroundColor: "#27282c",
zIndex: 100,
bottom: 0,
right: 0,
overflow: "hidden",
borderTopLeftRadius: "10px",
borderTopRightRadius: "10px",
boxShadow: 4,
}}
>
<Box
sx={{
height: "100%",
width: "100%",
}}
>
<Box
sx={{
height: "40px",
display: "flex",
alignItems: "center",
padding: "5px",
justifyContent: "space-between",
}}
>
<Typography>Q-Wallets</Typography>
<ButtonBase
onClick={handleClose}
>
<CloseIcon
sx={{
color: "white",
}}
/>
</ButtonBase>
</Box>
<Divider />
<AppViewerContainer
customHeight="calc(100% - 40px - 60px)"
app={selectedTab}
isSelected
ref={iframeRef}
skipAuth={true}
/>
<AppsNavBarParent>
<AppsNavBarLeft sx={{
gap: '25px'
}}>
<ButtonBase
onClick={() => {
executeEvent(`navigateBackApp-${selectedTab?.tabId}`, {});
}}
disabled={isDisableBackButton}
sx={{
opacity: !isDisableBackButton ? 1 : 0.1,
cursor: !isDisableBackButton ? "pointer" : "default",
}}
>
<img src={NavBack} />
</ButtonBase>
<ButtonBase onClick={() => {
if (selectedTab?.refreshFunc) {
selectedTab.refreshFunc(selectedTab?.tabId);
} else {
executeEvent("refreshApp", {
tabId: selectedTab?.tabId,
});
}
}}>
<RefreshIcon
height={20}
sx={{
color: "rgba(250, 250, 250, 0.5)",
height: '30px',
width: 'auto'
}}
/>
</ButtonBase>
</AppsNavBarLeft>
</AppsNavBarParent>
</Box>
</Box>
)}
</>
);
};

View File

@ -28,6 +28,7 @@ import { useAppFullScreen } from "../../useAppFullscreen";
import { useHandlePaymentNotification } from "../../hooks/useHandlePaymentNotification"; import { useHandlePaymentNotification } from "../../hooks/useHandlePaymentNotification";
import { formatDate } from "../../utils/time"; import { formatDate } from "../../utils/time";
import AccountBalanceWalletIcon from "@mui/icons-material/AccountBalanceWallet"; import AccountBalanceWalletIcon from "@mui/icons-material/AccountBalanceWallet";
import { executeEvent } from "../../utils/events";
const Header = ({ const Header = ({
logoutFunc, logoutFunc,
@ -545,12 +546,12 @@ const Header = ({
width: "100%", width: "100%",
alignItems: "flex-start", alignItems: "flex-start",
textWrap: "auto", textWrap: "auto",
cursor: 'default'
}}
onClick={(e) => {
// executeEvent("addTab", { data: { service: 'APP', name: 'q-mail' } });
// executeEvent("open-apps-mode", { });
}} }}
onClick={() => {
executeEvent('openWalletsApp', {})
handleClose()
}}
> >
<Card sx={{ <Card sx={{
padding: '10px', padding: '10px',

View File

@ -4,6 +4,7 @@ import { getData, storeData } from '../utils/chromeStorage';
import { checkDifference, getNameInfoForOthers } from '../background'; import { checkDifference, getNameInfoForOthers } from '../background';
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import { lastPaymentSeenTimestampAtom } from '../atoms/global'; import { lastPaymentSeenTimestampAtom } from '../atoms/global';
import { subscribeToEvent, unsubscribeFromEvent } from '../utils/events';
export const useHandlePaymentNotification = (address) => { export const useHandlePaymentNotification = (address) => {
const [latestTx, setLatestTx] = useState(null); const [latestTx, setLatestTx] = useState(null);
@ -106,6 +107,21 @@ export const useHandlePaymentNotification = (address) => {
window.removeEventListener("message", messageHandler); window.removeEventListener("message", messageHandler);
}; };
}, [getLastSeenData]); }, [getLastSeenData]);
const setLastEnteredTimestampPaymentEventFunc = useCallback(
(e) => {
setLastEnteredTimestampPayment(Date.now)
},
[setLastEnteredTimestampPayment]
);
useEffect(() => {
subscribeToEvent("setLastEnteredTimestampPaymentEvent", setLastEnteredTimestampPaymentEventFunc);
return () => {
unsubscribeFromEvent("setLastEnteredTimestampPaymentEvent", setLastEnteredTimestampPaymentEventFunc);
};
}, [setLastEnteredTimestampPaymentEventFunc]);
return { return {
latestTx, latestTx,
getNameOrAddressOfSenderMiddle, getNameOrAddressOfSenderMiddle,

View File

@ -24,14 +24,14 @@ window.addEventListener("message", (event) => {
} }
}); });
export const sendMessageBackground = (action, data = {}, timeout = 180000, isExtension, appInfo) => { export const sendMessageBackground = (action, data = {}, timeout = 180000, isExtension, appInfo, skipAuth) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const requestId = generateRequestId(); // Unique ID for each request const requestId = generateRequestId(); // Unique ID for each request
callbackMap.set(requestId, { resolve, reject }); // Store both resolve and reject callbacks callbackMap.set(requestId, { resolve, reject }); // Store both resolve and reject callbacks
const targetOrigin = window.location.origin const targetOrigin = window.location.origin
// Send the message with `backgroundMessage` type // Send the message with `backgroundMessage` type
window.postMessage({ type: "backgroundMessage", action, requestId, payload: data, isExtension, appInfo }, targetOrigin); window.postMessage({ type: "backgroundMessage", action, requestId, payload: data, isExtension, appInfo, skipAuth }, targetOrigin);
// Set up a timeout to automatically reject if no response is received // Set up a timeout to automatically reject if no response is received
const timeoutId = setTimeout(() => { const timeoutId = setTimeout(() => {

View File

@ -71,6 +71,7 @@ export const isRunningGateway = async ()=> {
// Ensure the message is from a trusted source // Ensure the message is from a trusted source
const isFromExtension = request?.isExtension; const isFromExtension = request?.isExtension;
const appInfo = request?.appInfo; const appInfo = request?.appInfo;
const skipAuth = request?.skipAuth || false
if (request?.type !== "backgroundMessage") return; // Only process messages of type 'backgroundMessage' if (request?.type !== "backgroundMessage") return; // Only process messages of type 'backgroundMessage'
@ -78,7 +79,7 @@ export const isRunningGateway = async ()=> {
switch (request.action) { switch (request.action) {
case "GET_USER_ACCOUNT": { case "GET_USER_ACCOUNT": {
try { try {
const res = await getUserAccount({isFromExtension, appInfo}); const res = await getUserAccount({isFromExtension, appInfo, skipAuth});
event.source.postMessage({ event.source.postMessage({
requestId: request.requestId, requestId: request.requestId,
action: request.action, action: request.action,

View File

@ -354,13 +354,16 @@ async function getUserPermission(payload, isFromExtension) {
} }
export const getUserAccount = async ({isFromExtension, appInfo}) => { export const getUserAccount = async ({isFromExtension, appInfo, skipAuth}) => {
try { try {
const value = (await getPermission(`qAPPAutoAuth-${appInfo?.name}`)) || false; const value = (await getPermission(`qAPPAutoAuth-${appInfo?.name}`)) || false;
let skip = false; let skip = false;
if (value) { if (value) {
skip = true; skip = true;
} }
if(skipAuth){
skip = true
}
let resPermission let resPermission
if(!skip){ if(!skip){
resPermission = await getUserPermission({ resPermission = await getUserPermission({